Add wuffs_base__magic_number_guess_fourcc
diff --git a/example/imageviewer/imageviewer.c b/example/imageviewer/imageviewer.c
index 87c1779..d98b6b5 100644
--- a/example/imageviewer/imageviewer.c
+++ b/example/imageviewer/imageviewer.c
@@ -123,15 +123,23 @@
bool //
load_image_type() {
- while (g_src.meta.wi == 0) {
- if (!read_more_src()) {
+ int32_t fourcc = 0;
+ while (true) {
+ fourcc = wuffs_base__magic_number_guess_fourcc(
+ wuffs_base__io_buffer__reader_slice(&g_src));
+ if (fourcc >= 0) {
+ break;
+ } else if (wuffs_base__io_buffer__writer_length(&g_src) == 0) {
+ printf("%s: could not determine file format\n", g_filename);
+ return false;
+ } else if (!read_more_src()) {
return false;
}
}
wuffs_base__status status;
- switch (g_src_buffer_array[0]) {
- case 0x00:
+ switch (fourcc) {
+ case WUFFS_BASE__FOURCC__WBMP:
status = wuffs_wbmp__decoder__initialize(
&g_potential_decoders.wbmp, sizeof g_potential_decoders.wbmp,
WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
@@ -144,7 +152,7 @@
&g_potential_decoders.wbmp);
break;
- case 'B':
+ case WUFFS_BASE__FOURCC__BMP:
status = wuffs_bmp__decoder__initialize(
&g_potential_decoders.bmp, sizeof g_potential_decoders.bmp,
WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
@@ -157,7 +165,7 @@
&g_potential_decoders.bmp);
break;
- case 'G':
+ case WUFFS_BASE__FOURCC__GIF:
status = wuffs_gif__decoder__initialize(
&g_potential_decoders.gif, sizeof g_potential_decoders.gif,
WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
@@ -170,7 +178,7 @@
&g_potential_decoders.gif);
break;
- case 'n':
+ case WUFFS_BASE__FOURCC__NIE:
status = wuffs_nie__decoder__initialize(
&g_potential_decoders.nie, sizeof g_potential_decoders.nie,
WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
@@ -183,7 +191,7 @@
&g_potential_decoders.nie);
break;
- case 0x89:
+ case WUFFS_BASE__FOURCC__PNG:
status = wuffs_png__decoder__initialize(
&g_potential_decoders.png, sizeof g_potential_decoders.png,
WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
diff --git a/internal/cgen/base/all-impl.c b/internal/cgen/base/all-impl.c
index bfd21e6..feb3605 100644
--- a/internal/cgen/base/all-impl.c
+++ b/internal/cgen/base/all-impl.c
@@ -152,6 +152,15 @@
// defined(WUFFS_CONFIG__MODULE__BASE__INTCONV)
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \
+ defined(WUFFS_CONFIG__MODULE__BASE__MAGIC)
+
+// !! INSERT base/magic-submodule.c.
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__BASE) ||
+ // defined(WUFFS_CONFIG__MODULE__BASE__MAGIC)
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \
defined(WUFFS_CONFIG__MODULE__BASE__PIXCONV)
// !! INSERT base/pixconv-submodule.c.
diff --git a/internal/cgen/base/fundamental-public.h b/internal/cgen/base/fundamental-public.h
index 5280c48..cfcc753 100644
--- a/internal/cgen/base/fundamental-public.h
+++ b/internal/cgen/base/fundamental-public.h
@@ -1099,3 +1099,29 @@
}
return wuffs_base__make_slice_u8(NULL, 0);
}
+
+// ---------------- Magic Numbers
+
+// wuffs_base__magic_number_guess_fourcc guesses the file format of some data,
+// given its opening bytes. It returns a positive FourCC value on success.
+//
+// It returns zero if nothing matches its hard-coded list of 'magic numbers'.
+//
+// It returns a negative value if a longer prefix is required for a conclusive
+// result. For example, seeing a single 'B' byte is not enough to discriminate
+// the BMP and BPG image file formats.
+//
+// It does not do a full validity check. Like any guess made from a short
+// prefix of the data, it may return false positives. Data that starts with 99
+// bytes of valid JPEG followed by corruption or truncation is an invalid JPEG
+// image overall, but this function will still return WUFFS_BASE__FOURCC__JPEG.
+//
+// Another source of false positives is that some 'magic numbers' are valid
+// ASCII data. A file starting with "GIF87a and GIF89a are the two versions of
+// GIF" will match GIF's 'magic number' even if it's plain text, not an image.
+//
+// For modular builds that divide the base module into sub-modules, using this
+// function requires the WUFFS_CONFIG__MODULE__BASE__MAGIC sub-module, not just
+// WUFFS_CONFIG__MODULE__BASE__CORE.
+WUFFS_BASE__MAYBE_STATIC int32_t //
+wuffs_base__magic_number_guess_fourcc(wuffs_base__slice_u8 prefix);
diff --git a/internal/cgen/base/magic-submodule.c b/internal/cgen/base/magic-submodule.c
new file mode 100644
index 0000000..05a59c4
--- /dev/null
+++ b/internal/cgen/base/magic-submodule.c
@@ -0,0 +1,96 @@
+// After editing this file, run "go generate" in the ../data directory.
+
+// Copyright 2021 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// ---------------- Magic Numbers
+
+WUFFS_BASE__MAYBE_STATIC int32_t //
+wuffs_base__magic_number_guess_fourcc(wuffs_base__slice_u8 prefix) {
+ // table holds the 'magic numbers' (which are actually variable length
+ // strings). The strings may contain NUL bytes, so the "const char* magic"
+ // value starts with the length-minus-1 of the 'magic number'.
+ //
+ // Keep it sorted by magic[1], then magic[0] descending and finally by
+ // magic[2:]. When multiple entries match, the longest one wins.
+ static struct {
+ int32_t fourcc;
+ const char* magic;
+ } table[] = {
+ {0x57424D50, "\x01\x00\x00"}, // WBMP
+ {0x424D5020, "\x01\x42\x4D"}, // BMP
+ {0x47494620, "\x03\x47\x49\x46\x38"}, // GIF
+ {0x54494646, "\x03\x49\x49\x2A\x00"}, // TIFF (little-endian)
+ {0x54494646, "\x03\x4D\x4D\x00\x2A"}, // TIFF (big-endian)
+ {0x52494646, "\x03\x52\x49\x46\x46"}, // RIFF (see § below)
+ {0x4E494520, "\x02\x6E\xC3\xAF"}, // NIE
+ {0x504E4720, "\x03\x89\x50\x4E\x47"}, // PNG
+ {0x4A504547, "\x01\xFF\xD8"}, // JPEG
+ };
+ static const size_t table_len = sizeof(table) / sizeof(table[0]);
+
+ if (prefix.len == 0) {
+ return -1;
+ }
+ uint8_t pre_first_byte = prefix.ptr[0];
+
+ int32_t fourcc = 0;
+ size_t i;
+ for (i = 0; i < table_len; i++) {
+ uint8_t mag_first_byte = table[i].magic[1];
+ if (pre_first_byte < mag_first_byte) {
+ break;
+ } else if (pre_first_byte > mag_first_byte) {
+ continue;
+ }
+ fourcc = table[i].fourcc;
+
+ uint8_t mag_remaining_len = table[i].magic[0];
+ if (mag_remaining_len == 0) {
+ goto match;
+ }
+
+ const char* mag_remaining_ptr = table[i].magic + 2;
+ uint8_t* pre_remaining_ptr = prefix.ptr + 1;
+ size_t pre_remaining_len = prefix.len - 1;
+ if (pre_remaining_len < mag_remaining_len) {
+ if (!memcmp(pre_remaining_ptr, mag_remaining_ptr, pre_remaining_len)) {
+ return -1;
+ }
+ } else {
+ if (!memcmp(pre_remaining_ptr, mag_remaining_ptr, mag_remaining_len)) {
+ goto match;
+ }
+ }
+ }
+ return 0;
+
+match:
+ // Some FourCC values (see § above) are further specialized.
+ if (fourcc == 0x52494646) { // 'RIFF'be
+ if (prefix.len < 16) {
+ return -1;
+ }
+ uint32_t x = wuffs_base__peek_u32be__no_bounds_check(prefix.ptr + 8);
+ if (x == 0x57454250) { // 'WEBP'be
+ uint32_t y = wuffs_base__peek_u32be__no_bounds_check(prefix.ptr + 12);
+ if (y == 0x56503820) { // 'VP8 'be
+ return 0x57503820; // 'WP8 'be
+ } else if (y == 0x5650384C) { // 'VP8L'be
+ return 0x5750384C; // 'WP8L'be
+ }
+ }
+ }
+ return fourcc;
+}
diff --git a/internal/cgen/cgen.go b/internal/cgen/cgen.go
index 0eca973..50bb81f 100644
--- a/internal/cgen/cgen.go
+++ b/internal/cgen/cgen.go
@@ -100,6 +100,7 @@
"floatconv",
"intconv",
"interfaces",
+ "magic",
"pixconv",
"utf8",
}
@@ -129,6 +130,7 @@
"// !! INSERT base/copyright\n": insertBaseCopyright,
"// !! INSERT base/floatconv-submodule.c.\n": insertBaseFloatConvSubmoduleC,
"// !! INSERT base/intconv-submodule.c.\n": insertBaseIntConvSubmoduleC,
+ "// !! INSERT base/magic-submodule.c.\n": insertBaseMagicSubmoduleC,
"// !! INSERT base/pixconv-submodule.c.\n": insertBasePixConvSubmoduleC,
"// !! INSERT base/utf8-submodule.c.\n": insertBaseUTF8SubmoduleC,
"// !! INSERT vtable names.\n": func(b *buffer) error {
@@ -369,6 +371,11 @@
return nil
}
+func insertBaseMagicSubmoduleC(buf *buffer) error {
+ buf.writes(data.BaseMagicSubmoduleC)
+ return nil
+}
+
func insertBasePixConvSubmoduleC(buf *buffer) error {
buf.writes(data.BasePixConvSubmoduleC)
return nil
diff --git a/internal/cgen/data/data.go b/internal/cgen/data/data.go
index 119fd4b..e903aeb 100644
--- a/internal/cgen/data/data.go
+++ b/internal/cgen/data/data.go
@@ -24,8 +24,8 @@
"// ----------------\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__CORE)\n\nconst uint8_t wuffs_base__low_bits_mask__u8[9] = {\n 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF,\n};\n\nconst uint16_t wuffs_base__low_bits_mask__u16[17] = {\n 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF,\n 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,\n};\n\nconst uint32_t wuffs_base__low_bits_mask__u32[33] = {\n 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F,\n 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,\n 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF,\n 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,\n 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF,\n 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,\n};\n\nconst uint64_t wuffs_base__low_bits_mask__u64[65] = {\n 0x0000000000000000, 0x000" +
"0000000000001, 0x0000000000000003,\n 0x0000000000000007, 0x000000000000000F, 0x000000000000001F,\n 0x000000000000003F, 0x000000000000007F, 0x00000000000000FF,\n 0x00000000000001FF, 0x00000000000003FF, 0x00000000000007FF,\n 0x0000000000000FFF, 0x0000000000001FFF, 0x0000000000003FFF,\n 0x0000000000007FFF, 0x000000000000FFFF, 0x000000000001FFFF,\n 0x000000000003FFFF, 0x000000000007FFFF, 0x00000000000FFFFF,\n 0x00000000001FFFFF, 0x00000000003FFFFF, 0x00000000007FFFFF,\n 0x0000000000FFFFFF, 0x0000000001FFFFFF, 0x0000000003FFFFFF,\n 0x0000000007FFFFFF, 0x000000000FFFFFFF, 0x000000001FFFFFFF,\n 0x000000003FFFFFFF, 0x000000007FFFFFFF, 0x00000000FFFFFFFF,\n 0x00000001FFFFFFFF, 0x00000003FFFFFFFF, 0x00000007FFFFFFFF,\n 0x0000000FFFFFFFFF, 0x0000001FFFFFFFFF, 0x0000003FFFFFFFFF,\n 0x0000007FFFFFFFFF, 0x000000FFFFFFFFFF, 0x000001FFFFFFFFFF,\n 0x000003FFFFFFFFFF, 0x000007FFFFFFFFFF, 0x00000FFFFFFFFFFF,\n 0x00001FFFFFFFFFFF, 0x00003FFFFFFFFFFF, 0x00007FFFFFFFFFFF,\n 0x0000FFFFFFFFFFFF, 0x000" +
"1FFFFFFFFFFFF, 0x0003FFFFFFFFFFFF,\n 0x0007FFFFFFFFFFFF, 0x000FFFFFFFFFFFFF, 0x001FFFFFFFFFFFFF,\n 0x003FFFFFFFFFFFFF, 0x007FFFFFFFFFFFFF, 0x00FFFFFFFFFFFFFF,\n 0x01FFFFFFFFFFFFFF, 0x03FFFFFFFFFFFFFF, 0x07FFFFFFFFFFFFFF,\n 0x0FFFFFFFFFFFFFFF, 0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF,\n 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,\n};\n\nconst uint32_t wuffs_base__pixel_format__bits_per_channel[16] = {\n 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\n 0x08, 0x0A, 0x0C, 0x10, 0x18, 0x20, 0x30, 0x40,\n};\n\n// !! INSERT wuffs_base__status strings.\n\n// !! INSERT vtable names.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__CORE)\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__INTERFACES)\n\n// !! INSERT InterfaceDefinitions.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_" +
- "CONFIG__MODULE__BASE__INTERFACES)\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__FLOATCONV)\n\n// !! INSERT base/floatconv-submodule.c.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__FLOATCONV)\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__INTCONV)\n\n// !! INSERT base/intconv-submodule.c.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__INTCONV)\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__PIXCONV)\n\n// !! INSERT base/pixconv-submodule.c.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__PIXCONV)\n\n#if !defined(W" +
- "UFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__UTF8)\n\n// !! INSERT base/utf8-submodule.c.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__UTF8)\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n#endif // WUFFS_IMPLEMENTATION\n\n// !! WUFFS MONOLITHIC RELEASE DISCARDS EVERYTHING BELOW.\n\n#endif // WUFFS_INCLUDE_GUARD__BASE\n" +
+ "CONFIG__MODULE__BASE__INTERFACES)\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__FLOATCONV)\n\n// !! INSERT base/floatconv-submodule.c.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__FLOATCONV)\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__INTCONV)\n\n// !! INSERT base/intconv-submodule.c.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__INTCONV)\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__MAGIC)\n\n// !! INSERT base/magic-submodule.c.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__MAGIC)\n\n#if !defined(WUFFS_C" +
+ "ONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__PIXCONV)\n\n// !! INSERT base/pixconv-submodule.c.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__PIXCONV)\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \\\n defined(WUFFS_CONFIG__MODULE__BASE__UTF8)\n\n// !! INSERT base/utf8-submodule.c.\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__BASE) ||\n // defined(WUFFS_CONFIG__MODULE__BASE__UTF8)\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n#endif // WUFFS_IMPLEMENTATION\n\n// !! WUFFS MONOLITHIC RELEASE DISCARDS EVERYTHING BELOW.\n\n#endif // WUFFS_INCLUDE_GUARD__BASE\n" +
""
const BaseFundamentalPrivateH = "" +
@@ -112,7 +112,10 @@
"int8_t) wuffs_base__table_u8;\ntypedef WUFFS_BASE__TABLE(uint16_t) wuffs_base__table_u16;\ntypedef WUFFS_BASE__TABLE(uint32_t) wuffs_base__table_u32;\ntypedef WUFFS_BASE__TABLE(uint64_t) wuffs_base__table_u64;\n\nstatic inline wuffs_base__slice_u8 //\nwuffs_base__make_slice_u8(uint8_t* ptr, size_t len) {\n wuffs_base__slice_u8 ret;\n ret.ptr = ptr;\n ret.len = len;\n return ret;\n}\n\nstatic inline wuffs_base__slice_u16 //\nwuffs_base__make_slice_u16(uint16_t* ptr, size_t len) {\n wuffs_base__slice_u16 ret;\n ret.ptr = ptr;\n ret.len = len;\n return ret;\n}\n\nstatic inline wuffs_base__slice_u32 //\nwuffs_base__make_slice_u32(uint32_t* ptr, size_t len) {\n wuffs_base__slice_u32 ret;\n ret.ptr = ptr;\n ret.len = len;\n return ret;\n}\n\nstatic inline wuffs_base__slice_u64 //\nwuffs_base__make_slice_u64(uint64_t* ptr, size_t len) {\n wuffs_base__slice_u64 ret;\n ret.ptr = ptr;\n ret.len = len;\n return ret;\n}\n\nstatic inline wuffs_base__slice_u8 //\nwuffs_base__empty_slice_u8() {\n wuffs_base__slice_u8 ret;\n ret.ptr = NULL;\n" +
" ret.len = 0;\n return ret;\n}\n\nstatic inline wuffs_base__slice_u16 //\nwuffs_base__empty_slice_u16() {\n wuffs_base__slice_u16 ret;\n ret.ptr = NULL;\n ret.len = 0;\n return ret;\n}\n\nstatic inline wuffs_base__slice_u32 //\nwuffs_base__empty_slice_u32() {\n wuffs_base__slice_u32 ret;\n ret.ptr = NULL;\n ret.len = 0;\n return ret;\n}\n\nstatic inline wuffs_base__slice_u64 //\nwuffs_base__empty_slice_u64() {\n wuffs_base__slice_u64 ret;\n ret.ptr = NULL;\n ret.len = 0;\n return ret;\n}\n\nstatic inline wuffs_base__table_u8 //\nwuffs_base__empty_table_u8() {\n wuffs_base__table_u8 ret;\n ret.ptr = NULL;\n ret.width = 0;\n ret.height = 0;\n ret.stride = 0;\n return ret;\n}\n\nstatic inline wuffs_base__table_u16 //\nwuffs_base__empty_table_u16() {\n wuffs_base__table_u16 ret;\n ret.ptr = NULL;\n ret.width = 0;\n ret.height = 0;\n ret.stride = 0;\n return ret;\n}\n\nstatic inline wuffs_base__table_u32 //\nwuffs_base__empty_table_u32() {\n wuffs_base__table_u32 ret;\n ret.ptr = NULL;\n ret.width = 0;\n ret.height = 0;\n ret.stri" +
"de = 0;\n return ret;\n}\n\nstatic inline wuffs_base__table_u64 //\nwuffs_base__empty_table_u64() {\n wuffs_base__table_u64 ret;\n ret.ptr = NULL;\n ret.width = 0;\n ret.height = 0;\n ret.stride = 0;\n return ret;\n}\n\n// wuffs_base__slice_u8__subslice_i returns s[i:].\n//\n// It returns an empty slice if i is out of bounds.\nstatic inline wuffs_base__slice_u8 //\nwuffs_base__slice_u8__subslice_i(wuffs_base__slice_u8 s, uint64_t i) {\n if ((i <= SIZE_MAX) && (i <= s.len)) {\n return wuffs_base__make_slice_u8(s.ptr + i, ((size_t)(s.len - i)));\n }\n return wuffs_base__make_slice_u8(NULL, 0);\n}\n\n// wuffs_base__slice_u8__subslice_j returns s[:j].\n//\n// It returns an empty slice if j is out of bounds.\nstatic inline wuffs_base__slice_u8 //\nwuffs_base__slice_u8__subslice_j(wuffs_base__slice_u8 s, uint64_t j) {\n if ((j <= SIZE_MAX) && (j <= s.len)) {\n return wuffs_base__make_slice_u8(s.ptr, ((size_t)j));\n }\n return wuffs_base__make_slice_u8(NULL, 0);\n}\n\n// wuffs_base__slice_u8__subslice_ij returns s[i:j].\n//\n// It " +
- "returns an empty slice if i or j is out of bounds.\nstatic inline wuffs_base__slice_u8 //\nwuffs_base__slice_u8__subslice_ij(wuffs_base__slice_u8 s,\n uint64_t i,\n uint64_t j) {\n if ((i <= j) && (j <= SIZE_MAX) && (j <= s.len)) {\n return wuffs_base__make_slice_u8(s.ptr + i, ((size_t)(j - i)));\n }\n return wuffs_base__make_slice_u8(NULL, 0);\n}\n" +
+ "returns an empty slice if i or j is out of bounds.\nstatic inline wuffs_base__slice_u8 //\nwuffs_base__slice_u8__subslice_ij(wuffs_base__slice_u8 s,\n uint64_t i,\n uint64_t j) {\n if ((i <= j) && (j <= SIZE_MAX) && (j <= s.len)) {\n return wuffs_base__make_slice_u8(s.ptr + i, ((size_t)(j - i)));\n }\n return wuffs_base__make_slice_u8(NULL, 0);\n}\n\n" +
+ "" +
+ "// ---------------- Magic Numbers\n\n// wuffs_base__magic_number_guess_fourcc guesses the file format of some data,\n// given its opening bytes. It returns a positive FourCC value on success.\n//\n// It returns zero if nothing matches its hard-coded list of 'magic numbers'.\n//\n// It returns a negative value if a longer prefix is required for a conclusive\n// result. For example, seeing a single 'B' byte is not enough to discriminate\n// the BMP and BPG image file formats.\n//\n// It does not do a full validity check. Like any guess made from a short\n// prefix of the data, it may return false positives. Data that starts with 99\n// bytes of valid JPEG followed by corruption or truncation is an invalid JPEG\n// image overall, but this function will still return WUFFS_BASE__FOURCC__JPEG.\n//\n// Another source of false positives is that some 'magic numbers' are valid\n// ASCII data. A file starting with \"GIF87a and GIF89a are the two versions of\n// GIF\" will match GIF's 'magic number' even if it's plain text, not an image.\n//" +
+ "\n// For modular builds that divide the base module into sub-modules, using this\n// function requires the WUFFS_CONFIG__MODULE__BASE__MAGIC sub-module, not just\n// WUFFS_CONFIG__MODULE__BASE__CORE.\nWUFFS_BASE__MAYBE_STATIC int32_t //\nwuffs_base__magic_number_guess_fourcc(wuffs_base__slice_u8 prefix);\n" +
""
const BaseMemoryPrivateH = "" +
@@ -534,6 +537,12 @@
" goto done;\n\n } else if (s_len == 1) {\n if (d_len <\n ((options & WUFFS_BASE__BASE_64__ENCODE_EMIT_PADDING) ? 4 : 2)) {\n o.status.repr = wuffs_base__suspension__short_write;\n goto done;\n }\n uint32_t s = ((uint32_t)(wuffs_base__peek_u8__no_bounds_check(s_ptr)))\n << 16;\n s_ptr += 1;\n *d_ptr++ = alphabet[0x3F & (s >> 18)];\n *d_ptr++ = alphabet[0x3F & (s >> 12)];\n if (options & WUFFS_BASE__BASE_64__ENCODE_EMIT_PADDING) {\n *d_ptr++ = '=';\n *d_ptr++ = '=';\n }\n o.status.repr = NULL;\n goto done;\n\n } else {\n o.status.repr = NULL;\n goto done;\n }\n } while (0);\n\ndone:\n o.num_dst = (size_t)(d_ptr - dst.ptr);\n o.num_src = (size_t)(s_ptr - src.ptr);\n return o;\n}\n" +
""
+const BaseMagicSubmoduleC = "" +
+ "// ---------------- Magic Numbers\n\nWUFFS_BASE__MAYBE_STATIC int32_t //\nwuffs_base__magic_number_guess_fourcc(wuffs_base__slice_u8 prefix) {\n // table holds the 'magic numbers' (which are actually variable length\n // strings). The strings may contain NUL bytes, so the \"const char* magic\"\n // value starts with the length-minus-1 of the 'magic number'.\n //\n // Keep it sorted by magic[1], then magic[0] descending and finally by\n // magic[2:]. When multiple entries match, the longest one wins.\n static struct {\n int32_t fourcc;\n const char* magic;\n } table[] = {\n {0x57424D50, \"\\x01\\x00\\x00\"}, // WBMP\n {0x424D5020, \"\\x01\\x42\\x4D\"}, // BMP\n {0x47494620, \"\\x03\\x47\\x49\\x46\\x38\"}, // GIF\n {0x54494646, \"\\x03\\x49\\x49\\x2A\\x00\"}, // TIFF (little-endian)\n {0x54494646, \"\\x03\\x4D\\x4D\\x00\\x2A\"}, // TIFF (big-endian)\n {0x52494646, \"\\x03\\x52\\x49\\x46\\x46\"}, // RIFF (see § below)\n {0x4E494520, \"\\x02\\x6E\\xC3\\xAF\"}, // NIE\n {0x504E4720, \"\\x03\\x89\\x50\\x" +
+ "4E\\x47\"}, // PNG\n {0x4A504547, \"\\x01\\xFF\\xD8\"}, // JPEG\n };\n static const size_t table_len = sizeof(table) / sizeof(table[0]);\n\n if (prefix.len == 0) {\n return -1;\n }\n uint8_t pre_first_byte = prefix.ptr[0];\n\n int32_t fourcc = 0;\n size_t i;\n for (i = 0; i < table_len; i++) {\n uint8_t mag_first_byte = table[i].magic[1];\n if (pre_first_byte < mag_first_byte) {\n break;\n } else if (pre_first_byte > mag_first_byte) {\n continue;\n }\n fourcc = table[i].fourcc;\n\n uint8_t mag_remaining_len = table[i].magic[0];\n if (mag_remaining_len == 0) {\n goto match;\n }\n\n const char* mag_remaining_ptr = table[i].magic + 2;\n uint8_t* pre_remaining_ptr = prefix.ptr + 1;\n size_t pre_remaining_len = prefix.len - 1;\n if (pre_remaining_len < mag_remaining_len) {\n if (!memcmp(pre_remaining_ptr, mag_remaining_ptr, pre_remaining_len)) {\n return -1;\n }\n } else {\n if (!memcmp(pre_remaining_ptr, mag_remaining_ptr, mag_remaining_len)) {\n " +
+ "goto match;\n }\n }\n }\n return 0;\n\nmatch:\n // Some FourCC values (see § above) are further specialized.\n if (fourcc == 0x52494646) { // 'RIFF'be\n if (prefix.len < 16) {\n return -1;\n }\n uint32_t x = wuffs_base__peek_u32be__no_bounds_check(prefix.ptr + 8);\n if (x == 0x57454250) { // 'WEBP'be\n uint32_t y = wuffs_base__peek_u32be__no_bounds_check(prefix.ptr + 12);\n if (y == 0x56503820) { // 'VP8 'be\n return 0x57503820; // 'WP8 'be\n } else if (y == 0x5650384C) { // 'VP8L'be\n return 0x5750384C; // 'WP8L'be\n }\n }\n }\n return fourcc;\n}\n" +
+ ""
+
const BasePixConvSubmoduleC = "" +
"// ---------------- Pixel Swizzler\n\nstatic inline uint32_t //\nwuffs_base__swap_u32_argb_abgr(uint32_t u) {\n uint32_t o = u & 0xFF00FF00;\n uint32_t r = u & 0x00FF0000;\n uint32_t b = u & 0x000000FF;\n return o | (r >> 16) | (b << 16);\n}\n\n" +
"" +
diff --git a/internal/cgen/data/gen.go b/internal/cgen/data/gen.go
index 65a9c85..b84c85e 100644
--- a/internal/cgen/data/gen.go
+++ b/internal/cgen/data/gen.go
@@ -94,6 +94,7 @@
{"../base/floatconv-submodule-code.c", "BaseFloatConvSubmoduleCodeC"},
{"../base/floatconv-submodule-data.c", "BaseFloatConvSubmoduleDataC"},
{"../base/intconv-submodule.c", "BaseIntConvSubmoduleC"},
+ {"../base/magic-submodule.c", "BaseMagicSubmoduleC"},
{"../base/pixconv-submodule.c", "BasePixConvSubmoduleC"},
{"../base/utf8-submodule.c", "BaseUTF8SubmoduleC"},
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 69f6672..8df4bbb 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -1306,6 +1306,32 @@
return wuffs_base__make_slice_u8(NULL, 0);
}
+// ---------------- Magic Numbers
+
+// wuffs_base__magic_number_guess_fourcc guesses the file format of some data,
+// given its opening bytes. It returns a positive FourCC value on success.
+//
+// It returns zero if nothing matches its hard-coded list of 'magic numbers'.
+//
+// It returns a negative value if a longer prefix is required for a conclusive
+// result. For example, seeing a single 'B' byte is not enough to discriminate
+// the BMP and BPG image file formats.
+//
+// It does not do a full validity check. Like any guess made from a short
+// prefix of the data, it may return false positives. Data that starts with 99
+// bytes of valid JPEG followed by corruption or truncation is an invalid JPEG
+// image overall, but this function will still return WUFFS_BASE__FOURCC__JPEG.
+//
+// Another source of false positives is that some 'magic numbers' are valid
+// ASCII data. A file starting with "GIF87a and GIF89a are the two versions of
+// GIF" will match GIF's 'magic number' even if it's plain text, not an image.
+//
+// For modular builds that divide the base module into sub-modules, using this
+// function requires the WUFFS_CONFIG__MODULE__BASE__MAGIC sub-module, not just
+// WUFFS_CONFIG__MODULE__BASE__CORE.
+WUFFS_BASE__MAYBE_STATIC int32_t //
+wuffs_base__magic_number_guess_fourcc(wuffs_base__slice_u8 prefix);
+
// ---------------- Ranges and Rects
// See https://github.com/google/wuffs/blob/main/doc/note/ranges-and-rects.md
@@ -14282,6 +14308,94 @@
// defined(WUFFS_CONFIG__MODULE__BASE__INTCONV)
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \
+ defined(WUFFS_CONFIG__MODULE__BASE__MAGIC)
+
+// ---------------- Magic Numbers
+
+WUFFS_BASE__MAYBE_STATIC int32_t //
+wuffs_base__magic_number_guess_fourcc(wuffs_base__slice_u8 prefix) {
+ // table holds the 'magic numbers' (which are actually variable length
+ // strings). The strings may contain NUL bytes, so the "const char* magic"
+ // value starts with the length-minus-1 of the 'magic number'.
+ //
+ // Keep it sorted by magic[1], then magic[0] descending and finally by
+ // magic[2:]. When multiple entries match, the longest one wins.
+ static struct {
+ int32_t fourcc;
+ const char* magic;
+ } table[] = {
+ {0x57424D50, "\x01\x00\x00"}, // WBMP
+ {0x424D5020, "\x01\x42\x4D"}, // BMP
+ {0x47494620, "\x03\x47\x49\x46\x38"}, // GIF
+ {0x54494646, "\x03\x49\x49\x2A\x00"}, // TIFF (little-endian)
+ {0x54494646, "\x03\x4D\x4D\x00\x2A"}, // TIFF (big-endian)
+ {0x52494646, "\x03\x52\x49\x46\x46"}, // RIFF (see § below)
+ {0x4E494520, "\x02\x6E\xC3\xAF"}, // NIE
+ {0x504E4720, "\x03\x89\x50\x4E\x47"}, // PNG
+ {0x4A504547, "\x01\xFF\xD8"}, // JPEG
+ };
+ static const size_t table_len = sizeof(table) / sizeof(table[0]);
+
+ if (prefix.len == 0) {
+ return -1;
+ }
+ uint8_t pre_first_byte = prefix.ptr[0];
+
+ int32_t fourcc = 0;
+ size_t i;
+ for (i = 0; i < table_len; i++) {
+ uint8_t mag_first_byte = table[i].magic[1];
+ if (pre_first_byte < mag_first_byte) {
+ break;
+ } else if (pre_first_byte > mag_first_byte) {
+ continue;
+ }
+ fourcc = table[i].fourcc;
+
+ uint8_t mag_remaining_len = table[i].magic[0];
+ if (mag_remaining_len == 0) {
+ goto match;
+ }
+
+ const char* mag_remaining_ptr = table[i].magic + 2;
+ uint8_t* pre_remaining_ptr = prefix.ptr + 1;
+ size_t pre_remaining_len = prefix.len - 1;
+ if (pre_remaining_len < mag_remaining_len) {
+ if (!memcmp(pre_remaining_ptr, mag_remaining_ptr, pre_remaining_len)) {
+ return -1;
+ }
+ } else {
+ if (!memcmp(pre_remaining_ptr, mag_remaining_ptr, mag_remaining_len)) {
+ goto match;
+ }
+ }
+ }
+ return 0;
+
+match:
+ // Some FourCC values (see § above) are further specialized.
+ if (fourcc == 0x52494646) { // 'RIFF'be
+ if (prefix.len < 16) {
+ return -1;
+ }
+ uint32_t x = wuffs_base__peek_u32be__no_bounds_check(prefix.ptr + 8);
+ if (x == 0x57454250) { // 'WEBP'be
+ uint32_t y = wuffs_base__peek_u32be__no_bounds_check(prefix.ptr + 12);
+ if (y == 0x56503820) { // 'VP8 'be
+ return 0x57503820; // 'WP8 'be
+ } else if (y == 0x5650384C) { // 'VP8L'be
+ return 0x5750384C; // 'WP8L'be
+ }
+ }
+ }
+ return fourcc;
+}
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__BASE) ||
+ // defined(WUFFS_CONFIG__MODULE__BASE__MAGIC)
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) || \
defined(WUFFS_CONFIG__MODULE__BASE__PIXCONV)
// ---------------- Pixel Swizzler