Add aux library
diff --git a/cmd/wuffs-c/release.go b/cmd/wuffs-c/release.go
index d1e9354..89cb671 100644
--- a/cmd/wuffs-c/release.go
+++ b/cmd/wuffs-c/release.go
@@ -24,6 +24,8 @@
"sort"
"strings"
+ "github.com/google/wuffs/internal/cgen/data"
+
cf "github.com/google/wuffs/cmd/commonflags"
)
@@ -105,7 +107,16 @@
}
}
- out.Write(grImplStartsHere[1:]) // [1:] skips the initial '\n'.
+ out.WriteString("#if defined(__cplusplus) && (__cplusplus >= 201103L)\n\n")
+ out.WriteString(data.AuxBaseHh)
+ out.WriteString("\n")
+ for _, f := range data.AuxNonBaseHhFiles {
+ out.WriteString(f)
+ out.WriteString("\n")
+ }
+ out.WriteString("#endif // defined(__cplusplus) && (__cplusplus >= 201103L)\n")
+
+ out.Write(grImplStartsHere)
out.WriteString("\n")
h.seen = map[string]bool{}
@@ -115,6 +126,15 @@
}
}
+ out.WriteString("#if defined(__cplusplus) && (__cplusplus >= 201103L)\n\n")
+ out.WriteString(data.AuxBaseCc)
+ out.WriteString("\n")
+ for _, f := range data.AuxNonBaseCcFiles {
+ out.WriteString(f)
+ out.WriteString("\n")
+ }
+ out.WriteString("#endif // defined(__cplusplus) && (__cplusplus >= 201103L)\n\n")
+
out.Write(grImplEndsHere)
out.WriteString(grPragmaPop)
out.WriteString("#endif // WUFFS_INCLUDE_GUARD\n")
diff --git a/doc/changelog.md b/doc/changelog.md
index 8f04555..2554778 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -6,6 +6,7 @@
- Added `WUFFS_BASE__PIXEL_BLEND__SRC_OVER`.
- Added `WUFFS_BASE__PIXEL_FORMAT__BGR_565`.
- Added `WUFFS_CONFIG__MODULE__BASE__ETC` sub-modules.
+- Added `aux` library.
- Added `base` library support for UTF-8.
- Added `base` library support for `atoi`-like string conversion.
- Added `endwhile` syntax.
diff --git a/doc/glossary.md b/doc/glossary.md
index 33b9f3b..af399e1 100644
--- a/doc/glossary.md
+++ b/doc/glossary.md
@@ -1,5 +1,10 @@
# Glossary
+#### Auxiliary Code
+
+Additional library code that is not written in the Wuffs programming language.
+See the [auxiliary code](/doc/note/auxiliary-code.md) note for more details.
+
#### Axiom
A named rule for asserting new facts. See the
diff --git a/doc/note/auxiliary-code.md b/doc/note/auxiliary-code.md
new file mode 100644
index 0000000..136465c
--- /dev/null
+++ b/doc/note/auxiliary-code.md
@@ -0,0 +1,47 @@
+# Auxiliary Code
+
+Wuffs is a [memory-safe](/doc/note/memory-safety.md) programming language,
+achieving that safety in part because Wuffs code doesn't even have the
+*capability* to dynamically allocate and free memory. Wuffs code is also
+transpiled to C (which is very portable and easy to bind to other, higher-level
+languages), but C lacks modern conveniences like a built-in string type that's
+safe and easy to use.
+
+Wuffs' C/C++ form (a "single file library") also contains auxiliary C++ code
+(in the `wuffs_aux` namespace) that compensates for that. For example, the JSON
+decoder that is written in the memory-safe Wuffs language works with low-level
+[tokens](/doc/note/tokens.md). The high-level `wuffs_aux::DecodeJson` auxiliary
+function instead uses e.g. a `std::string` and a `double` for the JSON inputs
+`"foo\tbar"` and `0.3`. This is certainly a more *convenient* API for the
+programmer who uses Wuffs-the-library, but there are several trade-offs:
+
+- Technically, `0.99999999999999999` and `1.0` are different JSON values, and
+ the low-level Wuffs JSON decoder can distinguish them, but the high-level
+ `wuffs_aux` JSON decoder will treat them identically (as a `double`).
+- The low-level API is more modular and can work with a variety of
+ StringToDouble implementations (which convert the string `0.3` to the number
+ `0.3`). The high-level API always uses Wuffs' own StringToDouble
+ implementation, which makes one particular choice on the [binary size versus
+ runtime performance](https://github.com/google/double-conversion/issues/137)
+ frontier. Other choices are viable too, especially when integrating with an
+ existing C/C++ project that already uses another StringToDouble library.
+- In the worst case (when the JSON input is just one giant string), `wuffs_aux`
+ requires `O(N)` memory, where `N` is the input length. In comparison, the
+ [example/jsonptr](/example/jsonptr/jsonptr.cc) program, which works with the
+ low-level token API, can process arbitrarily long input in `O(1)` memory.
+- The auxiliary code is hand-written C++. It's carefully written and there's
+ [not a lot of it](/internal/cgen/aux), but unlike code written in the Wuffs
+ language, its memory-safety is not enforced by the Wuffs toolchain.
+- For simplicity, it assumes that all I/O can be performed synchronously. This
+ is trivially true if the input is already entirely in memory (e.g. as a
+ `std::vector` or a `std::string`). However, the auxiliary code should not be
+ used on e.g. the GUI main thread if it could wait for network I/O (and make
+ the GUI unresponsive). In such cases, use the low-level Wuffs API instead.
+
+Similarly, decoding an image using the written-in-Wuffs low-level API involves
+[multiple steps](/doc/note/memory-safety.md#allocation-free-apis) and the
+`wuffs_aux::DecodeImage` high-level API provides something more convenient,
+albeit with similar trade-offs.
+
+Grepping the [examples directory](/example) for `wuffs_aux` should reveal code
+examples with and without using the auxiliary code library.
diff --git a/doc/note/memory-safety.md b/doc/note/memory-safety.md
index 2072a74..0a21758 100644
--- a/doc/note/memory-safety.md
+++ b/doc/note/memory-safety.md
@@ -63,6 +63,11 @@
parses and filters [JSON](https://www.json.org/), is another example that does
useful work within a strict sandbox.
+Alternatively, Wuffs-the-library's [auxiliary
+code](/doc/note/auxiliary-code.md), augmenting the Wuffs-the-language code with
+hand-written C++ helpers, provides higher level (allocating) APIs, albeit with
+several trade-offs.
+
## Thread-Safety
diff --git a/example/jsonfindptrs/jsonfindptrs.cc b/example/jsonfindptrs/jsonfindptrs.cc
index de60115..b21ea0a 100644
--- a/example/jsonfindptrs/jsonfindptrs.cc
+++ b/example/jsonfindptrs/jsonfindptrs.cc
@@ -25,9 +25,9 @@
This program uses Wuffs' JSON decoder at a relatively high level, building
in-memory representations of JSON 'things' (e.g. numbers, strings, objects).
After the entire input has been converted, walking the tree prints the output
-(in sorted order). The core conversion mechanism is to call JsonThing::parse,
-which consumes a variable number of tokens (the output of Wuffs' JSON decoder).
-JsonThing::parse can call itself recursively, as JSON values can nest.
+(in sorted order). The wuffs_aux::DecodeJson library function converts the
+lower level token stream to higher level callbacks. This .cc file deals only
+with those callbacks, not with tokens per se.
This approach is centered around JSON things. Each JSON thing comprises one or
more JSON tokens.
@@ -61,9 +61,7 @@
#error "This C++ program requires -std=c++11 or later"
#endif
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
+#include <stdio.h>
#include <iostream>
#include <map>
@@ -86,6 +84,8 @@
// 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__JSON
#define WUFFS_CONFIG__MODULE__BASE
#define WUFFS_CONFIG__MODULE__JSON
@@ -179,6 +179,8 @@
// ----
+std::vector<uint32_t> g_quirks;
+
struct {
int remaining_argc;
char** remaining_argv;
@@ -229,7 +231,7 @@
return g_usage;
}
if (!strcmp(arg, "input-json-extra-comma")) {
- g_flags.input_json_extra_comma = true;
+ g_quirks.push_back(WUFFS_JSON__QUIRK_ALLOW_EXTRA_COMMA);
continue;
}
if (!strcmp(arg, "strict-json-pointer-syntax")) {
@@ -247,152 +249,8 @@
// ----
-#define WORK_BUFFER_ARRAY_SIZE \
- WUFFS_JSON__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE
-
-#ifndef SRC_BUFFER_ARRAY_SIZE
-#define SRC_BUFFER_ARRAY_SIZE (4 * 1024)
-#endif
-#ifndef TOKEN_BUFFER_ARRAY_SIZE
-#define TOKEN_BUFFER_ARRAY_SIZE (1 * 1024)
-#endif
-
-class TokenStream {
- public:
- struct Result {
- std::string status_msg;
- wuffs_base__token token;
- // src_data is a sub-slice of m_src (a slice is a pointer-length pair).
- // Calling TokenStream::peek or TokenStream::next may change the backing
- // array's contents, so handling a TokenStream::Result may require copying
- // this src_data slice's contents.
- wuffs_base__slice_u8 src_data;
-
- Result(std::string s)
- : status_msg(s),
- token(wuffs_base__make_token(0)),
- src_data(wuffs_base__empty_slice_u8()) {}
-
- Result(std::string s, wuffs_base__token t, wuffs_base__slice_u8 d)
- : status_msg(s), token(t), src_data(d) {}
- };
-
- TokenStream(int input_file_descriptor)
- : m_status(wuffs_base__make_status(nullptr)),
- m_src(wuffs_base__make_io_buffer(
- wuffs_base__make_slice_u8(m_src_array, SRC_BUFFER_ARRAY_SIZE),
- wuffs_base__empty_io_buffer_meta())),
- m_tok(wuffs_base__make_token_buffer(
- wuffs_base__make_slice_token(m_tok_array, TOKEN_BUFFER_ARRAY_SIZE),
- wuffs_base__empty_token_buffer_meta())),
- m_input_file_descriptor(input_file_descriptor),
- m_curr_token_end_src_index(0) {
- m_status =
- m_dec.initialize(sizeof__wuffs_json__decoder(), WUFFS_VERSION, 0);
-
- if (m_status.is_ok()) {
- // Uncomment this line to enable the WUFFS_JSON__QUIRK_ALLOW_BACKSLASH_X
- // option, discussed in a separate comment.
- // m_dec.set_quirk_enabled(WUFFS_JSON__QUIRK_ALLOW_BACKSLASH_X, true);
-
- if (g_flags.input_json_extra_comma) {
- m_dec.set_quirk_enabled(WUFFS_JSON__QUIRK_ALLOW_EXTRA_COMMA, true);
- }
- }
- }
-
- Result peek() { return peek_or_next(false); }
- Result next() { return peek_or_next(true); }
-
- private:
- Result peek_or_next(bool next) {
- while (m_tok.meta.ri >= m_tok.meta.wi) {
- if (m_status.repr == nullptr) {
- // No-op.
- } else if (m_status.repr == wuffs_base__suspension__short_read) {
- if (m_curr_token_end_src_index != m_src.meta.ri) {
- return Result(
- "TokenStream: internal error: inconsistent src indexes");
- }
- const char* z = read_src();
- m_curr_token_end_src_index = m_src.meta.ri;
- if (z) {
- return Result(z);
- }
- } else if (m_status.repr == wuffs_base__suspension__short_write) {
- m_tok.compact();
- } else {
- return Result(m_status.message());
- }
-
- m_status =
- m_dec.decode_tokens(&m_tok, &m_src,
- wuffs_base__make_slice_u8(
- m_work_buffer_array, WORK_BUFFER_ARRAY_SIZE));
- }
-
- wuffs_base__token t = m_tok.data.ptr[m_tok.meta.ri];
- size_t i = m_curr_token_end_src_index;
- uint64_t n = t.length();
- if ((m_src.meta.ri < i) || ((m_src.meta.ri - i) < n)) {
- return Result("TokenStream: internal error: inconsistent src indexes");
- }
- if (next) {
- m_tok.meta.ri++;
- m_curr_token_end_src_index += n;
- }
- return Result("", t, wuffs_base__make_slice_u8(m_src.data.ptr + i, n));
- }
-
- const char* //
- read_src() {
- if (m_src.meta.closed) {
- return "main: internal error: read requested on a closed source";
- }
- m_src.compact();
- if (m_src.meta.wi >= m_src.data.len) {
- return "main: src buffer is full";
- }
- while (true) {
- ssize_t n = read(m_input_file_descriptor, m_src.writer_pointer(),
- m_src.writer_length());
- if (n >= 0) {
- m_src.meta.wi += n;
- m_src.meta.closed = n == 0;
- break;
- } else if (errno != EINTR) {
- return strerror(errno);
- }
- }
- return nullptr;
- }
-
- wuffs_base__status m_status;
- wuffs_base__io_buffer m_src;
- wuffs_base__token_buffer m_tok;
- int m_input_file_descriptor;
- // m_curr_token_end_src_index is the m_src.data.ptr index of the end of the
- // current token. An invariant is that (m_curr_token_end_src_index <=
- // m_src.meta.ri).
- size_t m_curr_token_end_src_index;
-
- wuffs_base__token m_tok_array[TOKEN_BUFFER_ARRAY_SIZE];
- uint8_t m_src_array[SRC_BUFFER_ARRAY_SIZE];
-#if WORK_BUFFER_ARRAY_SIZE > 0
- uint8_t m_work_buffer_array[WORK_BUFFER_ARRAY_SIZE];
-#else
- // Not all C/C++ compilers support 0-length arrays.
- uint8_t m_work_buffer_array[1];
-#endif
- wuffs_json__decoder m_dec;
-};
-
-// ----
-
class JsonThing {
public:
- struct Result;
-
using Vector = std::vector<JsonThing>;
// We use a std::map in this example program to avoid dependencies outside of
@@ -421,289 +279,8 @@
Vector a;
Map o;
} value;
-
- static JsonThing::Result parse(TokenStream& ts);
-
- private:
- static JsonThing::Result parse_array(TokenStream& ts);
- static JsonThing::Result parse_literal(TokenStream::Result tsr);
- static JsonThing::Result parse_number(TokenStream::Result tsr);
- static JsonThing::Result parse_object(TokenStream& ts);
- static JsonThing::Result parse_string(TokenStream& ts,
- TokenStream::Result tsr);
};
-struct JsonThing::Result {
- std::string status_msg;
- JsonThing thing;
-
- Result(std::string s) : status_msg(s), thing(JsonThing()) {}
-
- Result(std::string s, JsonThing t) : status_msg(s), thing(t) {}
-};
-
-JsonThing::Result //
-JsonThing::parse(TokenStream& ts) {
- while (true) {
- TokenStream::Result tsr = ts.next();
- if (!tsr.status_msg.empty()) {
- return Result(std::move(tsr.status_msg));
- }
-
- int64_t vbc = tsr.token.value_base_category();
- uint64_t vbd = tsr.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) {
- if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST) {
- return parse_array(ts);
- } else if (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_DICT) {
- return parse_object(ts);
- }
- }
- break;
- case WUFFS_BASE__TOKEN__VBC__STRING:
- return parse_string(ts, tsr);
- case WUFFS_BASE__TOKEN__VBC__LITERAL:
- return parse_literal(tsr);
- case WUFFS_BASE__TOKEN__VBC__NUMBER:
- return parse_number(tsr);
- }
-
- return Result("main: internal error: unexpected token");
- }
-}
-
-JsonThing::Result //
-JsonThing::parse_array(TokenStream& ts) {
- JsonThing jt;
- jt.kind = Kind::Array;
- while (true) {
- TokenStream::Result tsr = ts.peek();
- if (!tsr.status_msg.empty()) {
- return Result(std::move(tsr.status_msg));
- }
- int64_t vbc = tsr.token.value_base_category();
- uint64_t vbd = tsr.token.value_base_detail();
- if (vbc == WUFFS_BASE__TOKEN__VBC__FILLER) {
- ts.next();
- continue;
- } else if ((vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) &&
- (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__POP)) {
- ts.next();
- break;
- }
-
- JsonThing::Result jtr = JsonThing::parse(ts);
- if (!jtr.status_msg.empty()) {
- return Result(std::move(jtr.status_msg));
- }
- jt.value.a.push_back(std::move(jtr.thing));
- }
- return Result("", jt);
-}
-
-JsonThing::Result //
-JsonThing::parse_literal(TokenStream::Result tsr) {
- uint64_t vbd = tsr.token.value_base_detail();
- if (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__NULL) {
- JsonThing jt;
- jt.kind = Kind::Null;
- return Result("", jt);
- } else if (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__FALSE) {
- JsonThing jt;
- jt.kind = Kind::Bool;
- jt.value.b = false;
- return Result("", jt);
- } else if (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__TRUE) {
- JsonThing jt;
- jt.kind = Kind::Bool;
- jt.value.b = true;
- return Result("", jt);
- }
- return Result("main: internal error: unexpected token");
-}
-
-JsonThing::Result //
-JsonThing::parse_number(TokenStream::Result tsr) {
- // Parsing the number from its string representation (converting from "123"
- // to 123) isn't necessary for the jsonfindptrs program, but if you're
- // copy/pasting this JsonThing code, here's how to do it.
- uint64_t vbd = tsr.token.value_base_detail();
- if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT) {
- if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED) {
- static constexpr int64_t m = 0x001FFFFFFFFFFFFF; // ((1<<53) - 1).
- wuffs_base__result_i64 r = wuffs_base__parse_number_i64(
- tsr.src_data, WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
- if (!r.status.is_ok()) {
- return Result(r.status.message());
- } else if ((r.value < -m) || (+m < r.value)) {
- return Result(wuffs_base__error__out_of_bounds);
- }
- JsonThing jt;
- jt.kind = Kind::Int64;
- jt.value.i = r.value;
- return Result("", jt);
- } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT) {
- wuffs_base__result_f64 r = wuffs_base__parse_number_f64(
- tsr.src_data, WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
- if (!r.status.is_ok()) {
- return Result(r.status.message());
- }
- JsonThing jt;
- jt.kind = Kind::Float64;
- jt.value.f = r.value;
- return Result("", jt);
- }
- }
- return Result("main: internal error: unexpected number");
-}
-
-JsonThing::Result //
-JsonThing::parse_object(TokenStream& ts) {
- JsonThing jt;
- jt.kind = Kind::Object;
-
- std::string key;
- bool have_key = false;
-
- while (true) {
- TokenStream::Result tsr = ts.peek();
- if (!tsr.status_msg.empty()) {
- return Result(std::move(tsr.status_msg));
- }
- int64_t vbc = tsr.token.value_base_category();
- uint64_t vbd = tsr.token.value_base_detail();
- if (vbc == WUFFS_BASE__TOKEN__VBC__FILLER) {
- ts.next();
- continue;
- } else if ((vbc == WUFFS_BASE__TOKEN__VBC__STRUCTURE) &&
- (vbd & WUFFS_BASE__TOKEN__VBD__STRUCTURE__POP)) {
- ts.next();
- break;
- }
-
- JsonThing::Result jtr = JsonThing::parse(ts);
- if (!jtr.status_msg.empty()) {
- return Result(std::move(jtr.status_msg));
- }
-
- if (have_key) {
- have_key = false;
- auto iter = jt.value.o.find(key);
- if (iter == jt.value.o.end()) {
- jt.value.o.insert(
- iter, Map::value_type(std::move(key), std::move(jtr.thing)));
- } else {
- return Result("main: duplicate key: " + key);
- }
- } else if (jtr.thing.kind == Kind::String) {
- have_key = true;
- key = std::move(jtr.thing.value.s);
- } else {
- return Result("main: internal error: unexpected non-string key");
- }
- }
- if (have_key) {
- return Result("main: internal error: unpaired key");
- }
- return Result("", jt);
-}
-
-JsonThing::Result //
-JsonThing::parse_string(TokenStream& ts, TokenStream::Result tsr) {
- JsonThing jt;
- jt.kind = Kind::String;
- while (true) {
- int64_t vbc = tsr.token.value_base_category();
- uint64_t vbd = tsr.token.value_base_detail();
-
- switch (vbc) {
- 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*>(tsr.src_data.ptr));
- jt.value.s.append(ptr, tsr.src_data.len);
-
- } else if (
- vbd &
- WUFFS_BASE__TOKEN__VBD__STRING__CONVERT_1_DST_4_SRC_BACKSLASH_X) {
- // We shouldn't get here unless we enable the
- // WUFFS_JSON__QUIRK_ALLOW_BACKSLASH_X option. The jsonfindptrs
- // program doesn't enable that by default, but if you're copy/pasting
- // this JsonThing code and your program does enable that option,
- // here's how to handle it.
- //
- // As per the quirk documentation, there are two options for how to
- // interpret a backslash-x: as a byte or as a Unicode code point.
- // This implementation chooses as a byte.
- wuffs_base__slice_u8 encoded = tsr.src_data;
- if (encoded.len & 3) {
- return Result(
- "main: internal error: \\x token length not a multiple of 4",
- JsonThing());
- }
- while (encoded.len) {
- uint8_t decoded[64];
- const bool src_closed = true;
- wuffs_base__transform__output o = wuffs_base__base_16__decode4(
- wuffs_base__make_slice_u8(&decoded[0], 64), encoded, src_closed,
- WUFFS_BASE__BASE_16__DEFAULT_OPTIONS);
- if (o.status.is_error()) {
- return Result(o.status.message(), JsonThing());
- } else if ((o.num_dst > 64) || (o.num_src > encoded.len)) {
- return Result(
- "main: internal error: inconsistent hexadecimal decoding",
- JsonThing());
- }
- const char* ptr = // Convert from (uint8_t*).
- static_cast<const char*>(static_cast<void*>(&decoded[0]));
- jt.value.s.append(ptr, o.num_dst);
- encoded.ptr += o.num_src;
- encoded.len -= o.num_src;
- }
-
- } else {
- return Result(
- "main: internal error: unexpected string-token conversion",
- JsonThing());
- }
- 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),
- vbd);
- const char* ptr = // Convert from (uint8_t*).
- static_cast<const char*>(static_cast<void*>(&u[0]));
- jt.value.s.append(ptr, n);
- break;
- }
-
- default:
- return Result("main: internal error: unexpected token");
- }
-
- if (!tsr.token.continued()) {
- break;
- }
- tsr = ts.next();
- if (!tsr.status_msg.empty()) {
- return Result(std::move(tsr.status_msg));
- }
- }
- return Result("", jt);
-}
-
// ----
std::string //
@@ -776,27 +353,146 @@
return "";
}
+// ----
+
+class Callbacks : public wuffs_aux::DecodeJsonCallbacks {
+ public:
+ struct Entry {
+ Entry(JsonThing&& jt)
+ : thing(std::move(jt)), has_map_key(false), map_key() {}
+
+ JsonThing thing;
+ bool has_map_key;
+ std::string map_key;
+ };
+
+ Callbacks() = default;
+
+ std::string Append(JsonThing&& jt) {
+ if (m_stack.empty()) {
+ m_stack.push_back(Entry(std::move(jt)));
+ return "";
+ }
+ Entry& top = m_stack.back();
+ switch (top.thing.kind) {
+ case JsonThing::Kind::Array:
+ top.thing.value.a.push_back(std::move(jt));
+ return "";
+ case JsonThing::Kind::Object:
+ if (top.has_map_key) {
+ top.has_map_key = false;
+ auto iter = top.thing.value.o.find(top.map_key);
+ if (iter != top.thing.value.o.end()) {
+ return "main: duplicate key: " + top.map_key;
+ }
+ top.thing.value.o.insert(
+ iter, JsonThing::Map::value_type(std::move(top.map_key),
+ std::move(jt)));
+ return "";
+ } else if (jt.kind == JsonThing::Kind::String) {
+ top.has_map_key = true;
+ top.map_key = std::move(jt.value.s);
+ return "";
+ }
+ return "main: internal error: non-string map key";
+ }
+ return "main: internal error: non-container stack entry";
+ }
+
+ virtual std::string AppendNull() {
+ JsonThing jt;
+ jt.kind = JsonThing::Kind::Null;
+ return Append(std::move(jt));
+ }
+
+ virtual std::string AppendBool(bool val) {
+ JsonThing jt;
+ jt.kind = JsonThing::Kind::Bool;
+ jt.value.b = val;
+ return Append(std::move(jt));
+ }
+
+ virtual std::string AppendI64(int64_t val) {
+ JsonThing jt;
+ jt.kind = JsonThing::Kind::Int64;
+ jt.value.i = val;
+ return Append(std::move(jt));
+ }
+
+ virtual std::string AppendF64(double val) {
+ JsonThing jt;
+ jt.kind = JsonThing::Kind::Float64;
+ jt.value.f = val;
+ return Append(std::move(jt));
+ }
+
+ virtual std::string AppendString(std::string&& val) {
+ JsonThing jt;
+ jt.kind = JsonThing::Kind::String;
+ jt.value.s = std::move(val);
+ return Append(std::move(jt));
+ }
+
+ virtual std::string Push(uint32_t flags) {
+ if (flags & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_LIST) {
+ JsonThing jt;
+ jt.kind = JsonThing::Kind::Array;
+ m_stack.push_back(std::move(jt));
+ return "";
+ } else if (flags & WUFFS_BASE__TOKEN__VBD__STRUCTURE__TO_DICT) {
+ JsonThing jt;
+ jt.kind = JsonThing::Kind::Object;
+ m_stack.push_back(std::move(jt));
+ return "";
+ }
+ return "main: internal error: bad push";
+ }
+
+ virtual std::string Pop(uint32_t flags) {
+ if (m_stack.empty()) {
+ return "main: internal error: bad pop";
+ }
+ JsonThing jt = std::move(m_stack.back().thing);
+ m_stack.pop_back();
+ return Append(std::move(jt));
+ }
+
+ virtual void Done(wuffs_aux::DecodeJsonResult& result,
+ wuffs_aux::sync_io::Input& input,
+ wuffs_aux::IOBuffer& buffer) {
+ if (!result.error_message.empty()) {
+ return;
+ } else if (m_stack.size() != 1) {
+ result.error_message = "main: internal error: bad depth";
+ return;
+ }
+ result.error_message = print_json_pointers(m_stack.back().thing, "", 0);
+ }
+
+ private:
+ std::vector<Entry> m_stack;
+};
+
+// ----
+
std::string //
main1(int argc, char** argv) {
TRY(parse_flags(argc, argv));
- int input_file_descriptor = 0; // A 0 default means stdin.
+ FILE* in = stdin;
if (g_flags.remaining_argc > 1) {
return g_usage;
} else if (g_flags.remaining_argc == 1) {
- const char* arg = g_flags.remaining_argv[0];
- input_file_descriptor = open(arg, O_RDONLY);
- if (input_file_descriptor < 0) {
- return std::string("main: cannot read ") + arg + ": " + strerror(errno);
+ in = fopen(g_flags.remaining_argv[0], "r");
+ if (!in) {
+ return std::string("main: cannot read input file");
}
}
- TokenStream ts(input_file_descriptor);
- JsonThing::Result jtr = JsonThing::parse(ts);
- if (!jtr.status_msg.empty()) {
- return jtr.status_msg;
- }
- return print_json_pointers(jtr.thing, "", 0);
+ return wuffs_aux::DecodeJson(
+ Callbacks(), wuffs_aux::sync_io::FileInput(in),
+ wuffs_base__make_slice_u32(g_quirks.data(), g_quirks.size()))
+ .error_message;
}
// ----
diff --git a/internal/cgen/aux/base.cc b/internal/cgen/aux/base.cc
new file mode 100644
index 0000000..73e643a
--- /dev/null
+++ b/internal/cgen/aux/base.cc
@@ -0,0 +1,85 @@
+// 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 - Base
+
+// Auxiliary code is discussed at
+// https://github.com/google/wuffs/blob/master/doc/note/auxiliary-code.md
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__BASE)
+
+namespace wuffs_aux {
+
+namespace sync_io {
+
+// --------
+
+IOBuffer* //
+Input::BringsItsOwnIOBuffer() {
+ return nullptr;
+}
+
+// --------
+
+FileInput::FileInput(FILE* f) : m_f(f) {}
+
+std::string //
+FileInput::CopyIn(IOBuffer* dst) {
+ if (!m_f) {
+ return "wuffs_aux::sync_io::FileInput: nullptr file";
+ } else if (dst && !dst->meta.closed) {
+ size_t n = fread(dst->writer_pointer(), 1, dst->writer_length(), m_f);
+ dst->meta.wi += n;
+ dst->meta.closed = feof(m_f);
+ if (ferror(m_f)) {
+ return "wuffs_aux::sync_io::FileInput: error reading file";
+ }
+ }
+ return "";
+}
+
+// --------
+
+MemoryInput::MemoryInput(const uint8_t* ptr, size_t len)
+ : m_io(wuffs_base__ptr_u8__reader(const_cast<uint8_t*>(ptr), len, true)) {}
+
+IOBuffer* //
+MemoryInput::BringsItsOwnIOBuffer() {
+ return &m_io;
+}
+
+std::string //
+MemoryInput::CopyIn(IOBuffer* dst) {
+ if (dst && !dst->meta.closed && (dst != &m_io)) {
+ size_t nd = dst->writer_length();
+ size_t ns = m_io.reader_length();
+ size_t n = (nd < ns) ? nd : ns;
+ memcpy(dst->writer_pointer(), m_io.reader_pointer(), n);
+ m_io.meta.ri += n;
+ dst->meta.wi += n;
+ dst->meta.closed = m_io.reader_length() == 0;
+ }
+ return "";
+}
+
+// --------
+
+} // namespace sync_io
+
+} // namespace wuffs_aux
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__AUX__BASE)
diff --git a/internal/cgen/aux/base.hh b/internal/cgen/aux/base.hh
new file mode 100644
index 0000000..276ad8e
--- /dev/null
+++ b/internal/cgen/aux/base.hh
@@ -0,0 +1,88 @@
+// 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 - Base
+
+// Auxiliary code is discussed at
+// https://github.com/google/wuffs/blob/master/doc/note/auxiliary-code.md
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__BASE)
+
+#include <stdio.h>
+
+#include <string>
+
+namespace wuffs_aux {
+
+using IOBuffer = wuffs_base__io_buffer;
+
+namespace sync_io {
+
+// --------
+
+class Input {
+ public:
+ virtual IOBuffer* BringsItsOwnIOBuffer();
+ virtual std::string CopyIn(IOBuffer* dst) = 0;
+};
+
+// --------
+
+// FileInput is an Input that reads from a file source.
+//
+// It does not take responsibility for closing the file when done.
+class FileInput : public Input {
+ public:
+ FileInput(FILE* f);
+
+ virtual std::string CopyIn(IOBuffer* dst);
+
+ private:
+ FILE* m_f;
+
+ // Delete the copy and assign constructors.
+ FileInput(const FileInput&) = delete;
+ FileInput& operator=(const FileInput&) = delete;
+};
+
+// --------
+
+// MemoryInput is an Input that reads from an in-memory source.
+//
+// It does not take responsibility for freeing the memory when done.
+class MemoryInput : public Input {
+ public:
+ MemoryInput(const uint8_t* ptr, size_t len);
+
+ virtual IOBuffer* BringsItsOwnIOBuffer();
+ virtual std::string CopyIn(IOBuffer* dst);
+
+ private:
+ IOBuffer m_io;
+
+ // Delete the copy and assign constructors.
+ MemoryInput(const MemoryInput&) = delete;
+ MemoryInput& operator=(const MemoryInput&) = delete;
+};
+
+// --------
+
+} // namespace sync_io
+
+} // namespace wuffs_aux
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__AUX__BASE)
diff --git a/internal/cgen/aux/json.cc b/internal/cgen/aux/json.cc
new file mode 100644
index 0000000..f2edd02
--- /dev/null
+++ b/internal/cgen/aux/json.cc
@@ -0,0 +1,259 @@
+// 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 - JSON
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__JSON)
+
+#include <utility>
+
+namespace wuffs_aux {
+
+DecodeJsonResult::DecodeJsonResult(std::string&& error_message0,
+ uint64_t cursor_position0)
+ : error_message(std::move(error_message0)),
+ cursor_position(cursor_position0) {}
+
+DecodeJsonResult //
+DecodeJson(DecodeJsonCallbacks&& 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 JSON decoder.
+ wuffs_json__decoder::unique_ptr dec = wuffs_json__decoder::alloc();
+ if (!dec) {
+ ret_error_message = "wuffs_aux::JsonDecoder: 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;
+
+ // 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::JsonDecoder: 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";
+ 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";
+ 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_JSON__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE != 0) {
+ ret_error_message =
+ "wuffs_aux::JsonDecoder: 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::JsonDecoder: 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.
+
+ 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) {
+ 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 = callbacks.AppendString(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: {
+ ret_error_message =
+ (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__NULL)
+ ? callbacks.AppendNull()
+ : callbacks.AppendBool(vbd &
+ WUFFS_BASE__TOKEN__VBD__LITERAL__TRUE);
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__NUMBER: {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT) {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED) {
+ wuffs_base__result_i64 r = wuffs_base__parse_number_i64(
+ wuffs_base__make_slice_u8(token_ptr, token_len),
+ WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
+ if (r.status.is_ok()) {
+ ret_error_message = callbacks.AppendI64(r.value);
+ goto parsed_a_value;
+ }
+ }
+ if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT) {
+ wuffs_base__result_f64 r = wuffs_base__parse_number_f64(
+ wuffs_base__make_slice_u8(token_ptr, token_len),
+ WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
+ if (r.status.is_ok()) {
+ ret_error_message = callbacks.AppendF64(r.value);
+ goto parsed_a_value;
+ }
+ }
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_INF) {
+ ret_error_message = callbacks.AppendF64(
+ wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ 0xFFF0000000000000ul));
+ goto parsed_a_value;
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_INF) {
+ ret_error_message = callbacks.AppendF64(
+ wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ 0x7FF0000000000000ul));
+ goto parsed_a_value;
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_NAN) {
+ ret_error_message = callbacks.AppendF64(
+ wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ 0xFFFFFFFFFFFFFFFFul));
+ goto parsed_a_value;
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_NAN) {
+ ret_error_message = callbacks.AppendF64(
+ wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ 0x7FFFFFFFFFFFFFFFul));
+ goto parsed_a_value;
+ }
+ }
+ }
+
+ fail:
+ ret_error_message =
+ "wuffs_aux::JsonDecoder: internal error: unexpected token";
+ goto done;
+
+ parsed_a_value:
+ if (!ret_error_message.empty() || (depth == 0)) {
+ goto done;
+ }
+ }
+ } while (false);
+
+done:
+ DecodeJsonResult 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__JSON)
diff --git a/internal/cgen/aux/json.hh b/internal/cgen/aux/json.hh
new file mode 100644
index 0000000..cab47bf
--- /dev/null
+++ b/internal/cgen/aux/json.hh
@@ -0,0 +1,83 @@
+// 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 - JSON
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__JSON)
+
+namespace wuffs_aux {
+
+struct DecodeJsonResult {
+ DecodeJsonResult(std::string&& error_message0, uint64_t cursor_position0);
+
+ std::string error_message;
+ uint64_t cursor_position;
+};
+
+class DecodeJsonCallbacks {
+ public:
+ // AppendXxx are called for leaf nodes: literals, numbers and strings. For
+ // strings, the Callbacks implementation is responsible for tracking map keys
+ // versus other values.
+
+ virtual std::string AppendNull() = 0;
+ virtual std::string AppendBool(bool val) = 0;
+ virtual std::string AppendI64(int64_t val) = 0;
+ virtual std::string AppendF64(double val) = 0;
+ virtual std::string AppendString(std::string&& val) = 0;
+
+ // Push and Pop are called for container nodes: JSON arrays (lists) and JSON
+ // objects (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 DecodeJson, whether or
+ // not parsing the input as JSON encountered an error. Even when successful,
+ // trailing data may remain in input and buffer. See "Unintuitive JSON
+ // Parsing" (https://nullprogram.com/blog/2019/12/28/) which discusses JSON
+ // parsing and when it stops.
+ //
+ // Do not keep a reference to buffer or buffer.data.ptr after Done returns,
+ // as DecodeJson may then de-allocate the backing array.
+ virtual void Done(DecodeJsonResult& result,
+ sync_io::Input& input,
+ IOBuffer& buffer) = 0;
+};
+
+// 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).
+DecodeJsonResult DecodeJson(DecodeJsonCallbacks&& callbacks,
+ sync_io::Input&& input,
+ wuffs_base__slice_u32 quirks);
+
+} // namespace wuffs_aux
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__AUX__JSON)
diff --git a/internal/cgen/data/data.go b/internal/cgen/data/data.go
index 5023301..e09a71f 100644
--- a/internal/cgen/data/data.go
+++ b/internal/cgen/data/data.go
@@ -599,6 +599,56 @@
"mize this by manually inlining the\n // wuffs_base__utf_8__next calls.\n size_t original_len = s_len;\n while (s_len > 0) {\n wuffs_base__utf_8__next__output o = wuffs_base__utf_8__next(s_ptr, s_len);\n if ((o.code_point > 0x7F) && (o.byte_length == 1)) {\n break;\n }\n s_ptr += o.byte_length;\n s_len -= o.byte_length;\n }\n return original_len - s_len;\n}\n\nWUFFS_BASE__MAYBE_STATIC size_t //\nwuffs_base__ascii__longest_valid_prefix(const uint8_t* s_ptr, size_t s_len) {\n // TODO: possibly optimize this by checking 4 or 8 bytes at a time.\n const uint8_t* original_ptr = s_ptr;\n const uint8_t* p = s_ptr;\n const uint8_t* q = s_ptr + s_len;\n for (; (p != q) && ((*p & 0x80) == 0); p++) {\n }\n return (size_t)(p - original_ptr);\n}\n" +
""
+const AuxBaseCc = "" +
+ "// ---------------- Auxiliary - Base\n\n// Auxiliary code is discussed at\n// https://github.com/google/wuffs/blob/master/doc/note/auxiliary-code.md\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__BASE)\n\nnamespace wuffs_aux {\n\nnamespace sync_io {\n\n" +
+ "" +
+ "// --------\n\nIOBuffer* //\nInput::BringsItsOwnIOBuffer() {\n return nullptr;\n}\n\n" +
+ "" +
+ "// --------\n\nFileInput::FileInput(FILE* f) : m_f(f) {}\n\nstd::string //\nFileInput::CopyIn(IOBuffer* dst) {\n if (!m_f) {\n return \"wuffs_aux::sync_io::FileInput: nullptr file\";\n } else if (dst && !dst->meta.closed) {\n size_t n = fread(dst->writer_pointer(), 1, dst->writer_length(), m_f);\n dst->meta.wi += n;\n dst->meta.closed = feof(m_f);\n if (ferror(m_f)) {\n return \"wuffs_aux::sync_io::FileInput: error reading file\";\n }\n }\n return \"\";\n}\n\n" +
+ "" +
+ "// --------\n\nMemoryInput::MemoryInput(const uint8_t* ptr, size_t len)\n : m_io(wuffs_base__ptr_u8__reader(const_cast<uint8_t*>(ptr), len, true)) {}\n\nIOBuffer* //\nMemoryInput::BringsItsOwnIOBuffer() {\n return &m_io;\n}\n\nstd::string //\nMemoryInput::CopyIn(IOBuffer* dst) {\n if (dst && !dst->meta.closed && (dst != &m_io)) {\n size_t nd = dst->writer_length();\n size_t ns = m_io.reader_length();\n size_t n = (nd < ns) ? nd : ns;\n memcpy(dst->writer_pointer(), m_io.reader_pointer(), n);\n m_io.meta.ri += n;\n dst->meta.wi += n;\n dst->meta.closed = m_io.reader_length() == 0;\n }\n return \"\";\n}\n\n" +
+ "" +
+ "// --------\n\n} // namespace sync_io\n\n} // namespace wuffs_aux\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__AUX__BASE)\n" +
+ ""
+
+const AuxBaseHh = "" +
+ "// ---------------- Auxiliary - Base\n\n// Auxiliary code is discussed at\n// https://github.com/google/wuffs/blob/master/doc/note/auxiliary-code.md\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__BASE)\n\n#include <stdio.h>\n\n#include <string>\n\nnamespace wuffs_aux {\n\nusing IOBuffer = wuffs_base__io_buffer;\n\nnamespace sync_io {\n\n" +
+ "" +
+ "// --------\n\nclass Input {\n public:\n virtual IOBuffer* BringsItsOwnIOBuffer();\n virtual std::string CopyIn(IOBuffer* dst) = 0;\n};\n\n" +
+ "" +
+ "// --------\n\n// FileInput is an Input that reads from a file source.\n//\n// It does not take responsibility for closing the file when done.\nclass FileInput : public Input {\n public:\n FileInput(FILE* f);\n\n virtual std::string CopyIn(IOBuffer* dst);\n\n private:\n FILE* m_f;\n\n // Delete the copy and assign constructors.\n FileInput(const FileInput&) = delete;\n FileInput& operator=(const FileInput&) = delete;\n};\n\n" +
+ "" +
+ "// --------\n\n// MemoryInput is an Input that reads from an in-memory source.\n//\n// It does not take responsibility for freeing the memory when done.\nclass MemoryInput : public Input {\n public:\n MemoryInput(const uint8_t* ptr, size_t len);\n\n virtual IOBuffer* BringsItsOwnIOBuffer();\n virtual std::string CopyIn(IOBuffer* dst);\n\n private:\n IOBuffer m_io;\n\n // Delete the copy and assign constructors.\n MemoryInput(const MemoryInput&) = delete;\n MemoryInput& operator=(const MemoryInput&) = delete;\n};\n\n" +
+ "" +
+ "// --------\n\n} // namespace sync_io\n\n} // namespace wuffs_aux\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__AUX__BASE)\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" +
+ "tus.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::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->compact();\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->me" +
+ "ta.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_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\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\n // 2. Process that token.\n\n int64_t vbc = token.value_base_c" +
+ "ategory();\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 {\n goto fail;\n }\n if (token.continued()) {\n continue;\n }\n ret_error_message = callbacks.AppendString(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.AppendN" +
+ "ull()\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_base__parse_number_f64(\n wuffs_base__make_slice_u8(token_ptr, token_len),\n WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIO" +
+ "NS);\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 }\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} // namespace wuffs_aux\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__AUX__JSON)\n" +
+ ""
+
+const AuxJsonHh = "" +
+ "// ---------------- Auxiliary - JSON\n\n#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__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 virtual std::string AppendNull() = 0;\n virtual std::string AppendBool(bool val) = 0;\n virtual std::string AppendI64(int64_t val) = 0;\n virtual std::string AppendF64(double val) = 0;\n virtual std::string AppendString(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__STRUCTU" +
+ "RE__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 the 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 virtual void Done(DecodeJsonResult& result,\n sync_io::Input& input,\n IOBuffer& buffer) = 0;\n};\n\n// DecodeJson calls callbacks based on the JSON-formatted data i" +
+ "n 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(DecodeJsonCallbacks&& callbacks,\n sync_io::Input&& input,\n wuffs_base__slice_u32 quirks);\n\n} // namespace wuffs_aux\n\n#endif // !defined(WUFFS_CONFIG__MODULES) ||\n // defined(WUFFS_CONFIG__MODULE__AUX__JSON)\n" +
+ ""
+
+var AuxNonBaseCcFiles = []string{
+ AuxJsonCc,
+}
+
+var AuxNonBaseHhFiles = []string{
+ AuxJsonHh,
+}
+
const BaseCopyright = "" +
"// Copyright 2017 The Wuffs Authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n" +
""
diff --git a/internal/cgen/data/gen.go b/internal/cgen/data/gen.go
index ded8936..6f286fa 100644
--- a/internal/cgen/data/gen.go
+++ b/internal/cgen/data/gen.go
@@ -26,6 +26,7 @@
"go/format"
"io/ioutil"
"os"
+ "strings"
)
const columns = 1024
@@ -58,7 +59,7 @@
out.WriteString("package data\n")
out.WriteString("\n")
- if err := genBase(out); err != nil {
+ if err := genData(out); err != nil {
return err
}
@@ -69,7 +70,7 @@
return ioutil.WriteFile("data.go", formatted, 0644)
}
-func genBase(out *bytes.Buffer) error {
+func genData(out *bytes.Buffer) error {
files := []struct {
filename, varname string
}{
@@ -95,6 +96,11 @@
{"../base/intconv-submodule.c", "BaseIntConvSubmoduleC"},
{"../base/pixconv-submodule.c", "BasePixConvSubmoduleC"},
{"../base/utf8-submodule.c", "BaseUTF8SubmoduleC"},
+
+ {"../aux/base.cc", "AuxBaseCc"},
+ {"../aux/base.hh", "AuxBaseHh"},
+ {"../aux/json.cc", "AuxJsonCc"},
+ {"../aux/json.hh", "AuxJsonHh"},
}
prefixAfterEditing := []byte("// After editing this file,")
@@ -130,6 +136,26 @@
out.WriteString("\"\"\n\n")
}
+ fmt.Fprintf(out, "var AuxNonBaseCcFiles = []string{\n")
+ for _, f := range files {
+ if strings.HasPrefix(f.varname, "Aux") &&
+ strings.HasSuffix(f.varname, "Cc") &&
+ (f.varname != "AuxBaseCc") {
+ fmt.Fprintf(out, "%s,\n", f.varname)
+ }
+ }
+ fmt.Fprintf(out, "}\n\n")
+
+ fmt.Fprintf(out, "var AuxNonBaseHhFiles = []string{\n")
+ for _, f := range files {
+ if strings.HasPrefix(f.varname, "Aux") &&
+ strings.HasSuffix(f.varname, "Hh") &&
+ (f.varname != "AuxBaseHh") {
+ fmt.Fprintf(out, "%s,\n", f.varname)
+ }
+ }
+ fmt.Fprintf(out, "}\n\n")
+
fmt.Fprintf(out, "const BaseCopyright = \"\" +\n")
writeStringConst(out, copyright)
out.WriteString("\"\"\n\n")
diff --git a/internal/cgen/data/placeholder.go b/internal/cgen/data/placeholder.go
index 83661d6..645c5ff 100644
--- a/internal/cgen/data/placeholder.go
+++ b/internal/cgen/data/placeholder.go
@@ -14,7 +14,8 @@
//go:generate go run gen.go
-// Package data contains the files in the sibling "../base" directory: the
-// hand-written C code that is the 'runtime library' for Wuffs-transpiled-to-C.
-// In this Go package, those files' contents are provided as string constants.
+// Package data contains the files in the sibling "../base" and "../aux"
+// directories: the hand-written C and C++ code that is the 'runtime library'
+// for Wuffs-transpiled-to-C. In this Go package, those files' contents are
+// provided as string constants.
package data
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 0b7a5d0..bb12c67 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -8281,6 +8281,151 @@
} // extern "C"
#endif
+#if defined(__cplusplus) && (__cplusplus >= 201103L)
+
+// ---------------- Auxiliary - Base
+
+// Auxiliary code is discussed at
+// https://github.com/google/wuffs/blob/master/doc/note/auxiliary-code.md
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__BASE)
+
+#include <stdio.h>
+
+#include <string>
+
+namespace wuffs_aux {
+
+using IOBuffer = wuffs_base__io_buffer;
+
+namespace sync_io {
+
+// --------
+
+class Input {
+ public:
+ virtual IOBuffer* BringsItsOwnIOBuffer();
+ virtual std::string CopyIn(IOBuffer* dst) = 0;
+};
+
+// --------
+
+// FileInput is an Input that reads from a file source.
+//
+// It does not take responsibility for closing the file when done.
+class FileInput : public Input {
+ public:
+ FileInput(FILE* f);
+
+ virtual std::string CopyIn(IOBuffer* dst);
+
+ private:
+ FILE* m_f;
+
+ // Delete the copy and assign constructors.
+ FileInput(const FileInput&) = delete;
+ FileInput& operator=(const FileInput&) = delete;
+};
+
+// --------
+
+// MemoryInput is an Input that reads from an in-memory source.
+//
+// It does not take responsibility for freeing the memory when done.
+class MemoryInput : public Input {
+ public:
+ MemoryInput(const uint8_t* ptr, size_t len);
+
+ virtual IOBuffer* BringsItsOwnIOBuffer();
+ virtual std::string CopyIn(IOBuffer* dst);
+
+ private:
+ IOBuffer m_io;
+
+ // Delete the copy and assign constructors.
+ MemoryInput(const MemoryInput&) = delete;
+ MemoryInput& operator=(const MemoryInput&) = delete;
+};
+
+// --------
+
+} // namespace sync_io
+
+} // namespace wuffs_aux
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__AUX__BASE)
+
+// ---------------- Auxiliary - JSON
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__JSON)
+
+namespace wuffs_aux {
+
+struct DecodeJsonResult {
+ DecodeJsonResult(std::string&& error_message0, uint64_t cursor_position0);
+
+ std::string error_message;
+ uint64_t cursor_position;
+};
+
+class DecodeJsonCallbacks {
+ public:
+ // AppendXxx are called for leaf nodes: literals, numbers and strings. For
+ // strings, the Callbacks implementation is responsible for tracking map keys
+ // versus other values.
+
+ virtual std::string AppendNull() = 0;
+ virtual std::string AppendBool(bool val) = 0;
+ virtual std::string AppendI64(int64_t val) = 0;
+ virtual std::string AppendF64(double val) = 0;
+ virtual std::string AppendString(std::string&& val) = 0;
+
+ // Push and Pop are called for container nodes: JSON arrays (lists) and JSON
+ // objects (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 DecodeJson, whether or
+ // not parsing the input as JSON encountered an error. Even when successful,
+ // trailing data may remain in input and buffer. See "Unintuitive JSON
+ // Parsing" (https://nullprogram.com/blog/2019/12/28/) which discusses JSON
+ // parsing and when it stops.
+ //
+ // Do not keep a reference to buffer or buffer.data.ptr after Done returns,
+ // as DecodeJson may then de-allocate the backing array.
+ virtual void Done(DecodeJsonResult& result,
+ sync_io::Input& input,
+ IOBuffer& buffer) = 0;
+};
+
+// 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).
+DecodeJsonResult DecodeJson(DecodeJsonCallbacks&& callbacks,
+ sync_io::Input&& input,
+ wuffs_base__slice_u32 quirks);
+
+} // namespace wuffs_aux
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__AUX__JSON)
+
+#endif // defined(__cplusplus) && (__cplusplus >= 201103L)
+
// WUFFS C HEADER ENDS HERE.
#ifdef WUFFS_IMPLEMENTATION
@@ -28545,6 +28690,324 @@
#endif // !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__ZLIB)
+#if defined(__cplusplus) && (__cplusplus >= 201103L)
+
+// ---------------- Auxiliary - Base
+
+// Auxiliary code is discussed at
+// https://github.com/google/wuffs/blob/master/doc/note/auxiliary-code.md
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__BASE)
+
+namespace wuffs_aux {
+
+namespace sync_io {
+
+// --------
+
+IOBuffer* //
+Input::BringsItsOwnIOBuffer() {
+ return nullptr;
+}
+
+// --------
+
+FileInput::FileInput(FILE* f) : m_f(f) {}
+
+std::string //
+FileInput::CopyIn(IOBuffer* dst) {
+ if (!m_f) {
+ return "wuffs_aux::sync_io::FileInput: nullptr file";
+ } else if (dst && !dst->meta.closed) {
+ size_t n = fread(dst->writer_pointer(), 1, dst->writer_length(), m_f);
+ dst->meta.wi += n;
+ dst->meta.closed = feof(m_f);
+ if (ferror(m_f)) {
+ return "wuffs_aux::sync_io::FileInput: error reading file";
+ }
+ }
+ return "";
+}
+
+// --------
+
+MemoryInput::MemoryInput(const uint8_t* ptr, size_t len)
+ : m_io(wuffs_base__ptr_u8__reader(const_cast<uint8_t*>(ptr), len, true)) {}
+
+IOBuffer* //
+MemoryInput::BringsItsOwnIOBuffer() {
+ return &m_io;
+}
+
+std::string //
+MemoryInput::CopyIn(IOBuffer* dst) {
+ if (dst && !dst->meta.closed && (dst != &m_io)) {
+ size_t nd = dst->writer_length();
+ size_t ns = m_io.reader_length();
+ size_t n = (nd < ns) ? nd : ns;
+ memcpy(dst->writer_pointer(), m_io.reader_pointer(), n);
+ m_io.meta.ri += n;
+ dst->meta.wi += n;
+ dst->meta.closed = m_io.reader_length() == 0;
+ }
+ return "";
+}
+
+// --------
+
+} // namespace sync_io
+
+} // namespace wuffs_aux
+
+#endif // !defined(WUFFS_CONFIG__MODULES) ||
+ // defined(WUFFS_CONFIG__MODULE__AUX__BASE)
+
+// ---------------- Auxiliary - JSON
+
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__JSON)
+
+#include <utility>
+
+namespace wuffs_aux {
+
+DecodeJsonResult::DecodeJsonResult(std::string&& error_message0,
+ uint64_t cursor_position0)
+ : error_message(std::move(error_message0)),
+ cursor_position(cursor_position0) {}
+
+DecodeJsonResult //
+DecodeJson(DecodeJsonCallbacks&& 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 JSON decoder.
+ wuffs_json__decoder::unique_ptr dec = wuffs_json__decoder::alloc();
+ if (!dec) {
+ ret_error_message = "wuffs_aux::JsonDecoder: 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;
+
+ // 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::JsonDecoder: 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";
+ 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";
+ 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_JSON__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE != 0) {
+ ret_error_message =
+ "wuffs_aux::JsonDecoder: 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::JsonDecoder: 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.
+
+ 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) {
+ 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 = callbacks.AppendString(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: {
+ ret_error_message =
+ (vbd & WUFFS_BASE__TOKEN__VBD__LITERAL__NULL)
+ ? callbacks.AppendNull()
+ : callbacks.AppendBool(vbd &
+ WUFFS_BASE__TOKEN__VBD__LITERAL__TRUE);
+ goto parsed_a_value;
+ }
+
+ case WUFFS_BASE__TOKEN__VBC__NUMBER: {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT) {
+ if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED) {
+ wuffs_base__result_i64 r = wuffs_base__parse_number_i64(
+ wuffs_base__make_slice_u8(token_ptr, token_len),
+ WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
+ if (r.status.is_ok()) {
+ ret_error_message = callbacks.AppendI64(r.value);
+ goto parsed_a_value;
+ }
+ }
+ if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT) {
+ wuffs_base__result_f64 r = wuffs_base__parse_number_f64(
+ wuffs_base__make_slice_u8(token_ptr, token_len),
+ WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
+ if (r.status.is_ok()) {
+ ret_error_message = callbacks.AppendF64(r.value);
+ goto parsed_a_value;
+ }
+ }
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_INF) {
+ ret_error_message = callbacks.AppendF64(
+ wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ 0xFFF0000000000000ul));
+ goto parsed_a_value;
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_INF) {
+ ret_error_message = callbacks.AppendF64(
+ wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ 0x7FF0000000000000ul));
+ goto parsed_a_value;
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_NAN) {
+ ret_error_message = callbacks.AppendF64(
+ wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ 0xFFFFFFFFFFFFFFFFul));
+ goto parsed_a_value;
+ } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_NAN) {
+ ret_error_message = callbacks.AppendF64(
+ wuffs_base__ieee_754_bit_representation__from_u64_to_f64(
+ 0x7FFFFFFFFFFFFFFFul));
+ goto parsed_a_value;
+ }
+ }
+ }
+
+ fail:
+ ret_error_message =
+ "wuffs_aux::JsonDecoder: internal error: unexpected token";
+ goto done;
+
+ parsed_a_value:
+ if (!ret_error_message.empty() || (depth == 0)) {
+ goto done;
+ }
+ }
+ } while (false);
+
+done:
+ DecodeJsonResult 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__JSON)
+
+#endif // defined(__cplusplus) && (__cplusplus >= 201103L)
+
#endif // WUFFS_IMPLEMENTATION
#ifdef __clang__