• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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