nostrdb

an unfairly fast embedded nostr database backed by lmdb
git clone git://jb55.com/nostrdb
Log | Files | Refs | Submodules | README | LICENSE

codegen_c_json_parser.c (69028B)


      1 #include <stdlib.h>
      2 #include "codegen_c.h"
      3 #include "flatcc/flatcc_types.h"
      4 #include "catalog.h"
      5 
      6 /* -DFLATCC_PORTABLE may help if inttypes.h is missing. */
      7 #ifndef PRId64
      8 #include <inttypes.h>
      9 #endif
     10 
     11 #define PRINTLN_SPMAX 64
     12 static char println_spaces[PRINTLN_SPMAX];
     13 
     14 static void println(fb_output_t *out, const char * format, ...)
     15 {
     16     int i = out->indent * out->opts->cgen_spacing;
     17     va_list ap;
     18 
     19     if (println_spaces[0] == 0) {
     20         memset(println_spaces, 0x20, PRINTLN_SPMAX);
     21     }
     22     /* Don't indent on blank lines. */
     23     if (*format) {
     24         while (i > PRINTLN_SPMAX) {
     25             fprintf(out->fp, "%.*s", (int)PRINTLN_SPMAX, println_spaces);
     26             i -= PRINTLN_SPMAX;
     27         }
     28         /* Use modulo to reset margin if we go too far. */
     29         fprintf(out->fp, "%.*s", i, println_spaces);
     30         va_start (ap, format);
     31         vfprintf (out->fp, format, ap);
     32         va_end (ap);
     33     }
     34     fprintf(out->fp, "\n");
     35 }
     36 
     37 /*
     38  * Unknown fields and unknown union members can be failed
     39  * rather than ignored with a config flag.
     40  *
     41  * Default values an be forced with a config flat.
     42  *
     43  * Forward schema isn't perfect: Unknown symbolic constants
     44  * cannot be used with known fields but will be ignored
     45  * in ignored fields.
     46  */
     47 
     48 static int gen_json_parser_pretext(fb_output_t *out)
     49 {
     50     println(out, "#ifndef %s_JSON_PARSER_H", out->S->basenameup);
     51     println(out, "#define %s_JSON_PARSER_H", out->S->basenameup);
     52     println(out, "");
     53     println(out, "/* " FLATCC_GENERATED_BY " */");
     54     println(out, "");
     55     println(out, "#include \"flatcc/flatcc_json_parser.h\"");
     56     fb_gen_c_includes(out, "_json_parser.h", "_JSON_PARSER_H");
     57     gen_prologue(out);
     58     println(out, "");
     59     return 0;
     60 }
     61 
     62 static int gen_json_parser_footer(fb_output_t *out)
     63 {
     64     gen_epilogue(out);
     65     println(out, "#endif /* %s_JSON_PARSER_H */", out->S->basenameup);
     66     return 0;
     67 }
     68 
     69 typedef struct dict_entry dict_entry_t;
     70 struct dict_entry {
     71     const char *text;
     72     int len;
     73     void *data;
     74     int hint;
     75 };
     76 
     77 /* Returns length of name that reminds after tag at current position. */
     78 static int get_dict_suffix_len(dict_entry_t *de, int pos)
     79 {
     80     int n;
     81 
     82     n = de->len;
     83     if (pos + 8 > n) {
     84         return 0;
     85     }
     86     return n - pos - 8;
     87 }
     88 
     89 /*
     90  * Returns the length name that reminds if it terminates at the tag
     91  * and 0 if it has a suffix.
     92  */
     93 static int get_dict_tag_len(dict_entry_t *de, int pos)
     94 {
     95     int n;
     96 
     97     n = de->len;
     98     if (pos + 8 >= n) {
     99         return n - pos;
    100     }
    101     return 0;
    102 }
    103 
    104 /*
    105  * 8 byte word part of the name starting at characert `pos` in big
    106  * endian encoding with first char always at msb, zero padded at lsb.
    107  * Returns length of tag [0;8].
    108  */
    109 static int get_dict_tag(dict_entry_t *de, int pos, uint64_t *tag, uint64_t *mask,
    110         const char **tag_name, int *tag_len)
    111 {
    112     int i, n = 0;
    113     const char *a = 0;
    114     uint64_t w = 0;
    115 
    116     if (pos > de->len) {
    117         goto done;
    118     }
    119     a = de->text + pos;
    120     n = de->len - pos;
    121     if (n > 8) {
    122         n = 8;
    123     }
    124     i = n;
    125     while (i--) {
    126         w |= ((uint64_t)a[i]) << (56 - (i * 8));
    127     }
    128     *tag = w;
    129     *mask = ~(((uint64_t)(1) << (8 - n) * 8) - 1);
    130 done:
    131     if (tag_name) {
    132         *tag_name = a;
    133     }
    134     if (tag_len) {
    135         *tag_len = n;
    136     }
    137     return n;
    138 }
    139 
    140 
    141 /*
    142  * Find the median, but move earlier if the previous entry
    143  * is a strict prefix within the range.
    144  *
    145  * `b` is inclusive.
    146  *
    147  * The `pos` is a window into the key at an 8 byte multiple.
    148  *
    149  * Only consider the range `[pos;pos+8)` and move the median
    150  * up if an earlier key is a prefix or match within this
    151  * window. This is needed to handle trailing data in
    152  * a compared external key, and also to handle sub-tree
    153  * branching when two keys has same tag at pos.
    154  *
    155  * Worst case we get a linear search of length 8 if all
    156  * keys are perfect prefixes of their successor key:
    157  *   `a, ab, abc, ..., abcdefgh`
    158  * While the midpoint stills seeks towards 'a' for longer
    159  * such sequences, the branch logic will pool those
    160  * squences the share prefix groups of length 8.
    161  */
    162 static int split_dict_left(dict_entry_t *dict, int a, int b, int pos)
    163 {
    164     int m = a + (b - a) / 2;
    165     uint64_t wf = 0, wg = 0, wmf = 0, wmg = 0;
    166 
    167     while (m > a) {
    168         get_dict_tag(&dict[m - 1], pos, &wf, &wmf, 0, 0);
    169         get_dict_tag(&dict[m], pos, &wg, &wmg, 0, 0);
    170         if (((wf ^ wg) & wmf) != 0) {
    171             return m;
    172         }
    173         --m;
    174     }
    175     return m;
    176 }
    177 
    178 /*
    179  * When multiple tags are identical after split_dict_left has moved
    180  * intersection up so a == m, we need to split in the opposite direction
    181  * to ensure progress untill all tags in the range are identical
    182  * at which point the trie must descend.
    183  *
    184  * If all tags are the same from intersection to end, b + 1 is returned
    185  * which is not a valid element.
    186  */
    187 static int split_dict_right(dict_entry_t *dict, int a, int b, int pos)
    188 {
    189     int m = a + (b - a) / 2;
    190     uint64_t wf = 0, wg = 0, wmf = 0, wmg = 0;
    191 
    192     while (m < b) {
    193         get_dict_tag(&dict[m], pos, &wf, &wmf, 0, 0);
    194         get_dict_tag(&dict[m + 1], pos, &wg, &wmg, 0, 0);
    195         if (((wf ^ wg) & wmf) != 0) {
    196             return m + 1;
    197         }
    198         ++m;
    199     }
    200     return m + 1;
    201 }
    202 
    203 /*
    204  * Returns the first index where the tag does not terminate at
    205  * [pos..pos+7], or b + 1 if none exists.
    206  */
    207 static int split_dict_descend(dict_entry_t *dict, int a, int b, int pos)
    208 {
    209     while (a <= b) {
    210         if (0 < get_dict_suffix_len(&dict[a], pos)) {
    211             break;
    212         }
    213         ++a;
    214     }
    215     return a;
    216 }
    217 
    218 
    219 static int dict_cmp(const void *x, const void *y)
    220 {
    221     const dict_entry_t *a = x, *b = y;
    222     int k, n = a->len > b->len ? b->len : a->len;
    223 
    224     k = memcmp(a->text, b->text, (size_t)n);
    225     return k ? k : a->len - b->len;
    226 }
    227 
    228 /* Includes union vectors. */
    229 static inline int is_union_member(fb_member_t *member)
    230 {
    231     return (member->type.type == vt_compound_type_ref || member->type.type == vt_vector_compound_type_ref)
    232             && member->type.ct->symbol.kind == fb_is_union;
    233 }
    234 
    235 static dict_entry_t *build_compound_dict(fb_compound_type_t *ct, int *count_out)
    236 {
    237     fb_symbol_t *sym;
    238     fb_member_t *member;
    239     size_t n;
    240     dict_entry_t *dict, *de;
    241     char *strbuf = 0;
    242     size_t strbufsiz = 0;
    243     int is_union;
    244     size_t union_index = 0;
    245 
    246     n = 0;
    247     for (sym = ct->members; sym; sym = sym->link) {
    248         member = (fb_member_t *)sym;
    249         if (member->metadata_flags & fb_f_deprecated) {
    250             continue;
    251         }
    252         is_union = is_union_member(member);
    253         if (is_union) {
    254             ++n;
    255             strbufsiz += (size_t)member->symbol.ident->len + 6;
    256         }
    257         ++n;
    258     }
    259     *count_out = (int)n;
    260     if (n == 0) {
    261         return 0;
    262     }
    263     dict = malloc(n * sizeof(dict_entry_t) + strbufsiz);
    264     if (!dict) {
    265         return 0;
    266     }
    267     strbuf = (char *)dict + n * sizeof(dict_entry_t);
    268     de = dict;
    269     for (sym = ct->members; sym; sym = sym->link) {
    270         member = (fb_member_t *)sym;
    271         if (member->metadata_flags & fb_f_deprecated) {
    272             continue;
    273         }
    274         de->text = member->symbol.ident->text;
    275         de->len = (int)member->symbol.ident->len;
    276         de->data = member;
    277         de->hint = 0;
    278         ++de;
    279         is_union = is_union_member(member);
    280         if (is_union) {
    281             member->export_index = union_index++;
    282             de->len = (int)member->symbol.ident->len + 5;
    283             de->text = strbuf;
    284             memcpy(strbuf, member->symbol.ident->text, (size_t)member->symbol.ident->len);
    285             strbuf += member->symbol.ident->len;
    286             strcpy(strbuf, "_type");
    287             strbuf += 6;
    288             de->data = member;
    289             de->hint = 1;
    290             ++de;
    291         }
    292     }
    293     qsort(dict, n, sizeof(dict[0]), dict_cmp);
    294     return dict;
    295 }
    296 
    297 typedef struct {
    298     int count;
    299     fb_schema_t *schema;
    300     dict_entry_t *de;
    301 } install_enum_context_t;
    302 
    303 static void count_visible_enum_symbol(void *context, fb_symbol_t *sym)
    304 {
    305     install_enum_context_t *p = context;
    306 
    307     if (get_enum_if_visible(p->schema, sym)) {
    308         p->count++;
    309     }
    310 }
    311 
    312 static void install_visible_enum_symbol(void *context, fb_symbol_t *sym)
    313 {
    314     install_enum_context_t *p = context;
    315 
    316     if (get_enum_if_visible(p->schema, sym)) {
    317         p->de->text = sym->ident->text;
    318         p->de->len = (int)sym->ident->len;
    319         p->de->data = sym;
    320         p->de++;
    321     }
    322 }
    323 
    324 /*
    325  * A scope dictionary contains all the enum types defined under the given
    326  * namespace of the scope. The actually namespace is not contained in
    327  * the name - it is an implicit prefix. It is used when looking up a
    328  * symbolic constant assigned to a field such that the constant is first
    329  * searched for in the same scope (namespace) as the one that defined
    330  * the table owning the field assigned to. If that fails, a global
    331  * namespace prefixed lookup is needed, but this is separate from this
    332  * dictionary. In case of conflicts the local scope takes precedence
    333  * and must be searched first. Because each table parsed can a have a
    334  * unique local scope, we cannot install the the unprefixed lookup in
    335  * the same dictionary as the global lookup.
    336  *
    337  * NOTE: the scope may have been contanimated by being expanded by a
    338  * parent schema so we check that each symbol is visible to the current
    339  * schema. If we didn't do this, we would risk referring to enum parsers
    340  * that are not included in the generated source. The default empty
    341  * namespace (i.e. scope) is an example where this easily could happen.
    342  */
    343 static dict_entry_t *build_local_scope_dict(fb_schema_t *schema, fb_scope_t *scope, int *count_out)
    344 {
    345     dict_entry_t *dict;
    346     install_enum_context_t iec;
    347 
    348     fb_clear(iec);
    349 
    350     iec.schema = schema;
    351 
    352     fb_symbol_table_visit(&scope->symbol_index, count_visible_enum_symbol, &iec);
    353     *count_out = iec.count;
    354 
    355     if (iec.count == 0) {
    356         return 0;
    357     }
    358     dict = malloc((size_t)iec.count * sizeof(dict[0]));
    359     if (!dict) {
    360         return 0;
    361     }
    362     iec.de = dict;
    363     fb_symbol_table_visit(&scope->symbol_index, install_visible_enum_symbol, &iec);
    364     qsort(dict, (size_t)iec.count, sizeof(dict[0]), dict_cmp);
    365     return dict;
    366 }
    367 
    368 static dict_entry_t *build_global_scope_dict(catalog_t *catalog, int *count_out)
    369 {
    370     size_t i, n = (size_t)catalog->nenums;
    371     dict_entry_t *dict;
    372 
    373     *count_out = (int)n;
    374     if (n == 0) {
    375         return 0;
    376     }
    377     dict = malloc(n * sizeof(dict[0]));
    378     if (!dict) {
    379         return 0;
    380     }
    381     for (i = 0; i < (size_t)catalog->nenums; ++i) {
    382         dict[i].text = catalog->enums[i].name;
    383         dict[i].len = (int)strlen(catalog->enums[i].name);
    384         dict[i].data = catalog->enums[i].ct;
    385         dict[i].hint = 0;
    386     }
    387     qsort(dict, (size_t)catalog->nenums, sizeof(dict[0]), dict_cmp);
    388     *count_out = catalog->nenums;
    389     return dict;
    390 }
    391 
    392 static void clear_dict(dict_entry_t *dict)
    393 {
    394     if (dict) {
    395         free(dict);
    396     }
    397 }
    398 
    399 static int gen_field_match_handler(fb_output_t *out, fb_compound_type_t *ct, void *data, int is_union_type)
    400 {
    401     fb_member_t *member = data;
    402     fb_scoped_name_t snref;
    403     fb_symbol_text_t scope_name;
    404 
    405     int is_struct_container;
    406     int is_string = 0;
    407     int is_enum = 0;
    408     int is_vector = 0;
    409     int is_offset = 0;
    410     int is_scalar = 0;
    411     int is_optional = 0;
    412     int is_table = 0;
    413     int is_struct = 0;
    414     int is_union = 0;
    415     int is_union_vector = 0;
    416     int is_union_type_vector = 0;
    417     int is_base64 = 0;
    418     int is_base64url = 0;
    419     int is_nested = 0;
    420     int is_array = 0;
    421     int is_char_array = 0;
    422     size_t array_len = 0;
    423     fb_scalar_type_t st = 0;
    424     const char *tname_prefix = "n/a", *tname = "n/a"; /* suppress compiler warnigns */
    425     fb_literal_t literal;
    426 
    427     fb_clear(snref);
    428 
    429     fb_copy_scope(ct->scope, scope_name);
    430     is_struct_container = ct->symbol.kind == fb_is_struct;
    431     is_optional = !!(member->flags & fb_fm_optional);
    432 
    433     switch (member->type.type) {
    434     case vt_vector_type:
    435     case vt_vector_compound_type_ref:
    436     case vt_vector_string_type:
    437         is_vector = 1;
    438         break;
    439     }
    440 
    441     switch (member->type.type) {
    442     case vt_fixed_array_compound_type_ref:
    443     case vt_vector_compound_type_ref:
    444     case vt_compound_type_ref:
    445         fb_compound_name(member->type.ct, &snref);
    446         is_enum = member->type.ct->symbol.kind == fb_is_enum;
    447         is_struct = member->type.ct->symbol.kind == fb_is_struct;
    448         is_table = member->type.ct->symbol.kind == fb_is_table;
    449         is_union = member->type.ct->symbol.kind == fb_is_union && !is_union_type;
    450         if (is_enum) {
    451             st = member->type.ct->type.st;
    452             is_scalar = 1;
    453         }
    454         break;
    455     case vt_vector_string_type:
    456     case vt_string_type:
    457         is_string = 1;
    458         break;
    459     case vt_vector_type:
    460         /* Nested types are processed twice, once as an array, once as an object. */
    461         is_nested = member->nest != 0;
    462         is_base64 = member->metadata_flags & fb_f_base64;
    463         is_base64url = member->metadata_flags & fb_f_base64url;
    464         is_scalar = 1;
    465         st = member->type.st;
    466         break;
    467     case vt_fixed_array_type:
    468         is_scalar = 1;
    469         is_array = 1;
    470         array_len = member->type.len;
    471         st = member->type.st;
    472         break;
    473     case vt_scalar_type:
    474         is_scalar = 1;
    475         st = member->type.st;
    476         break;
    477     }
    478     if (member->type.type == vt_fixed_array_compound_type_ref) {
    479         assert(is_struct_container);
    480         is_array = 1;
    481         array_len = member->type.len;
    482     }
    483     if (is_base64 || is_base64url) {
    484         /* Even if it is nested, parse it as a regular base64 or base64url encoded vector. */
    485         if (st != fb_ubyte || !is_vector) {
    486             gen_panic(out, "internal error: unexpected base64 or base64url field type\n");
    487             return -1;
    488         }
    489         is_nested = 0;
    490         is_vector = 0;
    491         is_scalar = 0;
    492     }
    493     if (is_union_type) {
    494         is_scalar = 0;
    495     }
    496     if (is_vector && is_union_type) {
    497         is_union_type_vector = 1;
    498         is_vector = 0;
    499     }
    500     if (is_vector && is_union) {
    501         is_union_vector = 1;
    502         is_vector = 0;
    503     }
    504     if (is_array && is_scalar && st == fb_char) {
    505         is_array = 0;
    506         is_scalar = 0;
    507         is_char_array = 1;
    508     }
    509     if (is_nested == 1) {
    510         println(out, "if (buf != end && *buf == '[') { /* begin nested */"); indent();
    511     }
    512 repeat_nested:
    513     if (is_nested == 2) {
    514         unindent(); println(out, "} else { /* nested */"); indent();
    515         fb_compound_name((fb_compound_type_t *)&member->nest->symbol, &snref);
    516         if (member->nest->symbol.kind == fb_is_table) {
    517             is_table = 1;
    518         } else {
    519             is_struct = 1;
    520         }
    521         is_vector = 0;
    522         is_scalar = 0;
    523         println(out, "if (flatcc_builder_start_buffer(ctx->ctx, 0, 0, 0)) goto failed;");
    524     }
    525     is_offset = !is_scalar && !is_struct && !is_union_type;
    526 
    527     if (is_scalar) {
    528         tname_prefix = scalar_type_prefix(st);
    529         tname = st == fb_bool ? "uint8_t" : scalar_type_name(st);
    530     }
    531 
    532     /* Other types can also be vector, so we wrap. */
    533     if (is_vector) {
    534         if (is_offset) {
    535             println(out, "if (flatcc_builder_start_offset_vector(ctx->ctx)) goto failed;");
    536         } else {
    537             println(out,
    538                 "if (flatcc_builder_start_vector(ctx->ctx, %"PRIu64", %hu, UINT64_C(%"PRIu64"))) goto failed;",
    539                 (uint64_t)member->size, (short)member->align,
    540                 (uint64_t)FLATBUFFERS_COUNT_MAX(member->size));
    541         }
    542     }
    543     if (is_array) {
    544         if (is_scalar) {
    545             println(out, "size_t count = %d;", array_len);
    546             println(out, "%s *base = (%s *)((size_t)struct_base + %"PRIu64");",
    547                     tname, tname, (uint64_t)member->offset);
    548         }
    549         else {
    550             println(out, "size_t count = %d;", array_len);
    551             println(out, "void *base = (void *)((size_t)struct_base + %"PRIu64");",
    552                     (uint64_t)member->offset);
    553         }
    554     }
    555     if (is_char_array) {
    556         println(out, "char *base = (char *)((size_t)struct_base + %"PRIu64");",
    557                     (uint64_t)member->offset);
    558         println(out, "buf = flatcc_json_parser_char_array(ctx, buf, end, base, %d);", array_len);
    559     }
    560     if (is_array || is_vector) {
    561         println(out, "buf = flatcc_json_parser_array_start(ctx, buf, end, &more);");
    562         /* Note that we reuse `more` which is safe because it is updated at the end of the main loop. */
    563         println(out, "while (more) {"); indent();
    564     }
    565     if (is_scalar) {
    566         println(out, "%s val = 0;", tname);
    567         println(out, "static flatcc_json_parser_integral_symbol_f *symbolic_parsers[] = {");
    568         indent(); indent();
    569         /*
    570          * The scope name may be empty when no namespace is used. In that
    571          * case the global scope is the same, but performance the
    572          * duplicate doesn't matter.
    573          */
    574         if (is_enum) {
    575             println(out, "%s_parse_json_enum,", snref.text);
    576             println(out, "%s_local_%sjson_parser_enum,", out->S->basename, scope_name);
    577             println(out, "%s_global_json_parser_enum, 0 };", out->S->basename);
    578         } else {
    579             println(out, "%s_local_%sjson_parser_enum,", out->S->basename, scope_name);
    580             println(out, "%s_global_json_parser_enum, 0 };", out->S->basename);
    581         }
    582         unindent(); unindent();
    583     }
    584     /* It is not safe to acquire the pointer before building element table or string. */
    585     if (is_vector && !is_offset) {
    586         println(out, "if (!(pval = flatcc_builder_extend_vector(ctx->ctx, 1))) goto failed;");
    587     }
    588     if (is_struct_container) {
    589         if (!is_array && !is_char_array) {
    590             /* `struct_base` is given as argument to struct parsers. */
    591             println(out, "pval = (void *)((size_t)struct_base + %"PRIu64");", (uint64_t)member->offset);
    592         }
    593     } else if (is_struct && !is_vector) {
    594         /* Same logic as scalars in tables, but scalars must be tested for default. */
    595         println(out,
    596             "if (!(pval = flatcc_builder_table_add(ctx->ctx, %"PRIu64", %"PRIu64", %"PRIu16"))) goto failed;",
    597             (uint64_t)member->id, (uint64_t)member->size, (uint16_t)member->align);
    598     }
    599     if (is_scalar) {
    600         println(out, "buf = flatcc_json_parser_%s(ctx, (mark = buf), end, &val);", tname_prefix);
    601         println(out, "if (mark == buf) {"); indent();
    602         println(out, "buf = flatcc_json_parser_symbolic_%s(ctx, (mark = buf), end, symbolic_parsers, &val);", tname_prefix);
    603         println(out, "if (buf == mark || buf == end) goto failed;");
    604         unindent(); println(out, "}");
    605         if (!is_struct_container && !is_vector && !is_base64 && !is_base64url) {
    606 #if !FLATCC_JSON_PARSE_FORCE_DEFAULTS
    607             /* We need to create a check for the default value and create a table field if not the default. */
    608             if (!is_optional) {
    609                 if (!print_literal(st, &member->value, literal)) return -1;
    610                 println(out, "if (val != %s || (ctx->flags & flatcc_json_parser_f_force_add)) {", literal); indent();
    611             }
    612 #endif
    613             println(out, "if (!(pval = flatcc_builder_table_add(ctx->ctx, %"PRIu64", %"PRIu64", %hu))) goto failed;",
    614                     (uint64_t)member->id, (uint64_t)member->size, (short)member->align);
    615 #if !FLATCC_JSON_PARSE_FORCE_DEFAULTS
    616 #endif
    617         }
    618         /* For scalars in table field, and in struct container. */
    619         if (is_array) {
    620             println(out, "if (count) {"); indent();
    621             println(out, "%s%s_write_to_pe(base, val);", out->nsc, tname_prefix);
    622             println(out, "--count;");
    623             println(out, "++base;");
    624             unindent(); println(out, "} else if (!(ctx->flags & flatcc_json_parser_f_skip_array_overflow)) {"); indent();
    625             println(out, "return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_array_overflow);");
    626             unindent(); println(out, "}");
    627         } else {
    628             println(out, "%s%s_write_to_pe(pval, val);", out->nsc, tname_prefix);
    629         }
    630         if (!is_struct_container && !is_vector && !(is_scalar && is_optional)) {
    631             unindent(); println(out, "}");
    632         }
    633     } else if (is_struct) {
    634         if (is_array) {
    635             println(out, "if (count) {"); indent();
    636             println(out, "buf = %s_parse_json_struct_inline(ctx, buf, end, base);", snref.text);
    637             println(out, "--count;");
    638             println(out, "base = (void *)((size_t)base + %"PRIu64");", member->type.ct->size);
    639             unindent(); println(out, "} else if (!(ctx->flags & flatcc_json_parser_f_skip_array_overflow)) {"); indent();
    640             println(out, "return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_array_overflow);");
    641             unindent(); println(out, "}");
    642         } else {
    643             println(out, "buf = %s_parse_json_struct_inline(ctx, buf, end, pval);", snref.text);
    644         }
    645     } else if (is_string) {
    646         println(out, "buf = flatcc_json_parser_build_string(ctx, buf, end, &ref);");
    647     } else if (is_base64 || is_base64url) {
    648         println(out, "buf = flatcc_json_parser_build_uint8_vector_base64(ctx, buf, end, &ref, %u);",
    649                 !is_base64);
    650     } else if (is_table) {
    651         println(out, "buf = %s_parse_json_table(ctx, buf, end, &ref);", snref.text);
    652     } else if (is_union) {
    653         if (is_union_vector) {
    654             println(out, "buf = flatcc_json_parser_union_vector(ctx, buf, end, %"PRIu64", %"PRIu64", h_unions, %s_parse_json_union);",
    655                 (uint64_t)member->export_index, member->id, snref.text);
    656         } else {
    657             println(out, "buf = flatcc_json_parser_union(ctx, buf, end, %"PRIu64", %"PRIu64", h_unions, %s_parse_json_union);",
    658                 (uint64_t)member->export_index, member->id, snref.text);
    659         }
    660     } else if (is_union_type) {
    661         println(out, "static flatcc_json_parser_integral_symbol_f *symbolic_parsers[] = {");
    662         indent(); indent();
    663         println(out, "%s_parse_json_enum,", snref.text);
    664         println(out, "%s_local_%sjson_parser_enum,", out->S->basename, scope_name);
    665         println(out, "%s_global_json_parser_enum, 0 };", out->S->basename);
    666         unindent(); unindent();
    667         if (is_union_type_vector) {
    668         println(out, "buf = flatcc_json_parser_union_type_vector(ctx, buf, end, %"PRIu64", %"PRIu64", h_unions, symbolic_parsers, %s_parse_json_union, %s_json_union_accept_type);",
    669                 (uint64_t)member->export_index, member->id, snref.text, snref.text);
    670         } else {
    671             println(out, "buf = flatcc_json_parser_union_type(ctx, buf, end, %"PRIu64", %"PRIu64", h_unions, symbolic_parsers, %s_parse_json_union);",
    672                 (uint64_t)member->export_index, member->id, snref.text);
    673         }
    674     } else if (!is_vector && !is_char_array) {
    675         gen_panic(out, "internal error: unexpected type for trie member\n");
    676         return -1;
    677     }
    678     if (is_vector) {
    679         if (is_offset) {
    680             /* Deal with table and string vector elements - unions cannot be elements. */
    681             println(out, "if (!ref || !(pref = flatcc_builder_extend_offset_vector(ctx->ctx, 1))) goto failed;");
    682             /* We don't need to worry about endian conversion - offsets vectors fix this automatically. */
    683             println(out, "*pref = ref;");
    684         }
    685         println(out, "buf = flatcc_json_parser_array_end(ctx, buf, end, &more);");
    686         unindent(); println(out, "}");
    687         if (is_offset) {
    688             println(out, "ref = flatcc_builder_end_offset_vector(ctx->ctx);");
    689         } else {
    690             println(out, "ref = flatcc_builder_end_vector(ctx->ctx);");
    691         }
    692     }
    693     if (is_array) {
    694         println(out, "buf = flatcc_json_parser_array_end(ctx, buf, end, &more);");
    695         unindent(); println(out, "}");
    696         println(out, "if (count) {"); indent();
    697         println(out, "if (ctx->flags & flatcc_json_parser_f_reject_array_underflow) {"); indent();
    698         println(out, "return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_array_underflow);");
    699         unindent(); println(out, "}");
    700         if (is_scalar) {
    701             println(out, "memset(base, 0, count * sizeof(*base));");
    702         } else {
    703             println(out, "memset(base, 0, count * %"PRIu64");", (uint64_t)member->type.ct->size);
    704         }
    705         unindent(); println(out, "}");
    706     }
    707     if (is_nested == 1) {
    708         is_nested = 2;
    709         goto repeat_nested;
    710     }
    711     if (is_nested == 2) {
    712         println(out, "if (!ref) goto failed;");
    713         println(out, "ref = flatcc_builder_end_buffer(ctx->ctx, ref);");
    714         unindent(); println(out, "} /* end nested */");
    715     }
    716     if (is_nested || is_vector || is_table || is_string || is_base64 || is_base64url) {
    717         println(out, "if (!ref || !(pref = flatcc_builder_table_add_offset(ctx->ctx, %"PRIu64"))) goto failed;", member->id);
    718         println(out, "*pref = ref;");
    719     }
    720     return 0;
    721 }
    722 
    723 static void gen_field_match(fb_output_t *out, fb_compound_type_t *ct, void *data, int hint, int n)
    724 {
    725     println(out, "buf = flatcc_json_parser_match_symbol(ctx, (mark = buf), end, %d);", n);
    726     println(out, "if (mark != buf) {"); indent();
    727     gen_field_match_handler(out, ct, data, hint);
    728     unindent(); println(out, "} else {"); indent();
    729 }
    730 
    731 /* This also handles union type enumerations. */
    732 static void gen_enum_match_handler(fb_output_t *out, fb_compound_type_t *ct, void *data, int unused_hint)
    733 {
    734     fb_member_t *member = data;
    735 
    736     (void)unused_hint;
    737 
    738     /*
    739      * This is rather unrelated to the rest, we just use the same
    740      * trie generation logic. Here we simply need to assign a known
    741      * value to the enum parsers output arguments.
    742      */
    743     switch (ct->type.st) {
    744     case fb_bool:
    745     case fb_ubyte:
    746     case fb_ushort:
    747     case fb_uint:
    748     case fb_ulong:
    749         println(out, "*value = UINT64_C(%"PRIu64"), *value_sign = 0;",
    750                 member->value.u);
    751         break;
    752     case fb_byte:
    753     case fb_short:
    754     case fb_int:
    755     case fb_long:
    756         if (member->value.i < 0) {
    757             println(out, "*value = UINT64_C(%"PRIu64"), *value_sign = 1;", member->value.i);
    758         } else {
    759             println(out, "*value = UINT64_C(%"PRIu64"), *value_sign = 0;", member->value.i);
    760         }
    761         break;
    762     default:
    763         gen_panic(out, "internal error: invalid enum type\n");
    764     }
    765 }
    766 
    767 static void gen_enum_match(fb_output_t *out, fb_compound_type_t *ct, void *data, int hint, int n)
    768 {
    769     println(out, "buf = flatcc_json_parser_match_constant(ctx, (mark = buf), end, %d, aggregate);", n);
    770     println(out, "if (buf != mark) {"); indent();
    771     gen_enum_match_handler(out, ct, data, hint);
    772     unindent(); println(out, "} else {"); indent();
    773 }
    774 
    775 static void gen_scope_match_handler(fb_output_t *out, fb_compound_type_t *unused_ct, void *data, int unused_hint)
    776 {
    777     fb_compound_type_t *ct = data;
    778     fb_scoped_name_t snt;
    779 
    780     (void)unused_ct;
    781     (void)unused_hint;
    782     assert(ct->symbol.kind == fb_is_enum || ct->symbol.kind == fb_is_union);
    783 
    784     fb_clear(snt);
    785     fb_compound_name(ct, &snt);
    786     /* May be included from another file. Unions also have _enum parsers. */
    787     println(out, "buf = %s_parse_json_enum(ctx, buf, end, value_type, value, aggregate);", snt.text);
    788 }
    789 
    790 static void gen_scope_match(fb_output_t *out, fb_compound_type_t *ct, void *data, int hint, int n)
    791 {
    792     println(out, "buf = flatcc_json_parser_match_scope(ctx, (mark = buf), end, %d);", n);
    793     println(out, "if (buf != mark) {"); indent();
    794     gen_scope_match_handler(out, ct, data, hint);
    795     unindent(); println(out, "} else {"); indent();
    796 }
    797 
    798 static void gen_field_unmatched(fb_output_t *out)
    799 {
    800     println(out, "buf = flatcc_json_parser_unmatched_symbol(ctx, buf, end);");
    801 }
    802 
    803 static void gen_enum_unmatched(fb_output_t *out)
    804 {
    805     println(out, "return unmatched;");
    806 }
    807 
    808 static void gen_scope_unmatched(fb_output_t *out)
    809 {
    810     println(out, "return unmatched;");
    811 }
    812 
    813 /*
    814  * Generate a trie for all members or a compound type.
    815  * This may be a struct or a table.
    816  *
    817  * We have a ternary trie where a search word w compares:
    818  * w < wx_tag is one branch [a;x), iff a < x.
    819  * w > wx_tag is another branch (y;b], iff b > y
    820  * and w == wx_tag is a third branch [x;y].
    821  *
    822  * The sets [a;x) and (y;b] may be empty in which case a non-match
    823  * action is triggered.
    824  *
    825  * [x..y] is a set of one or more fields that share the same tag at the
    826  * current position. The first (and only the first) field name in this
    827  * set may terminate withint the current tag (when suffix length k ==
    828  * 0).  There is therefore potentially both a direct field action and a
    829  * sub-tree action. Once there is only one field in the set and the
    830  * field name terminates within the current tag, the search word is
    831  * masked and tested against the field tag and the search word is also
    832  * tested for termination in the buffer at the first position after the
    833  * field match. If the termination was not found a non-match action is
    834  * triggered.
    835  *
    836  * A non-match action may be to silently consume the rest of the
    837  * search identifier and then the json value, or to report and
    838  * error.
    839  *
    840  * A match action triggers a json value parse of a known type
    841  * which updates into a flatcc builder object. If the type is
    842  * basic (string or scalar) the update simple, otherwise if
    843  * the type is within the same schema, we push context
    844  * and switch to parse the nested type, otherwise we call
    845  * a parser in another schema. When a trie is done, we
    846  * switch back context if in the same schema. The context
    847  * lives on a stack. This avoids deep recursion because
    848  * schema parsers are not mutually recursive.
    849  *
    850  * The trie is also used to parse enums and scopes (namespace prefixes)
    851  * with a slight modification.
    852  */
    853 
    854 enum trie_type { table_trie, struct_trie, enum_trie, local_scope_trie, global_scope_trie };
    855 typedef struct trie trie_t;
    856 
    857 typedef void gen_match_f(fb_output_t *out, fb_compound_type_t *ct, void *data, int hint, int n);
    858 typedef void gen_unmatched_f(fb_output_t *out);
    859 
    860 struct trie {
    861     dict_entry_t *dict;
    862     gen_match_f *gen_match;
    863     gen_unmatched_f *gen_unmatched;
    864     /* Not used with scopes. */
    865     fb_compound_type_t *ct;
    866     int type;
    867     int union_total;
    868     int label;
    869 };
    870 
    871 /*
    872  * This function is a final handler of the `gen_trie` function. Often
    873  * just to handle a single match, but also to handle a prefix range
    874  * special case like keys in `{ a, alpha, alpha2 }`.
    875  *
    876  * (See also special case of two non-prefix keys below).
    877  *
    878  * We know that all keys [a..b] have length in the range [pos..pos+8)
    879  * and also that key x is proper prefix of key x + 1, x in [a..b).
    880  *
    881  * It is possible that `a == b`.
    882  *
    883  * We conduct a binary search by testing the middle for masked match and
    884  * gradually refine until we do not have a match or have a single
    885  * element match.
    886  *
    887  * (An alternative algorithm xors 8 byte tag with longest prefix and
    888  * finds ceiling of log 2 using a few bit logic operations or intrinsic
    889  * zero count and creates a jump table of at most 8 elements, but is
    890  * hardly worthwhile vs 3 comparisons and 3 AND operations and often
    891  * less than that.)
    892  *
    893  * Once we have a single element match we need to confirm the successor
    894  * symbol is not any valid key - this differs among trie types and is
    895  * therefore the polymorph match logic handles the final confirmed match
    896  * or mismatch.
    897  *
    898  * Each trie type has special operation for implementing a matched and
    899  * a failed match. Our job is to call these for each key in the range.
    900  *
    901  * While not the original intention, the `gen_prefix_trie` also handles the
    902  * special case where the set has two keys where one is not a prefix of
    903  * the other, but both terminate in the same tag. In this case we can
    904  * immediately do an exact match test and skip the less than
    905  * comparision. We need no special code for this, assuming the function
    906  * is called correctly. This significantly reduces the branching in a
    907  * case like "Red, Green, Blue".
    908  *
    909  * If `label` is positive, it is used to jump to additional match logic
    910  * when a prefix was not matched. If 0 there is no additional logic and
    911  * the symbol is considered unmatched immediately.
    912  */
    913 static void gen_prefix_trie(fb_output_t *out, trie_t *trie, int a, int b, int pos, int label)
    914 {
    915     int m, n;
    916     uint64_t tag = 00, mask = 0;
    917     const char *name;
    918     int len;
    919 
    920     /*
    921      * Weigh the intersection towards the longer prefix. Notably if we
    922      * have two keys it makes no sense to check the shorter key first.
    923      */
    924     m = a + (b - a + 1) / 2;
    925 
    926     n = get_dict_tag(&trie->dict[m], pos, &tag, &mask, &name, &len);
    927     if (n == 8) {
    928         println(out, "if (w == 0x%"PRIx64") { /* \"%.*s\" */", tag, len, name); indent();
    929     } else {
    930         println(out, "if ((w & 0x%"PRIx64") == 0x%"PRIx64") { /* \"%.*s\" */",
    931                 mask, tag, len, name); indent();
    932     }
    933     if (m == a) {
    934         /* There can be only one. */
    935         trie->gen_match(out, trie->ct, trie->dict[m].data, trie->dict[m].hint, n);
    936         if (label > 0) {
    937             println(out, "goto pfguard%d;", label);
    938         } else {
    939             trie->gen_unmatched(out);
    940         }
    941         unindent(); println(out, "}");
    942         unindent(); println(out, "} else { /* \"%.*s\" */", len, name); indent();
    943         if (label > 0) {
    944             println(out, "goto pfguard%d;", label);
    945         } else {
    946             trie->gen_unmatched(out);
    947         }
    948     } else {
    949         if (m == b) {
    950             trie->gen_match(out, trie->ct, trie->dict[m].data, trie->dict[m].hint, n);
    951             if (label > 0) {
    952                 println(out, "goto pfguard%d;", label);
    953             } else {
    954                 trie->gen_unmatched(out);
    955             }
    956             unindent(); println(out, "}");
    957         } else {
    958             gen_prefix_trie(out, trie, m, b, pos, label);
    959         }
    960         unindent(); println(out, "} else { /* \"%.*s\" */", len, name); indent();
    961         gen_prefix_trie(out, trie, a, m - 1, pos, label);
    962     }
    963     unindent(); println(out, "} /* \"%.*s\" */", len, name);
    964 }
    965 
    966 static void gen_trie(fb_output_t *out, trie_t *trie, int a, int b, int pos)
    967 {
    968     int x, k;
    969     uint64_t tag = 0, mask = 0;
    970     const char *name = "";
    971     int len = 0, has_prefix_key = 0, prefix_guard = 0, has_descend;
    972     int label = 0;
    973 
    974     /*
    975      * Process a trie at the level given by pos. A single level covers
    976      * one tag.
    977      *
    978      * A tag is a range of 8 characters [pos..pos+7] that is read as a
    979      * single big endian word and tested as against a ternary trie
    980      * generated in code. In generated code the tag is stored in "w".
    981      *
    982      * Normally trailing data in a tag is not a problem
    983      * because the difference between two keys happen in the middle and
    984      * trailing data is not valid key material. When the difference is
    985      * at the end, we get a lot of special cases to handle.
    986      *
    987      * Regardless, when we believe we have a match, a final check is
    988      * made to ensure that the next character after the match is not a
    989      * valid key character - for quoted keys a valid termiantot is a
    990      * quote, for unquoted keys it can be one of several characters -
    991      * therefore quoted keys are faster to parse, even if they consume
    992      * more space. The trie does not care about these details, the
    993      * gen_match function handles this transparently for different
    994      * symbol types.
    995      */
    996 
    997 
    998     /*
    999     * If we have one or two keys that terminate in this tag, there is no
   1000     * need to do a branch test before matching exactly.
   1001     *
   1002     * We observe that `gen_prefix_trie` actually handles this
   1003     * case well, even though it was not designed for it.
   1004     */
   1005     if ((get_dict_suffix_len(&trie->dict[a], pos) == 0) &&
   1006         (b == a || (b == a + 1 && get_dict_suffix_len(&trie->dict[b], pos) == 0))) {
   1007         gen_prefix_trie(out, trie, a, b, pos, 0);
   1008         return;
   1009     }
   1010 
   1011     /*
   1012      * Due trie nature, we have a left, middle, and right range where
   1013      * the middle range all compare the same at the current trie level
   1014      * when masked against shortest (and first) key in middle range.
   1015      */
   1016     x = split_dict_left(trie->dict, a, b, pos);
   1017 
   1018     if (x > a) {
   1019         /*
   1020          * This is normal early branch with a key `a < x < b` such that
   1021          * any shared prefix ranges do not span x.
   1022          */
   1023         get_dict_tag(&trie->dict[x], pos, &tag, &mask, &name, &len);
   1024         println(out, "if (w < 0x%"PRIx64") { /* branch \"%.*s\" */", tag, len, name); indent();
   1025         gen_trie(out, trie, a, x - 1, pos);
   1026         unindent(); println(out, "} else { /* branch \"%.*s\" */", len, name); indent();
   1027         gen_trie(out, trie, x, b, pos);
   1028         unindent(); println(out, "} /* branch \"%.*s\" */", len, name);
   1029         return;
   1030     }
   1031     x = split_dict_right(trie->dict, a, b, pos);
   1032 
   1033     /*
   1034      * [a .. x-1] is a non-empty sequence of prefixes,
   1035      * for example { a123, a1234, a12345 }.
   1036      * The keys might not terminate in the current tag. To find those
   1037      * that do, we will evaluate k such that:
   1038      * [a .. k-1] are prefixes that terminate in the current tag if any
   1039      * such exists.
   1040      * [x..b] are keys that are prefixes up to at least pos + 7 but
   1041      * do not terminate in the current tag.
   1042      * [k..x-1] are prefixes that do not termiante in the current tag.
   1043      * Note that they might not be prefixes when considering more than the
   1044      * current tag.
   1045      * The range [a .. x-1] can ge generated with `gen_prefix_trie`.
   1046      *
   1047      * We generally have the form
   1048      *
   1049      * [a..b] =
   1050      * (a)<prefixes>, (k-1)<descend-prefix>, (k)<descend>, (x)<reminder>
   1051      *
   1052      * Where <prefixes> are keys that terminate at the current tag.
   1053      * <descend> are keys that have the prefixes as prefix but do not
   1054      * terminate at the current tag.
   1055      * <descend-prerfix> is a single key that terminates exactly
   1056      * where the tag ends. If there are no descend keys it is part of
   1057      * prefixes, otherwise it is tested as a special case.
   1058      * <reminder> are any keys larger than the prefixes.
   1059      *
   1060      * The reminder keys cannot be tested before we are sure that no
   1061      * prefix is matching at least no prefixes that is not a
   1062      * descend-prefix. This is because less than comparisons are
   1063      * affected by trailing data within the tag caused by prefixes
   1064      * terminating early. Trailing data is not a problem if two keys are
   1065      * longer than the point where they differ even if they terminate
   1066      * within the current tag.
   1067      *
   1068      * Thus, if we have non-empty <descend> and non-empty <reminder>,
   1069      * the reminder must guard against any matches in prefix but not
   1070      * against any matches in <descend>. If <descend> is empty and
   1071      * <prefixes> == <descend-prefix> a guard is also not needed.
   1072      */
   1073 
   1074     /* Find first prefix that does not terminate at the current level, or x if absent */
   1075     k = split_dict_descend(trie->dict, a, x - 1, pos);
   1076     has_descend = k < x;
   1077 
   1078     /* If we have a descend, process that in isolation. */
   1079     if (has_descend) {
   1080         has_prefix_key = k > a && get_dict_tag_len(&trie->dict[k - 1], pos) == 8;
   1081         get_dict_tag(&trie->dict[k], pos, &tag, &mask, &name, &len);
   1082         println(out, "if (w == 0x%"PRIx64") { /* descend \"%.*s\" */", tag, len, name); indent();
   1083         if (has_prefix_key) {
   1084             /* We have a key that terminates at the descend prefix. */
   1085             println(out, "/* descend prefix key \"%.*s\" */", len, name);
   1086             trie->gen_match(out, trie->ct, trie->dict[k - 1].data, trie->dict[k - 1].hint, 8);
   1087             println(out, "/* descend suffix \"%.*s\" */", len, name);
   1088         }
   1089         println(out, "buf += 8;");
   1090         println(out, "w = flatcc_json_parser_symbol_part(buf, end);");
   1091         gen_trie(out, trie, k, x - 1, pos + 8);
   1092         if (has_prefix_key) {
   1093             unindent(); println(out, "} /* desend suffix \"%.*s\" */", len, name);
   1094             /* Here we move the <descend-prefix> key out of the <descend> range. */
   1095             --k;
   1096         }
   1097         unindent(); println(out, "} else { /* descend \"%.*s\" */", len, name); indent();
   1098     }
   1099     prefix_guard = a < k && x <= b;
   1100     if (prefix_guard) {
   1101         label = ++trie->label;
   1102     }
   1103     if (a < k) {
   1104         gen_prefix_trie(out, trie, a, k - 1, pos, label);
   1105     }
   1106     if (prefix_guard) {
   1107         /* All prefixes tested, but none matched. */
   1108         println(out, "goto endpfguard%d;", label);
   1109         margin();
   1110         println(out, "pfguard%d:", label);
   1111         unmargin();
   1112     }
   1113     if (x <= b) {
   1114         gen_trie(out, trie, x, b, pos);
   1115     } else if (a >= k) {
   1116         trie->gen_unmatched(out);
   1117     }
   1118     if (prefix_guard) {
   1119         margin();
   1120         println(out, "endpfguard%d:", label);
   1121         unmargin();
   1122         println(out, "(void)0;");
   1123     }
   1124     if (has_descend) {
   1125         unindent(); println(out, "} /* descend \"%.*s\" */", len, name);
   1126     }
   1127 }
   1128 
   1129 
   1130 /*
   1131  * Parsing symbolic constants:
   1132  *
   1133  * An enum parser parses the local symbols and translate them into
   1134  * numeric values.
   1135  *
   1136  * If a symbol wasn't matched, e.g. "Red", it might be matched with
   1137  * "Color.Red" but the enum parser does not handle this.
   1138  *
   1139  * Instead a scope parser maps each type in the scope to a call
   1140  * to an enum parser, e.g. "Color." maps to a color enum parser
   1141  * that understands "Red". If this also fails, a call is made
   1142  * to a global scope parser that maps a namespace to a local
   1143  * scope parser, for example "Graphics.Color.Red" first
   1144  * recognizes the namespace "Graphics." which may or may not
   1145  * be the same as the local scope tried earlier, then "Color."
   1146  * is matched and finally "Red".
   1147  *
   1148  * The scope and namespace parsers may cover extend namespaces from
   1149  * include files so each file calls into dependencies as necessary.
   1150  * This means the same scope can have multiple parsers and must
   1151  * therefore be name prefixed by the basename of the include file.
   1152  *
   1153  * The enums can only exist in a single file.
   1154  *
   1155  * The local scope is defined as the scope in which the consuming
   1156  * fields container is defined, so if Pen is a table in Graphics
   1157  * with a field named "ink" and the pen is parsed as
   1158  *   { "ink": "Color.Red" }, then Color would be parsed in the
   1159  * Graphics scope. If ink was and enum of type Color, the enum
   1160  * parser would be tried first. If ink was, say, an integer
   1161  * type, it would not try an enum parse first but try the local
   1162  * scope, then the namespace scope.
   1163  *
   1164  * It is permitted to have multiple symbols in a string when
   1165  * the enum type has flag attribute so values can be or'ed together.
   1166  * The parser does not attempt to validate this and will simple
   1167  * 'or' together multiple values after coercing each to the
   1168  * receiving field type: "Has.ink Has.shape Has.brush".
   1169  */
   1170 
   1171 
   1172 /*
   1173  * Used by scalar/enum/union_type table fields to look up symbolic
   1174  * constants in same scope as the table was defined, thus avoiding
   1175  * namespace prefix.
   1176  *
   1177  * Theh matched name then calls into the type specific parser which
   1178  * may be in a dependent file.
   1179  *
   1180  * Because each scope may be extended in dependent schema files
   1181  * we recreate the scope in full in each file.
   1182  */
   1183 static void gen_local_scope_parser(void *context, fb_scope_t *scope)
   1184 {
   1185     fb_output_t *out = context;
   1186     int n = 0;
   1187     trie_t trie;
   1188     fb_symbol_text_t scope_name;
   1189 
   1190     fb_clear(trie);
   1191     fb_copy_scope(scope, scope_name);
   1192     if (((trie.dict = build_local_scope_dict(out->S, scope, &n)) == 0) && n > 0) {
   1193         gen_panic(out, "internal error: could not build dictionary for json parser\n");
   1194         return;
   1195     }
   1196     /* Not used for scopes. */
   1197     trie.ct = 0;
   1198     trie.type = local_scope_trie;
   1199     trie.gen_match = gen_scope_match;
   1200     trie.gen_unmatched = gen_scope_unmatched;
   1201     println(out, "static const char *%s_local_%sjson_parser_enum(flatcc_json_parser_t *ctx, const char *buf, const char *end,",
   1202         out->S->basename, scope_name);
   1203     indent(); indent();
   1204     println(out, "int *value_type, uint64_t *value, int *aggregate)");
   1205     unindent(); unindent();
   1206     println(out, "{"); indent();
   1207     if (n == 0) {
   1208         println(out, "/* Scope has no enum / union types to look up. */");
   1209         println(out, "return buf; /* unmatched; */");
   1210         unindent(); println(out, "}");
   1211     } else {
   1212         println(out, "const char *unmatched = buf;");
   1213         println(out, "const char *mark;");
   1214         println(out, "uint64_t w;");
   1215         println(out, "");
   1216         println(out, "w = flatcc_json_parser_symbol_part(buf, end);");
   1217         gen_trie(out, &trie, 0, n - 1, 0);
   1218         println(out, "return buf;");
   1219         unindent(); println(out, "}");
   1220     }
   1221     println(out, "");
   1222     clear_dict(trie.dict);
   1223 }
   1224 
   1225 /*
   1226  * This parses namespace prefixed types. Because scopes can be extended
   1227  * in dependent schema files, each file has its own global scope parser.
   1228  * The matched types call into type specific parsers that may be in
   1229  * a dependent file.
   1230  *
   1231  * When a local scope is also parsed, it should be tried before the
   1232  * global scope.
   1233  */
   1234 static int gen_global_scope_parser(fb_output_t *out)
   1235 {
   1236     int n = 0;
   1237     trie_t trie;
   1238     catalog_t catalog;
   1239 
   1240     fb_clear(trie);
   1241     if (build_catalog(&catalog, out->S, 1, &out->S->root_schema->scope_index)) {
   1242         return -1;
   1243     }
   1244 
   1245     if ((trie.dict = build_global_scope_dict(&catalog, &n)) == 0 && n > 0) {
   1246         clear_catalog(&catalog);
   1247         gen_panic(out, "internal error: could not build dictionary for json parser\n");
   1248         return -1;
   1249     }
   1250     /* Not used for scopes. */
   1251     trie.ct = 0;
   1252     trie.type = global_scope_trie;
   1253     trie.gen_match = gen_scope_match;
   1254     trie.gen_unmatched = gen_scope_unmatched;
   1255     println(out, "static const char *%s_global_json_parser_enum(flatcc_json_parser_t *ctx, const char *buf, const char *end,", out->S->basename);
   1256     indent(); indent();
   1257     println(out, "int *value_type, uint64_t *value, int *aggregate)");
   1258     unindent(); unindent();
   1259     println(out, "{"); indent();
   1260     if (n == 0) {
   1261         println(out, "/* Global scope has no enum / union types to look up. */");
   1262         println(out, "return buf; /* unmatched; */");
   1263         unindent(); println(out, "}");
   1264     } else {
   1265         println(out, "const char *unmatched = buf;");
   1266         println(out, "const char *mark;");
   1267         println(out, "uint64_t w;");
   1268         println(out, "");
   1269         println(out, "w = flatcc_json_parser_symbol_part(buf, end);");
   1270         gen_trie(out, &trie, 0, n - 1, 0);
   1271         println(out, "return buf;");
   1272         unindent(); println(out, "}");
   1273     }
   1274     println(out, "");
   1275     clear_dict(trie.dict);
   1276     clear_catalog(&catalog);
   1277     return 0;
   1278 }
   1279 
   1280 /*
   1281  * Constants have the form `"Red"` or `Red` but may also be part
   1282  * of a list of flags: `"Normal High Average"` or `Normal High
   1283  * Average`. `more` indicates more symbols follow.
   1284  *
   1285  * Returns input argument if there was no valid match,
   1286  * `end` on syntax error, and `more=1` if matched and
   1287  * there are more constants to parse.
   1288  * Applies the mached and coerced constant to `pval`
   1289  * with a binary `or` operation so `pval` must be initialized
   1290  * to 0 before teh first constant in a list.
   1291  */
   1292 static int gen_enum_parser(fb_output_t *out, fb_compound_type_t *ct)
   1293 {
   1294     fb_scoped_name_t snt;
   1295     int n = 0;
   1296     trie_t trie;
   1297 
   1298     fb_clear(trie);
   1299     assert(ct->symbol.kind == fb_is_enum || ct->symbol.kind == fb_is_union);
   1300 
   1301     if ((trie.dict = build_compound_dict(ct, &n)) == 0 && n > 0) {
   1302         gen_panic(out, "internal error: could not build dictionary for json parser\n");
   1303         return -1;
   1304     }
   1305     trie.ct = ct;
   1306     trie.type = enum_trie;
   1307     trie.gen_match = gen_enum_match;
   1308     trie.gen_unmatched = gen_enum_unmatched;
   1309 
   1310     fb_clear(snt);
   1311     fb_compound_name(ct, &snt);
   1312 
   1313     println(out, "static const char *%s_parse_json_enum(flatcc_json_parser_t *ctx, const char *buf, const char *end,", snt.text);
   1314     indent(); indent();
   1315     println(out, "int *value_sign, uint64_t *value, int *aggregate)");
   1316     unindent(); unindent();
   1317     println(out, "{"); indent();
   1318     if (n == 0) {
   1319         println(out, "/* Enum has no fields. */");
   1320         println(out, "*aggregate = 0;");
   1321         println(out, "return buf; /* unmatched; */");
   1322         unindent(); println(out, "}");
   1323     } else {
   1324         println(out, "const char *unmatched = buf;");
   1325         println(out, "const char *mark;");
   1326         println(out, "uint64_t w;");
   1327         println(out, "");
   1328         println(out, "w = flatcc_json_parser_symbol_part(buf, end);");
   1329         gen_trie(out, &trie, 0, n - 1, 0);
   1330         println(out, "return buf;");
   1331         unindent(); println(out, "}");
   1332     }
   1333     println(out, "");
   1334     clear_dict(trie.dict);
   1335     return 0;
   1336 }
   1337 
   1338 /*
   1339  * We do not check for duplicate settings or missing struct fields.
   1340  * Missing fields are zeroed.
   1341  *
   1342  * TODO: we should track nesting level because nested structs do not
   1343  * interact with the builder so the builders level limit will not kick
   1344  * in. As long as we get input from our own parser we should, however,
   1345  * be reasonable safe as nesting is bounded.
   1346  */
   1347 static int gen_struct_parser_inline(fb_output_t *out, fb_compound_type_t *ct)
   1348 {
   1349     fb_scoped_name_t snt;
   1350     int n;
   1351     trie_t trie;
   1352 
   1353     fb_clear(trie);
   1354     assert(ct->symbol.kind == fb_is_struct);
   1355     if ((trie.dict = build_compound_dict(ct, &n)) == 0 && n > 0) {
   1356         gen_panic(out, "internal error: could not build dictionary for json parser\n");
   1357         return -1;
   1358     }
   1359     trie.ct = ct;
   1360     trie.type = struct_trie;
   1361     trie.gen_match = gen_field_match;
   1362     trie.gen_unmatched = gen_field_unmatched;
   1363 
   1364     fb_clear(snt);
   1365     fb_compound_name(ct, &snt);
   1366     println(out, "static const char *%s_parse_json_struct_inline(flatcc_json_parser_t *ctx, const char *buf, const char *end, void *struct_base)", snt.text);
   1367     println(out, "{"); indent();
   1368     println(out, "int more;");
   1369     if (n > 0) {
   1370         println(out, "flatcc_builder_ref_t ref;");
   1371         println(out, "void *pval;");
   1372         println(out, "const char *mark;");
   1373         println(out, "uint64_t w;");
   1374     }
   1375     println(out, "");
   1376     println(out, "buf = flatcc_json_parser_object_start(ctx, buf, end, &more);");
   1377     println(out, "while (more) {"); indent();
   1378     if (n == 0) {
   1379         println(out, "/* Empty struct. */");
   1380         println(out, "buf = flatcc_json_parser_unmatched_symbol(ctx, buf, end);");
   1381     } else {
   1382         println(out, "buf = flatcc_json_parser_symbol_start(ctx, buf, end);");
   1383         println(out, "w = flatcc_json_parser_symbol_part(buf, end);");
   1384         gen_trie(out, &trie, 0, n - 1, 0);
   1385     }
   1386     println(out, "buf = flatcc_json_parser_object_end(ctx, buf, end , &more);");
   1387     unindent(); println(out, "}");
   1388     println(out, "return buf;");
   1389     if (n > 0) {
   1390         /* Set runtime error if no other error was set already. */
   1391         margin();
   1392         println(out, "failed:");
   1393         unmargin();
   1394         println(out, "return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_runtime);");
   1395     }
   1396     unindent(); println(out, "}");
   1397     println(out, "");
   1398     clear_dict(trie.dict);
   1399     return 0;
   1400 }
   1401 
   1402 static int gen_struct_parser(fb_output_t *out, fb_compound_type_t *ct)
   1403 {
   1404     fb_scoped_name_t snt;
   1405 
   1406     assert(ct->symbol.kind == fb_is_struct);
   1407     fb_clear(snt);
   1408     fb_compound_name(ct, &snt);
   1409     println(out, "static const char *%s_parse_json_struct(flatcc_json_parser_t *ctx, const char *buf, const char *end, flatcc_builder_ref_t *result)", snt.text);
   1410     println(out, "{"); indent();
   1411     println(out, "void *pval;");
   1412     println(out, "");
   1413     println(out, "*result = 0;");
   1414     println(out, "if (!(pval = flatcc_builder_start_struct(ctx->ctx, %"PRIu64", %"PRIu16"))) goto failed;",
   1415             (uint64_t)ct->size, (uint16_t)ct->align);
   1416     println(out, "buf = %s_parse_json_struct_inline(ctx, buf, end, pval);", snt.text);
   1417     println(out, "if (ctx->error || !(*result = flatcc_builder_end_struct(ctx->ctx))) goto failed;");
   1418     println(out, "return buf;");
   1419     margin();
   1420     println(out, "failed:");
   1421     unmargin();
   1422     println(out, "return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_runtime);");
   1423     unindent(); println(out, "}");
   1424     println(out, "");
   1425     println(out, "static inline int %s_parse_json_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)", snt.text);
   1426     println(out, "{"); indent();
   1427     println(out, "return flatcc_json_parser_struct_as_root(B, ctx, buf, bufsiz, flags, fid, %s_parse_json_struct);",
   1428             snt.text);
   1429     unindent(); println(out, "}");
   1430     println(out, "");
   1431     return 0;
   1432 }
   1433 
   1434 static int gen_table_parser(fb_output_t *out, fb_compound_type_t *ct)
   1435 {
   1436     fb_scoped_name_t snt;
   1437     fb_member_t *member;
   1438     int first, i, n;
   1439     int is_union, is_required;
   1440     trie_t trie;
   1441 
   1442     fb_clear(trie);
   1443     assert(ct->symbol.kind == fb_is_table);
   1444     if ((trie.dict = build_compound_dict(ct, &n)) == 0 && n > 0) {
   1445         gen_panic(out, "internal error: could not build dictionary for json parser\n");
   1446         return -1;
   1447     }
   1448     trie.ct = ct;
   1449     trie.type = table_trie;
   1450     trie.gen_match = gen_field_match;
   1451     trie.gen_unmatched = gen_field_unmatched;
   1452 
   1453     trie.union_total = 0;
   1454     for (i = 0; i < n; ++i) {
   1455         trie.union_total += !!trie.dict[i].hint;
   1456     }
   1457 
   1458     fb_clear(snt);
   1459     fb_compound_name(ct, &snt);
   1460     println(out, "static const char *%s_parse_json_table(flatcc_json_parser_t *ctx, const char *buf, const char *end, flatcc_builder_ref_t *result)", snt.text);
   1461     println(out, "{"); indent();
   1462     println(out, "int more;");
   1463 
   1464     if (n > 0) {
   1465         println(out, "void *pval;");
   1466         println(out, "flatcc_builder_ref_t ref, *pref;");
   1467         println(out, "const char *mark;");
   1468         println(out, "uint64_t w;");
   1469     }
   1470     if (trie.union_total) {
   1471         println(out, "size_t h_unions;");
   1472     }
   1473     println(out, "");
   1474     println(out, "*result = 0;");
   1475     println(out, "if (flatcc_builder_start_table(ctx->ctx, %"PRIu64")) goto failed;",
   1476         ct->count);
   1477     if (trie.union_total) {
   1478         println(out, "if (end == flatcc_json_parser_prepare_unions(ctx, buf, end, %"PRIu64", &h_unions)) goto failed;", (uint64_t)trie.union_total);
   1479     }
   1480     println(out, "buf = flatcc_json_parser_object_start(ctx, buf, end, &more);");
   1481     println(out, "while (more) {"); indent();
   1482     println(out, "buf = flatcc_json_parser_symbol_start(ctx, buf, end);");
   1483     if (n > 0) {
   1484         println(out, "w = flatcc_json_parser_symbol_part(buf, end);");
   1485         gen_trie(out, &trie, 0, n - 1, 0);
   1486     } else {
   1487         println(out, "/* Table has no fields. */");
   1488         println(out, "buf = flatcc_json_parser_unmatched_symbol(ctx, buf, end);");
   1489     }
   1490     println(out, "buf = flatcc_json_parser_object_end(ctx, buf, end, &more);");
   1491     unindent(); println(out, "}");
   1492     println(out, "if (ctx->error) goto failed;");
   1493     for (first = 1, i = 0; i < n; ++i) {
   1494         member = trie.dict[i].data;
   1495         if (member->metadata_flags & fb_f_deprecated) {
   1496             continue;
   1497         }
   1498         is_union = is_union_member(member);
   1499         is_required = member->metadata_flags & fb_f_required;
   1500         if (is_required) {
   1501             if (first) {
   1502                 println(out, "if (!flatcc_builder_check_required_field(ctx->ctx, %"PRIu64")", member->id - !!is_union);
   1503                 indent();
   1504             } else {
   1505                 println(out, "||  !flatcc_builder_check_required_field(ctx->ctx, %"PRIu64")", member->id - !!is_union);
   1506             }
   1507             first = 0;
   1508         }
   1509     }
   1510     if (!first) {
   1511         unindent(); println(out, ") {"); indent();
   1512         println(out, "buf = flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_required);");
   1513         println(out, "goto failed;");
   1514         unindent(); println(out, "}");
   1515     }
   1516     if (trie.union_total) {
   1517         println(out, "buf = flatcc_json_parser_finalize_unions(ctx, buf, end, h_unions);");
   1518     }
   1519     println(out, "if (!(*result = flatcc_builder_end_table(ctx->ctx))) goto failed;");
   1520     println(out, "return buf;");
   1521     /* Set runtime error if no other error was set already. */
   1522     margin();
   1523     println(out, "failed:");
   1524     unmargin();
   1525     println(out, "return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_runtime);");
   1526     unindent(); println(out, "}");
   1527     println(out, "");
   1528     println(out, "static inline int %s_parse_json_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)", snt.text);
   1529     println(out, "{"); indent();
   1530     println(out, "return flatcc_json_parser_table_as_root(B, ctx, buf, bufsiz, flags, fid, %s_parse_json_table);",
   1531             snt.text);
   1532     unindent(); println(out, "}");
   1533     println(out, "");
   1534     clear_dict(trie.dict);
   1535     return 0;
   1536 }
   1537 
   1538 static int gen_union_parser(fb_output_t *out, fb_compound_type_t *ct)
   1539 {
   1540     fb_scoped_name_t snt, snref;
   1541     fb_symbol_t *sym;
   1542     fb_member_t *member;
   1543     int n;
   1544     const char *s;
   1545 
   1546     fb_clear(snt);
   1547     fb_clear(snref);
   1548     fb_compound_name(ct, &snt);
   1549     println(out, "static const char *%s_parse_json_union(flatcc_json_parser_t *ctx, const char *buf, const char *end, uint8_t type, flatcc_builder_ref_t *result)", snt.text);
   1550     println(out, "{"); indent();
   1551     println(out, "");
   1552     println(out, "*result = 0;");
   1553     println(out, "switch (type) {");
   1554     println(out, "case 0: /* NONE */"); indent();
   1555     println(out, "return flatcc_json_parser_none(ctx, buf, end);");
   1556     unindent();
   1557     for (sym = ct->members; sym; sym = sym->link) {
   1558         member = (fb_member_t *)sym;
   1559         symbol_name(sym, &n, &s);
   1560         switch (member->type.type) {
   1561         case vt_missing:
   1562             /* NONE is of type vt_missing and already handled. */
   1563             continue;
   1564         case vt_compound_type_ref:
   1565             fb_compound_name(member->type.ct, &snref);
   1566             println(out, "case %u: /* %.*s */", (unsigned)member->value.u, n, s); indent();
   1567             switch (member->type.ct->symbol.kind) {
   1568             case fb_is_table:
   1569                 println(out, "buf = %s_parse_json_table(ctx, buf, end, result);", snref.text);
   1570                 break;
   1571             case fb_is_struct:
   1572                 println(out, "buf = %s_parse_json_struct(ctx, buf, end, result);", snref.text);
   1573                 break;
   1574             default:
   1575                 gen_panic(out, "internal error: unexpected compound union member type\n");
   1576                 return -1;
   1577             }
   1578             println(out, "break;");
   1579             unindent();
   1580             continue;
   1581         case vt_string_type:
   1582             println(out, "case %u: /* %.*s */", (unsigned)member->value.u, n, s); indent();
   1583             println(out, "buf = flatcc_json_parser_build_string(ctx, buf, end, result);");
   1584             println(out, "break;");
   1585             unindent();
   1586             continue;
   1587         default:
   1588             gen_panic(out, "internal error: unexpected union member type\n");
   1589             return -1;
   1590         }
   1591     }
   1592     /* Unknown union, but not an error if we allow schema forwarding. */
   1593     println(out, "default:"); indent();
   1594     println(out, "if (!(ctx->flags & flatcc_json_parser_f_skip_unknown)) {"); indent();
   1595     println(out, "return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unknown_union);");
   1596     unindent(); println(out, "} else {"); indent();
   1597     println(out, "return flatcc_json_parser_generic_json(ctx, buf, end);");
   1598     unindent(); println(out, "}");
   1599     unindent(); println(out, "}");
   1600     println(out, "if (ctx->error) return buf;");
   1601     println(out, "if (!*result) {");
   1602     indent(); println(out, "return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_runtime);");
   1603     unindent(); println(out, "}");
   1604     println(out, "return buf;");
   1605     unindent(); println(out, "}");
   1606     println(out, "");
   1607     return 0;
   1608 }
   1609 
   1610 static int gen_union_accept_type(fb_output_t *out, fb_compound_type_t *ct)
   1611 {
   1612     fb_scoped_name_t snt, snref;
   1613     fb_symbol_t *sym;
   1614     fb_member_t *member;
   1615     int n;
   1616     const char *s;
   1617 
   1618     fb_clear(snt);
   1619     fb_clear(snref);
   1620     fb_compound_name(ct, &snt);
   1621     println(out, "static int %s_json_union_accept_type(uint8_t type)", snt.text);
   1622     println(out, "{"); indent();
   1623     println(out, "switch (type) {");
   1624     for (sym = ct->members; sym; sym = sym->link) {
   1625         member = (fb_member_t *)sym;
   1626         symbol_name(sym, &n, &s);
   1627         if (member->type.type == vt_missing) {
   1628             println(out, "case 0: return 1; /* NONE */");
   1629             continue;
   1630         }
   1631         println(out, "case %u: return 1; /* %.*s */", (unsigned)member->value.u, n, s);
   1632     }
   1633     /* Unknown union, but not an error if we allow schema forwarding. */
   1634     println(out, "default: return 0;"); indent();
   1635     unindent(); println(out, "}");
   1636     unindent(); println(out, "}");
   1637     println(out, "");
   1638     return 0;
   1639 }
   1640 
   1641 static void gen_local_scope_prototype(void *context, fb_scope_t *scope)
   1642 {
   1643     fb_output_t *out = context;
   1644     fb_symbol_text_t scope_name;
   1645 
   1646     fb_copy_scope(scope, scope_name);
   1647 
   1648     println(out, "static const char *%s_local_%sjson_parser_enum(flatcc_json_parser_t *ctx, const char *buf, const char *end,",
   1649         out->S->basename, scope_name);
   1650     println(out, "int *value_type, uint64_t *value, int *aggregate);");
   1651 }
   1652 
   1653 static int gen_root_table_parser(fb_output_t *out, fb_compound_type_t *ct)
   1654 {
   1655     fb_scoped_name_t snt;
   1656 
   1657     fb_clear(snt);
   1658     fb_compound_name(ct, &snt);
   1659 
   1660     println(out, "static int %s_parse_json(flatcc_builder_t *B, flatcc_json_parser_t *ctx,", out->S->basename);
   1661     indent(); indent();
   1662     println(out, "const char *buf, size_t bufsiz, flatcc_json_parser_flags_t flags)");
   1663     unindent(); unindent();
   1664     println(out, "{"); indent();
   1665     println(out, "flatcc_json_parser_t parser;");
   1666     println(out, "flatcc_builder_ref_t root;");
   1667     println(out, "");
   1668     println(out, "ctx = ctx ? ctx : &parser;");
   1669     println(out, "flatcc_json_parser_init(ctx, B, buf, buf + bufsiz, flags);");
   1670     if (out->S->file_identifier.type == vt_string) {
   1671         println(out, "if (flatcc_builder_start_buffer(B, \"%.*s\", 0, 0)) return -1;",
   1672         out->S->file_identifier.s.len, out->S->file_identifier.s.s);
   1673     } else {
   1674         println(out, "if (flatcc_builder_start_buffer(B, 0, 0, 0)) return -1;");
   1675     }
   1676     println(out, "%s_parse_json_table(ctx, buf, buf + bufsiz, &root);", snt.text);
   1677     println(out, "if (ctx->error) {"); indent();
   1678     println(out, "return ctx->error;");
   1679     unindent(); println(out, "}");
   1680     println(out, "if (!flatcc_builder_end_buffer(B, root)) return -1;");
   1681     println(out, "ctx->end_loc = buf;");
   1682     println(out, "return 0;");
   1683     unindent(); println(out, "}");
   1684     println(out, "");
   1685     return 0;
   1686 }
   1687 
   1688 static int gen_root_struct_parser(fb_output_t *out, fb_compound_type_t *ct)
   1689 {
   1690     fb_scoped_name_t snt;
   1691 
   1692     fb_clear(snt);
   1693     fb_compound_name(ct, &snt);
   1694 
   1695     println(out, "static int %s_parse_json(flatcc_builder_t *B, flatcc_json_parser_t *ctx,", out->S->basename);
   1696     indent(); indent();
   1697     println(out, "const char *buf, size_t bufsiz, int flags)");
   1698     unindent(); unindent();
   1699     println(out, "{"); indent();
   1700     println(out, "flatcc_json_parser_t ctx_;");
   1701     println(out, "flatcc_builder_ref_t root;");
   1702     println(out, "");
   1703     println(out, "ctx = ctx ? ctx : &ctx_;");
   1704     println(out, "flatcc_json_parser_init(ctx, B, buf, buf + bufsiz, flags);");
   1705     if (out->S->file_identifier.type == vt_string) {
   1706         println(out, "if (flatcc_builder_start_buffer(B, \"%.*s\", 0, 0)) return -1;",
   1707         out->S->file_identifier.s.len, out->S->file_identifier.s.s);
   1708     } else {
   1709         println(out, "if (flatcc_builder_start_buffer(B, 0, 0, 0)) return -1;");
   1710     }
   1711     println(out, "buf = %s_parse_json_struct(ctx, buf, buf + bufsiz, &root);", snt.text);
   1712     println(out, "if (ctx->error) {"); indent();
   1713     println(out, "return ctx->error;");
   1714     unindent(); println(out, "}");
   1715     println(out, "if (!flatcc_builder_end_buffer(B, root)) return -1;");
   1716     println(out, "ctx->end_loc = buf;");
   1717     println(out, "return 0;");
   1718     unindent(); println(out, "}");
   1719     println(out, "");
   1720     return 0;
   1721 }
   1722 
   1723 
   1724 static int gen_root_parser(fb_output_t *out)
   1725 {
   1726     fb_symbol_t *root_type = out->S->root_type.type;
   1727 
   1728     if (!root_type) {
   1729         return 0;
   1730     }
   1731     if (root_type) {
   1732         switch (root_type->kind) {
   1733         case fb_is_table:
   1734             return gen_root_table_parser(out, (fb_compound_type_t *)root_type);
   1735         case fb_is_struct:
   1736             return gen_root_struct_parser(out, (fb_compound_type_t *)root_type);
   1737         default:
   1738             break;
   1739         }
   1740     }
   1741     return 0;
   1742 }
   1743 
   1744 static int gen_json_parser_prototypes(fb_output_t *out)
   1745 {
   1746     fb_symbol_t *sym;
   1747     fb_scoped_name_t snt;
   1748     fb_symbol_t *root_type = out->S->root_type.type;
   1749 
   1750     fb_clear(snt);
   1751 
   1752     if (root_type)
   1753     switch (root_type->kind) {
   1754     case fb_is_table:
   1755     case fb_is_struct:
   1756         println(out, "/*");
   1757         println(out, " * Parses the default root table or struct of the schema and constructs a FlatBuffer.");
   1758         println(out, " *");
   1759         println(out, " * Builder `B` must be initialized. `ctx` can be null but will hold");
   1760         println(out, " * hold detailed error info on return when available.");
   1761         println(out, " * Returns 0 on success, or error code.");
   1762         println(out, " * `flags` : 0 by default, `flatcc_json_parser_f_skip_unknown` silently");
   1763         println(out, " * ignores unknown table and structs fields, and union types.");
   1764         println(out, " */");
   1765     println(out, "static int %s_parse_json(flatcc_builder_t *B, flatcc_json_parser_t *ctx,",
   1766             out->S->basename);
   1767     indent(); indent();
   1768     println(out, "const char *buf, size_t bufsiz, flatcc_json_parser_flags_t flags);");
   1769     unindent(); unindent();
   1770         println(out, "");
   1771         break;
   1772     default:
   1773         break;
   1774     }
   1775     for (sym = out->S->symbols; sym; sym = sym->link) {
   1776         switch (sym->kind) {
   1777         case fb_is_union:
   1778             fb_compound_name((fb_compound_type_t *)sym, &snt);
   1779             println(out, "static const char *%s_parse_json_union(flatcc_json_parser_t *ctx, const char *buf, const char *end, uint8_t type, flatcc_builder_ref_t *pref);", snt.text);
   1780             println(out, "static int %s_json_union_accept_type(uint8_t type);", snt.text);
   1781             /* A union also has an enum parser to get the type. */
   1782             println(out, "static const char *%s_parse_json_enum(flatcc_json_parser_t *ctx, const char *buf, const char *end,", snt.text);
   1783             indent(); indent();
   1784             println(out, "int *value_type, uint64_t *value, int *aggregate);");
   1785             unindent(); unindent();
   1786             break;
   1787         case fb_is_struct:
   1788             fb_compound_name((fb_compound_type_t *)sym, &snt);
   1789             println(out, "static const char *%s_parse_json_struct_inline(flatcc_json_parser_t *ctx, const char *buf, const char *end, void *struct_base);", snt.text);
   1790             println(out, "static const char *%s_parse_json_struct(flatcc_json_parser_t *ctx, const char *buf, const char *end, flatcc_builder_ref_t *result);", snt.text);
   1791             break;
   1792         case fb_is_table:
   1793             fb_compound_name((fb_compound_type_t *)sym, &snt);
   1794             println(out, "static const char *%s_parse_json_table(flatcc_json_parser_t *ctx, const char *buf, const char *end, flatcc_builder_ref_t *result);", snt.text);
   1795             break;
   1796         case fb_is_enum:
   1797             fb_compound_name((fb_compound_type_t *)sym, &snt);
   1798             println(out, "static const char *%s_parse_json_enum(flatcc_json_parser_t *ctx, const char *buf, const char *end,", snt.text);
   1799             indent(); indent();
   1800             println(out, "int *value_type, uint64_t *value, int *aggregate);", snt.text);
   1801             unindent(); unindent();
   1802             break;
   1803         }
   1804     }
   1805     fb_scope_table_visit(&out->S->root_schema->scope_index, gen_local_scope_prototype, out);
   1806     println(out, "static const char *%s_global_json_parser_enum(flatcc_json_parser_t *ctx, const char *buf, const char *end,", out->S->basename);
   1807     indent(); indent();
   1808     println(out, "int *value_type, uint64_t *value, int *aggregate);");
   1809     unindent(); unindent();
   1810     println(out, "");
   1811     return 0;
   1812 }
   1813 
   1814 static int gen_json_parsers(fb_output_t *out)
   1815 {
   1816     fb_symbol_t *sym;
   1817 
   1818     for (sym = out->S->symbols; sym; sym = sym->link) {
   1819         switch (sym->kind) {
   1820         case fb_is_union:
   1821             gen_union_parser(out, (fb_compound_type_t *)sym);
   1822             gen_union_accept_type(out, (fb_compound_type_t *)sym);
   1823             gen_enum_parser(out, (fb_compound_type_t *)sym);
   1824             break;
   1825         case fb_is_struct:
   1826             gen_struct_parser_inline(out, (fb_compound_type_t *)sym);
   1827             gen_struct_parser(out, (fb_compound_type_t *)sym);
   1828             break;
   1829         case fb_is_table:
   1830             gen_table_parser(out, (fb_compound_type_t *)sym);
   1831             break;
   1832         case fb_is_enum:
   1833             gen_enum_parser(out, (fb_compound_type_t *)sym);
   1834             break;
   1835         }
   1836     }
   1837     fb_scope_table_visit(&out->S->root_schema->scope_index, gen_local_scope_parser, out);
   1838     gen_global_scope_parser(out);
   1839     gen_root_parser(out);
   1840     return 0;
   1841 }
   1842 
   1843 int fb_gen_c_json_parser(fb_output_t *out)
   1844 {
   1845     gen_json_parser_pretext(out);
   1846     gen_json_parser_prototypes(out);
   1847     gen_json_parsers(out);
   1848     gen_json_parser_footer(out);
   1849     return 0;
   1850 }