Branch data Line data Source code
1 : : #ifndef FLATCC_JSON_PARSE_H
2 : : #define FLATCC_JSON_PARSE_H
3 : :
4 : : /*
5 : : * JSON RFC:
6 : : * http://www.ietf.org/rfc/rfc4627.txt?number=4627
7 : : *
8 : : * With several flatbuffers specific extensions.
9 : : */
10 : :
11 : : #include <stdlib.h>
12 : : #include <string.h>
13 : : #include <assert.h>
14 : :
15 : : #include "flatcc/flatcc_rtconfig.h"
16 : : #include "flatcc/flatcc_builder.h"
17 : : #include "flatcc/flatcc_unaligned.h"
18 : :
19 : : #define PDIAGNOSTIC_IGNORE_UNUSED
20 : : #include "flatcc/portable/pdiagnostic_push.h"
21 : :
22 : : enum flatcc_json_parser_flags {
23 : : flatcc_json_parser_f_skip_unknown = 1,
24 : : flatcc_json_parser_f_force_add = 2
25 : : };
26 : :
27 : : #define FLATCC_JSON_PARSE_ERROR_MAP(XX) \
28 : : XX(ok, "ok") \
29 : : XX(eof, "eof") \
30 : : XX(deep_nesting, "deep nesting") \
31 : : XX(trailing_comma, "trailing comma") \
32 : : XX(expected_colon, "expected colon") \
33 : : XX(unexpected_character, "unexpected character") \
34 : : XX(invalid_numeric, "invalid numeric") \
35 : : XX(overflow, "overflow") \
36 : : XX(underflow, "underflow") \
37 : : XX(unbalanced_array, "unbalanced array") \
38 : : XX(unbalanced_object, "unbalanced object") \
39 : : XX(precision_loss, "precision loss") \
40 : : XX(float_unexpected, "float unexpected") \
41 : : XX(unknown_symbol, "unknown symbol") \
42 : : XX(unquoted_symbolic_list, "unquoted list of symbols") \
43 : : XX(unknown_union, "unknown union type") \
44 : : XX(expected_string, "expected string") \
45 : : XX(invalid_character, "invalid character") \
46 : : XX(invalid_escape, "invalid escape") \
47 : : XX(invalid_type, "invalid type") \
48 : : XX(unterminated_string, "unterminated string") \
49 : : XX(expected_object, "expected object") \
50 : : XX(expected_array, "expected array") \
51 : : XX(expected_scalar, "expected literal or symbolic scalar") \
52 : : XX(expected_union_type, "expected union type") \
53 : : XX(union_none, "union is NONE") \
54 : : XX(union_incomplete, "table has incomplete union") \
55 : : XX(duplicate, "table has duplicate field") \
56 : : XX(required, "required field missing") \
57 : : XX(runtime, "runtime error") \
58 : : XX(not_supported, "not supported")
59 : :
60 : : enum flatcc_json_parser_error_no {
61 : : #define XX(no, str) flatcc_json_parser_error_##no,
62 : : FLATCC_JSON_PARSE_ERROR_MAP(XX)
63 : : #undef XX
64 : : };
65 : :
66 : : const char *flatcc_json_parser_error_string(int err);
67 : :
68 : : #define flatcc_json_parser_ok flatcc_json_parser_error_ok
69 : : #define flatcc_json_parser_eof flatcc_json_parser_error_eof
70 : :
71 : : /*
72 : : * The struct may be zero initialized in which case the line count will
73 : : * start at line zero, or the line may be set to 1 initially. The ctx
74 : : * is only used for error reporting and tracking non-standard unquoted
75 : : * ctx.
76 : : *
77 : : * `ctx` may for example hold a flatcc_builder_t pointer.
78 : : */
79 : : typedef struct flatcc_json_parser_ctx flatcc_json_parser_t;
80 : : struct flatcc_json_parser_ctx {
81 : : void *ctx;
82 : : const char *line_start;
83 : : int flags;
84 : : #if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
85 : : int unquoted;
86 : : #endif
87 : :
88 : : int line, pos;
89 : : int error;
90 : : const char *start;
91 : : const char *end;
92 : : const char *error_loc;
93 : : /* Set at end of successful parse. */
94 : : const char *end_loc;
95 : : };
96 : :
97 : : static inline int flatcc_json_parser_get_error(flatcc_json_parser_t *ctx)
98 : : {
99 : : return ctx->error;
100 : : }
101 : :
102 : : static inline void flatcc_json_parser_init(flatcc_json_parser_t *ctx, flatcc_builder_t *B, const char *buf, const char *end, int flags)
103 : : {
104 : : memset(ctx, 0, sizeof(*ctx));
105 : : ctx->ctx = B;
106 : : ctx->line_start = buf;
107 : : ctx->line = 1;
108 : : ctx->flags = flags;
109 : : /* These are not needed for parsing, but may be helpful in reporting etc. */
110 : : ctx->start = buf;
111 : : ctx->end = end;
112 : : ctx->error_loc = buf;
113 : : }
114 : :
115 : : const char *flatcc_json_parser_set_error(flatcc_json_parser_t *ctx, const char *loc, const char *end, int reason);
116 : :
117 : : /*
118 : : * Wide space is not necessarily beneficial in the typical space, but it
119 : : * also isn't expensive so it may be added when there are applications
120 : : * that can benefit.
121 : : */
122 : : const char *flatcc_json_parser_space_ext(flatcc_json_parser_t *ctx, const char *buf, const char *end);
123 : :
124 : 84 : static inline const char *flatcc_json_parser_space(flatcc_json_parser_t *ctx, const char *buf, const char *end)
125 : : {
126 [ + + ]: 84 : if (end - buf > 1) {
127 [ + + ]: 72 : if (buf[0] > 0x20) {
128 : : return buf;
129 : : }
130 [ + - ][ + + ]: 32 : if (buf[0] == 0x20 && buf[1] > 0x20) {
131 : 29 : return buf + 1;
132 : : }
133 : : }
134 : 15 : return flatcc_json_parser_space_ext(ctx, buf, end);
135 : : }
136 : :
137 : :
138 : : static inline const char *flatcc_json_parser_string_start(flatcc_json_parser_t *ctx, const char *buf, const char *end)
139 : : {
140 [ + - ][ - + ]: 4 : if (buf == end || *buf != '\"') {
141 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_string);
142 : : }
143 : 4 : return ++buf;
144 : : }
145 : :
146 : : static inline const char *flatcc_json_parser_string_end(flatcc_json_parser_t *ctx, const char *buf, const char *end)
147 : : {
148 [ + - ][ - + ]: 4 : if (buf == end || *buf != '\"') {
149 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unterminated_string);
150 : : }
151 : 4 : return ++buf;
152 : : }
153 : :
154 : : typedef char flatcc_json_parser_escape_buffer_t[4];
155 : : /*
156 : : * If the buffer does not hold a valid escape sequence, an error is
157 : : * returned with code[0] = 0/
158 : : *
159 : : * Otherwise code[0] the length (1-3) of the remaining
160 : : * characters in the code, transcoded from the escape sequence.
161 : : *
162 : : * The JSON extension `\xXX` is supported and may produced invalid UTF-8
163 : : * characters such as 0xff. The standard JSON escape `\uXXXX` is not
164 : : * checked for invalid code points and may produce invlalid UTF-8.
165 : : *
166 : : * Regular characters are expected to valid UTF-8 but they are not checked
167 : : * and may therefore produce invalid UTF-8.
168 : : *
169 : : * Control characters within a string are rejected except in the
170 : : * standard JSON escpaped form for `\n \r \t \b \f`.
171 : : *
172 : : * Additional escape codes as per standard JSON: `\\ \/ \"`.
173 : : */
174 : : const char *flatcc_json_parser_string_escape(flatcc_json_parser_t *ctx, const char *buf, const char *end, flatcc_json_parser_escape_buffer_t code);
175 : :
176 : : /*
177 : : * Parses the longest unescaped run of string content followed by either
178 : : * an escape encoding, string termination, or error.
179 : : */
180 : : const char *flatcc_json_parser_string_part(flatcc_json_parser_t *ctx, const char *buf, const char *end);
181 : :
182 : : static inline const char *flatcc_json_parser_symbol_start(flatcc_json_parser_t *ctx, const char *buf, const char *end)
183 : : {
184 [ + - ]: 6 : if (buf == end) {
185 : : return buf;
186 : : }
187 [ - + ][ - + ]: 14 : if (*buf == '\"') {
188 : 0 : ++buf;
189 : 0 : ctx->unquoted = 0;
190 : : } else {
191 : : #if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
192 [ - + ][ - + ]: 14 : if (*buf == '.') {
193 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
194 : : }
195 : 14 : ctx->unquoted = 1;
196 : : #else
197 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
198 : : #endif
199 : : }
200 : : return buf;
201 : : }
202 : :
203 : : static inline uint64_t flatcc_json_parser_symbol_part_ext(const char *buf, const char *end)
204 : : {
205 : : uint64_t w = 0;
206 : : size_t n = end - buf;
207 : :
208 : : if (n > 8) {
209 : : n = 8;
210 : : }
211 : : /* This can bloat inlining for a rarely executed case. */
212 : : #if 1
213 : : switch (n) {
214 : : case 8: w |= ((uint64_t)buf[7]) << (0 * 8);
215 : : case 7: w |= ((uint64_t)buf[6]) << (1 * 8);
216 : : case 6: w |= ((uint64_t)buf[5]) << (2 * 8);
217 : : case 5: w |= ((uint64_t)buf[4]) << (3 * 8);
218 : : case 4: w |= ((uint64_t)buf[3]) << (4 * 8);
219 : : case 3: w |= ((uint64_t)buf[2]) << (5 * 8);
220 : : case 2: w |= ((uint64_t)buf[1]) << (6 * 8);
221 : : case 1: w |= ((uint64_t)buf[0]) << (7 * 8);
222 : : case 0:
223 : : break;
224 : : }
225 : : #else
226 : : /* But this is hardly much of an improvement. */
227 : : {
228 : : size_t i;
229 : : for (i = 0; i < n; ++i) {
230 : : w <<= 8;
231 : : if (i < n) {
232 : : w = buf[i];
233 : : }
234 : : }
235 : : }
236 : : #endif
237 : : return w;
238 : : }
239 : :
240 : : /*
241 : : * Read out string as a big endian word. This allows for trie lookup,
242 : : * also when trailing characters are beyond keyword. This assumes the
243 : : * external words tested against are valid and therefore there need be
244 : : * no checks here. If a match is not made, the symbol_end function will
245 : : * consume and check any unmatched content - from _before_ this function
246 : : * was called - i.e. the returned buffer is tentative for use only if we
247 : : * accept the part returned here.
248 : : *
249 : : * Used for both symbols and symbolic constants.
250 : : */
251 : : static inline uint64_t flatcc_json_parser_symbol_part(const char *buf, const char *end)
252 : : {
253 : : size_t n = end - buf;
254 : :
255 : : #if FLATCC_ALLOW_UNALIGNED_ACCESS
256 : : if (n >= 8) {
257 : : return be64toh(*(uint64_t *)buf);
258 : : }
259 : : #endif
260 : : return flatcc_json_parser_symbol_part_ext(buf, end);
261 : : }
262 : :
263 : : /* Don't allow space in dot notation neither inside nor outside strings. */
264 : : static inline const char *flatcc_json_parser_match_scope(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos)
265 : : {
266 : : const char *mark = buf;
267 : :
268 : : (void)ctx;
269 : :
270 : : if (end - buf <= pos) {
271 : : return mark;
272 : : }
273 : : if (buf[pos] != '.') {
274 : : return mark;
275 : : }
276 : : return buf + pos + 1;
277 : : }
278 : :
279 : : const char *flatcc_json_parser_match_constant(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos, int *more);
280 : :
281 : : /* We allow '.' in unquoted symbols, but not at the start or end. */
282 : 12 : static inline const char *flatcc_json_parser_symbol_end(flatcc_json_parser_t *ctx, const char *buf, const char *end)
283 : : {
284 : : char c, clast = 0;
285 : :
286 : :
287 : : #if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
288 [ - + ]: 12 : if (ctx->unquoted) {
289 [ + - ][ + - ]: 58 : while (buf != end && *buf > 0x20) {
290 : : clast = c = *buf;
291 [ + - ][ + - ]: 58 : if (c == '_' || c == '.' || (c & 0x80) || (c >= '0' && c <= '9')) {
[ + + ]
292 : 1 : ++buf;
293 : 1 : continue;
294 : : }
295 : : /* Lower case. */
296 : 57 : c |= 0x20;
297 [ + + ]: 57 : if (c >= 'a' && c <= 'z') {
298 : 45 : ++buf;
299 : 46 : continue;
300 : : }
301 : : break;
302 : : }
303 [ - + ]: 12 : if (clast == '.') {
304 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
305 : : }
306 : : } else {
307 : : #else
308 : : {
309 : : #endif
310 [ # # ][ # # ]: 0 : while (buf != end && *buf != '\"') {
311 [ # # ]: 0 : if (*buf == '\\') {
312 [ # # ]: 0 : if (end - buf < 2) {
313 : : break;
314 : : }
315 : 0 : ++buf;
316 : : }
317 : 0 : ++buf;
318 : : }
319 [ # # ][ # # ]: 0 : if (buf == end || *buf != '\"') {
320 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unterminated_string);
321 : : }
322 : 0 : ++buf;
323 : : }
324 : : return buf;
325 : : }
326 : :
327 : 6 : static inline const char *flatcc_json_parser_constant_start(flatcc_json_parser_t *ctx, const char *buf, const char *end)
328 : : {
329 : : buf = flatcc_json_parser_symbol_start(ctx, buf, end);
330 [ - + ]: 6 : if (!ctx->unquoted) {
331 : 0 : buf = flatcc_json_parser_space(ctx, buf, end);
332 : : }
333 : 6 : return buf;
334 : : }
335 : :
336 : : static inline const char *flatcc_json_parser_object_start(flatcc_json_parser_t *ctx, const char *buf, const char *end, int *more)
337 : : {
338 : : if (buf == end || *buf != '{') {
339 : : *more = 0;
340 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_object);
341 : : }
342 : : buf = flatcc_json_parser_space(ctx, buf + 1, end);
343 : : if (buf != end && *buf == '}') {
344 : : *more = 0;
345 : : return flatcc_json_parser_space(ctx, buf + 1, end);
346 : : }
347 : : *more = 1;
348 : : return buf;
349 : : }
350 : :
351 : 9 : static inline const char *flatcc_json_parser_object_end(flatcc_json_parser_t *ctx, const char *buf,
352 : : const char *end, int *more)
353 : : {
354 : 9 : buf = flatcc_json_parser_space(ctx, buf, end);
355 [ - + ]: 9 : if (buf == end) {
356 : 0 : *more = 0;
357 : 0 : return buf;
358 : : }
359 [ + + ]: 9 : if (*buf != ',') {
360 : 4 : *more = 0;
361 [ - + ]: 4 : if (*buf != '}') {
362 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_object);
363 : : } else {
364 : 4 : return flatcc_json_parser_space(ctx, buf + 1, end);
365 : : }
366 : : }
367 : 5 : buf = flatcc_json_parser_space(ctx, buf + 1, end);
368 [ - + ]: 5 : if (buf == end) {
369 : 0 : *more = 0;
370 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_object);
371 : : }
372 : : #if FLATCC_JSON_PARSE_ALLOW_TRAILING_COMMA
373 [ + + ]: 5 : if (*buf == '}') {
374 : 1 : *more = 0;
375 : 1 : return flatcc_json_parser_space(ctx, buf + 1, end);
376 : : }
377 : : #endif
378 : 4 : *more = 1;
379 : 4 : return buf;
380 : : }
381 : :
382 : : static inline const char *flatcc_json_parser_array_start(flatcc_json_parser_t *ctx, const char *buf, const char *end, int *more)
383 : : {
384 : : if (buf == end || *buf != '[') {
385 : : *more = 0;
386 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_array);
387 : : }
388 : : buf = flatcc_json_parser_space(ctx, buf + 1, end);
389 : : if (buf != end && *buf == ']') {
390 : : *more = 0;
391 : : return flatcc_json_parser_space(ctx, buf + 1, end);
392 : : }
393 : : *more = 1;
394 : : return buf;
395 : : }
396 : :
397 : 3 : static inline const char *flatcc_json_parser_array_end(flatcc_json_parser_t *ctx, const char *buf,
398 : : const char *end, int *more)
399 : : {
400 : 3 : buf = flatcc_json_parser_space(ctx, buf, end);
401 [ + - ][ + + ]: 3 : if (buf != end && *buf != ',') {
402 : 1 : *more = 0;
403 [ - + ]: 1 : if (*buf != ']') {
404 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_array);
405 : : } else {
406 : 1 : return flatcc_json_parser_space(ctx, buf + 1, end);
407 : : }
408 : : }
409 : 2 : buf = flatcc_json_parser_space(ctx, buf + 1, end);
410 [ - + ]: 2 : if (buf == end) {
411 : 0 : *more = 0;
412 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_array);
413 : : }
414 : : #if FLATCC_JSON_PARSE_ALLOW_TRAILING_COMMA
415 [ + + ]: 2 : if (*buf == ']') {
416 : 1 : *more = 0;
417 : 1 : return flatcc_json_parser_space(ctx, buf + 1, end);
418 : : }
419 : : #endif
420 : 1 : *more = 1;
421 : 1 : return buf;
422 : : }
423 : :
424 : : /*
425 : : * Detects if a symbol terminates at a given `pos` relative to the
426 : : * buffer pointer, or return fast.
427 : : *
428 : : * Failure to match is not an error but a recommendation to try
429 : : * alternative longer suffixes - only if such do not exist will
430 : : * there be an error. If a match was not eventually found,
431 : : * the `flatcc_json_parser_unmatched_symbol` should be called to consume
432 : : * the symbol and generate error messages.
433 : : *
434 : : * If a match was detected, ':' and surrounding space is consumed,
435 : : * or an error is generated.
436 : : */
437 : : static inline const char *flatcc_json_parser_match_symbol(flatcc_json_parser_t *ctx, const char *buf,
438 : : const char *end, int pos)
439 : : {
440 : : const char *mark = buf;
441 : :
442 : : if (end - buf <= pos) {
443 : : return mark;
444 : : }
445 : : #if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
446 : : if (ctx->unquoted) {
447 : : if (buf[pos] > 0x20 && buf[pos] != ':') {
448 : : return mark;
449 : : }
450 : : buf += pos;
451 : : ctx->unquoted = 0;
452 : : } else {
453 : : #else
454 : : {
455 : : #endif
456 : : if (buf[pos] != '\"') {
457 : : return mark;
458 : : }
459 : : buf += pos + 1;
460 : : }
461 : : buf = flatcc_json_parser_space(ctx, buf, end);
462 : : if (buf != end && *buf == ':') {
463 : : ++buf;
464 : : return flatcc_json_parser_space(ctx, buf, end);
465 : : }
466 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_colon);
467 : : }
468 : :
469 : : static inline const char *flatcc_json_parser_match_type_suffix(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos)
470 : : {
471 : : if (end - buf <= pos + 5) {
472 : : return buf;
473 : : }
474 : : if (memcmp(buf + pos, "_type", 5)) {
475 : : return buf;
476 : : }
477 : : return flatcc_json_parser_match_symbol(ctx, buf, end, pos + 5);
478 : : }
479 : :
480 : : const char *flatcc_json_parser_unmatched_symbol(flatcc_json_parser_t *ctx, const char *buf, const char *end);
481 : :
482 : : static inline const char *flatcc_json_parser_coerce_uint64(
483 : : flatcc_json_parser_t *ctx, const char *buf,
484 : : const char *end, int value_sign, uint64_t value, uint64_t *v)
485 : : {
486 : : if (value_sign) {
487 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
488 : : }
489 : : *v = value;
490 : : return buf;
491 : : }
492 : :
493 : : static inline const char *flatcc_json_parser_coerce_bool(flatcc_json_parser_t *ctx, const char *buf,
494 : : const char *end, int value_sign, uint64_t value, uint8_t *v)
495 : : {
496 : : if (value_sign) {
497 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
498 : : }
499 : : *v = (uint8_t)!!value;
500 : : return buf;
501 : : }
502 : :
503 : : #define __flatcc_json_parser_define_coerce_unsigned(type, basetype, uctype) \
504 : : static inline const char *flatcc_json_parser_coerce_ ## type( \
505 : : flatcc_json_parser_t *ctx, const char *buf, \
506 : : const char *end, int value_sign, uint64_t value, basetype *v) \
507 : : { \
508 : : if (value_sign) { \
509 : : return flatcc_json_parser_set_error(ctx, buf, end, \
510 : : flatcc_json_parser_error_underflow); \
511 : : } \
512 : : if (value > uctype ## _MAX) { \
513 : : return flatcc_json_parser_set_error(ctx, buf, end, \
514 : : flatcc_json_parser_error_overflow); \
515 : : } \
516 : : *v = (basetype)value; \
517 : : return buf; \
518 : : }
519 : :
520 : : __flatcc_json_parser_define_coerce_unsigned(uint32, uint32_t, UINT32)
521 : : __flatcc_json_parser_define_coerce_unsigned(uint16, uint16_t, UINT16)
522 [ - + ][ - + ]: 7 : __flatcc_json_parser_define_coerce_unsigned(uint8, uint8_t, UINT8)
[ - + ][ - + ]
523 : :
524 : : #define __flatcc_json_parser_define_coerce_signed(type, basetype, uctype) \
525 : : static inline const char *flatcc_json_parser_coerce_ ## type( \
526 : : flatcc_json_parser_t *ctx, const char *buf, \
527 : : const char *end, int value_sign, uint64_t value, basetype *v) \
528 : : { \
529 : : if (value_sign) { \
530 : : if (value > (uint64_t)(uctype ## _MAX) + 1) { \
531 : : return flatcc_json_parser_set_error(ctx, buf, end, \
532 : : flatcc_json_parser_error_underflow); \
533 : : } \
534 : : *v = (basetype)-(int64_t)value; \
535 : : } else { \
536 : : if (value > uctype ## _MAX) { \
537 : : return flatcc_json_parser_set_error(ctx, buf, end, \
538 : : flatcc_json_parser_error_overflow); \
539 : : } \
540 : : *v = (basetype)value; \
541 : : } \
542 : : return buf; \
543 : : }
544 : :
545 : : __flatcc_json_parser_define_coerce_signed(int64, int64_t, INT64)
546 : : __flatcc_json_parser_define_coerce_signed(int32, int32_t, INT32)
547 : : __flatcc_json_parser_define_coerce_signed(int16, int16_t, INT16)
548 : : __flatcc_json_parser_define_coerce_signed(int8, int8_t, INT8)
549 : :
550 : : static inline const char *flatcc_json_parser_coerce_float(
551 : : flatcc_json_parser_t *ctx, const char *buf,
552 : : const char *end, int value_sign, uint64_t value, float *v)
553 : : {
554 : : (void)ctx;
555 : : (void)end;
556 : :
557 : : *v = value_sign ? -(float)value : (float)value;
558 : : return buf;
559 : : }
560 : :
561 : : static inline const char *flatcc_json_parser_coerce_double(
562 : : flatcc_json_parser_t *ctx, const char *buf,
563 : : const char *end, int value_sign, uint64_t value, double *v)
564 : : {
565 : : (void)ctx;
566 : : (void)end;
567 : :
568 : : *v = value_sign ? -(double)value : (double)value;
569 : : return buf;
570 : : }
571 : :
572 : : const char *flatcc_json_parser_double(flatcc_json_parser_t *ctx, const char *buf, const char *end, double *v);
573 : :
574 : : const char *flatcc_json_parser_float(flatcc_json_parser_t *ctx, const char *buf, const char *end, float *v);
575 : :
576 : : /*
577 : : * If the buffer does not contain a valid start character for a numeric
578 : : * value, the function will return the the input buffer without failure.
579 : : * This makes is possible to try a symbolic parse.
580 : : */
581 : : const char *flatcc_json_parser_integer(flatcc_json_parser_t *ctx, const char *buf, const char *end,
582 : : int *value_sign, uint64_t *value);
583 : :
584 : : /* Returns unchanged buffer without error if `null` is not matched. */
585 : : static inline const char *flatcc_json_parser_null(const char *buf, const char *end)
586 : : {
587 : : if (end - buf >= 4 && memcmp(buf, "null", 4) == 0) {
588 : : return buf + 4;
589 : : }
590 : : return buf;
591 : : }
592 : :
593 : : /*
594 : : * `parsers` is a null terminated array of parsers with at least one
595 : : * valid parser. A numeric literal parser may also be included.
596 : : */
597 : : #define __flatcc_json_parser_define_integral_parser(type, basetype) \
598 : : static inline const char *flatcc_json_parser_ ## type( \
599 : : flatcc_json_parser_t *ctx, \
600 : : const char *buf, const char *end, basetype *v) \
601 : : { \
602 : : uint64_t value = 0; \
603 : : int value_sign = 0; \
604 : : const char *mark = buf; \
605 : : \
606 : : *v = 0; \
607 : : if (buf == end) { \
608 : : return buf; \
609 : : } \
610 : : buf = flatcc_json_parser_integer(ctx, buf, end, &value_sign, &value); \
611 : : if (buf != mark) { \
612 : : return flatcc_json_parser_coerce_ ## type(ctx, \
613 : : buf, end, value_sign, value, v); \
614 : : } \
615 : : return buf; \
616 : : }
617 : :
618 : : __flatcc_json_parser_define_integral_parser(uint64, uint64_t)
619 : : __flatcc_json_parser_define_integral_parser(uint32, uint32_t)
620 : : __flatcc_json_parser_define_integral_parser(uint16, uint16_t)
621 [ + - ][ + + ]: 14 : __flatcc_json_parser_define_integral_parser(uint8, uint8_t)
622 : : __flatcc_json_parser_define_integral_parser(int64, int64_t)
623 : : __flatcc_json_parser_define_integral_parser(int32, int32_t)
624 : : __flatcc_json_parser_define_integral_parser(int16, int16_t)
625 : : __flatcc_json_parser_define_integral_parser(int8, int8_t)
626 : :
627 : : static inline const char *flatcc_json_parser_bool(flatcc_json_parser_t *ctx, const char *buf, const char *end, uint8_t *v)
628 : : {
629 : : const char *k;
630 : : uint8_t tmp;
631 : :
632 : : k = buf;
633 : : if (end - buf >= 4 && memcmp(buf, "true", 4) == 0) {
634 : : *v = 1;
635 : : return k + 4;
636 : : } else if (end - buf >= 5 && memcmp(buf, "false", 5) == 0) {
637 : : *v = 0;
638 : : return k + 5;
639 : : }
640 : : buf = flatcc_json_parser_uint8(ctx, buf, end, &tmp);
641 : : *v = !!tmp;
642 : : return buf;
643 : : }
644 : :
645 : : /*
646 : : * The `parsers` argument is a zero terminated array of parser
647 : : * functions with increasingly general scopes.
648 : : *
649 : : * Symbols can be be or'ed together by listing multiple space separated
650 : : * flags in source being parsed, like `{ x : "Red Blue" }`.
651 : : * Intended for flags, but generally available.
652 : : *
653 : : * `aggregate` means there are more symbols to follow.
654 : : *
655 : : * This function does not return input `buf` value if match was
656 : : * unsuccessful. It will either match or error.
657 : : */
658 : : typedef const char *flatcc_json_parser_integral_symbol_f(flatcc_json_parser_t *ctx,
659 : : const char *buf, const char *end, int *value_sign, uint64_t *value, int *aggregate);
660 : :
661 : : /*
662 : : * Raise an error if a syntax like `color: Red Green` is seen unless
663 : : * explicitly permitted. `color: "Red Green"` or `"color": "Red Green"
664 : : * or `color: Red` is permitted if unquoted is permitted but not
665 : : * unquoted list. Googles flatc JSON parser does not allow multiple
666 : : * symbolic values unless quoted, so this is the default.
667 : : */
668 : : #if !FLATCC_JSON_PARSE_ALLOW_UNQUOTED || FLATCC_JSON_PARSE_ALLOW_UNQUOTED_LIST
669 : : #define __flatcc_json_parser_init_check_unquoted_list()
670 : : #define __flatcc_json_parser_check_unquoted_list()
671 : : #else
672 : : #define __flatcc_json_parser_init_check_unquoted_list() int list_count = 0;
673 : : #define __flatcc_json_parser_check_unquoted_list() \
674 : : if (list_count++ && ctx->unquoted) { \
675 : : return flatcc_json_parser_set_error(ctx, buf, end, \
676 : : flatcc_json_parser_error_unquoted_symbolic_list); \
677 : : }
678 : : #endif
679 : :
680 : : #define __flatcc_json_parser_define_symbolic_integral_parser(type, basetype)\
681 : : static const char *flatcc_json_parser_symbolic_ ## type( \
682 : : flatcc_json_parser_t *ctx, \
683 : : const char *buf, const char *end, \
684 : : flatcc_json_parser_integral_symbol_f *parsers[], \
685 : : basetype *v) \
686 : : { \
687 : : flatcc_json_parser_integral_symbol_f **p; \
688 : : const char *mark; \
689 : : basetype tmp = 0; \
690 : : uint64_t value; \
691 : : int value_sign, aggregate; \
692 : : __flatcc_json_parser_init_check_unquoted_list() \
693 : : \
694 : : *v = 0; \
695 : : buf = flatcc_json_parser_constant_start(ctx, buf, end); \
696 : : if (buf == end) { \
697 : : return buf; \
698 : : } \
699 : : do { \
700 : : p = parsers; \
701 : : do { \
702 : : /* call parser function */ \
703 : : buf = (*p)(ctx, (mark = buf), end, \
704 : : &value_sign, &value, &aggregate); \
705 : : if (buf == end) { \
706 : : return buf; \
707 : : } \
708 : : } while (buf == mark && *++p); \
709 : : if (mark == buf) { \
710 : : return flatcc_json_parser_set_error(ctx, buf, end, \
711 : : flatcc_json_parser_error_expected_scalar); \
712 : : } \
713 : : __flatcc_json_parser_check_unquoted_list() \
714 : : if (end == flatcc_json_parser_coerce_ ## type(ctx, \
715 : : buf, end, value_sign, value, &tmp)) { \
716 : : return end; \
717 : : } \
718 : : /* \
719 : : * `+=`, not `|=` because we also coerce to float and double, \
720 : : * and because we need to handle signed values. This may give \
721 : : * unexpected results with duplicate flags. \
722 : : */ \
723 : : *v += tmp; \
724 : : } while (aggregate); \
725 : : return buf; \
726 : : }
727 : :
728 : : __flatcc_json_parser_define_symbolic_integral_parser(uint64, uint64_t)
729 : : __flatcc_json_parser_define_symbolic_integral_parser(uint32, uint32_t)
730 : : __flatcc_json_parser_define_symbolic_integral_parser(uint16, uint16_t)
731 [ + - ][ + - ]: 18 : __flatcc_json_parser_define_symbolic_integral_parser(uint8, uint8_t)
[ - + ][ # # ]
[ - + ][ - + ]
[ # # ][ + - ]
[ - + ]
732 : : __flatcc_json_parser_define_symbolic_integral_parser(int64, int64_t)
733 : : __flatcc_json_parser_define_symbolic_integral_parser(int32, int32_t)
734 : : __flatcc_json_parser_define_symbolic_integral_parser(int16, int16_t)
735 : : __flatcc_json_parser_define_symbolic_integral_parser(int8, int8_t)
736 : :
737 : : __flatcc_json_parser_define_symbolic_integral_parser(bool, uint8_t)
738 : :
739 : : /* We still parse integral values, but coerce to float or double. */
740 : : __flatcc_json_parser_define_symbolic_integral_parser(float, float)
741 : : __flatcc_json_parser_define_symbolic_integral_parser(double, double)
742 : :
743 : : /*
744 : : * This doesn't do anything other than validate and advance past
745 : : * a JSON value which may use unquoted symbols.
746 : : *
747 : : * Upon call it is assumed that leading space has been stripped and that
748 : : * a JSON value is expected (i.e. root, or just after ':' in a
749 : : * container object, or less likely as an array member). Any trailing
750 : : * comma is assumed to belong to the parent context. Returns a parse
751 : : * location stripped from space so container should post call expect
752 : : * ',', '}', or ']', or EOF if the JSON is valid.
753 : : */
754 : : const char *flatcc_json_parser_generic_json(flatcc_json_parser_t *ctx, const char *buf, const char *end);
755 : :
756 : : typedef const char *flatcc_json_parser_union_f(flatcc_json_parser_t *ctx,
757 : : const char *buf, const char *end, uint8_t type, flatbuffers_voffset_t id);
758 : :
759 : : /* Called at start by table parsers with at least 1 union. */
760 : : const char *flatcc_json_parser_prepare_unions(flatcc_json_parser_t *ctx,
761 : : const char *buf, const char *end, size_t union_total);
762 : :
763 : : const char *flatcc_json_parser_finalize_unions(flatcc_json_parser_t *ctx,
764 : : const char *buf, const char *end);
765 : :
766 : : const char *flatcc_json_parser_union(flatcc_json_parser_t *ctx,
767 : : const char *buf, const char *end, size_t union_index,
768 : : flatbuffers_voffset_t id, flatcc_json_parser_union_f *parse);
769 : :
770 : : const char *flatcc_json_parser_union_type(flatcc_json_parser_t *ctx,
771 : : const char *buf, const char *end, size_t union_index, flatbuffers_voffset_t id,
772 : : flatcc_json_parser_integral_symbol_f *type_parsers[],
773 : : flatcc_json_parser_union_f *union_parser);
774 : :
775 : : #include "flatcc/portable/pdiagnostic_pop.h"
776 : : #endif /* FLATCC_JSON_PARSE_H */
|