Branch data Line data Source code
1 : : #include "flatcc/flatcc_rtconfig.h"
2 : : #include "flatcc/flatcc_json_parser.h"
3 : :
4 : : #if FLATCC_USE_GRISU3 && !defined(PORTABLE_USE_GRISU3)
5 : : #define PORTABLE_USE_GRISU3 1
6 : : #endif
7 : : #include "flatcc/portable/pparsefp.h"
8 : :
9 : : #if FLATCC_USE_SSE4_2
10 : : #ifdef __SSE4_2__
11 : : #define USE_SSE4_2
12 : : #endif
13 : : #endif
14 : :
15 : : #ifdef USE_SSE4_2
16 : : #include <nmmintrin.h>
17 : : #define cmpistri(end, haystack, needle, flags) \
18 : : if (end - haystack >= 16) do { \
19 : : int i; \
20 : : __m128i a = _mm_loadu_si128((const __m128i *)(needle)); \
21 : : do { \
22 : : __m128i b = _mm_loadu_si128((const __m128i *)(haystack)); \
23 : : i = _mm_cmpistri(a, b, flags); \
24 : : haystack += i; \
25 : : } while (i == 16 && end - haystack >= 16); \
26 : : } while(0)
27 : : #endif
28 : :
29 [ # # ]: 0 : const char *flatcc_json_parser_error_string(int err)
30 : : {
31 : : switch (err) {
32 : : #define XX(no, str) \
33 : : case flatcc_json_parser_error_##no: \
34 : : return str;
35 : : FLATCC_JSON_PARSE_ERROR_MAP(XX)
36 : : #undef XX
37 : : default:
38 : : return "unknown";
39 : : }
40 : : }
41 : :
42 : 0 : const char *flatcc_json_parser_set_error(flatcc_json_parser_t *ctx, const char *loc, const char *end, int err)
43 : : {
44 [ # # ][ # # ]: 0 : if (!ctx->error) {
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
45 : 0 : ctx->error = err;
46 : 0 : ctx->pos = (int)(loc - ctx->line_start + 1);
47 : 52 : ctx->error_loc = loc;
48 : : }
49 : 0 : return end;
50 : : }
51 : :
52 : 71 : const char *flatcc_json_parser_string_part(flatcc_json_parser_t *ctx, const char *buf, const char *end)
53 : : {
54 : : /*
55 : : * Disabled because it doesn't catch all control characters, but is
56 : : * useful for performance testing.
57 : : */
58 : : #if 0
59 : : //#ifdef USE_SSE4_2
60 : : cmpistri(end, buf, "\"\\\0\r\n\t\v\f", _SIDD_POSITIVE_POLARITY);
61 : : #else
62 : : /*
63 : : * Testing for signed char >= 0x20 would also capture UTF-8
64 : : * encodings that we could verify, and also invalid encodings like
65 : : * 0xff, but we do not wan't to enforce strict UTF-8.
66 : : */
67 [ + - ][ + + ]: 512 : while (buf != end && *buf != '\"' && ((unsigned char)*buf) >= 0x20 && *buf != '\\') {
[ + - ][ + + ]
68 : 441 : ++buf;
69 : : }
70 : : #endif
71 [ - + ]: 71 : if (buf == end) {
72 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unterminated_string);
73 : : }
74 [ + + ]: 71 : if (*buf == '"') {
75 : : return buf;
76 : : }
77 [ - + ]: 14 : if (*buf < 0x20) {
78 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_character);
79 : : }
80 : : return buf;
81 : : }
82 : :
83 : 308 : const char *flatcc_json_parser_space_ext(flatcc_json_parser_t *ctx, const char *buf, const char *end)
84 : : {
85 : : again:
86 : : #ifdef USE_SSE4_2
87 : : /*
88 : : * We can include line break, but then error reporting suffers and
89 : : * it really makes no big difference.
90 : : */
91 : : //cmpistri(end, buf, "\x20\t\v\f\r\n", _SIDD_NEGATIVE_POLARITY);
92 : : cmpistri(end, buf, "\x20\t\v\f", _SIDD_NEGATIVE_POLARITY);
93 : : #else
94 : : #if FLATCC_ALLOW_UNALIGNED_ACCESS
95 [ + + ]: 287 : while (end - buf >= 16) {
96 [ + - ]: 107 : if (*buf > 0x20) {
97 : : return buf;
98 : : }
99 : : #if FLATCC_JSON_PARSE_WIDE_SPACE
100 : : if (((uint64_t *)buf)[0] != 0x2020202020202020) {
101 : : descend:
102 : : if (((uint32_t *)buf)[0] == 0x20202020) {
103 : : buf += 4;
104 : : }
105 : : #endif
106 [ + + ]: 107 : if (((uint16_t *)buf)[0] == 0x2020) {
107 : 59 : buf += 2;
108 : : }
109 [ + + ]: 107 : if (*buf == 0x20) {
110 : 38 : ++buf;
111 : : }
112 [ + + ]: 107 : if (*buf > 0x20) {
113 : : return buf;
114 : : }
115 : : break;
116 : : #if FLATCC_JSON_PARSE_WIDE_SPACE
117 : : }
118 : : if (((uint64_t *)buf)[1] != 0x2020202020202020) {
119 : : buf += 8;
120 : : goto descend;
121 : : }
122 : : buf += 16;
123 : : #endif
124 : : }
125 : : #endif
126 : : #endif
127 [ + + ][ + + ]: 755 : while (buf != end && *buf == 0x20) {
128 : 575 : ++buf;
129 : : }
130 [ + + ][ + + ]: 232 : while (buf != end && *buf <= 0x20) {
131 [ + + + - : 102 : switch (*buf) {
- ]
132 [ + - ][ - + ]: 1 : case 0x0d: buf += (end - buf > 1 && buf[1] == 0x0a);
133 : : /* Fall through consuming following LF or treating CR as LF. */
134 : 52 : case 0x0a: ++ctx->line; ctx->line_start = ++buf; continue;
135 : 0 : case 0x09: ++buf; continue;
136 : : case 0x20: goto again; /* Don't consume here, sync with power of 2 spaces. */
137 : : default: return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
138 : : }
139 : : }
140 : : return buf;
141 : : }
142 : :
143 : 14 : 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)
144 : : {
145 : : char c, v;
146 : : unsigned short u, x;
147 : :
148 [ + - ][ - + ]: 14 : if (end - buf < 2 || buf[0] != '\\') {
149 : 0 : code[0] = 0;
150 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
151 : : }
152 [ + + + + : 14 : switch (buf[1]) {
+ + + + +
+ - ]
153 : : case 'x':
154 : : v = 0;
155 : 3 : code[0] = 1;
156 [ - + ]: 3 : if (end - buf < 4) {
157 : 0 : code[0] = 0;
158 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
159 : : }
160 : 3 : c = buf[2];
161 [ + + ]: 3 : if (c >= '0' && c <= '9') {
162 : 1 : v |= (c - '0') << 4;
163 : : } else {
164 : : /* Lower case. */
165 : 2 : c |= 0x20;
166 [ + - ]: 2 : if (c >= 'a' && c <= 'f') {
167 : 2 : v |= (c - 'a' + 10) << 4;
168 : : } else {
169 : 0 : code[0] = 0;
170 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
171 : : }
172 : : }
173 : 3 : c = buf[3];
174 [ + - ]: 3 : if (c >= '0' && c <= '9') {
175 : 3 : v |= c - '0';
176 : : } else {
177 : : /* Lower case. */
178 : 0 : c |= 0x20;
179 [ # # ]: 0 : if (c >= 'a' && c <= 'f') {
180 : 0 : v |= c - 'a' + 10;
181 : : } else {
182 : 0 : code[0] = 0;
183 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
184 : : }
185 : : }
186 : 3 : code[1] = v;
187 : 3 : return buf + 4;
188 : : case 'u':
189 [ - + ]: 3 : if (end - buf < 6) {
190 : 0 : code[0] = 0;
191 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
192 : : }
193 : : u = 0;
194 : 3 : c = buf[2];
195 [ + - ]: 3 : if (c >= '0' && c <= '9') {
196 : 3 : x = c - '0';
197 : 3 : u = x << 12;
198 : : } else {
199 : : /* Lower case. */
200 : 0 : c |= 0x20;
201 [ # # ]: 0 : if (c >= 'a' && c <= 'f') {
202 : 0 : x = c - 'a' + 10;
203 : 0 : u |= x << 12;
204 : : } else {
205 : 0 : code[0] = 0;
206 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
207 : : }
208 : : }
209 : 3 : c = buf[3];
210 [ + - ]: 3 : if (c >= '0' && c <= '9') {
211 : 3 : x = c - '0';
212 : 3 : u |= x << 8;
213 : : } else {
214 : : /* Lower case. */
215 : 0 : c |= 0x20;
216 [ # # ]: 0 : if (c >= 'a' && c <= 'f') {
217 : 0 : x = c - 'a' + 10;
218 : 0 : u |= x << 8;
219 : : } else {
220 : 0 : code[0] = 0;
221 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
222 : : }
223 : : }
224 : 3 : c = buf[4];
225 [ + + ]: 3 : if (c >= '0' && c <= '9') {
226 : 2 : x = c - '0';
227 : 2 : u |= x << 4;
228 : : } else {
229 : : /* Lower case. */
230 : 1 : c |= 0x20;
231 [ + - ]: 1 : if (c >= 'a' && c <= 'f') {
232 : 1 : x = c - 'a' + 10;
233 : 1 : u |= x << 4;
234 : : } else {
235 : 0 : code[0] = 0;
236 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
237 : : }
238 : : }
239 : 3 : c = buf[5];
240 [ + + ]: 3 : if (c >= '0' && c <= '9') {
241 : 2 : x = c - '0';
242 : 2 : u |= x;
243 : : } else {
244 : : /* Lower case. */
245 : 1 : c |= 0x20;
246 [ + - ]: 1 : if (c >= 'a' && c <= 'f') {
247 : 1 : x = c - 'a' + 10;
248 : 1 : u |= x;
249 : : } else {
250 : 0 : code[0] = 0;
251 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
252 : : }
253 : : }
254 [ - + ]: 3 : if (u <= 0x7f) {
255 : 0 : code[0] = 1;
256 : 0 : code[1] = (char)u;
257 [ + + ]: 3 : } else if (u <= 0x7ff) {
258 : 1 : code[0] = 2;
259 : 1 : code[1] = (char)(0xc0 | (u >> 6));
260 : 1 : code[2] = (char)(0x80 | (u & 0x3f));
261 : : } else {
262 : 2 : code[0] = 3;
263 : 2 : code[1] = (char)(0xe0 | (u >> 12));
264 : 2 : code[2] = (char)(0x80 | ((u >> 6) & 0x3f));
265 : 2 : code[3] = (char)(0x80 | (u & 0x3f));
266 : : /* We do not report failure on invalid unicode range. */
267 : : }
268 : 3 : return buf + 6;
269 : : case 't':
270 : 1 : code[0] = 1;
271 : 1 : code[1] = '\t';
272 : 1 : return buf + 2;
273 : : case 'n':
274 : 1 : code[0] = 1;
275 : 1 : code[1] = '\n';
276 : 1 : return buf + 2;
277 : : case 'r':
278 : 1 : code[0] = 1;
279 : 1 : code[1] = '\r';
280 : 1 : return buf + 2;
281 : : case 'b':
282 : 1 : code[0] = 1;
283 : 1 : code[1] = '\b';
284 : 1 : return buf + 2;
285 : : case 'f':
286 : 1 : code[0] = 1;
287 : 1 : code[1] = '\f';
288 : 1 : return buf + 2;
289 : : case '\"':
290 : 1 : code[0] = 1;
291 : 1 : code[1] = '\"';
292 : 1 : return buf + 2;
293 : : case '\\':
294 : 1 : code[0] = 1;
295 : 1 : code[1] = '\\';
296 : 1 : return buf + 2;
297 : : case '/':
298 : 1 : code[0] = 1;
299 : 1 : code[1] = '/';
300 : 1 : return buf + 2;
301 : : default:
302 : 0 : code[0] = 0;
303 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
304 : : }
305 : : }
306 : :
307 : : /* Only applies to unquoted constants during generic parsring, otherwise it is skipped as a string. */
308 : 4 : const char *flatcc_json_parser_skip_constant(flatcc_json_parser_t *ctx, const char *buf, const char *end)
309 : : {
310 : : char c;
311 : : const char *k;
312 : :
313 [ + - ]: 23 : while (buf != end) {
314 : 23 : c = *buf;
315 [ + - ][ + - ]: 23 : if ((c & 0x80) || (c == '_') || (c >= '0' && c <= '9') || c == '.') {
[ - + ]
316 : 0 : ++buf;
317 : 0 : continue;
318 : : }
319 : : /* Upper case. */
320 : 23 : c |= 0x20;
321 [ + + ]: 23 : if (c >= 'a' && c <= 'z') {
322 : 19 : ++buf;
323 : 19 : continue;
324 : : }
325 : 4 : buf = flatcc_json_parser_space(ctx, (k = buf), end);
326 [ - + ]: 23 : if (buf == k) {
327 : : return buf;
328 : : }
329 : : }
330 : : return buf;
331 : : }
332 : :
333 : 31 : const char *flatcc_json_parser_match_constant(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos, int *more)
334 : : {
335 : 31 : const char *mark = buf, *k = buf + pos;
336 : :
337 [ - + ]: 31 : if (end - buf <= pos) {
338 : 0 : *more = 0;
339 : 0 : return buf;
340 : : }
341 : : #if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
342 [ + + ]: 31 : if (ctx->unquoted) {
343 : 17 : buf = flatcc_json_parser_space(ctx, k, end);
344 [ - + ]: 17 : if (buf == end) {
345 : : /*
346 : : * We cannot make a decision on more.
347 : : * Just return end and let parser handle sync point in
348 : : * case it is able to resume parse later on.
349 : : * For the same reason we do not lower ctx->unquoted.
350 : : */
351 : 0 : *more = 0;
352 : 0 : return buf;
353 : : }
354 [ + + ]: 17 : if (buf != k) {
355 : 7 : char c = *buf;
356 : : /*
357 : : * Space was seen - and thus we have a valid match.
358 : : * If the next char is an identifier start symbol
359 : : * we raise the more flag to support syntax like:
360 : : *
361 : : * `flags: Hungry Sleepy Awake, ...`
362 : : */
363 [ - + ]: 7 : if (c == '_' || (c & 0x80)) {
364 : 0 : *more = 1;
365 : 0 : return buf;
366 : : }
367 : 7 : c |= 0x20;
368 [ - + ]: 7 : if (c >= 'a' && c <= 'z') {
369 : 0 : *more = 1;
370 : 0 : return buf;
371 : : }
372 : : }
373 : : /*
374 : : * Space was not seen, so the match is only valid if followed
375 : : * by a JSON separator symbol, and there cannot be more values
376 : : * following so `more` is lowered.
377 : : */
378 : 17 : *more = 0;
379 [ - + ][ # # ]: 17 : if (*buf == ',' || *buf == '}' || *buf == ']') {
380 : : return buf;
381 : : }
382 : 0 : return mark;
383 : : }
384 : : #endif
385 : : buf = k;
386 [ + + ]: 14 : if (*buf == 0x20) {
387 : 10 : ++buf;
388 [ + - ][ + + ]: 15 : while (buf != end && *buf == 0x20) {
389 : 5 : ++buf;
390 : : }
391 [ - + ]: 10 : if (buf == end) {
392 : 0 : *more = 0;
393 : 0 : return buf;
394 : : }
395 : : /* We accept untrimmed space like " Green Blue ". */
396 [ + + ]: 10 : if (*buf != '\"') {
397 : 8 : *more = 1;
398 : 8 : return buf;
399 : : }
400 : : }
401 [ - + - ]: 6 : switch (*buf) {
402 : : case '\\':
403 : 0 : *more = 0;
404 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
405 : : case '\"':
406 : 6 : buf = flatcc_json_parser_space(ctx, buf + 1, 0);
407 : 6 : *more = 0;
408 : 6 : return buf;
409 : : }
410 : 0 : *more = 0;
411 : 0 : return mark;
412 : : }
413 : :
414 : 4 : const char *flatcc_json_parser_unmatched_symbol(flatcc_json_parser_t *ctx, const char *buf, const char *end)
415 : : {
416 [ + - ]: 4 : if (ctx->flags & flatcc_json_parser_f_skip_unknown) {
417 : 4 : buf = flatcc_json_parser_symbol_end(ctx, buf, end);
418 : 4 : buf = flatcc_json_parser_space(ctx, buf, end);
419 [ + - ][ + - ]: 4 : if (buf != end && *buf == ':') {
420 : 4 : ++buf;
421 : 4 : buf = flatcc_json_parser_space(ctx, buf, end);
422 : : } else {
423 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_colon);
424 : : }
425 : 4 : return flatcc_json_parser_generic_json(ctx, buf, end);
426 : : } else {
427 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unknown_symbol);
428 : : }
429 : : }
430 : :
431 : 2 : static const char *__flatcc_json_parser_number(flatcc_json_parser_t *ctx, const char *buf, const char *end)
432 : : {
433 [ + - ]: 2 : if (buf == end) {
434 : : return buf;
435 : : }
436 [ - + ]: 2 : if (*buf == '-') {
437 : 0 : ++buf;
438 [ # # ]: 0 : if (buf == end) {
439 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
440 : : }
441 : : }
442 [ - + ]: 2 : if (*buf == '0') {
443 : 0 : ++buf;
444 : : } else {
445 [ - + ]: 2 : if (*buf < '1' || *buf > '9') {
446 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
447 : : }
448 : 2 : ++buf;
449 [ + - ][ - + ]: 2 : while (buf != end && *buf >= '0' && *buf <= '9') {
[ # # ]
450 : 0 : ++buf;
451 : : }
452 : : }
453 [ + - ]: 2 : if (buf != end) {
454 [ + + ]: 2 : if (*buf == '.') {
455 : 1 : ++buf;
456 [ - + ]: 1 : if (*buf < '0' || *buf > '9') {
457 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
458 : : }
459 : 1 : ++buf;
460 [ + - ][ - + ]: 1 : while (buf != end && *buf >= '0' && *buf <= '9') {
[ # # ]
461 : 0 : ++buf;
462 : : }
463 : : }
464 : : }
465 [ + - ][ - + ]: 2 : if (buf != end && (*buf == 'e' || *buf == 'E')) {
466 : 0 : ++buf;
467 [ # # ]: 0 : if (buf == end) {
468 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
469 : : }
470 [ # # ]: 0 : if (*buf == '+' || *buf == '-') {
471 : 0 : ++buf;
472 : : }
473 [ # # ][ # # ]: 0 : if (buf == end || *buf < '0' || *buf > '9') {
[ # # ]
474 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
475 : : }
476 : 0 : ++buf;
477 [ # # ][ # # ]: 0 : while (buf != end && *buf >= '0' && *buf <= '9') {
[ # # ]
478 : 0 : ++buf;
479 : : }
480 : : }
481 : :
482 : : /*
483 : : * For strtod termination we must ensure the tail is not valid
484 : : * including non-json exponent types. The simplest approach is
485 : : * to accept anything that could be valid json successor
486 : : * characters and reject end of buffer since we expect a closing
487 : : * '}'.
488 : : *
489 : : * The ',' is actually not safe if strtod uses a non-POSIX locale.
490 : : */
491 [ + - ]: 2 : if (buf != end) {
492 [ - + ]: 2 : switch (*buf) {
493 : : case ',':
494 : : case ':':
495 : : case ']':
496 : : case '}':
497 : : case ' ':
498 : : case '\r':
499 : : case '\t':
500 : : case '\n':
501 : : case '\v':
502 : : return buf;
503 : : }
504 : : }
505 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
506 : : }
507 : :
508 : 2 : const char *flatcc_json_parser_double(flatcc_json_parser_t *ctx, const char *buf, const char *end, double *v)
509 : : {
510 : : int of;
511 : :
512 : 2 : *v = 0.0;
513 : 2 : buf = parse_double(buf, (int)(end - buf), v);
514 [ - + ]: 2 : if (buf == 0) {
515 [ # # ]: 0 : if ((of = parse_double_is_range_error(*v))) {
516 [ # # ]: 0 : if (of > 0) {
517 : : flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_overflow);
518 : : } else {
519 : : flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
520 : : }
521 : : } else {
522 : : flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
523 : : }
524 : : return end;
525 : : }
526 : : return buf;
527 : : }
528 : :
529 : 3 : const char *flatcc_json_parser_float(flatcc_json_parser_t *ctx, const char *buf, const char *end, float *v)
530 : : {
531 : : int of;
532 : :
533 : 3 : *v = 0.0;
534 : 3 : buf = parse_float(buf, (int)(end - buf), v);
535 [ - + ]: 3 : if (buf == 0) {
536 [ # # ]: 0 : if ((of = parse_float_is_range_error(*v))) {
537 [ # # ]: 0 : if (of > 0) {
538 : : flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_overflow);
539 : : } else {
540 : : flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
541 : : }
542 : : } else {
543 : : flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
544 : : }
545 : : return end;
546 : : }
547 : : return buf;
548 : : }
549 : :
550 : 7 : const char *flatcc_json_parser_generic_json(flatcc_json_parser_t *ctx, const char *buf, const char *end)
551 : : {
552 : : char stack[FLATCC_JSON_PARSE_GENERIC_MAX_NEST];
553 : : char *sp, *spend;
554 : : const char *k;
555 : : char code[4];
556 : 17 : int more = 0;
557 : :
558 : : sp = stack;
559 : : spend = sp + FLATCC_JSON_PARSE_GENERIC_MAX_NEST;
560 : :
561 : : again:
562 [ + - ]: 17 : if (buf == end) {
563 : : return buf;
564 : : }
565 [ + + ][ + + ]: 17 : if (sp != stack && sp[-1] == '}') {
566 : : /* Inside an object, about to read field name. */
567 : : buf = flatcc_json_parser_symbol_start(ctx, buf, end);
568 : 8 : buf = flatcc_json_parser_symbol_end(ctx, buf, end);
569 : 8 : buf = flatcc_json_parser_space(ctx, buf, end);
570 [ - + ]: 8 : if (buf == end) {
571 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_object);
572 : : }
573 [ - + ]: 8 : if (*buf != ':') {
574 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_colon);
575 : : }
576 : 8 : buf = flatcc_json_parser_space(ctx, buf + 1, end);
577 : : }
578 [ + + + + : 17 : switch (*buf) {
+ ]
579 : : case '\"':
580 : : buf = flatcc_json_parser_string_start(ctx, buf, end);
581 [ + - ][ + - ]: 4 : while (buf != end && *buf != '\"') {
582 : 4 : buf = flatcc_json_parser_string_part(ctx, buf, end);
583 [ + - ][ - + ]: 4 : if (buf != end && *buf == '\"') {
584 : : break;
585 : : }
586 : 0 : buf = flatcc_json_parser_string_escape(ctx, buf, end, code);
587 : : }
588 : : buf = flatcc_json_parser_string_end(ctx, buf, end);
589 : : break;
590 : : case '-':
591 : : case '0': case '1': case '2': case '3': case '4':
592 : : case '5': case '6': case '7': case '8': case '9':
593 : 2 : buf = __flatcc_json_parser_number(ctx, buf, end);
594 : 12 : break;
595 : : #if !FLATCC_JSON_PARSE_ALLOW_UNQUOTED
596 : : case 't': case 'f':
597 : : {
598 : : uint8_t v;
599 : : buf = flatcc_json_parser_bool(ctx, (k = buf), end, &v);
600 : : if (k == buf) {
601 : : return flatcc_json_parser_set_error(ctx, buf, end, end, flatcc_json_parser_error_unexpected_character);
602 : : }
603 : : }
604 : : break;
605 : : case 'n':
606 : : buf = flatcc_json_parser_null(ctx, (k = buf), end);
607 : : if (k == buf) {
608 : : return flatcc_json_parser_set_error(ctx, buf, end, end, flatcc_json_parser_error_unexpected_character);
609 : : }
610 : : break;
611 : : #endif
612 : : case '[':
613 [ - + ]: 2 : if (sp == spend) {
614 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_deep_nesting);
615 : : }
616 : 2 : *sp++ = ']';
617 : 2 : buf = flatcc_json_parser_space(ctx, buf + 1, end);
618 [ - + ][ + + ]: 2 : if (buf != end && *buf == ']') {
619 : : break;
620 : : }
621 : : goto again;
622 : : case '{':
623 [ - + ]: 5 : if (sp == spend) {
624 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_deep_nesting);
625 : : }
626 : 5 : *sp++ = '}';
627 : 5 : buf = flatcc_json_parser_space(ctx, buf + 1, end);
628 [ - + ][ + + ]: 5 : if (buf != end && *buf == '}') {
629 : : break;
630 : : }
631 : : goto again;
632 : :
633 : : default:
634 : : #if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
635 : 4 : buf = flatcc_json_parser_skip_constant(ctx, (k = buf), end);
636 [ - + ]: 4 : if (k == buf) {
637 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
638 : : }
639 : : break;
640 : : #else
641 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
642 : : #endif
643 : : }
644 [ + - ][ + + ]: 19 : while (buf != end && sp != stack) {
645 : 12 : --sp;
646 [ + + ]: 12 : if (*sp == ']') {
647 : 3 : buf = flatcc_json_parser_array_end(ctx, buf, end, &more);
648 : : } else {
649 : 9 : buf = flatcc_json_parser_object_end(ctx, buf, end, &more);
650 : : }
651 [ + + ]: 12 : if (more) {
652 : : ++sp;
653 : : goto again;
654 : : }
655 : : }
656 [ - + ][ # # ]: 7 : if (buf == end && sp != stack) {
657 [ # # ]: 0 : return flatcc_json_parser_set_error(ctx, buf, end, sp[-1] == ']' ?
658 : : flatcc_json_parser_error_unbalanced_array :
659 : : flatcc_json_parser_error_unbalanced_object);
660 : : }
661 : : /* Any ',', ']', or '}' belongs to parent context. */
662 : : return buf;
663 : : }
664 : :
665 : 102 : const char *flatcc_json_parser_integer(flatcc_json_parser_t *ctx, const char *buf, const char *end,
666 : : int *value_sign, uint64_t *value)
667 : : {
668 : : uint64_t x0, x = 0;
669 : : const char *k;
670 : :
671 [ + - ]: 102 : if (buf == end) {
672 : : return buf;
673 : : }
674 : : k = buf;
675 : 102 : *value_sign = *buf == '-';
676 : 102 : buf += *value_sign;
677 [ + - ][ + + ]: 332 : while (buf != end && *buf >= '0' && *buf <= '9') {
[ + + ]
678 : : x0 = x;
679 : 230 : x = x * 10 + *buf - '0';
680 [ - + ]: 230 : if (x0 > x) {
681 [ # # ]: 0 : return flatcc_json_parser_set_error(ctx, buf, end, value_sign ?
682 : : flatcc_json_parser_error_underflow : flatcc_json_parser_error_overflow);
683 : : }
684 : 230 : ++buf;
685 : : }
686 [ + + ]: 102 : if (buf == k) {
687 : : /* Give up, but don't fail the parse just yet, it might be a valid symbol. */
688 : : return buf;
689 : : }
690 [ + - ][ + - ]: 79 : if (buf != end && (*buf == 'e' || *buf == 'E' || *buf == '.')) {
[ - + ]
691 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_float_unexpected);
692 : : }
693 : 79 : *value = x;
694 : 79 : return buf;
695 : : }
696 : :
697 : :
698 : : /* UNIONS */
699 : :
700 : : /*
701 : : * Unions are difficult to parse because the type field may appear after
702 : : * the union table and because having two fields opens up for many more
703 : : * possible error scenarios. We must store each union of a table
704 : : * temporarily - this cannot be in the generated table parser function
705 : : * because there could be many unions (about 2^15 with default voffsets)
706 : : * although usually there will be only a few. We can also not store the
707 : : * data encoded in the existing table buffer in builder because we may
708 : : * have to remove it due to schema forwarding and removing it messes up
709 : : * the table layout. We also cannot naively allocate it dynamically for
710 : : * performance reasons. Instead we place the temporary union data in a
711 : : * separate frame from the table buffer, but on a similar stack. This is
712 : : * called the user stack and we manage one frame per table that is known
713 : : * to contain unions.
714 : : *
715 : : * Even the temporary structures in place we still cannot parse a union
716 : : * before we know its type. Due to JSON typically sorting fields
717 : : * alphabetically in various pretty printers, we are likely to receive
718 : : * the type late with (`<union_name>_type` following `<union_name>`.
719 : : * To deal with this we store a backtracking pointer and parses the
720 : : * table generically in a first pass and reparse the table once the type
721 : : * is known. This can happen recursively with nested tables containing
722 : : * unions which is why we need to have a stack frame.
723 : : *
724 : : * If the type field is stored first we just store the type in the
725 : : * custom frame and immediately parses the table with the right type
726 : : * once we see it. The parse will be much faster and we can strongly
727 : : * recommend that flatbuffer serializers do this, but we cannot require
728 : : * it.
729 : : *
730 : : * The actual overhead of dealing with the custom stack frame is fairly
731 : : * cheap once we get past the first custom stack allocation.
732 : : *
733 : : * We cannot update the builder before both the table and table type
734 : : * has been parsed because the the type might have to be ingored due
735 : : * to schema forwarding. Therefore the union type must be cached or
736 : : * reread. This happens trivially be calling the union parser with the
737 : : * type as argument, but it is important to be aware of before
738 : : * refactoring the code.
739 : : *
740 : : * The user frame is created at table start and remains valid until
741 : : * table exit, but we cannot assume the pointers to the frame remain
742 : : * valid. Specifically we cannot use frame pointers after calling
743 : : * the union parser. This means the union type must be cached or reread
744 : : * so it can be added to the table. Because the type is passed to
745 : : * the union parser this caching happens automatically but it is still
746 : : * important to be aware that it is required.
747 : : *
748 : : * The frame reserves temporary information for all unions the table
749 : : * holds, enumerated 0 <= `union_index` < `union_total`
750 : : * where the `union_total` is fixed type specific number.
751 : : *
752 : : * The `type_present` is needed because union types range from 0..255
753 : : * and we need an extra bit do distinguish not present from union type
754 : : * `NONE = 0`.
755 : : */
756 : :
757 : : typedef struct {
758 : : const char *backtrace;
759 : : const char *line_start;
760 : : uint8_t type_present;
761 : : uint8_t type;
762 : : int line;
763 : : } __flatcc_json_parser_union_entry_t;
764 : :
765 : : typedef struct {
766 : : size_t union_total;
767 : : size_t union_count;
768 : : __flatcc_json_parser_union_entry_t unions[1];
769 : : } __flatcc_json_parser_union_frame_t;
770 : :
771 : 49 : const char *flatcc_json_parser_prepare_unions(flatcc_json_parser_t *ctx,
772 : : const char *buf, const char *end, size_t union_total)
773 : : {
774 : : __flatcc_json_parser_union_frame_t *f;
775 : :
776 [ - + ]: 49 : if (!(f = flatcc_builder_enter_user_frame(ctx->ctx,
777 : 49 : sizeof(__flatcc_json_parser_union_frame_t) + (union_total - 1) *
778 : : sizeof(__flatcc_json_parser_union_entry_t)))) {
779 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_runtime);
780 : : }
781 : : /* Frames have zeroed memory. */
782 : 49 : f->union_total = union_total;
783 : 49 : return buf;
784 : : }
785 : :
786 : 49 : const char *flatcc_json_parser_finalize_unions(flatcc_json_parser_t *ctx,
787 : : const char *buf, const char *end)
788 : : {
789 : 49 : __flatcc_json_parser_union_frame_t *f = flatcc_builder_get_user_frame(ctx->ctx);
790 : :
791 [ - + ]: 49 : if (f->union_count) {
792 : : buf = flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_union_incomplete);
793 : : }
794 : 49 : flatcc_builder_exit_user_frame(ctx->ctx);
795 : 49 : return buf;
796 : : }
797 : :
798 : 5 : const char *flatcc_json_parser_union(flatcc_json_parser_t *ctx,
799 : : const char *buf, const char *end, size_t union_index,
800 : : flatbuffers_voffset_t id, flatcc_json_parser_union_f *parse)
801 : : {
802 : 5 : __flatcc_json_parser_union_frame_t *f = flatcc_builder_get_user_frame(ctx->ctx);
803 : : __flatcc_json_parser_union_entry_t *e = &f->unions[union_index];
804 : :
805 [ - + ]: 5 : if (e->backtrace) {
806 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_duplicate);
807 : : }
808 [ + + ]: 5 : if (!e->type_present) {
809 : : /* If we supported table: null, we should not count it, but we don't. */
810 : 3 : ++f->union_count;
811 : 3 : e->line = ctx->line;
812 : 3 : e->line_start = ctx->line_start;
813 : 3 : buf = flatcc_json_parser_generic_json(ctx, (e->backtrace = buf), end);
814 : : } else {
815 [ - + ]: 2 : if (e->type == 0) {
816 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_union_none);
817 : : }
818 : 2 : --f->union_count;
819 : 2 : buf = parse(ctx, buf, end, e->type, id);
820 : : }
821 : : return buf;
822 : : }
823 : :
824 : 7 : const char *flatcc_json_parser_union_type(flatcc_json_parser_t *ctx,
825 : : const char *buf, const char *end, size_t union_index, flatbuffers_voffset_t id,
826 : : flatcc_json_parser_integral_symbol_f *type_parsers[],
827 : : flatcc_json_parser_union_f *union_parser)
828 : : {
829 : 7 : __flatcc_json_parser_union_frame_t *f = flatcc_builder_get_user_frame(ctx->ctx);
830 : 7 : __flatcc_json_parser_union_entry_t *e = f->unions + union_index;
831 : :
832 : : const char *mark;
833 : : int line;
834 : : const char *line_start;
835 : :
836 [ - + ]: 7 : if (e->type_present) {
837 : : return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_duplicate);
838 : : }
839 : 7 : e->type_present = 1;
840 : 7 : buf = flatcc_json_parser_uint8(ctx, (mark = buf), end, &e->type);
841 [ + + ]: 7 : if (mark == buf) {
842 : 6 : buf = flatcc_json_parser_symbolic_uint8(ctx, buf, end, type_parsers, &e->type);
843 : : }
844 : : /* Only count the union if the type is not NONE. */
845 [ + + ]: 7 : if (e->backtrace == 0) {
846 : 4 : f->union_count += e->type != 0;
847 : 4 : return buf;
848 : : }
849 : : assert(f->union_count);
850 : 3 : --f->union_count;
851 : : /*
852 : : * IMPORTANT: we cannot access any value in the frame or entry
853 : : * pointer after calling union parse because it might cause the
854 : : * stack to reallocate. We should read the frame pointer again if
855 : : * needed - we don't but remember it if refactoring code.
856 : : *
857 : : * IMPORTANT 2: Do not assign buf here. We are backtracking.
858 : : */
859 : 3 : line = ctx->line;
860 : 3 : line_start = ctx->line_start;
861 : 3 : ctx->line = e->line;
862 : 3 : ctx->line_start = e->line_start;
863 [ + - ]: 3 : if (end == union_parser(ctx, e->backtrace, end, e->type, id)) {
864 : : return end;
865 : : }
866 : 3 : ctx->line = line;
867 : 3 : ctx->line_start = line_start;
868 : 3 : return buf;
869 : : }
|