Add example/cbor-to-json
diff --git a/doc/changelog.md b/doc/changelog.md
index 6cb1da8..27150c5 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -10,6 +10,7 @@
- Added `base` library support for UTF-8.
- Added `base` library support for `atoi`-like string conversion.
- Added `endwhile` syntax.
+- Added `example/cbor-to-json`.
- Added `example/convert-to-nia`.
- Added `example/imageviewer`.
- Added `example/json-to-cbor`.
diff --git a/example/cbor-to-json/cbor-to-json.cc b/example/cbor-to-json/cbor-to-json.cc
new file mode 100644
index 0000000..0cfb1ac
--- /dev/null
+++ b/example/cbor-to-json/cbor-to-json.cc
@@ -0,0 +1,663 @@
+// Copyright 2020 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.
+
+// ----------------
+
+/*
+cbor-to-json reads CBOR (a binary format) from stdin and writes the equivalent
+formatted JSON (a text format) to stdout.
+
+See the "const char* g_usage" string below for details.
+
+----
+
+To run:
+
+$CXX cbor-to-json.cc && ./a.out < ../../test/data/json-things.cbor; rm -f a.out
+
+for a C++ compiler $CXX, such as clang++ or g++.
+*/
+
+#if defined(__cplusplus) && (__cplusplus < 201103L)
+#error "This C++ program requires -std=c++11 or later"
+#endif
+
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+// Wuffs ships as a "single file C library" or "header file library" as per
+// https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
+//
+// To use that single file as a "foo.c"-like implementation, instead of a
+// "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or
+// compiling it.
+#define WUFFS_IMPLEMENTATION
+
+// Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
+// release/c/etc.c whitelist which parts of Wuffs to build. That file contains
+// the entire Wuffs standard library, implementing a variety of codecs and file
+// formats. Without this macro definition, an optimizing compiler or linker may
+// very well discard Wuffs code for unused codecs, but listing the Wuffs
+// modules we use makes that process explicit. Preprocessing means that such
+// code simply isn't compiled.
+#define WUFFS_CONFIG__MODULES
+#define WUFFS_CONFIG__MODULE__AUX__BASE
+#define WUFFS_CONFIG__MODULE__AUX__CBOR
+#define WUFFS_CONFIG__MODULE__BASE
+#define WUFFS_CONFIG__MODULE__CBOR
+
+// If building this program in an environment that doesn't easily accommodate
+// relative includes, you can use the script/inline-c-relative-includes.go
+// program to generate a stand-alone C++ file.
+#include "../../release/c/wuffs-unsupported-snapshot.c"
+
+#define TRY(error_msg) \
+ do { \
+ std::string z = error_msg; \
+ if (!z.empty()) { \
+ return z; \
+ } \
+ } while (false)
+
+static const char* g_usage =
+ "Usage: cbor-to-json -flags input.cbor\n"
+ "\n"
+ "Flags:\n"
+ " -c -compact-output\n"
+ " -s=NUM -spaces=NUM\n"
+ " -t -tabs\n"
+ " -output-cbor-metadata-as-comments\n"
+ " -output-extra-comma\n"
+ " -output-inf-nan-numbers\n"
+ "\n"
+ "The input.cbor filename is optional. If absent, it reads from stdin.\n"
+ "\n"
+ "----\n"
+ "\n"
+ "cbor-to-json reads CBOR (a binary format) from stdin and writes the\n"
+ "equivalent formatted JSON (a text format) to stdout.\n"
+ "\n"
+ "The output JSON's arrays' and objects' elements are indented, each on\n"
+ "its own line. Configure this with the -c / -compact-output, -s=NUM /\n"
+ "-spaces=NUM (for NUM ranging from 0 to 8) and -t / -tabs flags.\n"
+ "\n"
+ "The conversion may be lossy. For example, CBOR metadata such as tags or\n"
+ "distinguishing undefined from null are either dropped or, with\n"
+ "-output-cbor-metadata-as-comments, converted to \"/*comments*/\". Such\n"
+ "comments are non-compliant with the JSON specification but many parsers\n"
+ "accept them.\n"
+ "\n"
+ "The -output-extra-comma flag writes output like \"[1,2,]\", with a comma\n"
+ "after the final element of a JSON list or dictionary. Such commas are\n"
+ "non-compliant with the JSON specification but many parsers accept them\n"
+ "and they can produce simpler line-based diffs. This flag is ignored when\n"
+ "-compact-output is set.\n"
+ "\n"
+ "The -output-inf-nan-numbers flag writes Inf and NaN instead of a\n"
+ "substitute null value. Such values are non-compliant with the JSON\n"
+ "specification but many parsers accept them.\n"
+ "\n"
+ "CBOR is more permissive about map keys but JSON only allows strings.\n"
+ "When converting from -i=cbor to -o=json, this program rejects keys other\n"
+ "than integers and strings (CBOR major types 0, 1, 2 and 3). Integer\n"
+ "keys like 123 quoted to be string keys like \"123\".\n"
+ "\n"
+ "The CBOR specification permits implementations to set their own maximum\n"
+ "input depth. This CBOR implementation sets it to 1024.";
+
+// ----
+
+// parse_flags enforces that g_flags.spaces <= 8 (the length of
+// INDENT_SPACES_STRING).
+#define INDENT_SPACES_STRING " "
+#define INDENT_TAB_STRING "\t"
+
+uint8_t g_dst_array[32768];
+wuffs_base__io_buffer g_dst;
+
+uint32_t g_depth;
+
+enum class context {
+ none,
+ in_list_after_bracket,
+ in_list_after_value,
+ in_dict_after_brace,
+ in_dict_after_key,
+ in_dict_after_value,
+} g_ctx;
+
+bool g_wrote_to_dst;
+
+std::vector<uint64_t> g_cbor_tags;
+
+std::vector<uint32_t> g_quirks;
+
+struct {
+ int remaining_argc;
+ char** remaining_argv;
+
+ bool compact_output;
+ bool output_cbor_metadata_as_comments;
+ bool output_extra_comma;
+ bool output_inf_nan_numbers;
+ size_t spaces;
+ bool tabs;
+} g_flags = {0};
+
+std::string //
+parse_flags(int argc, char** argv) {
+ g_flags.spaces = 4;
+
+ int c = (argc > 0) ? 1 : 0; // Skip argv[0], the program name.
+ for (; c < argc; c++) {
+ char* arg = argv[c];
+ if (*arg++ != '-') {
+ break;
+ }
+
+ // A double-dash "--foo" is equivalent to a single-dash "-foo". As special
+ // cases, a bare "-" is not a flag (some programs may interpret it as
+ // stdin) and a bare "--" means to stop parsing flags.
+ if (*arg == '\x00') {
+ break;
+ } else if (*arg == '-') {
+ arg++;
+ if (*arg == '\x00') {
+ c++;
+ break;
+ }
+ }
+
+ if (!strcmp(arg, "c") || !strcmp(arg, "compact-output")) {
+ g_flags.compact_output = true;
+ continue;
+ }
+ if (!strcmp(arg, "output-cbor-metadata-as-comments")) {
+ g_flags.output_cbor_metadata_as_comments = true;
+ continue;
+ }
+ if (!strcmp(arg, "output-extra-comma")) {
+ g_flags.output_extra_comma = true;
+ continue;
+ }
+ if (!strcmp(arg, "output-inf-nan-numbers")) {
+ g_flags.output_inf_nan_numbers = true;
+ continue;
+ }
+ if (!strncmp(arg, "s=", 2) || !strncmp(arg, "spaces=", 7)) {
+ while (*arg++ != '=') {
+ }
+ if (('0' <= arg[0]) && (arg[0] <= '8') && (arg[1] == '\x00')) {
+ g_flags.spaces = arg[0] - '0';
+ continue;
+ }
+ return g_usage;
+ }
+ if (!strcmp(arg, "t") || !strcmp(arg, "tabs")) {
+ g_flags.tabs = true;
+ continue;
+ }
+
+ return g_usage;
+ }
+
+ g_flags.remaining_argc = argc - c;
+ g_flags.remaining_argv = argv + c;
+ return "";
+}
+
+// ----
+
+std::string //
+flush_dst() {
+ while (true) {
+ size_t n = g_dst.reader_length();
+ if (n == 0) {
+ break;
+ }
+ ssize_t i = fwrite(g_dst.reader_pointer(), 1, n, stdout);
+ if (i >= 0) {
+ g_dst.meta.ri += i;
+ }
+ if (i < n) {
+ return "main: error writing to stdout";
+ }
+ }
+ g_dst.compact();
+ return "";
+}
+
+std::string //
+write_dst(const void* s, size_t n) {
+ const uint8_t* p = static_cast<const uint8_t*>(s);
+ while (n > 0) {
+ size_t i = g_dst.writer_length();
+ if (i == 0) {
+ TRY(flush_dst());
+ i = g_dst.writer_length();
+ if (i == 0) {
+ return "main: g_dst buffer is full";
+ }
+ }
+
+ if (i > n) {
+ i = n;
+ }
+ memcpy(g_dst.data.ptr + g_dst.meta.wi, p, i);
+ g_dst.meta.wi += i;
+ p += i;
+ n -= i;
+ g_wrote_to_dst = true;
+ }
+ return "";
+}
+
+// ----
+
+class Callbacks : public wuffs_aux::DecodeCborCallbacks {
+ public:
+ Callbacks() = default;
+
+ std::string WritePreambleAndUpdateContext() {
+ // Write preceding punctuation, whitespace and indentation. Update g_ctx.
+ do {
+ switch (g_ctx) {
+ case context::none:
+ // No-op.
+ break;
+ case context::in_list_after_bracket:
+ TRY(write_dst("\n", g_flags.compact_output ? 0 : 1));
+ g_ctx = context::in_list_after_value;
+ break;
+ case context::in_list_after_value:
+ TRY(write_dst(",\n", g_flags.compact_output ? 1 : 2));
+ break;
+ case context::in_dict_after_brace:
+ TRY(write_dst("\n", g_flags.compact_output ? 0 : 1));
+ g_ctx = context::in_dict_after_key;
+ break;
+ case context::in_dict_after_key:
+ TRY(write_dst(": ", g_flags.compact_output ? 1 : 2));
+ g_ctx = context::in_dict_after_value;
+ goto skip_indentation;
+ case context::in_dict_after_value:
+ TRY(write_dst(",\n", g_flags.compact_output ? 1 : 2));
+ g_ctx = context::in_dict_after_key;
+ break;
+ }
+
+ if (!g_flags.compact_output) {
+ for (size_t i = 0; i < g_depth; i++) {
+ TRY(write_dst(g_flags.tabs ? INDENT_TAB_STRING : INDENT_SPACES_STRING,
+ g_flags.tabs ? 1 : g_flags.spaces));
+ }
+ }
+ } while (false);
+ skip_indentation:
+
+ // Write any CBOR tags.
+ if (g_flags.output_cbor_metadata_as_comments) {
+ for (const auto& cbor_tag : g_cbor_tags) {
+ uint8_t buf[WUFFS_BASE__U64__BYTE_LENGTH__MAX_INCL];
+ size_t n = wuffs_base__render_number_u64(
+ wuffs_base__make_slice_u8(&buf[0],
+ WUFFS_BASE__U64__BYTE_LENGTH__MAX_INCL),
+ cbor_tag, WUFFS_BASE__RENDER_NUMBER_XXX__DEFAULT_OPTIONS);
+ TRY(write_dst("/*cbor:tag", 10));
+ TRY(write_dst(&buf[0], n));
+ TRY(write_dst("*/", 2));
+ }
+ g_cbor_tags.clear();
+ }
+
+ return "";
+ }
+
+ virtual std::string AppendNull() {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_ctx == context::in_dict_after_key) {
+ return "main: invalid JSON map key";
+ }
+ return write_dst("null", 4);
+ }
+
+ virtual std::string AppendUndefined() {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_ctx == context::in_dict_after_key) {
+ return "main: invalid JSON map key";
+ }
+ // JSON's closest approximation to "undefined" is "null".
+ if (g_flags.output_cbor_metadata_as_comments) {
+ return write_dst("/*cbor:undefined*/null", 22);
+ }
+ return write_dst("null", 4);
+ }
+
+ virtual std::string AppendBool(bool val) {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_ctx == context::in_dict_after_key) {
+ return "main: invalid JSON map key";
+ }
+ if (val) {
+ return write_dst("true", 4);
+ }
+ return write_dst("false", 5);
+ }
+
+ virtual std::string AppendF64(double val) {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_ctx == context::in_dict_after_key) {
+ return "main: invalid JSON map key";
+ }
+
+ uint8_t buf[64];
+ constexpr uint32_t precision = 0;
+ size_t n = wuffs_base__render_number_f64(
+ wuffs_base__make_slice_u8(&buf[0], sizeof buf), val, precision,
+ WUFFS_BASE__RENDER_NUMBER_FXX__JUST_ENOUGH_PRECISION);
+ if (!g_flags.output_inf_nan_numbers) {
+ // JSON numbers don't include Infinities or NaNs. For such numbers, their
+ // IEEE 754 bit representation's 11 exponent bits are all on.
+ uint64_t u =
+ wuffs_base__ieee_754_bit_representation__from_f64_to_u64(val);
+ if (((u >> 52) & 0x7FF) == 0x7FF) {
+ if (g_flags.output_cbor_metadata_as_comments) {
+ TRY(write_dst("/*cbor:", 7));
+ TRY(write_dst(&buf[0], n));
+ TRY(write_dst("*/", 2));
+ }
+ return write_dst("null", 4);
+ }
+ }
+ return write_dst(&buf[0], n);
+ }
+
+ virtual std::string AppendI64(int64_t val) {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_ctx == context::in_dict_after_key) {
+ TRY(write_dst("\"", 1));
+ }
+
+ uint8_t buf[WUFFS_BASE__I64__BYTE_LENGTH__MAX_INCL];
+ size_t n = wuffs_base__render_number_i64(
+ wuffs_base__make_slice_u8(&buf[0], sizeof buf), val,
+ WUFFS_BASE__RENDER_NUMBER_XXX__DEFAULT_OPTIONS);
+ TRY(write_dst(&buf[0], n));
+
+ if (g_ctx == context::in_dict_after_key) {
+ TRY(write_dst("\"", 1));
+ }
+ return "";
+ }
+
+ virtual std::string AppendU64(uint64_t val) {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_ctx == context::in_dict_after_key) {
+ TRY(write_dst("\"", 1));
+ }
+
+ uint8_t buf[WUFFS_BASE__U64__BYTE_LENGTH__MAX_INCL];
+ size_t n = wuffs_base__render_number_u64(
+ wuffs_base__make_slice_u8(&buf[0], sizeof buf), val,
+ WUFFS_BASE__RENDER_NUMBER_XXX__DEFAULT_OPTIONS);
+ TRY(write_dst(&buf[0], n));
+
+ if (g_ctx == context::in_dict_after_key) {
+ TRY(write_dst("\"", 1));
+ }
+ return "";
+ }
+
+ virtual std::string AppendByteString(std::string&& val) {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_flags.output_cbor_metadata_as_comments) {
+ TRY(write_dst("/*cbor:base64url*/\"", 19));
+ } else {
+ TRY(write_dst("\"", 1));
+ }
+
+ const uint8_t* ptr =
+ static_cast<const uint8_t*>(static_cast<const void*>(val.data()));
+ size_t len = val.length();
+ while (len > 0) {
+ constexpr bool closed = true;
+ wuffs_base__transform__output o = wuffs_base__base_64__encode(
+ g_dst.writer_slice(),
+ wuffs_base__make_slice_u8(const_cast<uint8_t*>(ptr), len), closed,
+ WUFFS_BASE__BASE_64__URL_ALPHABET);
+ g_dst.meta.wi += o.num_dst;
+ ptr += o.num_src;
+ len -= o.num_src;
+ if (o.status.repr == nullptr) {
+ if (len != 0) {
+ return "main: internal error: inconsistent base-64 length";
+ }
+ break;
+ } else if (o.status.repr != wuffs_base__suspension__short_write) {
+ return o.status.message();
+ }
+ TRY(flush_dst());
+ }
+
+ return write_dst("\"", 1);
+ }
+
+ virtual std::string AppendTextString(std::string&& val) {
+ TRY(WritePreambleAndUpdateContext());
+ TRY(write_dst("\"", 1));
+ const uint8_t* ptr =
+ static_cast<const uint8_t*>(static_cast<const void*>(val.data()));
+ size_t len = val.length();
+ loop:
+ if (len > 0) {
+ for (size_t i = 0; i < len; i++) {
+ uint8_t c = ptr[i];
+ if ((c == '"') || (c == '\\') || (c < 0x20)) {
+ TRY(write_dst(ptr, i));
+ TRY(AppendAsciiByte(c));
+ ptr += i + 1;
+ len -= i + 1;
+ goto loop;
+ }
+ }
+ TRY(write_dst(ptr, len));
+ }
+ return write_dst("\"", 1);
+ }
+
+ std::string AppendAsciiByte(uint8_t c) {
+ switch (c) {
+ case '\b':
+ return write_dst("\\b", 2);
+ case '\f':
+ return write_dst("\\f", 2);
+ case '\n':
+ return write_dst("\\n", 2);
+ case '\r':
+ return write_dst("\\r", 2);
+ case '\t':
+ return write_dst("\\t", 2);
+ case '\"':
+ return write_dst("\\\"", 2);
+ case '\\':
+ return write_dst("\\\\", 2);
+ }
+ static const char* hex_digits = "0123456789ABCDEF";
+ uint8_t esc6[6];
+ esc6[0] = '\\';
+ esc6[1] = 'u';
+ esc6[2] = '0';
+ esc6[3] = '0';
+ esc6[4] = hex_digits[c >> 4];
+ esc6[5] = hex_digits[c & 0x1F];
+ return write_dst(&esc6[0], 6);
+ }
+
+ virtual std::string AppendMinus1MinusX(uint64_t val) {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_ctx == context::in_dict_after_key) {
+ TRY(write_dst("\"", 1));
+ }
+
+ val++;
+ if (val == 0) {
+ // See the cbor.TOKEN_VALUE_MINOR__MINUS_1_MINUS_X comment re overflow.
+ TRY(write_dst("-18446744073709551616", 21));
+ } else {
+ uint8_t buf[1 + WUFFS_BASE__U64__BYTE_LENGTH__MAX_INCL];
+ uint8_t* b = &buf[0];
+ *b++ = '-';
+ size_t n = wuffs_base__render_number_u64(
+ wuffs_base__make_slice_u8(b, WUFFS_BASE__U64__BYTE_LENGTH__MAX_INCL),
+ val, WUFFS_BASE__RENDER_NUMBER_XXX__DEFAULT_OPTIONS);
+ TRY(write_dst(&buf[0], 1 + n));
+ }
+
+ if (g_ctx == context::in_dict_after_key) {
+ TRY(write_dst("\"", 1));
+ }
+ return "";
+ }
+
+ virtual std::string AppendCborSimpleValue(uint8_t val) {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_ctx == context::in_dict_after_key) {
+ return "main: invalid JSON map key";
+ }
+
+ if (!g_flags.output_cbor_metadata_as_comments) {
+ return write_dst("null", 4);
+ }
+ uint8_t buf[WUFFS_BASE__U64__BYTE_LENGTH__MAX_INCL];
+ size_t n = wuffs_base__render_number_u64(
+ wuffs_base__make_slice_u8(&buf[0],
+ WUFFS_BASE__U64__BYTE_LENGTH__MAX_INCL),
+ val, WUFFS_BASE__RENDER_NUMBER_XXX__DEFAULT_OPTIONS);
+ TRY(write_dst("/*cbor:simple", 13));
+ TRY(write_dst(&buf[0], n));
+ return write_dst("*/null", 6);
+ }
+
+ virtual std::string AppendCborTag(uint64_t val) {
+ // No call to WritePreambleAndUpdateContext. A CBOR tag isn't a value. It
+ // decorates the upcoming value.
+ if (g_flags.output_cbor_metadata_as_comments) {
+ g_cbor_tags.push_back(val);
+ }
+ return "";
+ }
+
+ virtual std::string Push(uint32_t flags) {
+ TRY(WritePreambleAndUpdateContext());
+ if (g_ctx == context::in_dict_after_key) {
+ return "main: invalid JSON map key";
+ }
+
+ g_depth++;
+ g_ctx = (flags & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST)
+ ? context::in_list_after_bracket
+ : context::in_dict_after_brace;
+ return write_dst(
+ (flags & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST) ? "[" : "{", 1);
+ }
+
+ virtual std::string Pop(uint32_t flags) {
+ // No call to WritePreambleAndUpdateContext. We write the extra comma,
+ // outdent, etc. ourselves.
+ g_depth--;
+ if (g_flags.compact_output) {
+ // No-op.
+ } else if ((g_ctx != context::in_list_after_bracket) &&
+ (g_ctx != context::in_dict_after_brace)) {
+ if (g_flags.output_extra_comma) {
+ TRY(write_dst(",\n", 2));
+ } else {
+ TRY(write_dst("\n", 1));
+ }
+ for (size_t i = 0; i < g_depth; i++) {
+ TRY(write_dst(g_flags.tabs ? INDENT_TAB_STRING : INDENT_SPACES_STRING,
+ g_flags.tabs ? 1 : g_flags.spaces));
+ }
+ }
+ g_ctx = (flags & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST)
+ ? context::in_list_after_value
+ : context::in_dict_after_value;
+ return write_dst(
+ (flags & WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_LIST) ? "]" : "}", 1);
+ }
+};
+
+// ----
+
+std::string //
+main1(int argc, char** argv) {
+ g_dst = wuffs_base__ptr_u8__writer(
+ &g_dst_array[0], (sizeof(g_dst_array) / sizeof(g_dst_array[0])));
+ g_depth = 0;
+ g_ctx = context::none;
+ g_wrote_to_dst = false;
+
+ TRY(parse_flags(argc, argv));
+
+ FILE* in = stdin;
+ if (g_flags.remaining_argc > 1) {
+ return g_usage;
+ } else if (g_flags.remaining_argc == 1) {
+ in = fopen(g_flags.remaining_argv[0], "r");
+ if (!in) {
+ return std::string("main: cannot read input file");
+ }
+ }
+
+ return wuffs_aux::DecodeCbor(
+ Callbacks(), wuffs_aux::sync_io::FileInput(in),
+ wuffs_base__make_slice_u32(g_quirks.data(), g_quirks.size()))
+ .error_message;
+}
+
+// ----
+
+int //
+compute_exit_code(std::string status_msg) {
+ if (status_msg.empty()) {
+ return 0;
+ }
+ fputs(status_msg.c_str(), stderr);
+ fputc('\n', stderr);
+ // Return an exit code of 1 for regular (forseen) errors, e.g. badly
+ // formatted or unsupported input.
+ //
+ // Return an exit code of 2 for internal (exceptional) errors, e.g. defensive
+ // run-time checks found that an internal invariant did not hold.
+ //
+ // Automated testing, including badly formatted inputs, can therefore
+ // discriminate between expected failure (exit code 1) and unexpected failure
+ // (other non-zero exit codes). Specifically, exit code 2 for internal
+ // invariant violation, exit code 139 (which is 128 + SIGSEGV on x86_64
+ // linux) for a segmentation fault (e.g. null pointer dereference).
+ return (status_msg.find("internal error:") != std::string::npos) ? 2 : 1;
+}
+
+int //
+main(int argc, char** argv) {
+ std::string z = main1(argc, argv);
+ if (g_wrote_to_dst) {
+ std::string z1 = write_dst("\n", 1);
+ std::string z2 = flush_dst();
+ z = !z.empty() ? z : (!z1.empty() ? z1 : z2);
+ }
+ return compute_exit_code(z);
+}
diff --git a/internal/cgen/auxiliary/cbor.cc b/internal/cgen/auxiliary/cbor.cc
new file mode 100644
index 0000000..976bf9b
--- /dev/null
+++ b/internal/cgen/auxiliary/cbor.cc
@@ -0,0 +1,343 @@
+// After editing this file, run "go generate" in the ../data directory.
+
+// Copyright 2020 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.
+
+// ---------------- Auxiliary - CBOR
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__CBOR)
+
+#include <utility>
+
+namespace wuffs_aux {
+
+DecodeCborResult::DecodeCborResult(std::string&& error_message0,
+ uint64_t cursor_position0)
+ : error_message(std::move(error_message0)),
+ cursor_position(cursor_position0) {}
+
+void DecodeCborCallbacks::Done(DecodeCborResult& result,
+ sync_io::Input& input,
+ IOBuffer& buffer) {}
+
+DecodeCborResult //
+DecodeCbor(DecodeCborCallbacks&& callbacks,
+ sync_io::Input&& input,
+ wuffs_base__slice_u32 quirks) {
+ // Prepare the wuffs_base__io_buffer and the resultant error_message.
+ wuffs_base__io_buffer* io_buf = input.BringsItsOwnIOBuffer();
+ wuffs_base__io_buffer fallback_io_buf = wuffs_base__empty_io_buffer();
+ std::unique_ptr<uint8_t[]> fallback_io_array(nullptr);
+ if (!io_buf) {
+ fallback_io_array = std::unique_ptr<uint8_t[]>(new uint8_t[4096]);
+ fallback_io_buf = wuffs_base__ptr_u8__writer(fallback_io_array.get(), 4096);
+ io_buf = &fallback_io_buf;
+ }
+ size_t cursor_index = 0;
+ std::string ret_error_message;
+ std::string io_error_message;
+
+ do {
+ // Prepare the low-level CBOR decoder.
+ wuffs_cbor__decoder::unique_ptr dec = wuffs_cbor__decoder::alloc();
+ if (!dec) {
+ ret_error_message = "wuffs_aux::CborDecoder: out of memory";
+ goto done;
+ }
+ for (size_t i = 0; i < quirks.len; i++) {
+ dec->set_quirk_enabled(quirks.ptr[i], true);
+ }
+
+ // Prepare the wuffs_base__tok_buffer.
+ wuffs_base__token tok_array[256];
+ wuffs_base__token_buffer tok_buf =
+ wuffs_base__slice_token__writer(wuffs_base__make_slice_token(
+ &tok_array[0], (sizeof(tok_array) / sizeof(tok_array[0]))));
+ wuffs_base__status tok_status = wuffs_base__make_status(nullptr);
+
+ // Prepare other state.
+ uint32_t depth = 0;
+ std::string str;
+ int64_t extension_category = 0;
+ uint64_t extension_detail = 0;
+
+ // Valid token's VBCs range in 0 ..= 15. Values over that are for tokens
+ // from outside of the base package, such as the CBOR package.
+ constexpr int64_t EXT_CAT__CBOR_TAG = 16;
+
+ // Loop, doing these two things:
+ // 1. Get the next token.
+ // 2. Process that token.
+ while (true) {
+ // 1. Get the next token.
+
+ while (tok_buf.meta.ri >= tok_buf.meta.wi) {
+ if (tok_status.repr == nullptr) {
+ // No-op.
+ } else if (tok_status.repr == wuffs_base__suspension__short_write) {
+ tok_buf.compact();
+ } else if (tok_status.repr == wuffs_base__suspension__short_read) {
+ // Read from input to io_buf.
+ if (!io_error_message.empty()) {
+ ret_error_message = std::move(io_error_message);
+ goto done;
+ } else if (cursor_index != io_buf->meta.ri) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: bad cursor_index";
+ goto done;
+ } else if (io_buf->meta.closed) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: io_buf is closed";
+ goto done;
+ }
+ io_buf->compact();
+ if (io_buf->meta.wi >= io_buf->data.len) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: io_buf is full";
+ goto done;
+ }
+ cursor_index = io_buf->meta.ri;
+ io_error_message = input.CopyIn(io_buf);
+ } else {
+ ret_error_message = tok_status.message();
+ goto done;
+ }
+
+ if (WUFFS_CBOR__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE != 0) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: bad WORKBUF_LEN";
+ goto done;
+ }
+ wuffs_base__slice_u8 work_buf = wuffs_base__empty_slice_u8();
+ tok_status = dec->decode_tokens(&tok_buf, io_buf, work_buf);
+ }
+
+ wuffs_base__token token = tok_buf.data.ptr[tok_buf.meta.ri++];
+ uint64_t token_len = token.length();
+ if ((io_buf->meta.ri < cursor_index) ||
+ ((io_buf->meta.ri - cursor_index) < token_len)) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: bad token indexes";
+ goto done;
+ }
+ uint8_t* token_ptr = io_buf->data.ptr + cursor_index;
+ cursor_index += token_len;
+
+ // 2. Process that token.
+
+ uint64_t vbd = token.value_base_detail();
+
+ if (extension_category != 0) {
+ int64_t ext = token.value_extension();
+ if ((ext >= 0) && !token.continued()) {
+ extension_detail = (extension_detail
+ << WUFFS_BASE__TOKEN__VALUE_EXTENSION__NUM_BITS) |
+ static_cast<uint64_t>(ext);
+ switch (extension_category) {
+ case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_SIGNED:
+ extension_category = 0;
+ ret_error_message =
+ callbacks.AppendI64(static_cast<int64_t>(extension_detail));
+ goto parsed_a_value;
+ case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_UNSIGNED:
+ extension_category = 0;
+ ret_error_message = callbacks.AppendU64(extension_detail);
+ goto parsed_a_value;
+ case EXT_CAT__CBOR_TAG:
+ extension_category = 0;
+ ret_error_message = callbacks.AppendCborTag(extension_detail);
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ continue;
+ }
+ }
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: bad extended token";
+ goto done;
+ }
+
+ switch (token.value_base_category()) {
+ case WUFFS_BASE__TOKEN__VBC__FILLER:
+ continue;
+
+ case WUFFS_BASE__TOKEN__VBC__STRUCTURE: {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {
+ ret_error_message = callbacks.Push(static_cast<uint32_t>(vbd));
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ depth++;
+ continue;
+ }
+ ret_error_message = callbacks.Pop(static_cast<uint32_t>(vbd));
+ depth--;
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__STRING: {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP) {
+ // No-op.
+ } else if (vbd &
+ WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_1_SRC_COPY) {
+ const char* ptr = // Convert from (uint8_t*).
+ static_cast<const char*>(static_cast<void*>(token_ptr));
+ str.append(ptr, token_len);
+ } else {
+ goto fail;
+ }
+ if (token.continued()) {
+ continue;
+ }
+ ret_error_message =
+ (vbd & WUFFS_BASE__TOKEN__VBD__STRING__CHAIN_MUST_BE_UTF_8)
+ ? callbacks.AppendTextString(std::move(str))
+ : callbacks.AppendByteString(std::move(str));
+ str.clear();
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__UNICODE_CODE_POINT: {
+ uint8_t u[WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL];
+ size_t n = wuffs_base__utf_8__encode(
+ wuffs_base__make_slice_u8(
+ &u[0], WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL),
+ static_cast<uint32_t>(vbd));
+ const char* ptr = // Convert from (uint8_t*).
+ static_cast<const char*>(static_cast<void*>(&u[0]));
+ str.append(ptr, n);
+ if (token.continued()) {
+ continue;
+ }
+ goto fail;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__LITERAL: {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__NULL) {
+ ret_error_message = callbacks.AppendNull();
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__UNDEFINED) {
+ ret_error_message = callbacks.AppendUndefined();
+ } else {
+ ret_error_message = callbacks.AppendBool(
+ vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__TRUE);
+ }
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__NUMBER: {
+ const uint64_t cfp_fbbe_fifb =
+ WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT |
+ WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN |
+ WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE;
+ if ((vbd & cfp_fbbe_fifb) == cfp_fbbe_fifb) {
+ double f;
+ switch (token_len) {
+ case 3:
+ f = wuffs_base__ieee_754_bit_representation__from_u16_to_f64(
+ wuffs_base__load_u16be__no_bounds_check(token_ptr + 1));
+ break;
+ case 5:
+ f = wuffs_base__ieee_754_bit_representation__from_u32_to_f64(
+ wuffs_base__load_u32be__no_bounds_check(token_ptr + 1));
+ break;
+ case 9:
+ f = wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ wuffs_base__load_u64be__no_bounds_check(token_ptr + 1));
+ break;
+ default:
+ goto fail;
+ }
+ ret_error_message = callbacks.AppendF64(f);
+ goto parsed_a_value;
+ }
+ goto fail;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_SIGNED: {
+ if (token.continued()) {
+ extension_category = WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_SIGNED;
+ extension_detail =
+ static_cast<uint64_t>(token.value_base_detail__sign_extended());
+ continue;
+ }
+ ret_error_message =
+ callbacks.AppendI64(token.value_base_detail__sign_extended());
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_UNSIGNED: {
+ if (token.continued()) {
+ extension_category =
+ WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_UNSIGNED;
+ extension_detail = vbd;
+ continue;
+ }
+ ret_error_message = callbacks.AppendU64(vbd);
+ goto parsed_a_value;
+ }
+ }
+
+ if (token.value_major() == WUFFS_CBOR__TOKEN_VALUE_MAJOR) {
+ uint64_t value_minor = token.value_minor();
+ if (value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__MINUS_1_MINUS_X) {
+ if (token_len == 9) {
+ ret_error_message = callbacks.AppendMinus1MinusX(
+ wuffs_base__load_u64be__no_bounds_check(token_ptr + 1));
+ goto parsed_a_value;
+ }
+ } else if (value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__SIMPLE_VALUE) {
+ ret_error_message =
+ callbacks.AppendCborSimpleValue(static_cast<uint8_t>(
+ value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_MASK));
+ goto parsed_a_value;
+ } else if (value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__TAG) {
+ if (token.continued()) {
+ extension_category = EXT_CAT__CBOR_TAG;
+ extension_detail =
+ value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_MASK;
+ continue;
+ }
+ ret_error_message = callbacks.AppendCborTag(
+ value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_MASK);
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ continue;
+ }
+ }
+
+ fail:
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: unexpected token";
+ goto done;
+
+ parsed_a_value:
+ if (!ret_error_message.empty() || (depth == 0)) {
+ goto done;
+ }
+ }
+ } while (false);
+
+done:
+ DecodeCborResult result(
+ std::move(ret_error_message),
+ wuffs_base__u64__sat_add(io_buf->meta.pos, cursor_index));
+ callbacks.Done(result, input, *io_buf);
+ return result;
+}
+
+} // namespace wuffs_aux
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__AUX__CBOR)
diff --git a/internal/cgen/auxiliary/cbor.hh b/internal/cgen/auxiliary/cbor.hh
new file mode 100644
index 0000000..a16b49a
--- /dev/null
+++ b/internal/cgen/auxiliary/cbor.hh
@@ -0,0 +1,80 @@
+// After editing this file, run "go generate" in the ../data directory.
+
+// Copyright 2020 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.
+
+// ---------------- Auxiliary - CBOR
+
+namespace wuffs_aux {
+
+struct DecodeCborResult {
+ DecodeCborResult(std::string&& error_message0, uint64_t cursor_position0);
+
+ std::string error_message;
+ uint64_t cursor_position;
+};
+
+class DecodeCborCallbacks {
+ public:
+ // AppendXxx are called for leaf nodes: literals, numbers, strings, etc.
+
+ virtual std::string AppendNull() = 0;
+ virtual std::string AppendUndefined() = 0;
+ virtual std::string AppendBool(bool val) = 0;
+ virtual std::string AppendF64(double val) = 0;
+ virtual std::string AppendI64(int64_t val) = 0;
+ virtual std::string AppendU64(uint64_t val) = 0;
+ virtual std::string AppendByteString(std::string&& val) = 0;
+ virtual std::string AppendTextString(std::string&& val) = 0;
+ virtual std::string AppendMinus1MinusX(uint64_t val) = 0;
+ virtual std::string AppendCborSimpleValue(uint8_t val) = 0;
+ virtual std::string AppendCborTag(uint64_t val) = 0;
+
+ // Push and Pop are called for container nodes: CBOR arrays (lists) and CBOR
+ // maps (dictionaries).
+ //
+ // The flags bits combine exactly one of:
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_NONE
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_LIST
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_DICT
+ // and exactly one of:
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_NONE
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_DICT
+
+ virtual std::string Push(uint32_t flags) = 0;
+ virtual std::string Pop(uint32_t flags) = 0;
+
+ // Done is always the last Callback method called by DecodeCbor, whether or
+ // not parsing the input as CBOR encountered an error. Even when successful,
+ // trailing data may remain in input and buffer.
+ //
+ // Do not keep a reference to buffer or buffer.data.ptr after Done returns,
+ // as DecodeCbor may then de-allocate the backing array.
+ virtual void Done(DecodeCborResult& result,
+ sync_io::Input& input,
+ IOBuffer& buffer);
+};
+
+// DecodeCbor calls callbacks based on the CBOR-formatted data in input.
+//
+// On success, the returned error_message is empty and cursor_position counts
+// the number of bytes consumed. On failure, error_message is non-empty and
+// cursor_position is the location of the error. That error may be a content
+// error (invalid CBOR) or an input error (e.g. network failure).
+DecodeCborResult DecodeCbor(DecodeCborCallbacks&& callbacks,
+ sync_io::Input&& input,
+ wuffs_base__slice_u32 quirks);
+
+} // namespace wuffs_aux
diff --git a/internal/cgen/data/data.go b/internal/cgen/data/data.go
index c3e8d27..2df619d 100644
--- a/internal/cgen/data/data.go
+++ b/internal/cgen/data/data.go
@@ -625,6 +625,27 @@
"// --------\n\n} // namespace sync_io\n\n} // namespace wuffs_aux\n" +
""
+const AuxCborCc = "" +
+ "// ---------------- Auxiliary - CBOR\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__CBOR)\n\n#include <utility>\n\nnamespace wuffs_aux {\n\nDecodeCborResult::DecodeCborResult(std::string&& error_message0,\n uint64_t cursor_position0)\n : error_message(std::move(error_message0)),\n cursor_position(cursor_position0) {}\n\nvoid DecodeCborCallbacks::Done(DecodeCborResult& result,\n sync_io::Input& input,\n IOBuffer& buffer) {}\n\nDecodeCborResult //\nDecodeCbor(DecodeCborCallbacks&& callbacks,\n sync_io::Input&& input,\n wuffs_base__slice_u32 quirks) {\n // Prepare the wuffs_base__io_buffer and the resultant error_message.\n wuffs_base__io_buffer* io_buf = input.BringsItsOwnIOBuffer();\n wuffs_base__io_buffer fallback_io_buf = wuffs_base__empty_io_buffer();\n std::unique_ptr<uint8_t[]> fallback_io_array(nullptr);\n if (!io_buf) {\n fallback_io_array = std::unique_ptr<uint8_t[]>(ne" +
+ "w uint8_t[4096]);\n fallback_io_buf = wuffs_base__ptr_u8__writer(fallback_io_array.get(), 4096);\n io_buf = &fallback_io_buf;\n }\n size_t cursor_index = 0;\n std::string ret_error_message;\n std::string io_error_message;\n\n do {\n // Prepare the low-level CBOR decoder.\n wuffs_cbor__decoder::unique_ptr dec = wuffs_cbor__decoder::alloc();\n if (!dec) {\n ret_error_message = \"wuffs_aux::CborDecoder: out of memory\";\n goto done;\n }\n for (size_t i = 0; i < quirks.len; i++) {\n dec->set_quirk_enabled(quirks.ptr[i], true);\n }\n\n // Prepare the wuffs_base__tok_buffer.\n wuffs_base__token tok_array[256];\n wuffs_base__token_buffer tok_buf =\n wuffs_base__slice_token__writer(wuffs_base__make_slice_token(\n &tok_array[0], (sizeof(tok_array) / sizeof(tok_array[0]))));\n wuffs_base__status tok_status = wuffs_base__make_status(nullptr);\n\n // Prepare other state.\n uint32_t depth = 0;\n std::string str;\n int64_t extension_category = 0;\n uint64_t extension" +
+ "_detail = 0;\n\n // Valid token's VBCs range in 0 ..= 15. Values over that are for tokens\n // from outside of the base package, such as the CBOR package.\n constexpr int64_t EXT_CAT__CBOR_TAG = 16;\n\n // Loop, doing these two things:\n // 1. Get the next token.\n // 2. Process that token.\n while (true) {\n // 1. Get the next token.\n\n while (tok_buf.meta.ri >= tok_buf.meta.wi) {\n if (tok_status.repr == nullptr) {\n // No-op.\n } else if (tok_status.repr == wuffs_base__suspension__short_write) {\n tok_buf.compact();\n } else if (tok_status.repr == wuffs_base__suspension__short_read) {\n // Read from input to io_buf.\n if (!io_error_message.empty()) {\n ret_error_message = std::move(io_error_message);\n goto done;\n } else if (cursor_index != io_buf->meta.ri) {\n ret_error_message =\n \"wuffs_aux::CborDecoder: internal error: bad cursor_index\";\n goto done;\n } else if" +
+ " (io_buf->meta.closed) {\n ret_error_message =\n \"wuffs_aux::CborDecoder: internal error: io_buf is closed\";\n goto done;\n }\n io_buf->compact();\n if (io_buf->meta.wi >= io_buf->data.len) {\n ret_error_message =\n \"wuffs_aux::CborDecoder: internal error: io_buf is full\";\n goto done;\n }\n cursor_index = io_buf->meta.ri;\n io_error_message = input.CopyIn(io_buf);\n } else {\n ret_error_message = tok_status.message();\n goto done;\n }\n\n if (WUFFS_CBOR__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE != 0) {\n ret_error_message =\n \"wuffs_aux::CborDecoder: internal error: bad WORKBUF_LEN\";\n goto done;\n }\n wuffs_base__slice_u8 work_buf = wuffs_base__empty_slice_u8();\n tok_status = dec->decode_tokens(&tok_buf, io_buf, work_buf);\n }\n\n wuffs_base__token token = tok_buf.data.ptr[tok_buf.meta.ri++];\n uint64_t t" +
+ "oken_len = token.length();\n if ((io_buf->meta.ri < cursor_index) ||\n ((io_buf->meta.ri - cursor_index) < token_len)) {\n ret_error_message =\n \"wuffs_aux::CborDecoder: internal error: bad token indexes\";\n goto done;\n }\n uint8_t* token_ptr = io_buf->data.ptr + cursor_index;\n cursor_index += token_len;\n\n // 2. Process that token.\n\n uint64_t vbd = token.value_base_detail();\n\n if (extension_category != 0) {\n int64_t ext = token.value_extension();\n if ((ext >= 0) && !token.continued()) {\n extension_detail = (extension_detail\n << WUFFS_BASE__TOKEN__VALUE_EXTENSION__NUM_BITS) |\n static_cast<uint64_t>(ext);\n switch (extension_category) {\n case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_SIGNED:\n extension_category = 0;\n ret_error_message =\n callbacks.AppendI64(static_cast<int64_t>(extension_detail));\n g" +
+ "oto parsed_a_value;\n case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_UNSIGNED:\n extension_category = 0;\n ret_error_message = callbacks.AppendU64(extension_detail);\n goto parsed_a_value;\n case EXT_CAT__CBOR_TAG:\n extension_category = 0;\n ret_error_message = callbacks.AppendCborTag(extension_detail);\n if (!ret_error_message.empty()) {\n goto done;\n }\n continue;\n }\n }\n ret_error_message =\n \"wuffs_aux::CborDecoder: internal error: bad extended token\";\n goto done;\n }\n\n switch (token.value_base_category()) {\n case WUFFS_BASE__TOKEN__VBC__FILLER:\n continue;\n\n case WUFFS_BASE__TOKEN__VBC__STRUCTURE: {\n if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {\n ret_error_message = callbacks.Push(static_cast<uint32_t>(vbd));\n if (!ret_error_message.empty()) {\n goto done;\n " +
+ " }\n depth++;\n continue;\n }\n ret_error_message = callbacks.Pop(static_cast<uint32_t>(vbd));\n depth--;\n goto parsed_a_value;\n }\n\n case WUFFS_BASE__TOKEN__VBC__STRING: {\n if (vbd & WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP) {\n // No-op.\n } else if (vbd &\n WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_1_SRC_COPY) {\n const char* ptr = // Convert from (uint8_t*).\n static_cast<const char*>(static_cast<void*>(token_ptr));\n str.append(ptr, token_len);\n } else {\n goto fail;\n }\n if (token.continued()) {\n continue;\n }\n ret_error_message =\n (vbd & WUFFS_BASE__TOKEN__VBD__STRING__CHAIN_MUST_BE_UTF_8)\n ? callbacks.AppendTextString(std::move(str))\n : callbacks.AppendByteString(std::move(str));\n str.clear();\n goto par" +
+ "sed_a_value;\n }\n\n case WUFFS_BASE__TOKEN__VBC__UNICODE_CODE_POINT: {\n uint8_t u[WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL];\n size_t n = wuffs_base__utf_8__encode(\n wuffs_base__make_slice_u8(\n &u[0], WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL),\n static_cast<uint32_t>(vbd));\n const char* ptr = // Convert from (uint8_t*).\n static_cast<const char*>(static_cast<void*>(&u[0]));\n str.append(ptr, n);\n if (token.continued()) {\n continue;\n }\n goto fail;\n }\n\n case WUFFS_BASE__TOKEN__VBC__LITERAL: {\n if (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__NULL) {\n ret_error_message = callbacks.AppendNull();\n } else if (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__UNDEFINED) {\n ret_error_message = callbacks.AppendUndefined();\n } else {\n ret_error_message = callbacks.AppendBool(\n vbd & WUFFS_BASE__TOKEN__VBD__LITER" +
+ "AL__TRUE);\n }\n goto parsed_a_value;\n }\n\n case WUFFS_BASE__TOKEN__VBC__NUMBER: {\n const uint64_t cfp_fbbe_fifb =\n WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT |\n WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN |\n WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE;\n if ((vbd & cfp_fbbe_fifb) == cfp_fbbe_fifb) {\n double f;\n switch (token_len) {\n case 3:\n f = wuffs_base__ieee_754_bit_representation__from_u16_to_f64(\n wuffs_base__load_u16be__no_bounds_check(token_ptr + 1));\n break;\n case 5:\n f = wuffs_base__ieee_754_bit_representation__from_u32_to_f64(\n wuffs_base__load_u32be__no_bounds_check(token_ptr + 1));\n break;\n case 9:\n f = wuffs_base__ieee_754_bit_representation__from_u64_to_f64(\n wuffs_base__load_u64be__" +
+ "no_bounds_check(token_ptr + 1));\n break;\n default:\n goto fail;\n }\n ret_error_message = callbacks.AppendF64(f);\n goto parsed_a_value;\n }\n goto fail;\n }\n\n case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_SIGNED: {\n if (token.continued()) {\n extension_category = WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_SIGNED;\n extension_detail =\n static_cast<uint64_t>(token.value_base_detail__sign_extended());\n continue;\n }\n ret_error_message =\n callbacks.AppendI64(token.value_base_detail__sign_extended());\n goto parsed_a_value;\n }\n\n case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_UNSIGNED: {\n if (token.continued()) {\n extension_category =\n WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_UNSIGNED;\n extension_detail = vbd;\n continue;\n }\n ret_error_message = ca" +
+ "llbacks.AppendU64(vbd);\n goto parsed_a_value;\n }\n }\n\n if (token.value_major() == WUFFS_CBOR__TOKEN_VALUE_MAJOR) {\n uint64_t value_minor = token.value_minor();\n if (value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__MINUS_1_MINUS_X) {\n if (token_len == 9) {\n ret_error_message = callbacks.AppendMinus1MinusX(\n wuffs_base__load_u64be__no_bounds_check(token_ptr + 1));\n goto parsed_a_value;\n }\n } else if (value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__SIMPLE_VALUE) {\n ret_error_message =\n callbacks.AppendCborSimpleValue(static_cast<uint8_t>(\n value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_MASK));\n goto parsed_a_value;\n } else if (value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__TAG) {\n if (token.continued()) {\n extension_category = EXT_CAT__CBOR_TAG;\n extension_detail =\n value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_" +
+ "MASK;\n continue;\n }\n ret_error_message = callbacks.AppendCborTag(\n value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_MASK);\n if (!ret_error_message.empty()) {\n goto done;\n }\n continue;\n }\n }\n\n fail:\n ret_error_message =\n \"wuffs_aux::CborDecoder: internal error: unexpected token\";\n goto done;\n\n parsed_a_value:\n if (!ret_error_message.empty() || (depth == 0)) {\n goto done;\n }\n }\n } while (false);\n\ndone:\n DecodeCborResult result(\n std::move(ret_error_message),\n wuffs_base__u64__sat_add(io_buf->meta.pos, cursor_index));\n callbacks.Done(result, input, *io_buf);\n return result;\n}\n\n} // namespace wuffs_aux\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__AUX__CBOR)\n" +
+ ""
+
+const AuxCborHh = "" +
+ "// ---------------- Auxiliary - CBOR\n\nnamespace wuffs_aux {\n\nstruct DecodeCborResult {\n DecodeCborResult(std::string&& error_message0, uint64_t cursor_position0);\n\n std::string error_message;\n uint64_t cursor_position;\n};\n\nclass DecodeCborCallbacks {\n public:\n // AppendXxx are called for leaf nodes: literals, numbers, strings, etc.\n\n virtual std::string AppendNull() = 0;\n virtual std::string AppendUndefined() = 0;\n virtual std::string AppendBool(bool val) = 0;\n virtual std::string AppendF64(double val) = 0;\n virtual std::string AppendI64(int64_t val) = 0;\n virtual std::string AppendU64(uint64_t val) = 0;\n virtual std::string AppendByteString(std::string&& val) = 0;\n virtual std::string AppendTextString(std::string&& val) = 0;\n virtual std::string AppendMinus1MinusX(uint64_t val) = 0;\n virtual std::string AppendCborSimpleValue(uint8_t val) = 0;\n virtual std::string AppendCborTag(uint64_t val) = 0;\n\n // Push and Pop are called for container nodes: CBOR arrays (lists) and CBOR\n // maps (dictiona" +
+ "ries).\n //\n // The flags bits combine exactly one of:\n // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_NONE\n // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_LIST\n // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_DICT\n // and exactly one of:\n // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_NONE\n // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST\n // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_DICT\n\n virtual std::string Push(uint32_t flags) = 0;\n virtual std::string Pop(uint32_t flags) = 0;\n\n // Done is always the last Callback method called by DecodeCbor, whether or\n // not parsing the input as CBOR encountered an error. Even when successful,\n // trailing data may remain in input and buffer.\n //\n // Do not keep a reference to buffer or buffer.data.ptr after Done returns,\n // as DecodeCbor may then de-allocate the backing array.\n virtual void Done(DecodeCborResult& result,\n sync_io::Input& input,\n IOBuffer& buffer);\n};\n\n// DecodeCbor calls callbacks based on the CBOR-form" +
+ "atted data in input.\n//\n// On success, the returned error_message is empty and cursor_position counts\n// the number of bytes consumed. On failure, error_message is non-empty and\n// cursor_position is the location of the error. That error may be a content\n// error (invalid CBOR) or an input error (e.g. network failure).\nDecodeCborResult DecodeCbor(DecodeCborCallbacks&& callbacks,\n sync_io::Input&& input,\n wuffs_base__slice_u32 quirks);\n\n} // namespace wuffs_aux\n" +
+ ""
+
const AuxJsonCc = "" +
"// ---------------- Auxiliary - JSON\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__JSON)\n\n#include <utility>\n\nnamespace wuffs_aux {\n\nDecodeJsonResult::DecodeJsonResult(std::string&& error_message0,\n uint64_t cursor_position0)\n : error_message(std::move(error_message0)),\n cursor_position(cursor_position0) {}\n\nDecodeJsonResult //\nDecodeJson(DecodeJsonCallbacks&& callbacks,\n sync_io::Input&& input,\n wuffs_base__slice_u32 quirks) {\n // Prepare the wuffs_base__io_buffer and the resultant error_message.\n wuffs_base__io_buffer* io_buf = input.BringsItsOwnIOBuffer();\n wuffs_base__io_buffer fallback_io_buf = wuffs_base__empty_io_buffer();\n std::unique_ptr<uint8_t[]> fallback_io_array(nullptr);\n if (!io_buf) {\n fallback_io_array = std::unique_ptr<uint8_t[]>(new uint8_t[4096]);\n fallback_io_buf = wuffs_base__ptr_u8__writer(fallback_io_array.get(), 4096);\n io_buf = &fallback_io_buf;\n }\n size_t cursor_index = 0;\n s" +
"td::string ret_error_message;\n std::string io_error_message;\n\n do {\n // Prepare the low-level JSON decoder.\n wuffs_json__decoder::unique_ptr dec = wuffs_json__decoder::alloc();\n if (!dec) {\n ret_error_message = \"wuffs_aux::JsonDecoder: out of memory\";\n goto done;\n }\n for (size_t i = 0; i < quirks.len; i++) {\n dec->set_quirk_enabled(quirks.ptr[i], true);\n }\n\n // Prepare the wuffs_base__tok_buffer.\n wuffs_base__token tok_array[256];\n wuffs_base__token_buffer tok_buf =\n wuffs_base__slice_token__writer(wuffs_base__make_slice_token(\n &tok_array[0], (sizeof(tok_array) / sizeof(tok_array[0]))));\n wuffs_base__status tok_status = wuffs_base__make_status(nullptr);\n\n // Prepare other state.\n uint32_t depth = 0;\n std::string str;\n\n // Loop, doing these two things:\n // 1. Get the next token.\n // 2. Process that token.\n while (true) {\n // 1. Get the next token.\n\n while (tok_buf.meta.ri >= tok_buf.meta.wi) {\n if (tok_sta" +
@@ -644,10 +665,12 @@
""
var AuxNonBaseCcFiles = []string{
+ AuxCborCc,
AuxJsonCc,
}
var AuxNonBaseHhFiles = []string{
+ AuxCborHh,
AuxJsonHh,
}
diff --git a/internal/cgen/data/gen.go b/internal/cgen/data/gen.go
index a9d732a..65a9c85 100644
--- a/internal/cgen/data/gen.go
+++ b/internal/cgen/data/gen.go
@@ -99,6 +99,8 @@
{"../auxiliary/base.cc", "AuxBaseCc"},
{"../auxiliary/base.hh", "AuxBaseHh"},
+ {"../auxiliary/cbor.cc", "AuxCborCc"},
+ {"../auxiliary/cbor.hh", "AuxCborHh"},
{"../auxiliary/json.cc", "AuxJsonCc"},
{"../auxiliary/json.hh", "AuxJsonHh"},
}
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 9e9a316..b0a2fb1 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -5648,6 +5648,8 @@
// ---------------- Public Consts
+#define WUFFS_CBOR__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE 0
+
#define WUFFS_CBOR__TOKEN_VALUE_MAJOR 787997
#define WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_MASK 262143
@@ -8409,6 +8411,71 @@
} // namespace wuffs_aux
+// ---------------- Auxiliary - CBOR
+
+namespace wuffs_aux {
+
+struct DecodeCborResult {
+ DecodeCborResult(std::string&& error_message0, uint64_t cursor_position0);
+
+ std::string error_message;
+ uint64_t cursor_position;
+};
+
+class DecodeCborCallbacks {
+ public:
+ // AppendXxx are called for leaf nodes: literals, numbers, strings, etc.
+
+ virtual std::string AppendNull() = 0;
+ virtual std::string AppendUndefined() = 0;
+ virtual std::string AppendBool(bool val) = 0;
+ virtual std::string AppendF64(double val) = 0;
+ virtual std::string AppendI64(int64_t val) = 0;
+ virtual std::string AppendU64(uint64_t val) = 0;
+ virtual std::string AppendByteString(std::string&& val) = 0;
+ virtual std::string AppendTextString(std::string&& val) = 0;
+ virtual std::string AppendMinus1MinusX(uint64_t val) = 0;
+ virtual std::string AppendCborSimpleValue(uint8_t val) = 0;
+ virtual std::string AppendCborTag(uint64_t val) = 0;
+
+ // Push and Pop are called for container nodes: CBOR arrays (lists) and CBOR
+ // maps (dictionaries).
+ //
+ // The flags bits combine exactly one of:
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_NONE
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_LIST
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__FROM_DICT
+ // and exactly one of:
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_NONE
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST
+ // - WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_DICT
+
+ virtual std::string Push(uint32_t flags) = 0;
+ virtual std::string Pop(uint32_t flags) = 0;
+
+ // Done is always the last Callback method called by DecodeCbor, whether or
+ // not parsing the input as CBOR encountered an error. Even when successful,
+ // trailing data may remain in input and buffer.
+ //
+ // Do not keep a reference to buffer or buffer.data.ptr after Done returns,
+ // as DecodeCbor may then de-allocate the backing array.
+ virtual void Done(DecodeCborResult& result,
+ sync_io::Input& input,
+ IOBuffer& buffer);
+};
+
+// DecodeCbor calls callbacks based on the CBOR-formatted data in input.
+//
+// On success, the returned error_message is empty and cursor_position counts
+// the number of bytes consumed. On failure, error_message is non-empty and
+// cursor_position is the location of the error. That error may be a content
+// error (invalid CBOR) or an input error (e.g. network failure).
+DecodeCborResult DecodeCbor(DecodeCborCallbacks&& callbacks,
+ sync_io::Input&& input,
+ wuffs_base__slice_u32 quirks);
+
+} // namespace wuffs_aux
+
// ---------------- Auxiliary - JSON
namespace wuffs_aux {
@@ -28859,6 +28926,334 @@
#endif // !defined(WUFFS_CONFIG__MODULES) ||
// defined(WUFFS_CONFIG__MODULE__AUX__BASE)
+// ---------------- Auxiliary - CBOR
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__CBOR)
+
+#include <utility>
+
+namespace wuffs_aux {
+
+DecodeCborResult::DecodeCborResult(std::string&& error_message0,
+ uint64_t cursor_position0)
+ : error_message(std::move(error_message0)),
+ cursor_position(cursor_position0) {}
+
+void DecodeCborCallbacks::Done(DecodeCborResult& result,
+ sync_io::Input& input,
+ IOBuffer& buffer) {}
+
+DecodeCborResult //
+DecodeCbor(DecodeCborCallbacks&& callbacks,
+ sync_io::Input&& input,
+ wuffs_base__slice_u32 quirks) {
+ // Prepare the wuffs_base__io_buffer and the resultant error_message.
+ wuffs_base__io_buffer* io_buf = input.BringsItsOwnIOBuffer();
+ wuffs_base__io_buffer fallback_io_buf = wuffs_base__empty_io_buffer();
+ std::unique_ptr<uint8_t[]> fallback_io_array(nullptr);
+ if (!io_buf) {
+ fallback_io_array = std::unique_ptr<uint8_t[]>(new uint8_t[4096]);
+ fallback_io_buf = wuffs_base__ptr_u8__writer(fallback_io_array.get(), 4096);
+ io_buf = &fallback_io_buf;
+ }
+ size_t cursor_index = 0;
+ std::string ret_error_message;
+ std::string io_error_message;
+
+ do {
+ // Prepare the low-level CBOR decoder.
+ wuffs_cbor__decoder::unique_ptr dec = wuffs_cbor__decoder::alloc();
+ if (!dec) {
+ ret_error_message = "wuffs_aux::CborDecoder: out of memory";
+ goto done;
+ }
+ for (size_t i = 0; i < quirks.len; i++) {
+ dec->set_quirk_enabled(quirks.ptr[i], true);
+ }
+
+ // Prepare the wuffs_base__tok_buffer.
+ wuffs_base__token tok_array[256];
+ wuffs_base__token_buffer tok_buf =
+ wuffs_base__slice_token__writer(wuffs_base__make_slice_token(
+ &tok_array[0], (sizeof(tok_array) / sizeof(tok_array[0]))));
+ wuffs_base__status tok_status = wuffs_base__make_status(nullptr);
+
+ // Prepare other state.
+ uint32_t depth = 0;
+ std::string str;
+ int64_t extension_category = 0;
+ uint64_t extension_detail = 0;
+
+ // Valid token's VBCs range in 0 ..= 15. Values over that are for tokens
+ // from outside of the base package, such as the CBOR package.
+ constexpr int64_t EXT_CAT__CBOR_TAG = 16;
+
+ // Loop, doing these two things:
+ // 1. Get the next token.
+ // 2. Process that token.
+ while (true) {
+ // 1. Get the next token.
+
+ while (tok_buf.meta.ri >= tok_buf.meta.wi) {
+ if (tok_status.repr == nullptr) {
+ // No-op.
+ } else if (tok_status.repr == wuffs_base__suspension__short_write) {
+ tok_buf.compact();
+ } else if (tok_status.repr == wuffs_base__suspension__short_read) {
+ // Read from input to io_buf.
+ if (!io_error_message.empty()) {
+ ret_error_message = std::move(io_error_message);
+ goto done;
+ } else if (cursor_index != io_buf->meta.ri) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: bad cursor_index";
+ goto done;
+ } else if (io_buf->meta.closed) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: io_buf is closed";
+ goto done;
+ }
+ io_buf->compact();
+ if (io_buf->meta.wi >= io_buf->data.len) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: io_buf is full";
+ goto done;
+ }
+ cursor_index = io_buf->meta.ri;
+ io_error_message = input.CopyIn(io_buf);
+ } else {
+ ret_error_message = tok_status.message();
+ goto done;
+ }
+
+ if (WUFFS_CBOR__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE != 0) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: bad WORKBUF_LEN";
+ goto done;
+ }
+ wuffs_base__slice_u8 work_buf = wuffs_base__empty_slice_u8();
+ tok_status = dec->decode_tokens(&tok_buf, io_buf, work_buf);
+ }
+
+ wuffs_base__token token = tok_buf.data.ptr[tok_buf.meta.ri++];
+ uint64_t token_len = token.length();
+ if ((io_buf->meta.ri < cursor_index) ||
+ ((io_buf->meta.ri - cursor_index) < token_len)) {
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: bad token indexes";
+ goto done;
+ }
+ uint8_t* token_ptr = io_buf->data.ptr + cursor_index;
+ cursor_index += token_len;
+
+ // 2. Process that token.
+
+ uint64_t vbd = token.value_base_detail();
+
+ if (extension_category != 0) {
+ int64_t ext = token.value_extension();
+ if ((ext >= 0) && !token.continued()) {
+ extension_detail = (extension_detail
+ << WUFFS_BASE__TOKEN__VALUE_EXTENSION__NUM_BITS) |
+ static_cast<uint64_t>(ext);
+ switch (extension_category) {
+ case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_SIGNED:
+ extension_category = 0;
+ ret_error_message =
+ callbacks.AppendI64(static_cast<int64_t>(extension_detail));
+ goto parsed_a_value;
+ case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_UNSIGNED:
+ extension_category = 0;
+ ret_error_message = callbacks.AppendU64(extension_detail);
+ goto parsed_a_value;
+ case EXT_CAT__CBOR_TAG:
+ extension_category = 0;
+ ret_error_message = callbacks.AppendCborTag(extension_detail);
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ continue;
+ }
+ }
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: bad extended token";
+ goto done;
+ }
+
+ switch (token.value_base_category()) {
+ case WUFFS_BASE__TOKEN__VBC__FILLER:
+ continue;
+
+ case WUFFS_BASE__TOKEN__VBC__STRUCTURE: {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {
+ ret_error_message = callbacks.Push(static_cast<uint32_t>(vbd));
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ depth++;
+ continue;
+ }
+ ret_error_message = callbacks.Pop(static_cast<uint32_t>(vbd));
+ depth--;
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__STRING: {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_0_DST_1_SRC_DROP) {
+ // No-op.
+ } else if (vbd &
+ WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_1_SRC_COPY) {
+ const char* ptr = // Convert from (uint8_t*).
+ static_cast<const char*>(static_cast<void*>(token_ptr));
+ str.append(ptr, token_len);
+ } else {
+ goto fail;
+ }
+ if (token.continued()) {
+ continue;
+ }
+ ret_error_message =
+ (vbd & WUFFS_BASE__TOKEN__VBD__STRING__CHAIN_MUST_BE_UTF_8)
+ ? callbacks.AppendTextString(std::move(str))
+ : callbacks.AppendByteString(std::move(str));
+ str.clear();
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__UNICODE_CODE_POINT: {
+ uint8_t u[WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL];
+ size_t n = wuffs_base__utf_8__encode(
+ wuffs_base__make_slice_u8(
+ &u[0], WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL),
+ static_cast<uint32_t>(vbd));
+ const char* ptr = // Convert from (uint8_t*).
+ static_cast<const char*>(static_cast<void*>(&u[0]));
+ str.append(ptr, n);
+ if (token.continued()) {
+ continue;
+ }
+ goto fail;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__LITERAL: {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__NULL) {
+ ret_error_message = callbacks.AppendNull();
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__UNDEFINED) {
+ ret_error_message = callbacks.AppendUndefined();
+ } else {
+ ret_error_message = callbacks.AppendBool(
+ vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__TRUE);
+ }
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__NUMBER: {
+ const uint64_t cfp_fbbe_fifb =
+ WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT |
+ WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN |
+ WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE;
+ if ((vbd & cfp_fbbe_fifb) == cfp_fbbe_fifb) {
+ double f;
+ switch (token_len) {
+ case 3:
+ f = wuffs_base__ieee_754_bit_representation__from_u16_to_f64(
+ wuffs_base__load_u16be__no_bounds_check(token_ptr + 1));
+ break;
+ case 5:
+ f = wuffs_base__ieee_754_bit_representation__from_u32_to_f64(
+ wuffs_base__load_u32be__no_bounds_check(token_ptr + 1));
+ break;
+ case 9:
+ f = wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ wuffs_base__load_u64be__no_bounds_check(token_ptr + 1));
+ break;
+ default:
+ goto fail;
+ }
+ ret_error_message = callbacks.AppendF64(f);
+ goto parsed_a_value;
+ }
+ goto fail;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_SIGNED: {
+ if (token.continued()) {
+ extension_category = WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_SIGNED;
+ extension_detail =
+ static_cast<uint64_t>(token.value_base_detail__sign_extended());
+ continue;
+ }
+ ret_error_message =
+ callbacks.AppendI64(token.value_base_detail__sign_extended());
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_UNSIGNED: {
+ if (token.continued()) {
+ extension_category =
+ WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER_UNSIGNED;
+ extension_detail = vbd;
+ continue;
+ }
+ ret_error_message = callbacks.AppendU64(vbd);
+ goto parsed_a_value;
+ }
+ }
+
+ if (token.value_major() == WUFFS_CBOR__TOKEN_VALUE_MAJOR) {
+ uint64_t value_minor = token.value_minor();
+ if (value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__MINUS_1_MINUS_X) {
+ if (token_len == 9) {
+ ret_error_message = callbacks.AppendMinus1MinusX(
+ wuffs_base__load_u64be__no_bounds_check(token_ptr + 1));
+ goto parsed_a_value;
+ }
+ } else if (value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__SIMPLE_VALUE) {
+ ret_error_message =
+ callbacks.AppendCborSimpleValue(static_cast<uint8_t>(
+ value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_MASK));
+ goto parsed_a_value;
+ } else if (value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__TAG) {
+ if (token.continued()) {
+ extension_category = EXT_CAT__CBOR_TAG;
+ extension_detail =
+ value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_MASK;
+ continue;
+ }
+ ret_error_message = callbacks.AppendCborTag(
+ value_minor & WUFFS_CBOR__TOKEN_VALUE_MINOR__DETAIL_MASK);
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ continue;
+ }
+ }
+
+ fail:
+ ret_error_message =
+ "wuffs_aux::CborDecoder: internal error: unexpected token";
+ goto done;
+
+ parsed_a_value:
+ if (!ret_error_message.empty() || (depth == 0)) {
+ goto done;
+ }
+ }
+ } while (false);
+
+done:
+ DecodeCborResult result(
+ std::move(ret_error_message),
+ wuffs_base__u64__sat_add(io_buf->meta.pos, cursor_index));
+ callbacks.Done(result, input, *io_buf);
+ return result;
+}
+
+} // namespace wuffs_aux
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__AUX__CBOR)
+
// ---------------- Auxiliary - JSON
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__JSON)
diff --git a/std/cbor/decode_cbor.wuffs b/std/cbor/decode_cbor.wuffs
index 4db9c56..7153384 100644
--- a/std/cbor/decode_cbor.wuffs
+++ b/std/cbor/decode_cbor.wuffs
@@ -18,6 +18,8 @@
pri status "#internal error: inconsistent I/O"
pri status "#internal error: inconsistent token length"
+pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
+
// TOKEN_VALUE_MAJOR is the base-38 encoding of "cbor".
pub const TOKEN_VALUE_MAJOR : base.u32 = 0x0C_061D
diff --git a/test/data/README.md b/test/data/README.md
index ba67800..7608679 100644
--- a/test/data/README.md
+++ b/test/data/README.md
@@ -34,7 +34,8 @@
<nigeltao@golang.org>.
`cbor-rfc-7049-examples.cbor` is the concatenated examples from RFC 7049. The
-`cbor-rfc-7049-examples.*.json` files were then generated by `example/jsonptr`.
+`cbor-rfc-7049-examples.*.json` files were then generated by
+`example/cbor-to-json`.
`crude-flag.*` is an original animation by Nigel Tao
<nigeltao@golang.org>. See the `lib/nie` documentation.