mirror of
git://jb55.com/damus
synced 2024-10-01 17:30:44 +00:00
6863e74c0f
update flatcc, including the patch that fixes japanese usenames Changelog-Fixed: Fix japanese profiles names not loading
910 lines
36 KiB
C
910 lines
36 KiB
C
#ifndef FLATCC_JSON_PARSE_H
|
|
#define FLATCC_JSON_PARSE_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/*
|
|
* JSON RFC:
|
|
* http://www.ietf.org/rfc/rfc4627.txt?number=4627
|
|
*
|
|
* With several flatbuffers specific extensions.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "flatcc_rtconfig.h"
|
|
#include "flatcc_builder.h"
|
|
#include "flatcc_unaligned.h"
|
|
|
|
#define PDIAGNOSTIC_IGNORE_UNUSED
|
|
#include "portable/pdiagnostic_push.h"
|
|
|
|
typedef uint32_t flatcc_json_parser_flags_t;
|
|
static const flatcc_json_parser_flags_t flatcc_json_parser_f_skip_unknown = 1;
|
|
static const flatcc_json_parser_flags_t flatcc_json_parser_f_force_add = 2;
|
|
static const flatcc_json_parser_flags_t flatcc_json_parser_f_with_size = 4;
|
|
static const flatcc_json_parser_flags_t flatcc_json_parser_f_skip_array_overflow = 8;
|
|
static const flatcc_json_parser_flags_t flatcc_json_parser_f_reject_array_underflow = 16;
|
|
|
|
#define FLATCC_JSON_PARSE_ERROR_MAP(XX) \
|
|
XX(ok, "ok") \
|
|
XX(eof, "eof") \
|
|
XX(deep_nesting, "deep nesting") \
|
|
XX(trailing_comma, "trailing comma") \
|
|
XX(expected_colon, "expected colon") \
|
|
XX(unexpected_character, "unexpected character") \
|
|
XX(invalid_numeric, "invalid numeric") \
|
|
XX(overflow, "overflow") \
|
|
XX(underflow, "underflow") \
|
|
XX(unbalanced_array, "unbalanced array") \
|
|
XX(unbalanced_object, "unbalanced object") \
|
|
XX(precision_loss, "precision loss") \
|
|
XX(float_unexpected, "float unexpected") \
|
|
XX(unknown_symbol, "unknown symbol") \
|
|
XX(unquoted_symbolic_list, "unquoted list of symbols") \
|
|
XX(unknown_union, "unknown union type") \
|
|
XX(expected_string, "expected string") \
|
|
XX(invalid_character, "invalid character") \
|
|
XX(invalid_escape, "invalid escape") \
|
|
XX(invalid_type, "invalid type") \
|
|
XX(unterminated_string, "unterminated string") \
|
|
XX(expected_object, "expected object") \
|
|
XX(expected_array, "expected array") \
|
|
XX(expected_scalar, "expected literal or symbolic scalar") \
|
|
XX(expected_union_type, "expected union type") \
|
|
XX(union_none_present, "union present with type NONE") \
|
|
XX(union_none_not_null, "union of type NONE is not null") \
|
|
XX(union_incomplete, "table has incomplete union") \
|
|
XX(duplicate, "table has duplicate field") \
|
|
XX(required, "required field missing") \
|
|
XX(union_vector_length, "union vector length mismatch") \
|
|
XX(base64, "invalid base64 content") \
|
|
XX(base64url, "invalid base64url content") \
|
|
XX(array_underflow, "fixed length array underflow") \
|
|
XX(array_overflow, "fixed length array overflow") \
|
|
XX(runtime, "runtime error") \
|
|
XX(not_supported, "not supported")
|
|
|
|
enum flatcc_json_parser_error_no {
|
|
#define XX(no, str) flatcc_json_parser_error_##no,
|
|
FLATCC_JSON_PARSE_ERROR_MAP(XX)
|
|
#undef XX
|
|
};
|
|
|
|
const char *flatcc_json_parser_error_string(int err);
|
|
|
|
#define flatcc_json_parser_ok flatcc_json_parser_error_ok
|
|
#define flatcc_json_parser_eof flatcc_json_parser_error_eof
|
|
|
|
/*
|
|
* The struct may be zero initialized in which case the line count will
|
|
* start at line zero, or the line may be set to 1 initially. The ctx
|
|
* is only used for error reporting and tracking non-standard unquoted
|
|
* ctx.
|
|
*
|
|
* `ctx` may for example hold a flatcc_builder_t pointer.
|
|
*/
|
|
typedef struct flatcc_json_parser_ctx flatcc_json_parser_t;
|
|
struct flatcc_json_parser_ctx {
|
|
flatcc_builder_t *ctx;
|
|
const char *line_start;
|
|
flatcc_json_parser_flags_t flags;
|
|
#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
|
|
int unquoted;
|
|
#endif
|
|
|
|
int line, pos;
|
|
int error;
|
|
const char *start;
|
|
const char *end;
|
|
const char *error_loc;
|
|
/* Set at end of successful parse. */
|
|
const char *end_loc;
|
|
};
|
|
|
|
static inline int flatcc_json_parser_get_error(flatcc_json_parser_t *ctx)
|
|
{
|
|
return ctx->error;
|
|
}
|
|
|
|
static inline void flatcc_json_parser_init(flatcc_json_parser_t *ctx, flatcc_builder_t *B, const char *buf, const char *end, flatcc_json_parser_flags_t flags)
|
|
{
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
ctx->ctx = B;
|
|
ctx->line_start = buf;
|
|
ctx->line = 1;
|
|
ctx->flags = flags;
|
|
/* These are not needed for parsing, but may be helpful in reporting etc. */
|
|
ctx->start = buf;
|
|
ctx->end = end;
|
|
ctx->error_loc = buf;
|
|
}
|
|
|
|
const char *flatcc_json_parser_set_error(flatcc_json_parser_t *ctx, const char *loc, const char *end, int reason);
|
|
|
|
/*
|
|
* Wide space is not necessarily beneficial in the typical space, but it
|
|
* also isn't expensive so it may be added when there are applications
|
|
* that can benefit.
|
|
*/
|
|
const char *flatcc_json_parser_space_ext(flatcc_json_parser_t *ctx, const char *buf, const char *end);
|
|
|
|
static inline const char *flatcc_json_parser_space(flatcc_json_parser_t *ctx, const char *buf, const char *end)
|
|
{
|
|
if (end - buf > 1) {
|
|
if (buf[0] > 0x20) {
|
|
return buf;
|
|
}
|
|
if (buf[0] == 0x20 && buf[1] > 0x20) {
|
|
return buf + 1;
|
|
}
|
|
}
|
|
return flatcc_json_parser_space_ext(ctx, buf, end);
|
|
}
|
|
|
|
|
|
static inline const char *flatcc_json_parser_string_start(flatcc_json_parser_t *ctx, const char *buf, const char *end)
|
|
{
|
|
if (buf == end || *buf != '\"') {
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_string);
|
|
}
|
|
return ++buf;
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_string_end(flatcc_json_parser_t *ctx, const char *buf, const char *end)
|
|
{
|
|
if (buf == end || *buf != '\"') {
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unterminated_string);
|
|
}
|
|
return ++buf;
|
|
}
|
|
|
|
/*
|
|
* Parse a string as a fixed length char array as `s` with length `n`.
|
|
* and raise errors according to overflow/underflow runtime flags. Zero
|
|
* and truncate as needed. A trailing zero is not inserted if the input
|
|
* is at least the same length as the char array.
|
|
*
|
|
* Runtime flags: `skip_array_overflow`, `pad_array_underflow`.
|
|
*/
|
|
const char *flatcc_json_parser_char_array(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, char *s, size_t n);
|
|
|
|
/*
|
|
* Creates a string. Returns *ref == 0 on unrecoverable error or
|
|
* sets *ref to a valid new string reference.
|
|
*/
|
|
const char *flatcc_json_parser_build_string(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, flatcc_builder_ref_t *ref);
|
|
|
|
typedef char flatcc_json_parser_escape_buffer_t[5];
|
|
/*
|
|
* If the buffer does not hold a valid escape sequence, an error is
|
|
* returned with code[0] = 0/
|
|
*
|
|
* Otherwise code[0] the length (1-4) of the remaining
|
|
* characters in the code, transcoded from the escape sequence
|
|
* where a length of 4 only happens with escapaped surrogate pairs.
|
|
*
|
|
* The JSON extension `\xXX` is supported and may produced invalid UTF-8
|
|
* characters such as 0xff. The standard JSON escape `\uXXXX` is not
|
|
* checked for invalid code points and may produce invalid UTF-8.
|
|
*
|
|
* Regular characters are expected to valid UTF-8 but they are not checked
|
|
* and may therefore produce invalid UTF-8.
|
|
*
|
|
* Control characters within a string are rejected except in the
|
|
* standard JSON escpaped form for `\n \r \t \b \f`.
|
|
*
|
|
* Additional escape codes as per standard JSON: `\\ \/ \"`.
|
|
*/
|
|
const char *flatcc_json_parser_string_escape(flatcc_json_parser_t *ctx, const char *buf, const char *end, flatcc_json_parser_escape_buffer_t code);
|
|
|
|
/*
|
|
* Parses the longest unescaped run of string content followed by either
|
|
* an escape encoding, string termination, or error.
|
|
*/
|
|
const char *flatcc_json_parser_string_part(flatcc_json_parser_t *ctx, const char *buf, const char *end);
|
|
|
|
static inline const char *flatcc_json_parser_symbol_start(flatcc_json_parser_t *ctx, const char *buf, const char *end)
|
|
{
|
|
if (buf == end) {
|
|
return buf;
|
|
}
|
|
if (*buf == '\"') {
|
|
++buf;
|
|
#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
|
|
ctx->unquoted = 0;
|
|
#endif
|
|
} else {
|
|
#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
|
|
if (*buf == '.') {
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
|
|
}
|
|
ctx->unquoted = 1;
|
|
#else
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
|
|
#endif
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static inline uint64_t flatcc_json_parser_symbol_part_ext(const char *buf, const char *end)
|
|
{
|
|
uint64_t w = 0;
|
|
size_t n = (size_t)(end - buf);
|
|
const uint8_t *ubuf = (const uint8_t*)buf;
|
|
|
|
if (n > 8) {
|
|
n = 8;
|
|
}
|
|
/* This can bloat inlining for a rarely executed case. */
|
|
#if 1
|
|
switch (n) {
|
|
case 8:
|
|
w |= ((uint64_t)ubuf[7]) << (0 * 8);
|
|
goto lbl_n_7;
|
|
case 7:
|
|
lbl_n_7:
|
|
w |= ((uint64_t)ubuf[6]) << (1 * 8);
|
|
goto lbl_n_6;
|
|
case 6:
|
|
lbl_n_6:
|
|
w |= ((uint64_t)ubuf[5]) << (2 * 8);
|
|
goto lbl_n_5;
|
|
case 5:
|
|
lbl_n_5:
|
|
w |= ((uint64_t)ubuf[4]) << (3 * 8);
|
|
goto lbl_n_4;
|
|
case 4:
|
|
lbl_n_4:
|
|
w |= ((uint64_t)ubuf[3]) << (4 * 8);
|
|
goto lbl_n_3;
|
|
case 3:
|
|
lbl_n_3:
|
|
w |= ((uint64_t)ubuf[2]) << (5 * 8);
|
|
goto lbl_n_2;
|
|
case 2:
|
|
lbl_n_2:
|
|
w |= ((uint64_t)ubuf[1]) << (6 * 8);
|
|
goto lbl_n_1;
|
|
case 1:
|
|
lbl_n_1:
|
|
w |= ((uint64_t)ubuf[0]) << (7 * 8);
|
|
break;
|
|
case 0:
|
|
break;
|
|
}
|
|
#else
|
|
/* But this is hardly much of an improvement. */
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < n; ++i) {
|
|
w <<= 8;
|
|
if (i < n) {
|
|
w = buf[i];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return w;
|
|
}
|
|
|
|
/*
|
|
* Read out string as a big endian word. This allows for trie lookup,
|
|
* also when trailing characters are beyond keyword. This assumes the
|
|
* external words tested against are valid and therefore there need be
|
|
* no checks here. If a match is not made, the symbol_end function will
|
|
* consume and check any unmatched content - from _before_ this function
|
|
* was called - i.e. the returned buffer is tentative for use only if we
|
|
* accept the part returned here.
|
|
*
|
|
* Used for both symbols and symbolic constants.
|
|
*/
|
|
static inline uint64_t flatcc_json_parser_symbol_part(const char *buf, const char *end)
|
|
{
|
|
size_t n = (size_t)(end - buf);
|
|
|
|
#if FLATCC_ALLOW_UNALIGNED_ACCESS
|
|
if (n >= 8) {
|
|
return be64toh(*(uint64_t *)buf);
|
|
}
|
|
#endif
|
|
return flatcc_json_parser_symbol_part_ext(buf, end);
|
|
}
|
|
|
|
/* Don't allow space in dot notation neither inside nor outside strings. */
|
|
static inline const char *flatcc_json_parser_match_scope(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos)
|
|
{
|
|
const char *mark = buf;
|
|
|
|
(void)ctx;
|
|
|
|
if (end - buf <= pos) {
|
|
return mark;
|
|
}
|
|
if (buf[pos] != '.') {
|
|
return mark;
|
|
}
|
|
return buf + pos + 1;
|
|
}
|
|
|
|
const char *flatcc_json_parser_match_constant(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos, int *more);
|
|
|
|
/* We allow '.' in unquoted symbols, but not at the start or end. */
|
|
static inline const char *flatcc_json_parser_symbol_end(flatcc_json_parser_t *ctx, const char *buf, const char *end)
|
|
{
|
|
char c, clast = 0;
|
|
|
|
|
|
#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
|
|
if (ctx->unquoted) {
|
|
while (buf != end && *buf > 0x20) {
|
|
clast = c = *buf;
|
|
if (c == '_' || c == '.' || (c & 0x80) || (c >= '0' && c <= '9')) {
|
|
++buf;
|
|
continue;
|
|
}
|
|
/* Lower case. */
|
|
c |= 0x20;
|
|
if (c >= 'a' && c <= 'z') {
|
|
++buf;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (clast == '.') {
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
|
|
}
|
|
} else {
|
|
#else
|
|
{
|
|
#endif
|
|
while (buf != end && *buf != '\"') {
|
|
if (*buf == '\\') {
|
|
if (end - buf < 2) {
|
|
break;
|
|
}
|
|
++buf;
|
|
}
|
|
++buf;
|
|
}
|
|
if (buf == end || *buf != '\"') {
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unterminated_string);
|
|
}
|
|
++buf;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_constant_start(flatcc_json_parser_t *ctx, const char *buf, const char *end)
|
|
{
|
|
buf = flatcc_json_parser_symbol_start(ctx, buf, end);
|
|
#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
|
|
if (!ctx->unquoted) {
|
|
#else
|
|
{
|
|
#endif
|
|
buf = flatcc_json_parser_space(ctx, buf, end);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_object_start(flatcc_json_parser_t *ctx, const char *buf, const char *end, int *more)
|
|
{
|
|
if (buf == end || *buf != '{') {
|
|
*more = 0;
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_object);
|
|
}
|
|
buf = flatcc_json_parser_space(ctx, buf + 1, end);
|
|
if (buf != end && *buf == '}') {
|
|
*more = 0;
|
|
return flatcc_json_parser_space(ctx, buf + 1, end);
|
|
}
|
|
*more = 1;
|
|
return buf;
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_object_end(flatcc_json_parser_t *ctx, const char *buf,
|
|
const char *end, int *more)
|
|
{
|
|
buf = flatcc_json_parser_space(ctx, buf, end);
|
|
if (buf == end) {
|
|
*more = 0;
|
|
return buf;
|
|
}
|
|
if (*buf != ',') {
|
|
*more = 0;
|
|
if (*buf != '}') {
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_object);
|
|
} else {
|
|
return flatcc_json_parser_space(ctx, buf + 1, end);
|
|
}
|
|
}
|
|
buf = flatcc_json_parser_space(ctx, buf + 1, end);
|
|
if (buf == end) {
|
|
*more = 0;
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_object);
|
|
}
|
|
#if FLATCC_JSON_PARSE_ALLOW_TRAILING_COMMA
|
|
if (*buf == '}') {
|
|
*more = 0;
|
|
return flatcc_json_parser_space(ctx, buf + 1, end);
|
|
}
|
|
#endif
|
|
*more = 1;
|
|
return buf;
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_array_start(flatcc_json_parser_t *ctx, const char *buf, const char *end, int *more)
|
|
{
|
|
if (buf == end || *buf != '[') {
|
|
*more = 0;
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_array);
|
|
}
|
|
buf = flatcc_json_parser_space(ctx, buf + 1, end);
|
|
if (buf != end && *buf == ']') {
|
|
*more = 0;
|
|
return flatcc_json_parser_space(ctx, buf + 1, end);
|
|
}
|
|
*more = 1;
|
|
return buf;
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_array_end(flatcc_json_parser_t *ctx, const char *buf,
|
|
const char *end, int *more)
|
|
{
|
|
buf = flatcc_json_parser_space(ctx, buf, end);
|
|
if (buf == end) {
|
|
*more = 0;
|
|
return buf;
|
|
}
|
|
if (*buf != ',') {
|
|
*more = 0;
|
|
if (*buf != ']') {
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_array);
|
|
} else {
|
|
return flatcc_json_parser_space(ctx, buf + 1, end);
|
|
}
|
|
}
|
|
buf = flatcc_json_parser_space(ctx, buf + 1, end);
|
|
if (buf == end) {
|
|
*more = 0;
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_array);
|
|
}
|
|
#if FLATCC_JSON_PARSE_ALLOW_TRAILING_COMMA
|
|
if (*buf == ']') {
|
|
*more = 0;
|
|
return flatcc_json_parser_space(ctx, buf + 1, end);
|
|
}
|
|
#endif
|
|
*more = 1;
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* Detects if a symbol terminates at a given `pos` relative to the
|
|
* buffer pointer, or return fast.
|
|
*
|
|
* Failure to match is not an error but a recommendation to try
|
|
* alternative longer suffixes - only if such do not exist will
|
|
* there be an error. If a match was not eventually found,
|
|
* the `flatcc_json_parser_unmatched_symbol` should be called to consume
|
|
* the symbol and generate error messages.
|
|
*
|
|
* If a match was detected, ':' and surrounding space is consumed,
|
|
* or an error is generated.
|
|
*/
|
|
static inline const char *flatcc_json_parser_match_symbol(flatcc_json_parser_t *ctx, const char *buf,
|
|
const char *end, int pos)
|
|
{
|
|
const char *mark = buf;
|
|
|
|
if (end - buf <= pos) {
|
|
return mark;
|
|
}
|
|
#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
|
|
if (ctx->unquoted) {
|
|
if (buf[pos] > 0x20 && buf[pos] != ':') {
|
|
return mark;
|
|
}
|
|
buf += pos;
|
|
ctx->unquoted = 0;
|
|
} else {
|
|
#else
|
|
{
|
|
#endif
|
|
if (buf[pos] != '\"') {
|
|
return mark;
|
|
}
|
|
buf += pos + 1;
|
|
}
|
|
buf = flatcc_json_parser_space(ctx, buf, end);
|
|
if (buf != end && *buf == ':') {
|
|
++buf;
|
|
return flatcc_json_parser_space(ctx, buf, end);
|
|
}
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_colon);
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_match_type_suffix(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos)
|
|
{
|
|
if (end - buf <= pos + 5) {
|
|
return buf;
|
|
}
|
|
if (memcmp(buf + pos, "_type", 5)) {
|
|
return buf;
|
|
}
|
|
return flatcc_json_parser_match_symbol(ctx, buf, end, pos + 5);
|
|
}
|
|
|
|
const char *flatcc_json_parser_unmatched_symbol(flatcc_json_parser_t *ctx, const char *buf, const char *end);
|
|
|
|
static inline const char *flatcc_json_parser_coerce_uint64(
|
|
flatcc_json_parser_t *ctx, const char *buf,
|
|
const char *end, int value_sign, uint64_t value, uint64_t *v)
|
|
{
|
|
if (value_sign) {
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
|
|
}
|
|
*v = value;
|
|
return buf;
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_coerce_bool(flatcc_json_parser_t *ctx, const char *buf,
|
|
const char *end, int value_sign, uint64_t value, uint8_t *v)
|
|
{
|
|
if (value_sign) {
|
|
return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
|
|
}
|
|
*v = (uint8_t)!!value;
|
|
return buf;
|
|
}
|
|
|
|
#define __flatcc_json_parser_define_coerce_unsigned(type, basetype, uctype) \
|
|
static inline const char *flatcc_json_parser_coerce_ ## type( \
|
|
flatcc_json_parser_t *ctx, const char *buf, \
|
|
const char *end, int value_sign, uint64_t value, basetype *v) \
|
|
{ \
|
|
if (value_sign) { \
|
|
return flatcc_json_parser_set_error(ctx, buf, end, \
|
|
flatcc_json_parser_error_underflow); \
|
|
} \
|
|
if (value > uctype ## _MAX) { \
|
|
return flatcc_json_parser_set_error(ctx, buf, end, \
|
|
flatcc_json_parser_error_overflow); \
|
|
} \
|
|
*v = (basetype)value; \
|
|
return buf; \
|
|
}
|
|
|
|
__flatcc_json_parser_define_coerce_unsigned(uint32, uint32_t, UINT32)
|
|
__flatcc_json_parser_define_coerce_unsigned(uint16, uint16_t, UINT16)
|
|
__flatcc_json_parser_define_coerce_unsigned(uint8, uint8_t, UINT8)
|
|
|
|
#define __flatcc_json_parser_define_coerce_signed(type, basetype, uctype) \
|
|
static inline const char *flatcc_json_parser_coerce_ ## type( \
|
|
flatcc_json_parser_t *ctx, const char *buf, \
|
|
const char *end, int value_sign, uint64_t value, basetype *v) \
|
|
{ \
|
|
if (value_sign) { \
|
|
if (value > (uint64_t)(uctype ## _MAX) + 1) { \
|
|
return flatcc_json_parser_set_error(ctx, buf, end, \
|
|
flatcc_json_parser_error_underflow); \
|
|
} \
|
|
*v = (basetype)-(int64_t)value; \
|
|
} else { \
|
|
if (value > uctype ## _MAX) { \
|
|
return flatcc_json_parser_set_error(ctx, buf, end, \
|
|
flatcc_json_parser_error_overflow); \
|
|
} \
|
|
*v = (basetype)value; \
|
|
} \
|
|
return buf; \
|
|
}
|
|
|
|
__flatcc_json_parser_define_coerce_signed(int64, int64_t, INT64)
|
|
__flatcc_json_parser_define_coerce_signed(int32, int32_t, INT32)
|
|
__flatcc_json_parser_define_coerce_signed(int16, int16_t, INT16)
|
|
__flatcc_json_parser_define_coerce_signed(int8, int8_t, INT8)
|
|
|
|
static inline const char *flatcc_json_parser_coerce_float(
|
|
flatcc_json_parser_t *ctx, const char *buf,
|
|
const char *end, int value_sign, uint64_t value, float *v)
|
|
{
|
|
(void)ctx;
|
|
(void)end;
|
|
|
|
*v = value_sign ? -(float)value : (float)value;
|
|
return buf;
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_coerce_double(
|
|
flatcc_json_parser_t *ctx, const char *buf,
|
|
const char *end, int value_sign, uint64_t value, double *v)
|
|
{
|
|
(void)ctx;
|
|
(void)end;
|
|
|
|
*v = value_sign ? -(double)value : (double)value;
|
|
return buf;
|
|
}
|
|
|
|
const char *flatcc_json_parser_double(flatcc_json_parser_t *ctx, const char *buf, const char *end, double *v);
|
|
|
|
const char *flatcc_json_parser_float(flatcc_json_parser_t *ctx, const char *buf, const char *end, float *v);
|
|
|
|
/*
|
|
* If the buffer does not contain a valid start character for a numeric
|
|
* value, the function will return the the input buffer without failure.
|
|
* This makes is possible to try a symbolic parse.
|
|
*/
|
|
const char *flatcc_json_parser_integer(flatcc_json_parser_t *ctx, const char *buf, const char *end,
|
|
int *value_sign, uint64_t *value);
|
|
|
|
/* Returns unchanged buffer without error if `null` is not matched. */
|
|
static inline const char *flatcc_json_parser_null(const char *buf, const char *end)
|
|
{
|
|
if (end - buf >= 4 && memcmp(buf, "null", 4) == 0) {
|
|
return buf + 4;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static inline const char *flatcc_json_parser_none(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end)
|
|
{
|
|
if (end - buf >= 4 && memcmp(buf, "null", 4) == 0) {
|
|
return buf + 4;
|
|
}
|
|
return flatcc_json_parser_set_error(ctx, buf, end,
|
|
flatcc_json_parser_error_union_none_not_null);
|
|
}
|
|
|
|
/*
|
|
* `parsers` is a null terminated array of parsers with at least one
|
|
* valid parser. A numeric literal parser may also be included.
|
|
*/
|
|
#define __flatcc_json_parser_define_integral_parser(type, basetype) \
|
|
static inline const char *flatcc_json_parser_ ## type( \
|
|
flatcc_json_parser_t *ctx, \
|
|
const char *buf, const char *end, basetype *v) \
|
|
{ \
|
|
uint64_t value = 0; \
|
|
int value_sign = 0; \
|
|
const char *mark = buf; \
|
|
\
|
|
*v = 0; \
|
|
if (buf == end) { \
|
|
return buf; \
|
|
} \
|
|
buf = flatcc_json_parser_integer(ctx, buf, end, &value_sign, &value); \
|
|
if (buf != mark) { \
|
|
return flatcc_json_parser_coerce_ ## type(ctx, \
|
|
buf, end, value_sign, value, v); \
|
|
} \
|
|
return buf; \
|
|
}
|
|
|
|
__flatcc_json_parser_define_integral_parser(uint64, uint64_t)
|
|
__flatcc_json_parser_define_integral_parser(uint32, uint32_t)
|
|
__flatcc_json_parser_define_integral_parser(uint16, uint16_t)
|
|
__flatcc_json_parser_define_integral_parser(uint8, uint8_t)
|
|
__flatcc_json_parser_define_integral_parser(int64, int64_t)
|
|
__flatcc_json_parser_define_integral_parser(int32, int32_t)
|
|
__flatcc_json_parser_define_integral_parser(int16, int16_t)
|
|
__flatcc_json_parser_define_integral_parser(int8, int8_t)
|
|
|
|
static inline const char *flatcc_json_parser_bool(flatcc_json_parser_t *ctx, const char *buf, const char *end, uint8_t *v)
|
|
{
|
|
const char *k;
|
|
uint8_t tmp;
|
|
|
|
k = buf;
|
|
if (end - buf >= 4 && memcmp(buf, "true", 4) == 0) {
|
|
*v = 1;
|
|
return k + 4;
|
|
} else if (end - buf >= 5 && memcmp(buf, "false", 5) == 0) {
|
|
*v = 0;
|
|
return k + 5;
|
|
}
|
|
buf = flatcc_json_parser_uint8(ctx, buf, end, &tmp);
|
|
*v = !!tmp;
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* The `parsers` argument is a zero terminated array of parser
|
|
* functions with increasingly general scopes.
|
|
*
|
|
* Symbols can be be or'ed together by listing multiple space separated
|
|
* flags in source being parsed, like `{ x : "Red Blue" }`.
|
|
* Intended for flags, but generally available.
|
|
*
|
|
* `aggregate` means there are more symbols to follow.
|
|
*
|
|
* This function does not return input `buf` value if match was
|
|
* unsuccessful. It will either match or error.
|
|
*/
|
|
typedef const char *flatcc_json_parser_integral_symbol_f(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, int *value_sign, uint64_t *value, int *aggregate);
|
|
|
|
/*
|
|
* Raise an error if a syntax like `color: Red Green` is seen unless
|
|
* explicitly permitted. `color: "Red Green"` or `"color": "Red Green"
|
|
* or `color: Red` is permitted if unquoted is permitted but not
|
|
* unquoted list. Googles flatc JSON parser does not allow multiple
|
|
* symbolic values unless quoted, so this is the default.
|
|
*/
|
|
#if !FLATCC_JSON_PARSE_ALLOW_UNQUOTED || FLATCC_JSON_PARSE_ALLOW_UNQUOTED_LIST
|
|
#define __flatcc_json_parser_init_check_unquoted_list()
|
|
#define __flatcc_json_parser_check_unquoted_list()
|
|
#else
|
|
#define __flatcc_json_parser_init_check_unquoted_list() int list_count = 0;
|
|
#define __flatcc_json_parser_check_unquoted_list() \
|
|
if (list_count++ && ctx->unquoted) { \
|
|
return flatcc_json_parser_set_error(ctx, buf, end, \
|
|
flatcc_json_parser_error_unquoted_symbolic_list); \
|
|
}
|
|
#endif
|
|
|
|
#define __flatcc_json_parser_define_symbolic_integral_parser(type, basetype)\
|
|
static const char *flatcc_json_parser_symbolic_ ## type( \
|
|
flatcc_json_parser_t *ctx, \
|
|
const char *buf, const char *end, \
|
|
flatcc_json_parser_integral_symbol_f *parsers[], \
|
|
basetype *v) \
|
|
{ \
|
|
flatcc_json_parser_integral_symbol_f **p; \
|
|
const char *mark; \
|
|
basetype tmp = 0; \
|
|
uint64_t value; \
|
|
int value_sign, aggregate; \
|
|
__flatcc_json_parser_init_check_unquoted_list() \
|
|
\
|
|
*v = 0; \
|
|
buf = flatcc_json_parser_constant_start(ctx, buf, end); \
|
|
if (buf == end) { \
|
|
return buf; \
|
|
} \
|
|
do { \
|
|
p = parsers; \
|
|
do { \
|
|
/* call parser function */ \
|
|
buf = (*p)(ctx, (mark = buf), end, \
|
|
&value_sign, &value, &aggregate); \
|
|
if (buf == end) { \
|
|
return buf; \
|
|
} \
|
|
} while (buf == mark && *++p); \
|
|
if (mark == buf) { \
|
|
return flatcc_json_parser_set_error(ctx, buf, end, \
|
|
flatcc_json_parser_error_expected_scalar); \
|
|
} \
|
|
__flatcc_json_parser_check_unquoted_list() \
|
|
if (end == flatcc_json_parser_coerce_ ## type(ctx, \
|
|
buf, end, value_sign, value, &tmp)) { \
|
|
return end; \
|
|
} \
|
|
/* \
|
|
* `+=`, not `|=` because we also coerce to float and double, \
|
|
* and because we need to handle signed values. This may give \
|
|
* unexpected results with duplicate flags. \
|
|
*/ \
|
|
*v += tmp; \
|
|
} while (aggregate); \
|
|
return buf; \
|
|
}
|
|
|
|
__flatcc_json_parser_define_symbolic_integral_parser(uint64, uint64_t)
|
|
__flatcc_json_parser_define_symbolic_integral_parser(uint32, uint32_t)
|
|
__flatcc_json_parser_define_symbolic_integral_parser(uint16, uint16_t)
|
|
__flatcc_json_parser_define_symbolic_integral_parser(uint8, uint8_t)
|
|
__flatcc_json_parser_define_symbolic_integral_parser(int64, int64_t)
|
|
__flatcc_json_parser_define_symbolic_integral_parser(int32, int32_t)
|
|
__flatcc_json_parser_define_symbolic_integral_parser(int16, int16_t)
|
|
__flatcc_json_parser_define_symbolic_integral_parser(int8, int8_t)
|
|
|
|
__flatcc_json_parser_define_symbolic_integral_parser(bool, uint8_t)
|
|
|
|
/* We still parse integral values, but coerce to float or double. */
|
|
__flatcc_json_parser_define_symbolic_integral_parser(float, float)
|
|
__flatcc_json_parser_define_symbolic_integral_parser(double, double)
|
|
|
|
/* Parse vector as a base64 or base64url encoded string with no spaces permitted. */
|
|
const char *flatcc_json_parser_build_uint8_vector_base64(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, flatcc_builder_ref_t *ref, int urlsafe);
|
|
|
|
/*
|
|
* This doesn't do anything other than validate and advance past
|
|
* a JSON value which may use unquoted symbols.
|
|
*
|
|
* Upon call it is assumed that leading space has been stripped and that
|
|
* a JSON value is expected (i.e. root, or just after ':' in a
|
|
* container object, or less likely as an array member). Any trailing
|
|
* comma is assumed to belong to the parent context. Returns a parse
|
|
* location stripped from space so container should post call expect
|
|
* ',', '}', or ']', or EOF if the JSON is valid.
|
|
*/
|
|
const char *flatcc_json_parser_generic_json(flatcc_json_parser_t *ctx, const char *buf, const char *end);
|
|
|
|
/* Parse a JSON table. */
|
|
typedef const char *flatcc_json_parser_table_f(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, flatcc_builder_ref_t *pref);
|
|
|
|
/* Parses a JSON struct. */
|
|
typedef const char *flatcc_json_parser_struct_f(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, flatcc_builder_ref_t *pref);
|
|
|
|
/* Constructs a table, struct, or string object unless the type is 0 or unknown. */
|
|
typedef const char *flatcc_json_parser_union_f(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, uint8_t type, flatcc_builder_ref_t *pref);
|
|
|
|
typedef int flatcc_json_parser_is_known_type_f(uint8_t type);
|
|
|
|
/* Called at start by table parsers with at least 1 union. */
|
|
const char *flatcc_json_parser_prepare_unions(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, size_t union_total, size_t *handle);
|
|
|
|
const char *flatcc_json_parser_finalize_unions(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, size_t handle);
|
|
|
|
const char *flatcc_json_parser_union(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, size_t union_index,
|
|
flatbuffers_voffset_t id, size_t handle,
|
|
flatcc_json_parser_union_f *union_parser);
|
|
|
|
const char *flatcc_json_parser_union_type(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, size_t union_index,
|
|
flatbuffers_voffset_t id, size_t handle,
|
|
flatcc_json_parser_integral_symbol_f *type_parsers[],
|
|
flatcc_json_parser_union_f *union_parser);
|
|
|
|
const char *flatcc_json_parser_union_vector(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, size_t union_index,
|
|
flatbuffers_voffset_t id, size_t handle,
|
|
flatcc_json_parser_union_f *union_parser);
|
|
|
|
const char *flatcc_json_parser_union_type_vector(flatcc_json_parser_t *ctx,
|
|
const char *buf, const char *end, size_t union_index,
|
|
flatbuffers_voffset_t id, size_t handle,
|
|
flatcc_json_parser_integral_symbol_f *type_parsers[],
|
|
flatcc_json_parser_union_f *union_parser,
|
|
flatcc_json_parser_is_known_type_f accept_type);
|
|
|
|
/*
|
|
* Parses a table as root.
|
|
*
|
|
* Use the flag `flatcc_json_parser_f_with_size` to create a buffer with
|
|
* size prefix.
|
|
*
|
|
* `ctx` may be null or an uninitialized json parser to receive parse results.
|
|
* `builder` must a newly initialized or reset builder object.
|
|
* `buf`, `bufsiz` may be larger than the parsed json if trailing
|
|
* space or zeroes are expected, but they must represent a valid memory buffer.
|
|
* `fid` must be null, or a valid file identifier.
|
|
* `flags` default to 0. See also `flatcc_json_parser_f_` constants.
|
|
*/
|
|
int flatcc_json_parser_table_as_root(flatcc_builder_t *B, flatcc_json_parser_t *ctx,
|
|
const char *buf, size_t bufsiz, flatcc_json_parser_flags_t flags, const char *fid,
|
|
flatcc_json_parser_table_f *parser);
|
|
|
|
/*
|
|
* Similar to `flatcc_json_parser_table_as_root` but parses a struct as
|
|
* root.
|
|
*/
|
|
int flatcc_json_parser_struct_as_root(flatcc_builder_t *B, flatcc_json_parser_t *ctx,
|
|
const char *buf, size_t bufsiz, flatcc_json_parser_flags_t flags, const char *fid,
|
|
flatcc_json_parser_struct_f *parser);
|
|
|
|
#include "portable/pdiagnostic_pop.h"
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* FLATCC_JSON_PARSE_H */
|