LCOV - code coverage report
Current view: top level - src/compiler - codegen_c_json_parser.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 672 724 92.8 %
Date: 2016-11-30 13:12:14 Functions: 34 35 97.1 %
Branches: 271 323 83.9 %

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

Generated by: LCOV version 1.12