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 : : }
|