Nyx Node
Loading...
Searching...
No Matches
json.c
1/* NyxNode
2 * Author: Jérôme ODIER <jerome.odier@lpsc.in2p3.fr>
3 * SPDX-License-Identifier: GPL-2.0-only (Mongoose backend) or GPL-3.0+
4 */
5
6/*--------------------------------------------------------------------------------------------------------------------*/
7
8#include <ctype.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include "../nyx_node_internal.h"
13
14/*--------------------------------------------------------------------------------------------------------------------*/
15/* DEFINITIONS */
16/*--------------------------------------------------------------------------------------------------------------------*/
17
18typedef enum
19{
20 JSON_TOKEN_EOF,
21 JSON_TOKEN_NULL,
22 JSON_TOKEN_TRUE,
23 JSON_TOKEN_FALSE,
24 JSON_TOKEN_NUMBER,
25 JSON_TOKEN_STRING,
26 JSON_TOKEN_CURLY_OPEN,
27 JSON_TOKEN_CURLY_CLOSE,
28 JSON_TOKEN_SQUARE_OPEN,
29 JSON_TOKEN_SQUARE_CLOSE,
30 JSON_TOKEN_COLON,
31 JSON_TOKEN_COMMA,
32 JSON_TOKEN_ERROR,
33
34} json_token_type_t;
35
36/*--------------------------------------------------------------------------------------------------------------------*/
37
38typedef struct
39{
40 str_t value;
41
42 json_token_type_t token_type;
43
44} json_token_t;
45
46/*--------------------------------------------------------------------------------------------------------------------*/
47
48typedef struct
49{
50 size_t size;
51 STR_t buff;
52
53 json_token_t curr_token;
54
55} json_parser_t;
56
57/*--------------------------------------------------------------------------------------------------------------------*/
58/* TOKENIZER */
59/*--------------------------------------------------------------------------------------------------------------------*/
60
61#define NEXT() \
62 tokenizer_next(parser)
63
64/*--------------------------------------------------------------------------------------------------------------------*/
65
66#define PEEK() \
67 (parser->curr_token)
68
69/*--------------------------------------------------------------------------------------------------------------------*/
70
71#define CHECK(t) \
72 (parser->curr_token.token_type == (t))
73
74/*--------------------------------------------------------------------------------------------------------------------*/
75
76#define RELEASE(t) \
77 if(CHECK(JSON_TOKEN_NUMBER) || CHECK(JSON_TOKEN_STRING)) \
78 { \
79 nyx_memory_free(PEEK().value); \
80 \
81 PEEK().value = NULL; \
82 } \
83 \
84 PEEK().token_type = (t) \
85
86/*--------------------------------------------------------------------------------------------------------------------*/
87
88#define TRIM(s, e) \
89 ({ \
90 size_t result = (size_t) (e) - (size_t) (s); \
91 \
92 while(result > 0 && (isspace((unsigned char) *((s) + 0)) || *((s) + 0) == '"')) { \
93 (s)++; \
94 result--; \
95 } \
96 \
97 while(result > 0 && (isspace((unsigned char) *((e) - 1)) || *((e) - 1) == '"')) { \
98 (e)--; \
99 result--; \
100 } \
101 \
102 result; \
103 })
104
105/*--------------------------------------------------------------------------------------------------------------------*/
106
107static bool jsoncpy(str_t p, STR_t s, STR_t e)
108{
109 while(s < e)
110 {
111 if(*s == '\\')
112 {
113 s++;
114
115 if(e - s >= 1)
116 {
117 switch(*s)
118 {
119 case '\"': *p++ = '\"'; s++; break;
120 case '\\': *p++ = '\\'; s++; break;
121 case '/': *p++ = '/' ; s++; break;
122 case 'b': *p++ = '\b'; s++; break;
123 case 'f': *p++ = '\f'; s++; break;
124 case 'n': *p++ = '\n'; s++; break;
125 case 'r': *p++ = '\r'; s++; break;
126 case 't': *p++ = '\t'; s++; break;
127 case 'u':
128 s++;
129
130 if(e - s >= 4)
131 {
132 char hex[5] = {
133 s[0], s[1],
134 s[2], s[3],
135 '\0',
136 };
137
138 uint32_t unicode_char = (uint32_t) strtol(hex, NULL, 16);
139
140 p += nyx_unicode_to_utf8(p, unicode_char);
141 s += 0x0000000000000000000000000000000004;
142 }
143 else
144 {
145 return false;
146 }
147 break;
148 default:
149 *p++ = *s++;
150 break;
151 }
152 }
153 else
154 {
155 return false;
156 }
157 }
158 else
159 {
160 *p++ = *s++;
161 /*--*/
162 }
163 }
164
165 *p = '\0';
166
167 return true;
168}
169
170/*--------------------------------------------------------------------------------------------------------------------*/
171
172static void tokenizer_next(json_parser_t *parser)
173{
174 /*----------------------------------------------------------------------------------------------------------------*/
175
176 PEEK().value = NULL;
177
178 /*----------------------------------------------------------------------------------------------------------------*/
179
180 while(parser->size > 0 && isspace((unsigned char) *parser->buff))
181 {
182 parser->buff++;
183 parser->size--;
184 }
185
186 /*----------------------------------------------------------------------------------------------------------------*/
187
188 if(parser->size == 0)
189 {
190 parser->curr_token.token_type = JSON_TOKEN_EOF;
191
192 return;
193 }
194
195 /*----------------------------------------------------------------------------------------------------------------*/
196
197 STR_t start = parser->buff;
198 STR_t end = parser->buff;
199
200 json_token_type_t type;
201
202 switch(*parser->buff)
203 {
204 /*------------------------------------------------------------------------------------------------------------*/
205
206 case '\0':
207 type = JSON_TOKEN_EOF;
208 break;
209
210 /*------------------------------------------------------------------------------------------------------------*/
211
212 case '{':
213 end++;
214 parser->size--;
215 type = JSON_TOKEN_CURLY_OPEN;
216 break;
217
218 /*------------------------------------------------------------------------------------------------------------*/
219
220 case '}':
221 end++;
222 parser->size--;
223 type = JSON_TOKEN_CURLY_CLOSE;
224 break;
225
226 /*------------------------------------------------------------------------------------------------------------*/
227
228 case '[':
229 end++;
230 parser->size--;
231 type = JSON_TOKEN_SQUARE_OPEN;
232 break;
233
234 /*------------------------------------------------------------------------------------------------------------*/
235
236 case ']':
237 end++;
238 parser->size--;
239 type = JSON_TOKEN_SQUARE_CLOSE;
240 break;
241
242 /*------------------------------------------------------------------------------------------------------------*/
243
244 case ':':
245 end++;
246 parser->size--;
247 type = JSON_TOKEN_COLON;
248 break;
249
250 /*------------------------------------------------------------------------------------------------------------*/
251
252 case ',':
253 end++;
254 parser->size--;
255 type = JSON_TOKEN_COMMA;
256 break;
257
258 /*------------------------------------------------------------------------------------------------------------*/
259
260 case '"':
261 end++;
262 parser->size--;
263 while(parser->size >= 1 && *end != '"')
264 {
265 if(*end == '\0')
266 {
267 type = JSON_TOKEN_ERROR;
268 goto _bye;
269 }
270
271 if(parser->size >= 2 && (
272 *(end + 0) == '\\'
273 &&
274 *(end + 1) != '\0'
275 )) {
276 end++;
277 parser->size--;
278 }
279
280 end++;
281 parser->size--;
282 }
283 if(parser->size < 1)
284 {
285 type = JSON_TOKEN_ERROR;
286 goto _bye;
287 }
288 end++;
289 parser->size--;
290 type = JSON_TOKEN_STRING;
291 break;
292
293 /*------------------------------------------------------------------------------------------------------------*/
294
295 default:
296 /**/ if(*end == '-' || isdigit((unsigned char) *end))
297 {
298 end++;
299 parser->size--;
300 while(parser->size >= 1 && (*end == '-' || *end == '+' || *end == '.' || *end == 'e' || *end == 'E' || isdigit((unsigned char) *end)))
301 {
302 end++;
303 parser->size--;
304 }
305 type = JSON_TOKEN_NUMBER;
306 }
307 else if(parser->size >= 4 && strncmp(end, "null", 4) == 0)
308 {
309 end += 4;
310 parser->size -= 4;
311 type = JSON_TOKEN_NULL;
312 }
313 else if(parser->size >= 4 && strncmp(end, "true", 4) == 0)
314 {
315 end += 4;
316 parser->size -= 4;
317 type = JSON_TOKEN_TRUE;
318 }
319 else if(parser->size >= 5 && strncmp(end, "false", 5) == 0)
320 {
321 end += 5;
322 parser->size -= 5;
323 type = JSON_TOKEN_FALSE;
324 }
325 else
326 {
327 type = JSON_TOKEN_ERROR;
328 }
329 break;
330
331 /*------------------------------------------------------------------------------------------------------------*/
332 }
333
334 /*----------------------------------------------------------------------------------------------------------------*/
335
336 /**/ if(type == JSON_TOKEN_NUMBER)
337 {
338 STR_t s = start;
339 STR_t e = end ;
340
341 size_t length = TRIM(s, e);
342
343 parser->curr_token.value = nyx_string_ndup(s, length);
344 }
345
346 /*----------------------------------------------------------------------------------------------------------------*/
347
348 else if(type == JSON_TOKEN_STRING)
349 {
350 STR_t s = start;
351 STR_t e = end ;
352
353 size_t length = TRIM(s, e);
354
355 str_t p = parser->curr_token.value = nyx_memory_alloc(length + 1);
356
357 if(jsoncpy(p, s, e) == false)
358 {
359 nyx_memory_free(parser->curr_token.value);
360 parser->curr_token.value = NULL;
361 type = JSON_TOKEN_ERROR;
362 goto _bye;
363 }
364 }
365
366 /*----------------------------------------------------------------------------------------------------------------*/
367
368_bye:
369 parser->curr_token.token_type = type;
370
371 parser->buff = end;
372}
373
374/*--------------------------------------------------------------------------------------------------------------------*/
375/* TOKENIZER */
376/*--------------------------------------------------------------------------------------------------------------------*/
377
378static nyx_dict_t *json_parse_dict(json_parser_t *parser);
379
380static nyx_list_t *json_parse_list(json_parser_t *parser);
381
382/*--------------------------------------------------------------------------------------------------------------------*/
383
384static nyx_null_t *json_parse_null(json_parser_t *parser)
385{
386 if(CHECK(JSON_TOKEN_NULL) == false)
387 {
388 return NULL;
389 }
390
391 NEXT();
392
393 return nyx_null_new();
394}
395
396/*--------------------------------------------------------------------------------------------------------------------*/
397
398static nyx_boolean_t *json_parse_true(json_parser_t *parser)
399{
400 if(CHECK(JSON_TOKEN_TRUE) == false)
401 {
402 return NULL;
403 }
404
405 NEXT();
406
407 return nyx_boolean_from(true);
408}
409
410/*--------------------------------------------------------------------------------------------------------------------*/
411
412static nyx_boolean_t *json_parse_false(json_parser_t *parser)
413{
414 if(CHECK(JSON_TOKEN_FALSE) == false)
415 {
416 return NULL;
417 }
418
419 NEXT();
420
421 return nyx_boolean_from(false);
422}
423
424/*--------------------------------------------------------------------------------------------------------------------*/
425
426static nyx_number_t *json_parse_number(json_parser_t *parser)
427{
428 if(CHECK(JSON_TOKEN_NUMBER) == false)
429 {
430 return NULL;
431 }
432
433 str_t value = PEEK().value;
434
435 nyx_number_t *result = nyx_number_from(atof(value)); // NOLINT(*-err34-c)
436
437 nyx_memory_free(value);
438
439 NEXT();
440
441 return result;
442}
443
444/*--------------------------------------------------------------------------------------------------------------------*/
445
446static nyx_string_t *json_parse_string(json_parser_t *parser)
447{
448 if(CHECK(JSON_TOKEN_STRING) == false)
449 {
450 return NULL;
451 }
452
453 str_t value = PEEK().value;
454
455 nyx_string_t *result = nyx_string_from(/**/(value), true); // NOLINT(*-err34-c)
456
457 // don't free the value
458
459 NEXT();
460
461 return result;
462}
463
464/*--------------------------------------------------------------------------------------------------------------------*/
465
466static nyx_dict_t *json_parse_dict(json_parser_t *parser) // NOLINT(misc-no-recursion)
467{
468 nyx_dict_t *result = nyx_dict_new();
469
470 /*----------------------------------------------------------------------------------------------------------------*/
471
472 if(CHECK(JSON_TOKEN_CURLY_OPEN) == false)
473 {
474 goto _err;
475 }
476
477 NEXT();
478
479 /*----------------------------------------------------------------------------------------------------------------*/
480
481 while(CHECK(JSON_TOKEN_CURLY_CLOSE) == false)
482 {
483 /*------------------------------------------------------------------------------------------------------------*/
484
485 if(CHECK(JSON_TOKEN_STRING) == false)
486 {
487 ////_memory_free(key);
488
489 goto _err;
490 }
491
492 str_t key = PEEK().value;
493
494 NEXT();
495
496 if(CHECK(JSON_TOKEN_COLON) == false)
497 {
498 nyx_memory_free(key);
499
500 goto _err;
501 }
502
503 NEXT();
504
505 /*------------------------------------------------------------------------------------------------------------*/
506
507 buff_t value = NULL;
508
509 /**/ if(CHECK(JSON_TOKEN_NULL)) {
510 value = json_parse_null(parser);
511 }
512 else if(CHECK(JSON_TOKEN_TRUE)) {
513 value = json_parse_true(parser);
514 }
515 else if(CHECK(JSON_TOKEN_FALSE)) {
516 value = json_parse_false(parser);
517 }
518 else if(CHECK(JSON_TOKEN_NUMBER)) {
519 value = json_parse_number(parser);
520 }
521 else if(CHECK(JSON_TOKEN_STRING)) {
522 value = json_parse_string(parser);
523 }
524 else if(CHECK(JSON_TOKEN_CURLY_OPEN)) {
525 value = json_parse_dict(parser);
526 }
527 else if(CHECK(JSON_TOKEN_SQUARE_OPEN)) {
528 value = json_parse_list(parser);
529 }
530
531 if(value == NULL)
532 {
533 nyx_memory_free(key);
534
535 goto _err;
536 }
537
538 /*------------------------------------------------------------------------------------------------------------*/
539
540 nyx_dict_set(result, key, value);
541
542 nyx_object_unref(value);
543
544 /*------------------------------------------------------------------------------------------------------------*/
545
546 nyx_memory_free(key);
547
548 /*------------------------------------------------------------------------------------------------------------*/
549
550 if(CHECK(JSON_TOKEN_COMMA))
551 {
552 NEXT();
553
554 if(CHECK(JSON_TOKEN_CURLY_CLOSE) == true)
555 {
556 goto _err;
557 }
558 }
559 else
560 {
561 if(CHECK(JSON_TOKEN_CURLY_CLOSE) == false)
562 {
563 goto _err;
564 }
565 }
566
567 /*------------------------------------------------------------------------------------------------------------*/
568 }
569
570 /*----------------------------------------------------------------------------------------------------------------*/
571
572 if(CHECK(JSON_TOKEN_CURLY_CLOSE) == false)
573 {
574 goto _err;
575 }
576
577 NEXT();
578
579 /*----------------------------------------------------------------------------------------------------------------*/
580
581 return result;
582
583_err:
584 RELEASE(JSON_TOKEN_ERROR);
585
586 nyx_dict_free(result);
587
588 return NULL;
589}
590
591/*--------------------------------------------------------------------------------------------------------------------*/
592
593static nyx_list_t *json_parse_list(json_parser_t *parser) // NOLINT(misc-no-recursion)
594{
595 nyx_list_t *result = nyx_list_new();
596
597 /*----------------------------------------------------------------------------------------------------------------*/
598
599 if(CHECK(JSON_TOKEN_SQUARE_OPEN) == false)
600 {
601 goto _err;
602 }
603
604 NEXT();
605
606 /*----------------------------------------------------------------------------------------------------------------*/
607
608 while(CHECK(JSON_TOKEN_SQUARE_CLOSE) == false)
609 {
610 /*------------------------------------------------------------------------------------------------------------*/
611
612 buff_t value = NULL;
613
614 /**/ if(CHECK(JSON_TOKEN_NULL)) {
615 value = json_parse_null(parser);
616 }
617 else if(CHECK(JSON_TOKEN_TRUE)) {
618 value = json_parse_true(parser);
619 }
620 else if(CHECK(JSON_TOKEN_FALSE)) {
621 value = json_parse_false(parser);
622 }
623 else if(CHECK(JSON_TOKEN_NUMBER)) {
624 value = json_parse_number(parser);
625 }
626 else if(CHECK(JSON_TOKEN_STRING)) {
627 value = json_parse_string(parser);
628 }
629 else if(CHECK(JSON_TOKEN_CURLY_OPEN)) {
630 value = json_parse_dict(parser);
631 }
632 else if(CHECK(JSON_TOKEN_SQUARE_OPEN)) {
633 value = json_parse_list(parser);
634 }
635
636 if(value == NULL)
637 {
638 goto _err;
639 }
640
641 /*------------------------------------------------------------------------------------------------------------*/
642
643 nyx_list_push(result, value);
644
645 nyx_object_unref(value);
646
647 /*------------------------------------------------------------------------------------------------------------*/
648
649 if(CHECK(JSON_TOKEN_COMMA))
650 {
651 NEXT();
652
653 if(CHECK(JSON_TOKEN_SQUARE_CLOSE) == true)
654 {
655 goto _err;
656 }
657 }
658 else
659 {
660 if(CHECK(JSON_TOKEN_SQUARE_CLOSE) == false)
661 {
662 goto _err;
663 }
664 }
665
666 /*------------------------------------------------------------------------------------------------------------*/
667 }
668
669 /*----------------------------------------------------------------------------------------------------------------*/
670
671 if(CHECK(JSON_TOKEN_SQUARE_CLOSE) == false)
672 {
673 goto _err;
674 }
675
676 NEXT();
677
678 /*----------------------------------------------------------------------------------------------------------------*/
679
680 return result;
681
682_err:
683 RELEASE(JSON_TOKEN_ERROR);
684
685 nyx_list_free(result);
686
687 return NULL;
688}
689
690/*--------------------------------------------------------------------------------------------------------------------*/
691
692nyx_object_t *nyx_object_parse_buff(size_t size, BUFF_t buff)
693{
694 if(size == 0x00
695 ||
696 buff == NULL
697 ) {
698 return NULL;
699 }
700
701 /*----------------------------------------------------------------------------------------------------------------*/
702
703 json_parser_t *parser = &(json_parser_t) {
704 .size = size,
705 .buff = buff,
706 .curr_token = {
707 .value = NULL,
708 .token_type = JSON_TOKEN_ERROR,
709 },
710 };
711
712 /*----------------------------------------------------------------------------------------------------------------*/
713
714 NEXT();
715
716 /*----------------------------------------------------------------------------------------------------------------*/
717
718 nyx_object_t *result = NULL;
719
720 /**/ if(CHECK(JSON_TOKEN_NULL)) {
721 result = (nyx_object_t *) json_parse_null(parser);
722 }
723 else if(CHECK(JSON_TOKEN_TRUE)) {
724 result = (nyx_object_t *) json_parse_true(parser);
725 }
726 else if(CHECK(JSON_TOKEN_FALSE)) {
727 result = (nyx_object_t *) json_parse_false(parser);
728 }
729 else if(CHECK(JSON_TOKEN_NUMBER)) {
730 result = (nyx_object_t *) json_parse_number(parser);
731 }
732 else if(CHECK(JSON_TOKEN_STRING)) {
733 result = (nyx_object_t *) json_parse_string(parser);
734 }
735 else if(CHECK(JSON_TOKEN_CURLY_OPEN)) {
736 result = (nyx_object_t *) json_parse_dict(parser);
737 }
738 else if(CHECK(JSON_TOKEN_SQUARE_OPEN)) {
739 result = (nyx_object_t *) json_parse_list(parser);
740 }
741
742 /*----------------------------------------------------------------------------------------------------------------*/
743
744 if(result == NULL || CHECK(JSON_TOKEN_EOF) == false)
745 {
746 if(parser->curr_token.value != NULL)
747 {
748 nyx_memory_free(parser->curr_token.value);
749
750 parser->curr_token.value = NULL;
751 }
752
753 nyx_object_unref(result);
754
755 result = NULL;
756 }
757
758 /*----------------------------------------------------------------------------------------------------------------*/
759
760 return result;
761}
762
763/*--------------------------------------------------------------------------------------------------------------------*/
764
765nyx_object_t *nyx_object_parse(STR_t string)
766{
767 if(string == NULL)
768 {
769 return NULL;
770 }
771
772 return nyx_object_parse_buff(
773 strlen(string),
774 buffof(string)
775 );
776}
777
778/*--------------------------------------------------------------------------------------------------------------------*/
Struct describing a JSON boolean object.
Definition nyx_node.h:821
Struct describing a JSON dict object.
Struct describing a JSON list object.
#define STR_t
Alias for const char *.
Definition nyx_node.h:71
__NYX_NULLABLE__ buff_t nyx_memory_alloc(__NYX_ZEROABLE__ size_t size)
Similar to libc malloc except that a memory overflow causes the node to stop.
__NYX_ZEROABLE__ size_t nyx_memory_free(__NYX_NULLABLE__ buff_t buff)
Similar to libc free except that it returns the amount of memory freed.
#define buff_t
Alias for void *.
Definition nyx_node.h:67
#define BUFF_t
Alias for const void *.
Definition nyx_node.h:68
__NYX_NULLABLE__ str_t nyx_string_ndup(__NYX_NULLABLE__ STR_t s, __NYX_ZEROABLE__ size_t n)
Similar to libc strndup.
#define str_t
Alias for char *.
Definition nyx_node.h:70
Struct describing a JSON null object.
Definition nyx_node.h:680
Struct describing a JSON number object.
Definition nyx_node.h:726
Struct describing a JSON object.
Struct describing a JSON string object.
Definition nyx_node.h:914