1 #include "jbl.h"
2 #include "utf8proc.h"
3 #include "jbl_internal.h"
4 #include <errno.h>
5
6 #define IS_WHITESPACE(c_) ((unsigned char) (c_) <= (unsigned char) ' ')
7
8 /** JSON parsing context */
9 typedef struct JCTX {
10 IWPOOL *pool;
11 JBL_NODE root;
12 const char *buf;
13 const char *sp;
14 iwrc rc;
15 } JCTX;
16
_jbn_add_item(JBL_NODE parent,JBL_NODE node)17 static void _jbn_add_item(JBL_NODE parent, JBL_NODE node) {
18 assert(parent && node);
19 node->next = 0;
20 node->parent = parent;
21 if (parent->child) {
22 JBL_NODE prev = parent->child->prev;
23 parent->child->prev = node;
24 if (prev) { // -V1051
25 prev->next = node;
26 node->prev = prev;
27 } else {
28 parent->child->next = node;
29 node->prev = parent->child;
30 }
31 } else {
32 parent->child = node;
33 }
34 if (parent->type == JBV_ARRAY) {
35 if (node->prev) {
36 node->klidx = node->prev->klidx + 1;
37 } else {
38 node->klidx = 0;
39 }
40 }
41 }
42
_jbl_json_create_node(jbl_type_t type,const char * key,int klidx,JBL_NODE parent,JCTX * ctx)43 static JBL_NODE _jbl_json_create_node(jbl_type_t type, const char *key, int klidx, JBL_NODE parent, JCTX *ctx) {
44
45 JBL_NODE node = iwpool_calloc(sizeof(*node), ctx->pool);
46 if (!node) {
47 ctx->rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
48 return 0;
49 }
50 node->type = type;
51 node->key = key;
52 node->klidx = klidx;
53 if (parent) {
54 _jbn_add_item(parent, node);
55 }
56 if (!ctx->root) {
57 ctx->root = node;
58 }
59 return node;
60 }
61
_jbl_skip_bom(JCTX * ctx)62 IW_INLINE void _jbl_skip_bom(JCTX *ctx) {
63 const char *p = ctx->buf;
64 if ((p[0] == '\xEF') && (p[1] == '\xBB') && (p[2] == '\xBF')) {
65 ctx->buf += 3;
66 }
67 }
68
_jbl_hex(char c)69 IW_INLINE int _jbl_hex(char c) {
70 if ((c >= '0') && (c <= '9')) {
71 return c - '0';
72 }
73 if ((c >= 'a') && (c <= 'f')) {
74 return c - 'a' + 10;
75 }
76 if ((c >= 'A') && (c <= 'F')) {
77 return c - 'A' + 10;
78 }
79 return -1;
80 }
81
_jbl_unescape_json_string(const char * p,char * d,int dlen,const char ** end,iwrc * rcp)82 static int _jbl_unescape_json_string(const char *p, char *d, int dlen, const char **end, iwrc *rcp) {
83 *rcp = 0;
84 char c;
85 char *ds = d;
86 char *de = d + dlen;
87
88 while ((c = *p++)) {
89 if (c == '"') { // string closing quotes
90 if (end) {
91 *end = p;
92 }
93 return d - ds;
94 } else if (c == '\\') {
95 switch (*p) {
96 case '\\':
97 case '/':
98 case '"':
99 if (d < de) {
100 *d = *p;
101 }
102 ++p, ++d;
103 break;
104 case 'b':
105 if (d < de) {
106 *d = '\b';
107 }
108 ++p, ++d;
109 break;
110 case 'f':
111 if (d < de) {
112 *d = '\f';
113 }
114 ++p, ++d;
115 break;
116 case 'n':
117 case 'r':
118 if (d < de) {
119 *d = '\n';
120 }
121 ++p, ++d;
122 break;
123 case 't':
124 if (d < de) {
125 *d = '\t';
126 }
127 ++p, ++d;
128 break;
129 case 'u': {
130 uint32_t cp, cp2;
131 int h1, h2, h3, h4;
132 if ( ((h1 = _jbl_hex(p[1])) < 0) || ((h2 = _jbl_hex(p[2])) < 0)
133 || ((h3 = _jbl_hex(p[3])) < 0) || ((h4 = _jbl_hex(p[4])) < 0)) {
134 *rcp = JBL_ERROR_PARSE_INVALID_CODEPOINT;
135 return 0;
136 }
137 cp = h1 << 12 | h2 << 8 | h3 << 4 | h4;
138 if ((cp & 0xfc00) == 0xd800) {
139 p += 6;
140 if ( (p[-1] != '\\') || (*p != 'u')
141 || ((h1 = _jbl_hex(p[1])) < 0) || ((h2 = _jbl_hex(p[2])) < 0)
142 || ((h3 = _jbl_hex(p[3])) < 0) || ((h4 = _jbl_hex(p[4])) < 0)) {
143 *rcp = JBL_ERROR_PARSE_INVALID_CODEPOINT;
144 return 0;
145 }
146 cp2 = h1 << 12 | h2 << 8 | h3 << 4 | h4;
147 if ((cp2 & 0xfc00) != 0xdc00) {
148 *rcp = JBL_ERROR_PARSE_INVALID_CODEPOINT;
149 return 0;
150 }
151 cp = 0x10000 + ((cp - 0xd800) << 10) + (cp2 - 0xdc00);
152 }
153 if (!utf8proc_codepoint_valid(cp)) {
154 *rcp = JBL_ERROR_PARSE_INVALID_CODEPOINT;
155 return 0;
156 }
157 uint8_t uchars[4];
158 utf8proc_ssize_t ulen = utf8proc_encode_char(cp, uchars);
159 assert(ulen <= sizeof(uchars));
160 for (int i = 0; i < ulen; ++i) {
161 if (d < de) {
162 *d = uchars[i];
163 }
164 ++d;
165 }
166 p += 5;
167 break;
168 }
169 default:
170 if (d < de) {
171 *d = c;
172 }
173 ++d;
174 }
175 } else {
176 if (d < de) {
177 *d = c;
178 }
179 ++d;
180 }
181 }
182 *rcp = JBL_ERROR_PARSE_UNQUOTED_STRING;
183 return 0;
184 }
185
_jbl_parse_key(const char ** key,const char * p,JCTX * ctx)186 static const char *_jbl_parse_key(const char **key, const char *p, JCTX *ctx) {
187 char c;
188 *key = "";
189 while ((c = *p++)) {
190 if (c == '"') {
191 int len = _jbl_unescape_json_string(p, 0, 0, 0, &ctx->rc);
192 if (ctx->rc) {
193 return 0;
194 }
195 if (len) {
196 char *kptr = iwpool_alloc(len + 1, ctx->pool);
197 if (!kptr) {
198 ctx->rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
199 return 0;
200 }
201 if ((len != _jbl_unescape_json_string(p, kptr, len, &p, &ctx->rc)) || ctx->rc) {
202 if (!ctx->rc) {
203 ctx->rc = JBL_ERROR_PARSE_JSON;
204 }
205 return 0;
206 }
207 kptr[len] = '\0';
208 *key = kptr;
209 }
210 while (*p && IS_WHITESPACE(*p)) p++;
211 if (*p == ':') {
212 return p + 1;
213 }
214 ctx->rc = JBL_ERROR_PARSE_JSON;
215 return 0;
216 } else if (c == '}') {
217 return p - 1;
218 } else if (IS_WHITESPACE(c) || (c == ',')) {
219 continue;
220 } else {
221 ctx->rc = JBL_ERROR_PARSE_JSON;
222 return 0;
223 }
224 }
225 ctx->rc = JBL_ERROR_PARSE_JSON;
226 return 0;
227 }
228
_jbl_parse_value(int lvl,JBL_NODE parent,const char * key,int klidx,const char * p,JCTX * ctx)229 static const char *_jbl_parse_value(
230 int lvl,
231 JBL_NODE parent,
232 const char *key, int klidx,
233 const char *p,
234 JCTX *ctx) {
235
236 if (lvl > JBL_MAX_NESTING_LEVEL) {
237 ctx->rc = JBL_ERROR_MAX_NESTING_LEVEL_EXCEEDED;
238 return 0;
239 }
240
241 JBL_NODE node;
242 while (1) {
243 switch (*p) {
244 case '\0':
245 ctx->rc = JBL_ERROR_PARSE_JSON;
246 return 0;
247 case ' ':
248 case '\t':
249 case '\n':
250 case '\r':
251 case ',':
252 ++p;
253 break;
254 case 'n':
255 if (!strncmp(p, "null", 4)) {
256 _jbl_json_create_node(JBV_NULL, key, klidx, parent, ctx);
257 if (ctx->rc) {
258 return 0;
259 }
260 return p + 4;
261 }
262 ctx->rc = JBL_ERROR_PARSE_JSON;
263 return 0;
264 case 't':
265 if (!strncmp(p, "true", 4)) {
266 node = _jbl_json_create_node(JBV_BOOL, key, klidx, parent, ctx);
267 if (ctx->rc) {
268 return 0;
269 }
270 node->vbool = true; // -V522
271 return p + 4;
272 }
273 ctx->rc = JBL_ERROR_PARSE_JSON;
274 return 0;
275 case 'f':
276 if (!strncmp(p, "false", 5)) {
277 node = _jbl_json_create_node(JBV_BOOL, key, klidx, parent, ctx);
278 if (ctx->rc) {
279 return 0;
280 }
281 node->vbool = false;
282 return p + 5;
283 }
284 ctx->rc = JBL_ERROR_PARSE_JSON;
285 return 0;
286 case '"':
287 ++p;
288 const char *end;
289 int len = _jbl_unescape_json_string(p, 0, 0, &end, &ctx->rc);
290 if (ctx->rc) {
291 return 0;
292 }
293 node = _jbl_json_create_node(JBV_STR, key, klidx, parent, ctx);
294 if (ctx->rc) {
295 return 0;
296 }
297 if (len) {
298 char *vptr = iwpool_alloc(len + 1, ctx->pool);
299 if (!vptr) {
300 ctx->rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
301 return 0;
302 }
303 if ((len != _jbl_unescape_json_string(p, vptr, len, &p, &ctx->rc)) || ctx->rc) {
304 if (!ctx->rc) {
305 ctx->rc = JBL_ERROR_PARSE_JSON;
306 }
307 return 0;
308 }
309 vptr[len] = '\0';
310 node->vptr = vptr;
311 node->vsize = len;
312 } else {
313 p = end;
314 node->vptr = "";
315 node->vsize = 0;
316 }
317 return p;
318 case '{':
319 node = _jbl_json_create_node(JBV_OBJECT, key, klidx, parent, ctx);
320 if (ctx->rc) {
321 return 0;
322 }
323 ++p;
324 while (1) {
325 const char *nkey;
326 p = _jbl_parse_key(&nkey, p, ctx);
327 if (ctx->rc) {
328 return 0;
329 }
330 if (*p == '}') {
331 return p + 1; // -V522
332 }
333 p = _jbl_parse_value(lvl + 1, node, nkey, strlen(nkey), p, ctx);
334 if (ctx->rc) {
335 return 0;
336 }
337 }
338 break;
339 case '[':
340 node = _jbl_json_create_node(JBV_ARRAY, key, klidx, parent, ctx);
341 if (ctx->rc) {
342 return 0;
343 }
344 ++p;
345 for (int i = 0; ; ++i) {
346 p = _jbl_parse_value(lvl + 1, node, 0, i, p, ctx);
347 if (ctx->rc) {
348 return 0;
349 }
350 if (*p == ']') {
351 return p + 1;
352 }
353 }
354 break;
355 case ']':
356 return p;
357 break;
358 case '-':
359 case '0':
360 case '1':
361 case '2':
362 case '3':
363 case '4':
364 case '5':
365 case '6':
366 case '7':
367 case '8':
368 case '9': {
369 node = _jbl_json_create_node(JBV_I64, key, klidx, parent, ctx);
370 if (ctx->rc) {
371 return 0;
372 }
373 char *pe;
374 node->vi64 = strtoll(p, &pe, 0);
375 if ((pe == p) || (errno == ERANGE)) {
376 ctx->rc = JBL_ERROR_PARSE_JSON;
377 return 0;
378 }
379 if ((*pe == '.') || (*pe == 'e') || (*pe == 'E')) {
380 node->type = JBV_F64;
381 node->vf64 = strtod(p, &pe);
382 if ((pe == p) || (errno == ERANGE)) {
383 ctx->rc = JBL_ERROR_PARSE_JSON;
384 return 0;
385 }
386 }
387 return pe;
388 }
389 default:
390 ctx->rc = JBL_ERROR_PARSE_JSON;
391 return 0;
392 }
393 }
394 return p;
395 }
396
_jbl_node_as_json(JBL_NODE node,jbl_json_printer pt,void * op,int lvl,jbl_print_flags_t pf)397 static iwrc _jbl_node_as_json(JBL_NODE node, jbl_json_printer pt, void *op, int lvl, jbl_print_flags_t pf) {
398 iwrc rc = 0;
399 bool pretty = pf & JBL_PRINT_PRETTY;
400
401 #define PT(data_, size_, ch_, count_) do { \
402 rc = pt(data_, size_, ch_, count_, op); \
403 RCRET(rc); \
404 } while (0)
405
406 switch (node->type) {
407 case JBV_ARRAY:
408 PT(0, 0, '[', 1);
409 if (node->child && pretty) {
410 PT(0, 0, '\n', 1);
411 }
412 for (JBL_NODE n = node->child; n; n = n->next) {
413 if (pretty) {
414 PT(0, 0, ' ', lvl + 1);
415 }
416 rc = _jbl_node_as_json(n, pt, op, lvl + 1, pf);
417 RCRET(rc);
418 if (n->next) {
419 PT(0, 0, ',', 1);
420 }
421 if (pretty) {
422 PT(0, 0, '\n', 1);
423 }
424 }
425 if (node->child && pretty) {
426 PT(0, 0, ' ', lvl);
427 }
428 PT(0, 0, ']', 1);
429 break;
430 case JBV_OBJECT:
431 PT(0, 0, '{', 1);
432 if (node->child && pretty) {
433 PT(0, 0, '\n', 1);
434 }
435 for (JBL_NODE n = node->child; n; n = n->next) {
436 if (pretty) {
437 PT(0, 0, ' ', lvl + 1);
438 }
439 rc = _jbl_write_string(n->key, n->klidx, pt, op, pf);
440 RCRET(rc);
441 if (pretty) {
442 PT(": ", -1, 0, 0);
443 } else {
444 PT(0, 0, ':', 1);
445 }
446 rc = _jbl_node_as_json(n, pt, op, lvl + 1, pf);
447 RCRET(rc);
448 if (n->next) {
449 PT(0, 0, ',', 1);
450 }
451 if (pretty) {
452 PT(0, 0, '\n', 1);
453 }
454 }
455 if (node->child && pretty) {
456 PT(0, 0, ' ', lvl);
457 }
458 PT(0, 0, '}', 1);
459 break;
460 case JBV_STR:
461 rc = _jbl_write_string(node->vptr, node->vsize, pt, op, pf);
462 break;
463 case JBV_I64:
464 rc = _jbl_write_int(node->vi64, pt, op);
465 break;
466 case JBV_F64:
467 rc = _jbl_write_double(node->vf64, pt, op);
468 break;
469 case JBV_BOOL:
470 if (node->vbool) {
471 PT("true", 4, 0, 1);
472 } else {
473 PT("false", 5, 0, 1);
474 }
475 break;
476 case JBV_NULL:
477 PT("null", 4, 0, 1);
478 break;
479 default:
480 iwlog_ecode_error3(IW_ERROR_ASSERTION);
481 return IW_ERROR_ASSERTION;
482 }
483 #undef PT
484 return rc;
485 }
486
_jbl_clone_node_struct(JBL_NODE src,IWPOOL * pool)487 static JBL_NODE _jbl_clone_node_struct(JBL_NODE src, IWPOOL *pool) {
488 iwrc rc;
489 JBL_NODE n = iwpool_calloc(sizeof(*n), pool);
490 if (!n) {
491 return 0;
492 }
493 n->vsize = src->vsize;
494 n->type = src->type;
495 n->klidx = src->klidx;
496 n->flags = src->flags;
497
498 if (src->key) {
499 n->key = iwpool_strndup(pool, src->key, src->klidx, &rc);
500 if (!n->key) {
501 return 0;
502 }
503 }
504 switch (src->type) {
505 case JBV_STR: {
506 n->vptr = iwpool_strndup(pool, src->vptr, src->vsize, &rc);
507 if (!n->vptr) {
508 return 0;
509 }
510 break;
511 }
512 case JBV_I64:
513 n->vi64 = src->vi64;
514 break;
515 case JBV_BOOL:
516 n->vbool = src->vbool;
517 break;
518 case JBV_F64:
519 n->vf64 = src->vf64;
520 break;
521 default:
522 break;
523 }
524 ;
525 return n;
526 }
527
_jbl_clone_node_visit(int lvl,JBL_NODE n,const char * key,int klidx,JBN_VCTX * vctx,iwrc * rc)528 static jbn_visitor_cmd_t _jbl_clone_node_visit(
529 int lvl, JBL_NODE n, const char *key, int klidx, JBN_VCTX *vctx,
530 iwrc *rc) {
531 if (lvl < 0) {
532 return JBL_VCMD_OK;
533 }
534 JBL_NODE parent = vctx->root;
535 if (lvl < vctx->pos) { // Pop
536 for ( ; lvl < vctx->pos; --vctx->pos) {
537 parent = parent->parent;
538 assert(parent);
539 }
540 vctx->root = parent;
541 assert(vctx->root);
542 } else if (lvl > vctx->pos) { // Push
543 vctx->pos = lvl;
544 parent = vctx->op;
545 vctx->root = parent;
546 assert(parent);
547 }
548 JBL_NODE nn = _jbl_clone_node_struct(n, vctx->pool);
549 if (!nn) {
550 *rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
551 return JBL_VCMD_TERMINATE;
552 }
553 _jbn_add_item(parent, nn);
554 if (nn->type >= JBV_OBJECT) {
555 vctx->op = nn; // Remeber the last container object
556 }
557 return JBL_VCMD_OK;
558 }
559
jbn_clone(JBL_NODE src,JBL_NODE * targetp,IWPOOL * pool)560 iwrc jbn_clone(JBL_NODE src, JBL_NODE *targetp, IWPOOL *pool) {
561 *targetp = 0;
562 JBL_NODE n = _jbl_clone_node_struct(src, pool);
563 if (!n) {
564 return iwrc_set_errno(IW_ERROR_ALLOC, errno);
565 }
566 JBN_VCTX vctx = {
567 .pool = pool,
568 .root = n,
569 .op = n
570 };
571 iwrc rc = jbn_visit(src, 0, &vctx, _jbl_clone_node_visit);
572 RCRET(rc);
573 *targetp = n;
574 return 0;
575 }
576
jbn_as_json(JBL_NODE node,jbl_json_printer pt,void * op,jbl_print_flags_t pf)577 iwrc jbn_as_json(JBL_NODE node, jbl_json_printer pt, void *op, jbl_print_flags_t pf) {
578 return _jbl_node_as_json(node, pt, op, 0, pf);
579 }
580
jbn_from_json(const char * json,JBL_NODE * node,IWPOOL * pool)581 iwrc jbn_from_json(const char *json, JBL_NODE *node, IWPOOL *pool) {
582 *node = 0;
583 JCTX ctx = {
584 .pool = pool,
585 .buf = json
586 };
587 _jbl_skip_bom(&ctx);
588 _jbl_parse_value(0, 0, 0, 0, ctx.buf, &ctx);
589 *node = ctx.root;
590 return ctx.rc;
591 }
592
593 // Depreacated
jbl_node_from_json(const char * json,JBL_NODE * node,IWPOOL * pool)594 iwrc jbl_node_from_json(const char *json, JBL_NODE *node, IWPOOL *pool) {
595 return jbn_from_json(json, node, pool);
596 }
597
598 // Depreacated
jbl_node_as_json(JBL_NODE node,jbl_json_printer pt,void * op,jbl_print_flags_t pf)599 iwrc jbl_node_as_json(JBL_NODE node, jbl_json_printer pt, void *op, jbl_print_flags_t pf) {
600 return _jbl_node_as_json(node, pt, op, 0, pf);
601 }
602