Add example/jsonfindptrs -query=STR flag
diff --git a/example/jsonfindptrs/jsonfindptrs.cc b/example/jsonfindptrs/jsonfindptrs.cc
index c26ff4e..4794279 100644
--- a/example/jsonfindptrs/jsonfindptrs.cc
+++ b/example/jsonfindptrs/jsonfindptrs.cc
@@ -106,6 +106,7 @@
"\n"
"Flags:\n"
" -d=NUM -max-output-depth=NUM\n"
+ " -q=STR -query=STR\n"
" -input-allow-comments\n"
" -input-allow-extra-comma\n"
" -input-allow-inf-nan-numbers\n"
@@ -193,6 +194,7 @@
char** remaining_argv;
uint32_t max_output_depth;
+ char* query_c_string;
bool strict_json_pointer_syntax;
} g_flags = {0};
@@ -249,6 +251,12 @@
g_quirks.push_back(WUFFS_JSON__QUIRK_ALLOW_INF_NAN_NUMBERS);
continue;
}
+ if (!strncmp(arg, "q=", 2) || !strncmp(arg, "query=", 6)) {
+ while (*arg++ != '=') {
+ }
+ g_flags.query_c_string = arg;
+ continue;
+ }
if (!strcmp(arg, "strict-json-pointer-syntax")) {
g_flags.strict_json_pointer_syntax = true;
continue;
@@ -506,7 +514,8 @@
return wuffs_aux::DecodeJson(
Callbacks(), wuffs_aux::sync_io::FileInput(in),
- wuffs_base__make_slice_u32(g_quirks.data(), g_quirks.size()))
+ wuffs_base__make_slice_u32(g_quirks.data(), g_quirks.size()),
+ (g_flags.query_c_string ? g_flags.query_c_string : ""))
.error_message;
}
diff --git a/internal/cgen/auxiliary/json.cc b/internal/cgen/auxiliary/json.cc
index 1613ff5..bcd3908 100644
--- a/internal/cgen/auxiliary/json.cc
+++ b/internal/cgen/auxiliary/json.cc
@@ -29,7 +29,7 @@
std::string //
DecodeJsonCallbacks::AppendByteString(std::string&& val) {
- return "wuffs_aux::JsonDecoder: unexpected JSON byte string";
+ return "wuffs_aux::DecodeJson: unexpected JSON byte string";
}
void //
@@ -37,6 +37,10 @@
sync_io::Input& input,
IOBuffer& buffer) {}
+const char DecodeJson_BadJsonPointer[] =
+ "wuffs_aux::DecodeJson: bad JSON Pointer";
+const char DecodeJson_NoMatch[] = "wuffs_aux::DecodeJson: no match";
+
#define WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN \
while (tok_buf.meta.ri >= tok_buf.meta.wi) { \
if (tok_status.repr == nullptr) { \
@@ -48,17 +52,17 @@
goto done; \
} else if (cursor_index != io_buf->meta.ri) { \
ret_error_message = \
- "wuffs_aux::JsonDecoder: internal error: bad cursor_index"; \
+ "wuffs_aux::DecodeJson: internal error: bad cursor_index"; \
goto done; \
} else if (io_buf->meta.closed) { \
ret_error_message = \
- "wuffs_aux::JsonDecoder: internal error: io_buf is closed"; \
+ "wuffs_aux::DecodeJson: 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::JsonDecoder: internal error: io_buf is full"; \
+ "wuffs_aux::DecodeJson: internal error: io_buf is full"; \
goto done; \
} \
cursor_index = io_buf->meta.ri; \
@@ -69,7 +73,7 @@
} \
if (WUFFS_JSON__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE != 0) { \
ret_error_message = \
- "wuffs_aux::JsonDecoder: internal error: bad WORKBUF_LEN"; \
+ "wuffs_aux::DecodeJson: internal error: bad WORKBUF_LEN"; \
goto done; \
} \
wuffs_base__slice_u8 work_buf = wuffs_base__empty_slice_u8(); \
@@ -80,16 +84,268 @@
if ((io_buf->meta.ri < cursor_index) || \
((io_buf->meta.ri - cursor_index) < token_len)) { \
ret_error_message = \
- "wuffs_aux::JsonDecoder: internal error: bad token indexes"; \
+ "wuffs_aux::DecodeJson: internal error: bad token indexes"; \
goto done; \
} \
uint8_t* token_ptr = io_buf->data.ptr + cursor_index; \
cursor_index += token_len
+namespace {
+
+// DecodeJson_SplitJsonPointer returns ("bar", 8) for ("/foo/bar/baz/qux", 5).
+// It returns a 0 size_t when s has invalid JSON Pointer syntax.
+std::pair<std::string, size_t> //
+DecodeJson_SplitJsonPointer(std::string& s, size_t i) {
+ std::string fragment;
+ while (i < s.size()) {
+ char c = s[i];
+ if (c == '/') {
+ break;
+ } else if (c != '~') {
+ fragment.push_back(c);
+ i++;
+ continue;
+ }
+ i++;
+ if (i >= s.size()) {
+ return std::make_pair(std::string(), 0);
+ }
+ c = s[i];
+ if (c == '0') {
+ fragment.push_back('~');
+ i++;
+ continue;
+ } else if (c == '1') {
+ fragment.push_back('/');
+ i++;
+ continue;
+ }
+ return std::make_pair(std::string(), 0);
+ }
+ return std::make_pair(std::move(fragment), i);
+}
+
+std::string //
+DecodeJson_DecodeBackslashX(std::string& str,
+ uint8_t* token_ptr,
+ size_t token_len) {
+ wuffs_base__slice_u8 encoded =
+ wuffs_base__make_slice_u8(token_ptr, token_len);
+ while (encoded.len > 0) {
+ uint8_t decoded[64];
+ constexpr bool src_closed = true;
+ wuffs_base__transform__output o = wuffs_base__base_16__decode4(
+ wuffs_base__make_slice_u8(&decoded[0], sizeof decoded), encoded,
+ src_closed, WUFFS_BASE__BASE_16__DEFAULT_OPTIONS);
+ if (o.status.is_error()) {
+ return o.status.message();
+ } else if ((o.num_dst > (sizeof decoded)) || (o.num_src > encoded.len)) {
+ return "wuffs_aux::DecodeJson: internal error: inconsistent base16 "
+ "decoding";
+ }
+ str.append( // Convert from (uint8_t*).
+ static_cast<const char*>(static_cast<void*>(&decoded[0])), o.num_dst);
+ encoded.ptr += o.num_src;
+ encoded.len -= o.num_src;
+ }
+ return "";
+}
+
+std::string //
+DecodeJson_WalkJsonPointerFragment(wuffs_base__token_buffer& tok_buf,
+ wuffs_base__status& tok_status,
+ wuffs_json__decoder::unique_ptr& dec,
+ wuffs_base__io_buffer* io_buf,
+ std::string& io_error_message,
+ size_t& cursor_index,
+ sync_io::Input& input,
+ std::string& json_pointer_fragment) {
+ std::string ret_error_message;
+ while (true) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ if (vbc == WUFFS_BASE__TOKEN__VBC__FILLER) {
+ continue;
+ } else if ((vbc != WUFFS_BASE__TOKEN__VBC__STRUCTURE) ||
+ !(vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH)) {
+ return DecodeJson_NoMatch;
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST) {
+ goto do_list;
+ }
+ goto do_dict;
+ }
+
+do_dict:
+ // Alternate between these two things:
+ // 1. Decode the next dict key (a string). If it matches the fragment, we're
+ // done (success). If we've reached the dict's end (VBD__STRUCTURE__POP)
+ // so that there was no next dict key, we're done (failure).
+ // 2. Otherwise, skip the next dict value.
+ while (true) {
+ for (std::string str; true;) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ switch (vbc) {
+ case WUFFS_BASE__TOKEN__VBC__FILLER:
+ continue;
+
+ case WUFFS_BASE__TOKEN__VBC__STRUCTURE:
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {
+ goto fail;
+ }
+ return DecodeJson_NoMatch;
+
+ 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 if (
+ vbd &
+ WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_4_SRC_BACKSLASH_X) {
+ ret_error_message =
+ DecodeJson_DecodeBackslashX(str, token_ptr, token_len);
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ } else {
+ goto fail;
+ }
+ break;
+ }
+
+ 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);
+ break;
+ }
+
+ default:
+ goto fail;
+ }
+
+ if (token.continued()) {
+ continue;
+ }
+ if (str == json_pointer_fragment) {
+ return "";
+ }
+ goto skip_the_next_dict_value;
+ }
+
+ skip_the_next_dict_value:
+ for (uint32_t skip_depth = 0; true;) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ if (token.continued() || (vbc == WUFFS_BASE__TOKEN__VBC__FILLER)) {
+ continue;
+ } else if (vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {
+ skip_depth++;
+ continue;
+ }
+ skip_depth--;
+ }
+
+ if (skip_depth == 0) {
+ break;
+ }
+ } // skip_the_next_dict_value
+ } // do_dict
+
+do_list:
+ do {
+ wuffs_base__result_u64 result_u64 = wuffs_base__parse_number_u64(
+ wuffs_base__make_slice_u8(
+ static_cast<uint8_t*>(static_cast<void*>(
+ const_cast<char*>(json_pointer_fragment.data()))),
+ json_pointer_fragment.size()),
+ WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
+ if (!result_u64.status.is_ok()) {
+ return DecodeJson_NoMatch;
+ }
+ uint64_t remaining = result_u64.value;
+ if (remaining == 0) {
+ goto check_that_a_value_follows;
+ }
+ for (uint32_t skip_depth = 0; true;) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ if (token.continued() || (vbc == WUFFS_BASE__TOKEN__VBC__FILLER)) {
+ continue;
+ } else if (vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {
+ skip_depth++;
+ continue;
+ }
+ if (skip_depth == 0) {
+ return DecodeJson_NoMatch;
+ }
+ skip_depth--;
+ }
+
+ if (skip_depth > 0) {
+ continue;
+ }
+ remaining--;
+ if (remaining == 0) {
+ goto check_that_a_value_follows;
+ }
+ }
+ } while (false); // do_list
+
+check_that_a_value_follows:
+ while (true) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ if (vbc == WUFFS_BASE__TOKEN__VBC__FILLER) {
+ continue;
+ }
+
+ // Undo the last part of WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN, so that
+ // we're only peeking at the next token.
+ tok_buf.meta.ri--;
+ cursor_index -= token_len;
+
+ if ((vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) &&
+ (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__POP)) {
+ return DecodeJson_NoMatch;
+ }
+ return "";
+ } // check_that_a_value_follows
+
+fail:
+ return "wuffs_aux::DecodeJson: internal error: unexpected token";
+done:
+ return ret_error_message;
+}
+
+} // namespace
+
DecodeJsonResult //
DecodeJson(DecodeJsonCallbacks&& callbacks,
sync_io::Input&& input,
- wuffs_base__slice_u32 quirks) {
+ wuffs_base__slice_u32 quirks,
+ std::string json_pointer) {
// 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();
@@ -107,7 +363,7 @@
// Prepare the low-level JSON decoder.
wuffs_json__decoder::unique_ptr dec = wuffs_json__decoder::alloc();
if (!dec) {
- ret_error_message = "wuffs_aux::JsonDecoder: out of memory";
+ ret_error_message = "wuffs_aux::DecodeJson: out of memory";
goto done;
}
for (size_t i = 0; i < quirks.len; i++) {
@@ -125,6 +381,27 @@
uint32_t depth = 0;
std::string str;
+ // Walk the (optional) JSON Pointer.
+ for (size_t i = 0; i < json_pointer.size();) {
+ if (json_pointer[i] != '/') {
+ ret_error_message = DecodeJson_BadJsonPointer;
+ goto done;
+ }
+ std::pair<std::string, size_t> split =
+ DecodeJson_SplitJsonPointer(json_pointer, i + 1);
+ i = std::move(split.second);
+ if (i == 0) {
+ ret_error_message = DecodeJson_BadJsonPointer;
+ goto done;
+ }
+ ret_error_message = DecodeJson_WalkJsonPointerFragment(
+ tok_buf, tok_status, dec, io_buf, io_error_message, cursor_index,
+ input, split.first);
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ }
+
// Loop, doing these two things:
// 1. Get the next token.
// 2. Process that token.
@@ -162,29 +439,10 @@
} else if (
vbd &
WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_4_SRC_BACKSLASH_X) {
- wuffs_base__slice_u8 encoded =
- wuffs_base__make_slice_u8(token_ptr, token_len);
- while (encoded.len > 0) {
- uint8_t decoded[64];
- constexpr bool src_closed = true;
- wuffs_base__transform__output o = wuffs_base__base_16__decode4(
- wuffs_base__make_slice_u8(&decoded[0], sizeof decoded),
- encoded, src_closed, WUFFS_BASE__BASE_16__DEFAULT_OPTIONS);
- if (o.status.is_error()) {
- ret_error_message = o.status.message();
- goto done;
- } else if ((o.num_dst > (sizeof decoded)) ||
- (o.num_src > encoded.len)) {
- ret_error_message =
- "wuffs_aux::JsonDecoder: internal error: inconsistent "
- "base16 decoding";
- goto done;
- }
- str.append( // Convert from (uint8_t*).
- static_cast<const char*>(static_cast<void*>(&decoded[0])),
- o.num_dst);
- encoded.ptr += o.num_src;
- encoded.len -= o.num_src;
+ ret_error_message =
+ DecodeJson_DecodeBackslashX(str, token_ptr, token_len);
+ if (!ret_error_message.empty()) {
+ goto done;
}
} else {
goto fail;
@@ -271,7 +529,7 @@
fail:
ret_error_message =
- "wuffs_aux::JsonDecoder: internal error: unexpected token";
+ "wuffs_aux::DecodeJson: internal error: unexpected token";
goto done;
parsed_a_value:
diff --git a/internal/cgen/auxiliary/json.hh b/internal/cgen/auxiliary/json.hh
index 413c45d..89998d0 100644
--- a/internal/cgen/auxiliary/json.hh
+++ b/internal/cgen/auxiliary/json.hh
@@ -78,15 +78,27 @@
IOBuffer& buffer);
};
+extern const char DecodeJson_BadJsonPointer[];
+extern const char DecodeJson_NoMatch[];
+
// DecodeJson calls callbacks based on the JSON-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 JSON) or an input error (e.g. network failure).
+//
+// json_pointer is a query in the JSON Pointer (RFC 6901) syntax. The callbacks
+// run for the input's sub-node that matches the query. DecodeJson_NoMatch is
+// returned if no matching sub-node was found. The empty query matches the
+// input's root node, consistent with JSON Pointer semantics.
+//
+// The JSON Pointer implementation is greedy: duplicate keys are not rejected
+// but only the first match for each '/'-separated fragment is followed.
DecodeJsonResult DecodeJson(
DecodeJsonCallbacks&& callbacks,
sync_io::Input&& input,
- wuffs_base__slice_u32 quirks = wuffs_base__empty_slice_u32());
+ wuffs_base__slice_u32 quirks = wuffs_base__empty_slice_u32(),
+ std::string json_pointer = std::string());
} // namespace wuffs_aux
diff --git a/internal/cgen/data/data.go b/internal/cgen/data/data.go
index d0fbc0f..97a3686 100644
--- a/internal/cgen/data/data.go
+++ b/internal/cgen/data/data.go
@@ -648,26 +648,33 @@
""
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\nstd::string //\nDecodeJsonCallbacks::AppendByteString(std::string&& val) {\n return \"wuffs_aux::JsonDecoder: unexpected JSON byte string\";\n}\n\nvoid //\nDecodeJsonCallbacks::Done(DecodeJsonResult& result,\n sync_io::Input& input,\n IOBuffer& buffer) {}\n\n#define WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN \\\n while (tok_buf.meta.ri >= tok_buf.meta.wi) { \\\n if (tok_status.repr == nullptr) { \\\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 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::JsonDecoder: internal error: bad cursor_index\"; \\\n goto done; \\\n } else if (io_buf->meta.closed) { \\\n ret_error_message = \\\n \"wuffs_aux::JsonDecoder: internal error: io_buf is closed\"; \\\n goto done; \\\n } \\\n io_buf->com" +
- "pact(); \\\n if (io_buf->meta.wi >= io_buf->data.len) { \\\n ret_error_message = \\\n \"wuffs_aux::JsonDecoder: 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 if (WUFFS_JSON__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE != 0) { \\\n ret_error_message = " +
- " \\\n \"wuffs_aux::JsonDecoder: 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 wuffs_base__token token = tok_buf.data.ptr[tok_buf.meta.ri++]; \\\n uint64_t token_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::JsonDecoder: 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\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 std::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::Jso" +
- "nDecoder: 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 WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;\n\n int64_t vbc = token.value_base_category();\n uint64_t vbd = token.value_base_detail();\n switch (vbc) {\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 if (\n vbd &\n WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_4_SRC_BACKSLASH_X) {\n wuffs_base__slice_u8 encoded =\n wuffs_base__make_slice_u8(token_ptr, token_len);\n " +
- " while (encoded.len > 0) {\n uint8_t decoded[64];\n constexpr bool src_closed = true;\n wuffs_base__transform__output o = wuffs_base__base_16__decode4(\n wuffs_base__make_slice_u8(&decoded[0], sizeof decoded),\n encoded, src_closed, WUFFS_BASE__BASE_16__DEFAULT_OPTIONS);\n if (o.status.is_error()) {\n ret_error_message = o.status.message();\n goto done;\n } else if ((o.num_dst > (sizeof decoded)) ||\n (o.num_src > encoded.len)) {\n ret_error_message =\n \"wuffs_aux::JsonDecoder: internal error: inconsistent \"\n \"base16 decoding\";\n goto done;\n }\n str.append( // Convert from (uint8_t*).\n static_cast<const char*>(static_cast<void*>(&decoded[0])),\n o.num_dst);\n encoded.ptr += o.num_src;\n encoded.len -= o.num_src;\n " +
- " }\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 parsed_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 ret_error_message =\n (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__NULL)\n ? callbacks.AppendNull()\n : callbacks.AppendBool(vbd &\n WUFFS_BASE__TOKEN__VBD__LITERAL__TRUE);\n goto parsed_a_value;\n }\n\n case WUFFS_BASE__TOKEN__VBC__NUMBER: {\n if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT) {\n if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED) {\n wuffs_base__result_i64 r = wuffs_base__parse_number_i64(\n wuffs_base__make_slice_u8(token_ptr, token_len),\n WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);\n if (r.status.is_ok()) {\n ret_error_message = callbacks.AppendI64(r.value);\n goto parsed_a_value;\n }\n }\n if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT) {\n wuffs_base__result_f64 r = wuffs_ba" +
- "se__parse_number_f64(\n wuffs_base__make_slice_u8(token_ptr, token_len),\n WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);\n if (r.status.is_ok()) {\n ret_error_message = callbacks.AppendF64(r.value);\n goto parsed_a_value;\n }\n }\n } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_INF) {\n ret_error_message = callbacks.AppendF64(\n wuffs_base__ieee_754_bit_representation__from_u64_to_f64(\n 0xFFF0000000000000ul));\n goto parsed_a_value;\n } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_INF) {\n ret_error_message = callbacks.AppendF64(\n wuffs_base__ieee_754_bit_representation__from_u64_to_f64(\n 0x7FF0000000000000ul));\n goto parsed_a_value;\n } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_NAN) {\n ret_error_message = callbacks.AppendF64(\n " +
- " wuffs_base__ieee_754_bit_representation__from_u64_to_f64(\n 0xFFFFFFFFFFFFFFFFul));\n goto parsed_a_value;\n } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_NAN) {\n ret_error_message = callbacks.AppendF64(\n wuffs_base__ieee_754_bit_representation__from_u64_to_f64(\n 0x7FFFFFFFFFFFFFFFul));\n goto parsed_a_value;\n }\n goto fail;\n }\n }\n\n fail:\n ret_error_message =\n \"wuffs_aux::JsonDecoder: 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 DecodeJsonResult 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#undef WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN\n\n} // namespace wuffs_aux\n\n#endif // !def" +
- "ined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__AUX__JSON)\n" +
+ "// ---------------- 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\nstd::string //\nDecodeJsonCallbacks::AppendByteString(std::string&& val) {\n return \"wuffs_aux::DecodeJson: unexpected JSON byte string\";\n}\n\nvoid //\nDecodeJsonCallbacks::Done(DecodeJsonResult& result,\n sync_io::Input& input,\n IOBuffer& buffer) {}\n\nconst char DecodeJson_BadJsonPointer[] =\n \"wuffs_aux::DecodeJson: bad JSON Pointer\";\nconst char DecodeJson_NoMatch[] = \"wuffs_aux::DecodeJson: no match\";\n\n#define WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN \\\n while (tok_buf.meta.ri >= tok_buf.meta.wi) { \\\n if (tok_status.repr == nullptr) " +
+ "{ \\\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 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::DecodeJson: internal error: bad cursor_index\"; \\\n goto done; \\\n } else if (io_buf->meta.closed) { \\\n ret_error_message = \\\n \"wuffs_aux::DecodeJson: internal error: io_buf is closed\"; \\\n go" +
+ "to done; \\\n } \\\n io_buf->compact(); \\\n if (io_buf->meta.wi >= io_buf->data.len) { \\\n ret_error_message = \\\n \"wuffs_aux::DecodeJson: 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 if (WUFFS_JSON__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE != 0) { \\\n ret_error_message = \\\n \"wuffs_aux::DecodeJson: 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 wuffs_base__token token = tok_buf.data.ptr[tok_buf.meta.ri++]; \\\n uint64_t token_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::DecodeJson: i" +
+ "nternal 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\nnamespace {\n\n// DecodeJson_SplitJsonPointer returns (\"bar\", 8) for (\"/foo/bar/baz/qux\", 5).\n// It returns a 0 size_t when s has invalid JSON Pointer syntax.\nstd::pair<std::string, size_t> //\nDecodeJson_SplitJsonPointer(std::string& s, size_t i) {\n std::string fragment;\n while (i < s.size()) {\n char c = s[i];\n if (c == '/') {\n break;\n } else if (c != '~') {\n fragment.push_back(c);\n i++;\n continue;\n }\n i++;\n if (i >= s.size()) {\n return std::make_pair(std::string(), 0);\n }\n c = s[i];\n if (c == '0') {\n fragment.push_back('~');\n i++;\n continue;\n } else if (c == '1') {\n fragment.push_back('/');\n i++;\n continue;\n }\n return std::make" +
+ "_pair(std::string(), 0);\n }\n return std::make_pair(std::move(fragment), i);\n}\n\nstd::string //\nDecodeJson_DecodeBackslashX(std::string& str,\n uint8_t* token_ptr,\n size_t token_len) {\n wuffs_base__slice_u8 encoded =\n wuffs_base__make_slice_u8(token_ptr, token_len);\n while (encoded.len > 0) {\n uint8_t decoded[64];\n constexpr bool src_closed = true;\n wuffs_base__transform__output o = wuffs_base__base_16__decode4(\n wuffs_base__make_slice_u8(&decoded[0], sizeof decoded), encoded,\n src_closed, WUFFS_BASE__BASE_16__DEFAULT_OPTIONS);\n if (o.status.is_error()) {\n return o.status.message();\n } else if ((o.num_dst > (sizeof decoded)) || (o.num_src > encoded.len)) {\n return \"wuffs_aux::DecodeJson: internal error: inconsistent base16 \"\n \"decoding\";\n }\n str.append( // Convert from (uint8_t*).\n static_cast<const char*>(static_cast<void*>(&decoded[0])), o.num_dst);\n encoded.ptr += o.num_src;\n " +
+ "encoded.len -= o.num_src;\n }\n return \"\";\n}\n\nstd::string //\nDecodeJson_WalkJsonPointerFragment(wuffs_base__token_buffer& tok_buf,\n wuffs_base__status& tok_status,\n wuffs_json__decoder::unique_ptr& dec,\n wuffs_base__io_buffer* io_buf,\n std::string& io_error_message,\n size_t& cursor_index,\n sync_io::Input& input,\n std::string& json_pointer_fragment) {\n std::string ret_error_message;\n while (true) {\n WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;\n\n int64_t vbc = token.value_base_category();\n uint64_t vbd = token.value_base_detail();\n if (vbc == WUFFS_BASE__TOKEN__VBC__FILLER) {\n continue;\n } else if ((vbc != WUFFS_BASE__TOKEN__VBC__STRUCTURE) ||\n !(vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH)) {\n return DecodeJson_NoMatch;\n } else if " +
+ "(vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST) {\n goto do_list;\n }\n goto do_dict;\n }\n\ndo_dict:\n // Alternate between these two things:\n // 1. Decode the next dict key (a string). If it matches the fragment, we're\n // done (success). If we've reached the dict's end (VBD__STRUCTURE__POP)\n // so that there was no next dict key, we're done (failure).\n // 2. Otherwise, skip the next dict value.\n while (true) {\n for (std::string str; true;) {\n WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;\n\n int64_t vbc = token.value_base_category();\n uint64_t vbd = token.value_base_detail();\n switch (vbc) {\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 goto fail;\n }\n return DecodeJson_NoMatch;\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 if (\n vbd &\n WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_4_SRC_BACKSLASH_X) {\n ret_error_message =\n DecodeJson_DecodeBackslashX(str, token_ptr, token_len);\n if (!ret_error_message.empty()) {\n goto done;\n }\n } else {\n goto fail;\n }\n break;\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 break;\n }\n\n default:\n goto fail;\n }\n\n if (token.continued()) {\n continue;\n }\n if (str == json_pointer_fragment) {\n return \"\";\n }\n goto skip_the_next_dict_value;\n }\n\n skip_the_next_dict_value:\n for (uint32_t skip_depth = 0; true;) {\n WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;\n\n int64_t vbc = token.value_base_category();\n uint64_t vbd = token.value_base_detail();\n if (token.continued() || (vbc == WUFFS_BASE__TOKEN__VBC__FILLER)) {\n continue;\n } else if (vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) {\n if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {\n skip_depth++;\n continue;\n }\n skip_depth--;\n }\n\n if (skip_depth == 0) {\n break;\n }\n } // skip_the_next_dict_value\n } // do_dict\n\ndo_list:\n " +
+ "do {\n wuffs_base__result_u64 result_u64 = wuffs_base__parse_number_u64(\n wuffs_base__make_slice_u8(\n static_cast<uint8_t*>(static_cast<void*>(\n const_cast<char*>(json_pointer_fragment.data()))),\n json_pointer_fragment.size()),\n WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);\n if (!result_u64.status.is_ok()) {\n return DecodeJson_NoMatch;\n }\n uint64_t remaining = result_u64.value;\n if (remaining == 0) {\n goto check_that_a_value_follows;\n }\n for (uint32_t skip_depth = 0; true;) {\n WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;\n\n int64_t vbc = token.value_base_category();\n uint64_t vbd = token.value_base_detail();\n if (token.continued() || (vbc == WUFFS_BASE__TOKEN__VBC__FILLER)) {\n continue;\n } else if (vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) {\n if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {\n skip_depth++;\n continue;\n }\n if (skip_depth == 0) {\n ret" +
+ "urn DecodeJson_NoMatch;\n }\n skip_depth--;\n }\n\n if (skip_depth > 0) {\n continue;\n }\n remaining--;\n if (remaining == 0) {\n goto check_that_a_value_follows;\n }\n }\n } while (false); // do_list\n\ncheck_that_a_value_follows:\n while (true) {\n WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;\n\n int64_t vbc = token.value_base_category();\n uint64_t vbd = token.value_base_detail();\n if (vbc == WUFFS_BASE__TOKEN__VBC__FILLER) {\n continue;\n }\n\n // Undo the last part of WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN, so that\n // we're only peeking at the next token.\n tok_buf.meta.ri--;\n cursor_index -= token_len;\n\n if ((vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) &&\n (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__POP)) {\n return DecodeJson_NoMatch;\n }\n return \"\";\n } // check_that_a_value_follows\n\nfail:\n return \"wuffs_aux::DecodeJson: internal error: unexpected token\";\ndone:\n return ret_error_message;\n}\n\n} // namespace\n\nD" +
+ "ecodeJsonResult //\nDecodeJson(DecodeJsonCallbacks&& callbacks,\n sync_io::Input&& input,\n wuffs_base__slice_u32 quirks,\n std::string json_pointer) {\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 std::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::DecodeJson: 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 // Walk the (optional) JSON Pointer.\n for (size_t i = 0; i < json_pointer.size();) {\n if (json_pointer[i] != '/') {\n ret_error_message = DecodeJson_BadJsonPointer;\n goto done;\n }\n std::pair<std::string, size_t> split =\n DecodeJson_SplitJsonPointer(json_pointer, i + 1);\n i = std::move(split.second);\n if (i == 0) {\n ret_error_message = DecodeJson_BadJsonPointer;\n goto done;\n }\n ret_error_message = DecodeJson_WalkJsonPointerFragment(\n tok_buf, tok_status, dec, io_buf, io_error_" +
+ "message, cursor_index,\n input, split.first);\n if (!ret_error_message.empty()) {\n goto done;\n }\n }\n\n // Loop, doing these two things:\n // 1. Get the next token.\n // 2. Process that token.\n while (true) {\n WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;\n\n int64_t vbc = token.value_base_category();\n uint64_t vbd = token.value_base_detail();\n switch (vbc) {\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__T" +
+ "OKEN__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 if (\n vbd &\n WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_4_SRC_BACKSLASH_X) {\n ret_error_message =\n DecodeJson_DecodeBackslashX(str, token_ptr, token_len);\n if (!ret_error_message.empty()) {\n goto done;\n }\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 parsed_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 ret_error_message =\n (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__NULL)\n ? callbacks.AppendNull()\n : callbacks.AppendBool(vbd &\n WUFFS_BASE__TOKEN__VBD__LITERAL__TRUE);\n goto parsed_a_value;\n }\n\n case WUFFS_BASE__TOKEN__VBC__NUM" +
+ "BER: {\n if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT) {\n if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED) {\n wuffs_base__result_i64 r = wuffs_base__parse_number_i64(\n wuffs_base__make_slice_u8(token_ptr, token_len),\n WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);\n if (r.status.is_ok()) {\n ret_error_message = callbacks.AppendI64(r.value);\n goto parsed_a_value;\n }\n }\n if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT) {\n wuffs_base__result_f64 r = wuffs_base__parse_number_f64(\n wuffs_base__make_slice_u8(token_ptr, token_len),\n WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);\n if (r.status.is_ok()) {\n ret_error_message = callbacks.AppendF64(r.value);\n goto parsed_a_value;\n }\n }\n } else if (vbd & WUFFS_BASE__T" +
+ "OKEN__VBD__NUMBER__CONTENT_NEG_INF) {\n ret_error_message = callbacks.AppendF64(\n wuffs_base__ieee_754_bit_representation__from_u64_to_f64(\n 0xFFF0000000000000ul));\n goto parsed_a_value;\n } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_INF) {\n ret_error_message = callbacks.AppendF64(\n wuffs_base__ieee_754_bit_representation__from_u64_to_f64(\n 0x7FF0000000000000ul));\n goto parsed_a_value;\n } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_NAN) {\n ret_error_message = callbacks.AppendF64(\n wuffs_base__ieee_754_bit_representation__from_u64_to_f64(\n 0xFFFFFFFFFFFFFFFFul));\n goto parsed_a_value;\n } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_NAN) {\n ret_error_message = callbacks.AppendF64(\n wuffs_base__ieee_754_bit_representation__from_u64_to_f64(\n " +
+ " 0x7FFFFFFFFFFFFFFFul));\n goto parsed_a_value;\n }\n goto fail;\n }\n }\n\n fail:\n ret_error_message =\n \"wuffs_aux::DecodeJson: 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 DecodeJsonResult 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#undef WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN\n\n} // namespace wuffs_aux\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__AUX__JSON)\n" +
""
const AuxJsonHh = "" +
"// ---------------- Auxiliary - JSON\n\nnamespace wuffs_aux {\n\nstruct DecodeJsonResult {\n DecodeJsonResult(std::string&& error_message0, uint64_t cursor_position0);\n\n std::string error_message;\n uint64_t cursor_position;\n};\n\nclass DecodeJsonCallbacks {\n public:\n // AppendXxx are called for leaf nodes: literals, numbers and strings. For\n // strings, the Callbacks implementation is responsible for tracking map keys\n // versus other values.\n\n // The JSON file format as specified deals only with (UTF-8) text strings,\n // but an unofficial extension allows \"ijk\\x89m\" escapes within those\n // strings. DecodeJsonCallbacks' AppendByteString will not be called unless\n // WUFFS_JSON__QUIRK_ALLOW_BACKSLASH_X_AS_BYTES is passed to DecodeJson. If\n // it is passed, AppendTextString will not be called and all byte strings are\n // potentially invalid UTF-8. It is up to the AppendByteString implementation\n // whether to test the std::string for UTF-8 validity.\n //\n // The default AppendByteString implementation r" +
"eturns an error message.\n\n virtual std::string AppendNull() = 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 AppendByteString(std::string&& val);\n virtual std::string AppendTextString(std::string&& val) = 0;\n\n // Push and Pop are called for container nodes: JSON arrays (lists) and JSON\n // objects (dictionaries).\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 DecodeJson, whether or\n // not parsing th" +
- "e input as JSON encountered an error. Even when successful,\n // trailing data may remain in input and buffer. See \"Unintuitive JSON\n // Parsing\" (https://nullprogram.com/blog/2019/12/28/) which discusses JSON\n // parsing and when it stops.\n //\n // Do not keep a reference to buffer or buffer.data.ptr after Done returns,\n // as DecodeJson may then de-allocate the backing array.\n //\n // The default Done implementation is a no-op.\n virtual void Done(DecodeJsonResult& result,\n sync_io::Input& input,\n IOBuffer& buffer);\n};\n\n// DecodeJson calls callbacks based on the JSON-formatted 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 JSON) or an input error (e.g. network failure).\nDecodeJsonResult DecodeJson(\n DecodeJsonCallbacks&& callbacks,\n sync_io::I" +
- "nput&& input,\n wuffs_base__slice_u32 quirks = wuffs_base__empty_slice_u32());\n\n} // namespace wuffs_aux\n" +
+ "e input as JSON encountered an error. Even when successful,\n // trailing data may remain in input and buffer. See \"Unintuitive JSON\n // Parsing\" (https://nullprogram.com/blog/2019/12/28/) which discusses JSON\n // parsing and when it stops.\n //\n // Do not keep a reference to buffer or buffer.data.ptr after Done returns,\n // as DecodeJson may then de-allocate the backing array.\n //\n // The default Done implementation is a no-op.\n virtual void Done(DecodeJsonResult& result,\n sync_io::Input& input,\n IOBuffer& buffer);\n};\n\nextern const char DecodeJson_BadJsonPointer[];\nextern const char DecodeJson_NoMatch[];\n\n// DecodeJson calls callbacks based on the JSON-formatted 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 JSON) or an input error (e.g. network fa" +
+ "ilure).\n//\n// json_pointer is a query in the JSON Pointer (RFC 6901) syntax. The callbacks\n// run for the input's sub-node that matches the query. DecodeJson_NoMatch is\n// returned if no matching sub-node was found. The empty query matches the\n// input's root node, consistent with JSON Pointer semantics.\n//\n// The JSON Pointer implementation is greedy: duplicate keys are not rejected\n// but only the first match for each '/'-separated fragment is followed.\nDecodeJsonResult DecodeJson(\n DecodeJsonCallbacks&& callbacks,\n sync_io::Input&& input,\n wuffs_base__slice_u32 quirks = wuffs_base__empty_slice_u32(),\n std::string json_pointer = std::string());\n\n} // namespace wuffs_aux\n" +
""
var AuxNonBaseCcFiles = []string{
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 80f5875..6e58bbc 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -8604,16 +8604,28 @@
IOBuffer& buffer);
};
+extern const char DecodeJson_BadJsonPointer[];
+extern const char DecodeJson_NoMatch[];
+
// DecodeJson calls callbacks based on the JSON-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 JSON) or an input error (e.g. network failure).
+//
+// json_pointer is a query in the JSON Pointer (RFC 6901) syntax. The callbacks
+// run for the input's sub-node that matches the query. DecodeJson_NoMatch is
+// returned if no matching sub-node was found. The empty query matches the
+// input's root node, consistent with JSON Pointer semantics.
+//
+// The JSON Pointer implementation is greedy: duplicate keys are not rejected
+// but only the first match for each '/'-separated fragment is followed.
DecodeJsonResult DecodeJson(
DecodeJsonCallbacks&& callbacks,
sync_io::Input&& input,
- wuffs_base__slice_u32 quirks = wuffs_base__empty_slice_u32());
+ wuffs_base__slice_u32 quirks = wuffs_base__empty_slice_u32(),
+ std::string json_pointer = std::string());
} // namespace wuffs_aux
@@ -29355,7 +29367,7 @@
std::string //
DecodeJsonCallbacks::AppendByteString(std::string&& val) {
- return "wuffs_aux::JsonDecoder: unexpected JSON byte string";
+ return "wuffs_aux::DecodeJson: unexpected JSON byte string";
}
void //
@@ -29363,6 +29375,10 @@
sync_io::Input& input,
IOBuffer& buffer) {}
+const char DecodeJson_BadJsonPointer[] =
+ "wuffs_aux::DecodeJson: bad JSON Pointer";
+const char DecodeJson_NoMatch[] = "wuffs_aux::DecodeJson: no match";
+
#define WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN \
while (tok_buf.meta.ri >= tok_buf.meta.wi) { \
if (tok_status.repr == nullptr) { \
@@ -29374,17 +29390,17 @@
goto done; \
} else if (cursor_index != io_buf->meta.ri) { \
ret_error_message = \
- "wuffs_aux::JsonDecoder: internal error: bad cursor_index"; \
+ "wuffs_aux::DecodeJson: internal error: bad cursor_index"; \
goto done; \
} else if (io_buf->meta.closed) { \
ret_error_message = \
- "wuffs_aux::JsonDecoder: internal error: io_buf is closed"; \
+ "wuffs_aux::DecodeJson: 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::JsonDecoder: internal error: io_buf is full"; \
+ "wuffs_aux::DecodeJson: internal error: io_buf is full"; \
goto done; \
} \
cursor_index = io_buf->meta.ri; \
@@ -29395,7 +29411,7 @@
} \
if (WUFFS_JSON__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE != 0) { \
ret_error_message = \
- "wuffs_aux::JsonDecoder: internal error: bad WORKBUF_LEN"; \
+ "wuffs_aux::DecodeJson: internal error: bad WORKBUF_LEN"; \
goto done; \
} \
wuffs_base__slice_u8 work_buf = wuffs_base__empty_slice_u8(); \
@@ -29406,16 +29422,268 @@
if ((io_buf->meta.ri < cursor_index) || \
((io_buf->meta.ri - cursor_index) < token_len)) { \
ret_error_message = \
- "wuffs_aux::JsonDecoder: internal error: bad token indexes"; \
+ "wuffs_aux::DecodeJson: internal error: bad token indexes"; \
goto done; \
} \
uint8_t* token_ptr = io_buf->data.ptr + cursor_index; \
cursor_index += token_len
+namespace {
+
+// DecodeJson_SplitJsonPointer returns ("bar", 8) for ("/foo/bar/baz/qux", 5).
+// It returns a 0 size_t when s has invalid JSON Pointer syntax.
+std::pair<std::string, size_t> //
+DecodeJson_SplitJsonPointer(std::string& s, size_t i) {
+ std::string fragment;
+ while (i < s.size()) {
+ char c = s[i];
+ if (c == '/') {
+ break;
+ } else if (c != '~') {
+ fragment.push_back(c);
+ i++;
+ continue;
+ }
+ i++;
+ if (i >= s.size()) {
+ return std::make_pair(std::string(), 0);
+ }
+ c = s[i];
+ if (c == '0') {
+ fragment.push_back('~');
+ i++;
+ continue;
+ } else if (c == '1') {
+ fragment.push_back('/');
+ i++;
+ continue;
+ }
+ return std::make_pair(std::string(), 0);
+ }
+ return std::make_pair(std::move(fragment), i);
+}
+
+std::string //
+DecodeJson_DecodeBackslashX(std::string& str,
+ uint8_t* token_ptr,
+ size_t token_len) {
+ wuffs_base__slice_u8 encoded =
+ wuffs_base__make_slice_u8(token_ptr, token_len);
+ while (encoded.len > 0) {
+ uint8_t decoded[64];
+ constexpr bool src_closed = true;
+ wuffs_base__transform__output o = wuffs_base__base_16__decode4(
+ wuffs_base__make_slice_u8(&decoded[0], sizeof decoded), encoded,
+ src_closed, WUFFS_BASE__BASE_16__DEFAULT_OPTIONS);
+ if (o.status.is_error()) {
+ return o.status.message();
+ } else if ((o.num_dst > (sizeof decoded)) || (o.num_src > encoded.len)) {
+ return "wuffs_aux::DecodeJson: internal error: inconsistent base16 "
+ "decoding";
+ }
+ str.append( // Convert from (uint8_t*).
+ static_cast<const char*>(static_cast<void*>(&decoded[0])), o.num_dst);
+ encoded.ptr += o.num_src;
+ encoded.len -= o.num_src;
+ }
+ return "";
+}
+
+std::string //
+DecodeJson_WalkJsonPointerFragment(wuffs_base__token_buffer& tok_buf,
+ wuffs_base__status& tok_status,
+ wuffs_json__decoder::unique_ptr& dec,
+ wuffs_base__io_buffer* io_buf,
+ std::string& io_error_message,
+ size_t& cursor_index,
+ sync_io::Input& input,
+ std::string& json_pointer_fragment) {
+ std::string ret_error_message;
+ while (true) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ if (vbc == WUFFS_BASE__TOKEN__VBC__FILLER) {
+ continue;
+ } else if ((vbc != WUFFS_BASE__TOKEN__VBC__STRUCTURE) ||
+ !(vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH)) {
+ return DecodeJson_NoMatch;
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST) {
+ goto do_list;
+ }
+ goto do_dict;
+ }
+
+do_dict:
+ // Alternate between these two things:
+ // 1. Decode the next dict key (a string). If it matches the fragment, we're
+ // done (success). If we've reached the dict's end (VBD__STRUCTURE__POP)
+ // so that there was no next dict key, we're done (failure).
+ // 2. Otherwise, skip the next dict value.
+ while (true) {
+ for (std::string str; true;) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ switch (vbc) {
+ case WUFFS_BASE__TOKEN__VBC__FILLER:
+ continue;
+
+ case WUFFS_BASE__TOKEN__VBC__STRUCTURE:
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {
+ goto fail;
+ }
+ return DecodeJson_NoMatch;
+
+ 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 if (
+ vbd &
+ WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_4_SRC_BACKSLASH_X) {
+ ret_error_message =
+ DecodeJson_DecodeBackslashX(str, token_ptr, token_len);
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ } else {
+ goto fail;
+ }
+ break;
+ }
+
+ 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);
+ break;
+ }
+
+ default:
+ goto fail;
+ }
+
+ if (token.continued()) {
+ continue;
+ }
+ if (str == json_pointer_fragment) {
+ return "";
+ }
+ goto skip_the_next_dict_value;
+ }
+
+ skip_the_next_dict_value:
+ for (uint32_t skip_depth = 0; true;) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ if (token.continued() || (vbc == WUFFS_BASE__TOKEN__VBC__FILLER)) {
+ continue;
+ } else if (vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {
+ skip_depth++;
+ continue;
+ }
+ skip_depth--;
+ }
+
+ if (skip_depth == 0) {
+ break;
+ }
+ } // skip_the_next_dict_value
+ } // do_dict
+
+do_list:
+ do {
+ wuffs_base__result_u64 result_u64 = wuffs_base__parse_number_u64(
+ wuffs_base__make_slice_u8(
+ static_cast<uint8_t*>(static_cast<void*>(
+ const_cast<char*>(json_pointer_fragment.data()))),
+ json_pointer_fragment.size()),
+ WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
+ if (!result_u64.status.is_ok()) {
+ return DecodeJson_NoMatch;
+ }
+ uint64_t remaining = result_u64.value;
+ if (remaining == 0) {
+ goto check_that_a_value_follows;
+ }
+ for (uint32_t skip_depth = 0; true;) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ if (token.continued() || (vbc == WUFFS_BASE__TOKEN__VBC__FILLER)) {
+ continue;
+ } else if (vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__PUSH) {
+ skip_depth++;
+ continue;
+ }
+ if (skip_depth == 0) {
+ return DecodeJson_NoMatch;
+ }
+ skip_depth--;
+ }
+
+ if (skip_depth > 0) {
+ continue;
+ }
+ remaining--;
+ if (remaining == 0) {
+ goto check_that_a_value_follows;
+ }
+ }
+ } while (false); // do_list
+
+check_that_a_value_follows:
+ while (true) {
+ WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN;
+
+ int64_t vbc = token.value_base_category();
+ uint64_t vbd = token.value_base_detail();
+ if (vbc == WUFFS_BASE__TOKEN__VBC__FILLER) {
+ continue;
+ }
+
+ // Undo the last part of WUFFS_AUX__DECODE_JSON__GET_THE_NEXT_TOKEN, so that
+ // we're only peeking at the next token.
+ tok_buf.meta.ri--;
+ cursor_index -= token_len;
+
+ if ((vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) &&
+ (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__POP)) {
+ return DecodeJson_NoMatch;
+ }
+ return "";
+ } // check_that_a_value_follows
+
+fail:
+ return "wuffs_aux::DecodeJson: internal error: unexpected token";
+done:
+ return ret_error_message;
+}
+
+} // namespace
+
DecodeJsonResult //
DecodeJson(DecodeJsonCallbacks&& callbacks,
sync_io::Input&& input,
- wuffs_base__slice_u32 quirks) {
+ wuffs_base__slice_u32 quirks,
+ std::string json_pointer) {
// 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();
@@ -29433,7 +29701,7 @@
// Prepare the low-level JSON decoder.
wuffs_json__decoder::unique_ptr dec = wuffs_json__decoder::alloc();
if (!dec) {
- ret_error_message = "wuffs_aux::JsonDecoder: out of memory";
+ ret_error_message = "wuffs_aux::DecodeJson: out of memory";
goto done;
}
for (size_t i = 0; i < quirks.len; i++) {
@@ -29451,6 +29719,27 @@
uint32_t depth = 0;
std::string str;
+ // Walk the (optional) JSON Pointer.
+ for (size_t i = 0; i < json_pointer.size();) {
+ if (json_pointer[i] != '/') {
+ ret_error_message = DecodeJson_BadJsonPointer;
+ goto done;
+ }
+ std::pair<std::string, size_t> split =
+ DecodeJson_SplitJsonPointer(json_pointer, i + 1);
+ i = std::move(split.second);
+ if (i == 0) {
+ ret_error_message = DecodeJson_BadJsonPointer;
+ goto done;
+ }
+ ret_error_message = DecodeJson_WalkJsonPointerFragment(
+ tok_buf, tok_status, dec, io_buf, io_error_message, cursor_index,
+ input, split.first);
+ if (!ret_error_message.empty()) {
+ goto done;
+ }
+ }
+
// Loop, doing these two things:
// 1. Get the next token.
// 2. Process that token.
@@ -29488,29 +29777,10 @@
} else if (
vbd &
WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_4_SRC_BACKSLASH_X) {
- wuffs_base__slice_u8 encoded =
- wuffs_base__make_slice_u8(token_ptr, token_len);
- while (encoded.len > 0) {
- uint8_t decoded[64];
- constexpr bool src_closed = true;
- wuffs_base__transform__output o = wuffs_base__base_16__decode4(
- wuffs_base__make_slice_u8(&decoded[0], sizeof decoded),
- encoded, src_closed, WUFFS_BASE__BASE_16__DEFAULT_OPTIONS);
- if (o.status.is_error()) {
- ret_error_message = o.status.message();
- goto done;
- } else if ((o.num_dst > (sizeof decoded)) ||
- (o.num_src > encoded.len)) {
- ret_error_message =
- "wuffs_aux::JsonDecoder: internal error: inconsistent "
- "base16 decoding";
- goto done;
- }
- str.append( // Convert from (uint8_t*).
- static_cast<const char*>(static_cast<void*>(&decoded[0])),
- o.num_dst);
- encoded.ptr += o.num_src;
- encoded.len -= o.num_src;
+ ret_error_message =
+ DecodeJson_DecodeBackslashX(str, token_ptr, token_len);
+ if (!ret_error_message.empty()) {
+ goto done;
}
} else {
goto fail;
@@ -29597,7 +29867,7 @@
fail:
ret_error_message =
- "wuffs_aux::JsonDecoder: internal error: unexpected token";
+ "wuffs_aux::DecodeJson: internal error: unexpected token";
goto done;
parsed_a_value: