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.