• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "iwjson.h"
2 #include "iwconv.h"
3 #include "utf8proc.h"
4 #include "iwjson_internal.h"
5 
6 #include <ctype.h>
7 #include <stdarg.h>
8 #include <errno.h>
9 #include <assert.h>
10 
11 #define _STRX(x) #x
12 #define _STR(x)  _STRX(x)
13 
_jbl_printf_estimate_size(const char * format,va_list ap)14 IW_INLINE int _jbl_printf_estimate_size(const char *format, va_list ap) {
15   char buf[1];
16   int ret = vsnprintf(buf, sizeof(buf), format, ap);
17   if (ret < 0) {
18     return ret;
19   } else {
20     return ret + 1;
21   }
22 }
23 
24 IW_INLINE void _jbn_remove_item(JBL_NODE parent, JBL_NODE child);
25 static void _jbn_add_item(JBL_NODE parent, JBL_NODE node);
26 
iwjson_ftoa(long double val,char buf[IWNUMBUF_SIZE],size_t * out_len)27 void iwjson_ftoa(long double val, char buf[IWNUMBUF_SIZE], size_t *out_len) {
28   // TODO: review
29   int len = snprintf(buf, IWNUMBUF_SIZE, "%.8Lf", val);
30   if (len <= 0) {
31     buf[0] = '\0';
32     *out_len = 0;
33     return;
34   }
35   while (len > 0 && buf[len - 1] == '0') { // trim zeroes from right
36     buf[len - 1] = '\0';
37     len--;
38   }
39   if ((len > 0) && (buf[len - 1] == '.')) {
40     buf[len - 1] = '\0';
41     len--;
42   }
43   *out_len = (size_t) len;
44 }
45 
jbl_create_empty_object(JBL * jblp)46 iwrc jbl_create_empty_object(JBL *jblp) {
47   *jblp = calloc(1, sizeof(**jblp));
48   if (!*jblp) {
49     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
50   }
51   binn_create(&(*jblp)->bn, BINN_OBJECT, 0, 0);
52   return 0;
53 }
54 
jbl_create_empty_array(JBL * jblp)55 iwrc jbl_create_empty_array(JBL *jblp) {
56   *jblp = calloc(1, sizeof(**jblp));
57   if (!*jblp) {
58     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
59   }
60   binn_create(&(*jblp)->bn, BINN_LIST, 0, 0);
61   return 0;
62 }
63 
jbl_set_user_data(JBL jbl,void * user_data,void (* user_data_free_fn)(void *))64 void jbl_set_user_data(JBL jbl, void *user_data, void (*user_data_free_fn)(void*)) {
65   binn_set_user_data(&jbl->bn, user_data, user_data_free_fn);
66 }
67 
jbl_get_user_data(JBL jbl)68 void* jbl_get_user_data(JBL jbl) {
69   return jbl->bn.user_data;
70 }
71 
jbl_set_int64(JBL jbl,const char * key,int64_t v)72 iwrc jbl_set_int64(JBL jbl, const char *key, int64_t v) {
73   jbl_type_t t = jbl_type(jbl);
74   if (((t != JBV_OBJECT) && (t != JBV_ARRAY)) || !jbl->bn.writable) {
75     return JBL_ERROR_CREATION;
76   }
77   binn *bv = &jbl->bn;
78   if (key) {
79     if (t == JBV_OBJECT) {
80       if (!binn_object_set_int64(bv, key, v)) {
81         return JBL_ERROR_CREATION;
82       }
83     } else {
84       return JBL_ERROR_CREATION;
85     }
86     return 0;
87   } else if (t == JBV_ARRAY) {
88     if (!binn_list_add_int64(bv, v)) {
89       return JBL_ERROR_CREATION;
90     }
91     return 0;
92   }
93   return JBL_ERROR_INVALID;
94 }
95 
jbl_set_f64(JBL jbl,const char * key,double v)96 iwrc jbl_set_f64(JBL jbl, const char *key, double v) {
97   jbl_type_t t = jbl_type(jbl);
98   if (((t != JBV_OBJECT) && (t != JBV_ARRAY)) || !jbl->bn.writable) {
99     return JBL_ERROR_CREATION;
100   }
101   binn *bv = &jbl->bn;
102   if (key) {
103     if (t == JBV_OBJECT) {
104       if (!binn_object_set_double(bv, key, v)) {
105         return JBL_ERROR_CREATION;
106       }
107     } else {
108       return JBL_ERROR_CREATION;
109     }
110     return 0;
111   } else if (t == JBV_ARRAY) {
112     if (!binn_list_add_double(bv, v)) {
113       return JBL_ERROR_CREATION;
114     }
115     return 0;
116   }
117   return JBL_ERROR_INVALID;
118 }
119 
jbl_set_string(JBL jbl,const char * key,const char * v)120 iwrc jbl_set_string(JBL jbl, const char *key, const char *v) {
121   jbl_type_t t = jbl_type(jbl);
122   if (((t != JBV_OBJECT) && (t != JBV_ARRAY)) || !jbl->bn.writable) {
123     return JBL_ERROR_CREATION;
124   }
125   binn *bv = &jbl->bn;
126   if (key) {
127     if (t == JBV_OBJECT) {
128       if (!binn_object_set_str(bv, key, v)) {
129         return JBL_ERROR_CREATION;
130       }
131     } else {
132       return JBL_ERROR_CREATION;
133     }
134     return 0;
135   } else if (t == JBV_ARRAY) {
136     if (!binn_list_add_const_str(bv, v)) {
137       return JBL_ERROR_CREATION;
138     }
139     return 0;
140   }
141   return JBL_ERROR_INVALID;
142 }
143 
jbl_set_string_printf(JBL jbl,const char * key,const char * format,...)144 iwrc jbl_set_string_printf(JBL jbl, const char *key, const char *format, ...) {
145   iwrc rc = 0;
146   va_list ap;
147 
148   va_start(ap, format);
149   int size = _jbl_printf_estimate_size(format, ap);
150   if (size < 0) {
151     va_end(ap);
152     return IW_ERROR_INVALID_ARGS;
153   }
154   va_end(ap);
155 
156   va_start(ap, format);
157   char *buf = malloc(size);
158   RCGA(buf, finish);
159   vsnprintf(buf, size, format, ap);
160   va_end(ap);
161 
162   rc = jbl_set_string(jbl, key, buf);
163 finish:
164   free(buf);
165   return rc;
166 }
167 
jbl_from_json_printf_va(JBL * jblp,const char * format,va_list va)168 iwrc jbl_from_json_printf_va(JBL *jblp, const char *format, va_list va) {
169   iwrc rc = 0;
170   va_list cva;
171 
172   va_copy(cva, va);
173   int size = _jbl_printf_estimate_size(format, va);
174   if (size < 0) {
175     va_end(cva);
176     return IW_ERROR_INVALID_ARGS;
177   }
178   char *buf = malloc(size);
179   RCGA(buf, finish);
180   vsnprintf(buf, size, format, cva);
181   va_end(cva);
182 
183   rc = jbl_from_json(jblp, buf);
184 
185 finish:
186   free(buf);
187   return rc;
188 }
189 
jbl_from_json_printf(JBL * jblp,const char * format,...)190 iwrc jbl_from_json_printf(JBL *jblp, const char *format, ...) {
191   va_list ap;
192 
193   va_start(ap, format);
194   iwrc rc = jbl_from_json_printf_va(jblp, format, ap);
195   va_end(ap);
196   return rc;
197 }
198 
jbn_from_json_printf_va(JBL_NODE * node,IWPOOL * pool,const char * format,va_list va)199 iwrc jbn_from_json_printf_va(JBL_NODE *node, IWPOOL *pool, const char *format, va_list va) {
200   iwrc rc = 0;
201   va_list cva;
202 
203   va_copy(cva, va);
204   int size = _jbl_printf_estimate_size(format, va);
205   if (size < 0) {
206     va_end(cva);
207     return IW_ERROR_INVALID_ARGS;
208   }
209   char *buf = malloc(size);
210   RCGA(buf, finish);
211   vsnprintf(buf, size, format, cva);
212   va_end(cva);
213 
214   rc = jbn_from_json(buf, node, pool);
215 
216 finish:
217   free(buf);
218   return rc;
219 }
220 
jbn_from_json_printf(JBL_NODE * node,IWPOOL * pool,const char * format,...)221 iwrc jbn_from_json_printf(JBL_NODE *node, IWPOOL *pool, const char *format, ...) {
222   va_list ap;
223 
224   va_start(ap, format);
225   iwrc rc = jbn_from_json_printf_va(node, pool, format, ap);
226   va_end(ap);
227   return rc;
228 }
229 
jbl_set_bool(JBL jbl,const char * key,bool v)230 iwrc jbl_set_bool(JBL jbl, const char *key, bool v) {
231   jbl_type_t t = jbl_type(jbl);
232   if (((t != JBV_OBJECT) && (t != JBV_ARRAY)) || !jbl->bn.writable) {
233     return JBL_ERROR_CREATION;
234   }
235   binn *bv = &jbl->bn;
236   if (key) {
237     if (t == JBV_OBJECT) {
238       if (!binn_object_set_bool(bv, key, v)) {
239         return JBL_ERROR_CREATION;
240       }
241     } else {
242       return JBL_ERROR_CREATION;
243     }
244     return 0;
245   } else if (t == JBV_ARRAY) {
246     if (!binn_list_add_bool(bv, v)) {
247       return JBL_ERROR_CREATION;
248     }
249     return 0;
250   }
251   return JBL_ERROR_INVALID;
252 }
253 
jbl_set_null(JBL jbl,const char * key)254 iwrc jbl_set_null(JBL jbl, const char *key) {
255   jbl_type_t t = jbl_type(jbl);
256   if (((t != JBV_OBJECT) && (t != JBV_ARRAY)) || !jbl->bn.writable) {
257     return JBL_ERROR_CREATION;
258   }
259   binn *bv = &jbl->bn;
260   if (key) {
261     if (t == JBV_OBJECT) {
262       if (!binn_object_set_null(bv, key)) {
263         return JBL_ERROR_CREATION;
264       }
265     } else {
266       return JBL_ERROR_CREATION;
267     }
268     return 0;
269   } else if (t == JBV_ARRAY) {
270     if (!binn_list_add_null(bv)) {
271       return JBL_ERROR_CREATION;
272     }
273     return 0;
274   }
275   return JBL_ERROR_INVALID;
276 }
277 
jbl_set_empty_array(JBL jbl,const char * key)278 iwrc jbl_set_empty_array(JBL jbl, const char *key) {
279   JBL v = 0;
280   iwrc rc = jbl_create_empty_array(&v);
281   RCGO(rc, finish);
282   rc = jbl_set_nested(jbl, key, v);
283 finish:
284   jbl_destroy(&v);
285   return rc;
286 }
287 
jbl_set_empty_object(JBL jbl,const char * key)288 iwrc jbl_set_empty_object(JBL jbl, const char *key) {
289   JBL v = 0;
290   iwrc rc = jbl_create_empty_object(&v);
291   RCGO(rc, finish);
292   rc = jbl_set_nested(jbl, key, v);
293 finish:
294   jbl_destroy(&v);
295   return rc;
296 }
297 
jbl_set_nested(JBL jbl,const char * key,JBL v)298 iwrc jbl_set_nested(JBL jbl, const char *key, JBL v) {
299   jbl_type_t t = jbl_type(jbl);
300   if (((t != JBV_OBJECT) && (t != JBV_ARRAY)) || !jbl->bn.writable) {
301     return JBL_ERROR_CREATION;
302   }
303   binn *bv = &jbl->bn;
304   if (key) {
305     if (t == JBV_OBJECT) {
306       if (!binn_object_set_value(bv, key, &v->bn)) {
307         return JBL_ERROR_CREATION;
308       }
309     } else {
310       return JBL_ERROR_CREATION;
311     }
312     return 0;
313   } else if (t == JBV_ARRAY) {
314     if (!binn_list_add_value(bv, &v->bn)) {
315       return JBL_ERROR_CREATION;
316     }
317     return 0;
318   }
319   return JBL_ERROR_INVALID;
320 }
321 
jbl_from_buf_keep(JBL * jblp,void * buf,size_t bufsz,bool keep_on_destroy)322 iwrc jbl_from_buf_keep(JBL *jblp, void *buf, size_t bufsz, bool keep_on_destroy) {
323   int type, size = 0, count = 0;
324   if ((bufsz < MIN_BINN_SIZE) || !binn_is_valid_header(buf, &type, &count, &size, NULL)) {
325     return JBL_ERROR_INVALID_BUFFER;
326   }
327   if (size > bufsz) {
328     return JBL_ERROR_INVALID_BUFFER;
329   }
330   *jblp = calloc(1, sizeof(**jblp));
331   if (!*jblp) {
332     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
333   }
334   JBL jbl = *jblp;
335   jbl->bn.header = BINN_MAGIC;
336   jbl->bn.type = type;
337   jbl->bn.ptr = buf;
338   jbl->bn.size = size;
339   jbl->bn.count = count;
340   jbl->bn.freefn = keep_on_destroy ? 0 : free;
341   return 0;
342 }
343 
jbl_clone(JBL src,JBL * targetp)344 iwrc jbl_clone(JBL src, JBL *targetp) {
345   *targetp = calloc(1, sizeof(**targetp));
346   JBL t = *targetp;
347   if (!t) {
348     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
349   }
350   binn *bn = binn_copy(&src->bn);
351   if (!bn) {
352     return JBL_ERROR_CREATION;
353   }
354   t->node = 0;
355   bn->allocated = 0;
356   memcpy(&t->bn, bn, sizeof(*bn));
357   free(bn);
358   return 0;
359 }
360 
jbl_object_copy_to(JBL src,JBL target)361 IW_EXPORT iwrc jbl_object_copy_to(JBL src, JBL target) {
362   iwrc rc = 0;
363   // According to binn spec keys are not null terminated
364   // and key length is not more than 255 bytes
365   char *key, kbuf[256];
366   int klen;
367   JBL holder = 0;
368   JBL_iterator it;
369 
370   if ((jbl_type(src) != JBV_OBJECT) || (jbl_type(target) != JBV_OBJECT)) {
371     return JBL_ERROR_NOT_AN_OBJECT;
372   }
373   RCC(rc, finish, jbl_create_iterator_holder(&holder));
374   RCC(rc, finish, jbl_iterator_init(src, &it));
375   while (jbl_iterator_next(&it, holder, &key, &klen)) {
376     memcpy(kbuf, key, klen);
377     kbuf[klen] = '\0';
378     RCC(rc, finish, jbl_set_nested(target, kbuf, holder));
379   }
380 
381 finish:
382   jbl_destroy(&holder);
383   return rc;
384 }
385 
jbl_clone_into_pool(JBL src,JBL * targetp,IWPOOL * pool)386 iwrc jbl_clone_into_pool(JBL src, JBL *targetp, IWPOOL *pool) {
387   *targetp = 0;
388   if (src->bn.writable && src->bn.dirty) {
389     if (!binn_save_header(&src->bn)) {
390       return JBL_ERROR_INVALID;
391     }
392   }
393   JBL jbl = iwpool_alloc(sizeof(*jbl) + src->bn.size, pool);
394   if (!jbl) {
395     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
396   }
397   jbl->node = 0;
398   memcpy(&jbl->bn, &src->bn, sizeof(jbl->bn));
399   jbl->bn.ptr = (char*) jbl + sizeof(*jbl);
400   memcpy(jbl->bn.ptr, src->bn.ptr, src->bn.size);
401   jbl->bn.freefn = 0;
402   *targetp = jbl;
403   return 0;
404 }
405 
jbl_from_buf_keep_onstack(JBL jbl,void * buf,size_t bufsz)406 iwrc jbl_from_buf_keep_onstack(JBL jbl, void *buf, size_t bufsz) {
407   int type, size = 0, count = 0;
408   if ((bufsz < MIN_BINN_SIZE) || !binn_is_valid_header(buf, &type, &count, &size, NULL)) {
409     return JBL_ERROR_INVALID_BUFFER;
410   }
411   if (size > bufsz) {
412     return JBL_ERROR_INVALID_BUFFER;
413   }
414   memset(jbl, 0, sizeof(*jbl));
415   jbl->bn.header = BINN_MAGIC;
416   jbl->bn.type = type;
417   jbl->bn.ptr = buf;
418   jbl->bn.size = size;
419   jbl->bn.count = count;
420   return 0;
421 }
422 
jbl_from_buf_keep_onstack2(JBL jbl,void * buf)423 iwrc jbl_from_buf_keep_onstack2(JBL jbl, void *buf) {
424   int type, size = 0, count = 0;
425   if (!binn_is_valid_header(buf, &type, &count, &size, NULL)) {
426     return JBL_ERROR_INVALID_BUFFER;
427   }
428   memset(jbl, 0, sizeof(*jbl));
429   jbl->bn.header = BINN_MAGIC;
430   jbl->bn.type = type;
431   jbl->bn.ptr = buf;
432   jbl->bn.size = size;
433   jbl->bn.count = count;
434   return 0;
435 }
436 
jbl_destroy(JBL * jblp)437 void jbl_destroy(JBL *jblp) {
438   if (*jblp) {
439     JBL jbl = *jblp;
440     binn_free(&jbl->bn);
441     free(jbl);
442     *jblp = 0;
443   }
444 }
445 
jbl_create_iterator_holder(JBL * jblp)446 iwrc jbl_create_iterator_holder(JBL *jblp) {
447   *jblp = calloc(1, sizeof(**jblp));
448   if (!*jblp) {
449     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
450   }
451   return 0;
452 }
453 
jbl_iterator_init(JBL jbl,JBL_iterator * iter)454 iwrc jbl_iterator_init(JBL jbl, JBL_iterator *iter) {
455   int btype = jbl->bn.type;
456   if ((btype != BINN_OBJECT) && (btype != BINN_LIST) && (btype != BINN_MAP)) {
457     memset(iter, 0, sizeof(*iter));
458     return 0;
459   }
460   binn_iter *biter = (binn_iter*) iter;
461   if (!binn_iter_init(biter, &jbl->bn, btype)) {
462     return JBL_ERROR_CREATION;
463   }
464   return 0;
465 }
466 
jbl_iterator_next(JBL_iterator * iter,JBL holder,char ** pkey,int * klen)467 bool jbl_iterator_next(JBL_iterator *iter, JBL holder, char **pkey, int *klen) {
468   binn_iter *biter = (binn_iter*) iter;
469   if (pkey) {
470     *pkey = 0;
471   }
472   if (klen) {
473     *klen = 0;
474   }
475   if (!iter || (iter->type == 0)) {
476     return false;
477   }
478   if (iter->type == BINN_LIST) {
479     if (klen) {
480       *klen = iter->current;
481     }
482     return binn_list_next(biter, &holder->bn);
483   } else {
484     return binn_read_next_pair2(iter->type, biter, klen, pkey, &holder->bn);
485   }
486   return false;
487 }
488 
_jbl_binn_type(int btype)489 IW_INLINE jbl_type_t _jbl_binn_type(int btype) {
490   switch (btype) {
491     case BINN_NULL:
492       return JBV_NULL;
493     case BINN_STRING:
494       return JBV_STR;
495     case BINN_OBJECT:
496     case BINN_MAP:
497       return JBV_OBJECT;
498     case BINN_LIST:
499       return JBV_ARRAY;
500     case BINN_BOOL:
501     case BINN_TRUE:
502     case BINN_FALSE:
503       return JBV_BOOL;
504     case BINN_UINT8:
505     case BINN_UINT16:
506     case BINN_UINT32:
507     case BINN_UINT64:
508     case BINN_INT8:
509     case BINN_INT16:
510     case BINN_INT32:
511     case BINN_INT64:
512       return JBV_I64;
513     case BINN_FLOAT32:
514     case BINN_FLOAT64:
515       return JBV_F64;
516     default:
517       return JBV_NONE;
518   }
519 }
520 
jbl_type(JBL jbl)521 jbl_type_t jbl_type(JBL jbl) {
522   if (jbl) {
523     return _jbl_binn_type(jbl->bn.type);
524   }
525   return JBV_NONE;
526 }
527 
jbl_count(JBL jbl)528 size_t jbl_count(JBL jbl) {
529   return (size_t) jbl->bn.count;
530 }
531 
jbl_size(JBL jbl)532 size_t jbl_size(JBL jbl) {
533   return (size_t) jbl->bn.size;
534 }
535 
jbl_structure_size(void)536 size_t jbl_structure_size(void) {
537   return sizeof(struct _JBL);
538 }
539 
jbl_from_json(JBL * jblp,const char * jsonstr)540 iwrc jbl_from_json(JBL *jblp, const char *jsonstr) {
541   *jblp = 0;
542   iwrc rc = 0;
543   IWPOOL *pool = iwpool_create(2 * strlen(jsonstr));
544   if (!pool) {
545     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
546   }
547   JBL jbl;
548   JBL_NODE node;
549   rc = jbn_from_json(jsonstr, &node, pool);
550   RCGO(rc, finish);
551   if (node->type == JBV_OBJECT) {
552     rc = jbl_create_empty_object(&jbl);
553     RCGO(rc, finish);
554   } else if (node->type == JBV_ARRAY) {
555     rc = jbl_create_empty_array(&jbl);
556     RCGO(rc, finish);
557   } else {
558     // TODO: Review
559     rc = JBL_ERROR_CREATION;
560     goto finish;
561   }
562   rc = jbl_fill_from_node(jbl, node);
563   if (!rc) {
564     *jblp = jbl;
565   }
566 
567 finish:
568   iwpool_destroy(pool);
569   return rc;
570 }
571 
_jbl_write_double(double num,jbl_json_printer pt,void * op)572 iwrc _jbl_write_double(double num, jbl_json_printer pt, void *op) {
573   size_t sz;
574   char buf[IWNUMBUF_SIZE];
575   iwjson_ftoa(num, buf, &sz);
576   return pt(buf, -1, 0, 0, op);
577 }
578 
_jbl_write_int(int64_t num,jbl_json_printer pt,void * op)579 iwrc _jbl_write_int(int64_t num, jbl_json_printer pt, void *op) {
580   char buf[IWNUMBUF_SIZE];
581   int sz = iwitoa(num, buf, sizeof(buf));
582   return pt(buf, sz, 0, 0, op);
583 }
584 
_jbl_write_string(const char * str,int len,jbl_json_printer pt,void * op,jbl_print_flags_t pf)585 iwrc _jbl_write_string(const char *str, int len, jbl_json_printer pt, void *op, jbl_print_flags_t pf) {
586   iwrc rc = pt(0, 0, '"', 1, op);
587   RCRET(rc);
588   static const char *specials = "btnvfr";
589   const uint8_t *p = (const uint8_t*) str;
590 
591 #define PT(data_, size_, ch_, count_) do { \
592     rc = pt((const char*) (data_), size_, ch_, count_, op); \
593     RCRET(rc); \
594 } while (0)
595 
596   if (len < 0) {
597     len = (int) strlen(str);
598   }
599   for (size_t i = 0; i < len; i++) {
600     uint8_t ch = p[i];
601     if ((ch == '"') || (ch == '\\')) {
602       PT(0, 0, '\\', 1);
603       PT(0, 0, ch, 1);
604     } else if ((ch >= '\b') && (ch <= '\r')) {
605       PT(0, 0, '\\', 1);
606       PT(0, 0, specials[ch - '\b'], 1);
607     } else if (isprint(ch)) {
608       PT(0, 0, ch, 1);
609     } else if (pf & JBL_PRINT_CODEPOINTS) {
610       char sbuf[7]; // escaped unicode seq
611       utf8proc_int32_t cp;
612       utf8proc_ssize_t sz = utf8proc_iterate(p + i, len - i, &cp);
613       if (sz < 0) {
614         return JBL_ERROR_PARSE_INVALID_UTF8;
615       }
616       if (cp > 0x0010000UL) {
617         uint32_t hs = 0xD800, ls = 0xDC00; // surrogates
618         cp -= 0x0010000UL;
619         hs |= ((cp >> 10) & 0x3FF);
620         ls |= (cp & 0x3FF);
621         snprintf(sbuf, 7, "\\u%04X", hs);
622         PT(sbuf, 6, 0, 0);
623         snprintf(sbuf, 7, "\\u%04X", ls);
624         PT(sbuf, 6, 0, 0);
625       } else {
626         snprintf(sbuf, 7, "\\u%04X", cp);
627         PT(sbuf, 6, 0, 0);
628       }
629       i += sz - 1;
630     } else {
631       PT(0, 0, ch, 1);
632     }
633   }
634   rc = pt(0, 0, '"', 1, op);
635   return rc;
636 #undef PT
637 }
638 
_jbl_as_json(binn * bn,jbl_json_printer pt,void * op,int lvl,jbl_print_flags_t pf)639 static iwrc _jbl_as_json(binn *bn, jbl_json_printer pt, void *op, int lvl, jbl_print_flags_t pf) {
640   iwrc rc = 0;
641   binn bv;
642   binn_iter iter;
643   int lv;
644   int64_t llv;
645   double dv;
646   char key[MAX_BIN_KEY_LEN + 1];
647   bool pretty = pf & JBL_PRINT_PRETTY;
648 
649 #define PT(data_, size_, ch_, count_) do { \
650     rc = pt(data_, size_, ch_, count_, op); \
651     RCGO(rc, finish); \
652 } while (0)
653 
654   switch (bn->type) {
655     case BINN_LIST:
656       if (!binn_iter_init(&iter, bn, bn->type)) {
657         rc = JBL_ERROR_INVALID;
658         goto finish;
659       }
660       PT(0, 0, '[', 1);
661       if (bn->count && pretty) {
662         PT(0, 0, '\n', 1);
663       }
664       for (int i = 0; binn_list_next(&iter, &bv); ++i) {
665         if (pretty) {
666           PT(0, 0, ' ', lvl + 1);
667         }
668         rc = _jbl_as_json(&bv, pt, op, lvl + 1, pf);
669         RCGO(rc, finish);
670         if (i < bn->count - 1) {
671           PT(0, 0, ',', 1);
672         }
673         if (pretty) {
674           PT(0, 0, '\n', 1);
675         }
676       }
677       if (bn->count && pretty) {
678         PT(0, 0, ' ', lvl);
679       }
680       PT(0, 0, ']', 1);
681       break;
682 
683     case BINN_OBJECT:
684     case BINN_MAP:
685       if (!binn_iter_init(&iter, bn, bn->type)) {
686         rc = JBL_ERROR_INVALID;
687         goto finish;
688       }
689       PT(0, 0, '{', 1);
690       if (bn->count && pretty) {
691         PT(0, 0, '\n', 1);
692       }
693       if (bn->type == BINN_OBJECT) {
694         for (int i = 0; binn_object_next(&iter, key, &bv); ++i) {
695           if (pretty) {
696             PT(0, 0, ' ', lvl + 1);
697           }
698           rc = _jbl_write_string(key, -1, pt, op, pf);
699           RCGO(rc, finish);
700           if (pretty) {
701             PT(": ", -1, 0, 0);
702           } else {
703             PT(0, 0, ':', 1);
704           }
705           rc = _jbl_as_json(&bv, pt, op, lvl + 1, pf);
706           RCGO(rc, finish);
707           if (i < bn->count - 1) {
708             PT(0, 0, ',', 1);
709           }
710           if (pretty) {
711             PT(0, 0, '\n', 1);
712           }
713         }
714       } else {
715         for (int i = 0; binn_map_next(&iter, &lv, &bv); ++i) {
716           if (pretty) {
717             PT(0, 0, ' ', lvl + 1);
718           }
719           PT(0, 0, '"', 1);
720           rc = _jbl_write_int(lv, pt, op);
721           RCGO(rc, finish);
722           PT(0, 0, '"', 1);
723           if (pretty) {
724             PT(": ", -1, 0, 0);
725           } else {
726             PT(0, 0, ':', 1);
727           }
728           rc = _jbl_as_json(&bv, pt, op, lvl + 1, pf);
729           RCGO(rc, finish);
730           if (i < bn->count - 1) {
731             PT(0, 0, ',', 1);
732           }
733           if (pretty) {
734             PT(0, 0, '\n', 1);
735           }
736         }
737       }
738       if (bn->count && pretty) {
739         PT(0, 0, ' ', lvl);
740       }
741       PT(0, 0, '}', 1);
742       break;
743 
744     case BINN_STRING:
745       rc = _jbl_write_string(bn->ptr, -1, pt, op, pf);
746       break;
747     case BINN_UINT8:
748       llv = bn->vuint8;
749       goto loc_int;
750     case BINN_UINT16:
751       llv = bn->vuint16;
752       goto loc_int;
753     case BINN_UINT32:
754       llv = bn->vuint32;
755       goto loc_int;
756     case BINN_INT8:
757       llv = bn->vint8; // NOLINT(bugprone-signed-char-misuse)
758       goto loc_int;
759     case BINN_INT16:
760       llv = bn->vint16;
761       goto loc_int;
762     case BINN_INT32:
763       llv = bn->vint32;
764       goto loc_int;
765     case BINN_INT64:
766       llv = bn->vint64;
767       goto loc_int;
768     case BINN_UINT64: // overflow?
769       llv = (int64_t) bn->vuint64;
770 loc_int:
771       rc = _jbl_write_int(llv, pt, op);
772       break;
773 
774     case BINN_FLOAT32:
775       dv = bn->vfloat;
776       goto loc_float;
777     case BINN_FLOAT64:
778       dv = bn->vdouble;
779 loc_float:
780       rc = _jbl_write_double(dv, pt, op);
781       break;
782 
783     case BINN_TRUE:
784       PT("true", 4, 0, 0);
785       break;
786     case BINN_FALSE:
787       PT("false", 5, 0, 0);
788       break;
789     case BINN_BOOL:
790       PT(bn->vbool ? "true" : "false", -1, 0, 1);
791       break;
792     case BINN_NULL:
793       PT("null", 4, 0, 0);
794       break;
795     default:
796       iwlog_ecode_error3(IW_ERROR_ASSERTION);
797       rc = IW_ERROR_ASSERTION;
798       break;
799   }
800 
801 finish:
802   return rc;
803 #undef PT
804 }
805 
jbl_as_json(JBL jbl,jbl_json_printer pt,void * op,jbl_print_flags_t pf)806 iwrc jbl_as_json(JBL jbl, jbl_json_printer pt, void *op, jbl_print_flags_t pf) {
807   if (!jbl || !pt) {
808     return IW_ERROR_INVALID_ARGS;
809   }
810   return _jbl_as_json(&jbl->bn, pt, op, 0, pf);
811 }
812 
jbl_fstream_json_printer(const char * data,int size,char ch,int count,void * op)813 iwrc jbl_fstream_json_printer(const char *data, int size, char ch, int count, void *op) {
814   FILE *file = op;
815   if (!file) {
816     return IW_ERROR_INVALID_ARGS;
817   }
818   if (!data) {
819     if (count) {
820       char cbuf[count]; // TODO: review overflow
821       memset(cbuf, ch, sizeof(cbuf));
822       size_t wc = fwrite(cbuf, 1, count, file);
823       if (wc != sizeof(cbuf)) {
824         return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
825       }
826     }
827   } else {
828     if (size < 0) {
829       size = (int) strlen(data);
830     }
831     if (!count) {
832       count = 1;
833     }
834     for (int i = 0; i < count; ++i) {
835       if (fprintf(file, "%.*s", size, data) < 0) {
836         return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
837       }
838     }
839   }
840   return 0;
841 }
842 
jbl_xstr_json_printer(const char * data,int size,char ch,int count,void * op)843 iwrc jbl_xstr_json_printer(const char *data, int size, char ch, int count, void *op) {
844   IWXSTR *xstr = op;
845   if (!xstr) {
846     return IW_ERROR_INVALID_ARGS;
847   }
848   if (!data) {
849     if (count) {
850       for (int i = 0; i < count; ++i) {
851         iwrc rc = iwxstr_cat(xstr, &ch, 1);
852         RCRET(rc);
853       }
854     }
855   } else {
856     if (size < 0) {
857       size = (int) strlen(data);
858     }
859     if (!count) {
860       count = 1;
861     }
862     for (int i = 0; i < count; ++i) {
863       iwrc rc = iwxstr_cat(xstr, data, size);
864       RCRET(rc);
865     }
866   }
867   return 0;
868 }
869 
jbl_count_json_printer(const char * data,int size,char ch,int count,void * op)870 iwrc jbl_count_json_printer(const char *data, int size, char ch, int count, void *op) {
871   int *cnt = op;
872   if (!data) {
873     *cnt = *cnt + count;
874   } else {
875     if (size < 0) {
876       size = (int) strlen(data);
877     }
878     if (!count) {
879       count = 1;
880     }
881     *cnt = *cnt + count * size;
882   }
883   return 0;
884 }
885 
jbl_get_i64(JBL jbl)886 int64_t jbl_get_i64(JBL jbl) {
887   assert(jbl);
888   switch (jbl->bn.type) {
889     case BINN_UINT8:
890       return jbl->bn.vuint8;
891     case BINN_UINT16:
892       return jbl->bn.vuint16;
893     case BINN_UINT32:
894       return jbl->bn.vuint32;
895     case BINN_UINT64:
896       return jbl->bn.vuint64;
897     case BINN_INT8:
898       return jbl->bn.vint8;
899     case BINN_INT16:
900       return jbl->bn.vint16;
901     case BINN_INT32:
902       return jbl->bn.vint32;
903     case BINN_INT64:
904       return jbl->bn.vint64;
905     case BINN_BOOL:
906       return jbl->bn.vbool;
907     case BINN_FLOAT32:
908       return (int64_t) jbl->bn.vfloat;
909     case BINN_FLOAT64:
910       return (int64_t) jbl->bn.vdouble;
911     default:
912       return 0;
913   }
914 }
915 
jbl_get_i32(JBL jbl)916 int32_t jbl_get_i32(JBL jbl) {
917   return (int32_t) jbl_get_i64(jbl);
918 }
919 
jbl_get_f64(JBL jbl)920 double jbl_get_f64(JBL jbl) {
921   assert(jbl);
922   switch (jbl->bn.type) {
923     case BINN_FLOAT64:
924       return jbl->bn.vdouble;
925     case BINN_FLOAT32:
926       return jbl->bn.vfloat;
927     case BINN_UINT8:
928       return jbl->bn.vuint8;
929     case BINN_UINT16:
930       return jbl->bn.vuint16;
931     case BINN_UINT32:
932       return jbl->bn.vuint32;
933     case BINN_UINT64:
934       return jbl->bn.vuint64;
935     case BINN_INT8:
936       return jbl->bn.vint8;
937     case BINN_INT16:
938       return jbl->bn.vint16;
939     case BINN_INT32:
940       return jbl->bn.vint32;
941     case BINN_INT64:
942       return jbl->bn.vint64;
943     case BINN_BOOL:
944       return jbl->bn.vbool;
945     default:
946       return 0.0;
947   }
948 }
949 
jbl_get_str(JBL jbl)950 const char* jbl_get_str(JBL jbl) {
951   assert(jbl && jbl->bn.type == BINN_STRING);
952   if (jbl->bn.type != BINN_STRING) {
953     return 0;
954   } else {
955     return jbl->bn.ptr;
956   }
957 }
958 
jbl_copy_strn(JBL jbl,char * buf,size_t bufsz)959 size_t jbl_copy_strn(JBL jbl, char *buf, size_t bufsz) {
960   assert(jbl && buf && jbl->bn.type == BINN_STRING);
961   if (jbl->bn.type != BINN_STRING) {
962     return 0;
963   }
964   size_t slen = strlen(jbl->bn.ptr);
965   size_t ret = MIN(slen, bufsz);
966   memcpy(buf, jbl->bn.ptr, ret);
967   return ret;
968 }
969 
jbl_object_get_type(JBL jbl,const char * key)970 jbl_type_t jbl_object_get_type(JBL jbl, const char *key) {
971   if (jbl->bn.type != BINN_OBJECT) {
972     return JBV_NONE;
973   }
974   binn bv;
975   if (!binn_object_get_value(&jbl->bn, key, &bv)) {
976     return JBV_NONE;
977   }
978   return _jbl_binn_type(bv.type);
979 }
980 
jbl_object_get_i64(JBL jbl,const char * key,int64_t * out)981 iwrc jbl_object_get_i64(JBL jbl, const char *key, int64_t *out) {
982   *out = 0;
983   if (jbl->bn.type != BINN_OBJECT) {
984     return JBL_ERROR_NOT_AN_OBJECT;
985   }
986   int64 v;
987   if (!binn_object_get_int64(&jbl->bn, key, &v)) {
988     return JBL_ERROR_CREATION;
989   }
990   *out = v;
991   return 0;
992 }
993 
jbl_object_get_f64(JBL jbl,const char * key,double * out)994 iwrc jbl_object_get_f64(JBL jbl, const char *key, double *out) {
995   *out = 0.0;
996   if (jbl->bn.type != BINN_OBJECT) {
997     return JBL_ERROR_NOT_AN_OBJECT;
998   }
999   if (!binn_object_get_double(&jbl->bn, key, out)) {
1000     return JBL_ERROR_CREATION;
1001   }
1002   return 0;
1003 }
1004 
jbl_object_get_bool(JBL jbl,const char * key,bool * out)1005 iwrc jbl_object_get_bool(JBL jbl, const char *key, bool *out) {
1006   *out = false;
1007   if (jbl->bn.type != BINN_OBJECT) {
1008     return JBL_ERROR_NOT_AN_OBJECT;
1009   }
1010   BOOL v;
1011   if (!binn_object_get_bool(&jbl->bn, key, &v)) {
1012     return JBL_ERROR_CREATION;
1013   }
1014   *out = v;
1015   return 0;
1016 }
1017 
jbl_object_get_str(JBL jbl,const char * key,const char ** out)1018 iwrc jbl_object_get_str(JBL jbl, const char *key, const char **out) {
1019   *out = 0;
1020   if (jbl->bn.type != BINN_OBJECT) {
1021     return JBL_ERROR_NOT_AN_OBJECT;
1022   }
1023   if (!binn_object_get_str(&jbl->bn, key, (char**) out)) {
1024     return JBL_ERROR_CREATION;
1025   }
1026   return 0;
1027 }
1028 
jbl_object_get_fill_jbl(JBL jbl,const char * key,JBL out)1029 iwrc jbl_object_get_fill_jbl(JBL jbl, const char *key, JBL out) {
1030   if (jbl->bn.type != BINN_OBJECT) {
1031     return JBL_ERROR_NOT_AN_OBJECT;
1032   }
1033   binn_free(&out->bn);
1034   if (!binn_object_get_value(&jbl->bn, key, &out->bn)) {
1035     return JBL_ERROR_CREATION;
1036   }
1037   return 0;
1038 }
1039 
jbl_as_buf(JBL jbl,void ** buf,size_t * size)1040 iwrc jbl_as_buf(JBL jbl, void **buf, size_t *size) {
1041   assert(jbl && buf && size);
1042   if (jbl->bn.writable && jbl->bn.dirty) {
1043     if (!binn_save_header(&jbl->bn)) {
1044       return JBL_ERROR_INVALID;
1045     }
1046   }
1047   *buf = jbl->bn.ptr;
1048   *size = (size_t) jbl->bn.size;
1049   return 0;
1050 }
1051 
1052 //----------------------------------------------------------------------------------------------------------
1053 
_jbl_ptr_pool(const char * path,JBL_PTR * jpp,IWPOOL * pool)1054 static iwrc _jbl_ptr_pool(const char *path, JBL_PTR *jpp, IWPOOL *pool) {
1055   iwrc rc = 0;
1056   int cnt = 0, len, sz, doff;
1057   int i, j, k;
1058   JBL_PTR jp;
1059   char *jpr; // raw pointer to jp
1060   *jpp = 0;
1061   if (!path || (path[0] != '/')) {
1062     return JBL_ERROR_JSON_POINTER;
1063   }
1064   for (i = 0; path[i]; ++i) {
1065     if (path[i] == '/') {
1066       ++cnt;
1067     }
1068   }
1069   len = i;
1070   if ((len > 1) && (path[len - 1] == '/')) {
1071     return JBL_ERROR_JSON_POINTER;
1072   }
1073   sz = (int) (sizeof(struct _JBL_PTR) + cnt * sizeof(char*) + len);
1074   if (pool) {
1075     jp = iwpool_alloc(sz, pool);
1076   } else {
1077     jp = malloc(sz);
1078   }
1079   if (!jp) {
1080     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
1081   }
1082   jpr = (char*) jp;
1083   jp->cnt = cnt;
1084   jp->sz = sz;
1085 
1086   doff = offsetof(struct _JBL_PTR, n) + cnt * sizeof(char*);
1087   assert(sz - doff >= len);
1088 
1089   for (i = 0, j = 0, cnt = 0; path[i] && cnt < jp->cnt; ++i, ++j) {
1090     if (path[i++] == '/') {
1091       jp->n[cnt] = jpr + doff + j;
1092       for (k = 0; ; ++i, ++k) {
1093         if (!path[i] || (path[i] == '/')) {
1094           --i;
1095           *(jp->n[cnt] + k) = '\0';
1096           break;
1097         }
1098         if (path[i] == '~') {
1099           if (path[i + 1] == '0') {
1100             *(jp->n[cnt] + k) = '~';
1101           } else if (path[i + 1] == '1') {
1102             *(jp->n[cnt] + k) = '/';
1103           }
1104           ++i;
1105         } else {
1106           *(jp->n[cnt] + k) = path[i];
1107         }
1108       }
1109       j += k;
1110       ++cnt;
1111     }
1112   }
1113   *jpp = jp;
1114   return rc;
1115 }
1116 
jbl_ptr_alloc(const char * path,JBL_PTR * jpp)1117 iwrc jbl_ptr_alloc(const char *path, JBL_PTR *jpp) {
1118   return _jbl_ptr_pool(path, jpp, 0);
1119 }
1120 
jbl_ptr_alloc_pool(const char * path,JBL_PTR * jpp,IWPOOL * pool)1121 iwrc jbl_ptr_alloc_pool(const char *path, JBL_PTR *jpp, IWPOOL *pool) {
1122   return _jbl_ptr_pool(path, jpp, pool);
1123 }
1124 
jbl_ptr_cmp(JBL_PTR p1,JBL_PTR p2)1125 int jbl_ptr_cmp(JBL_PTR p1, JBL_PTR p2) {
1126   if (p1->sz != p2->sz) {
1127     return p1->sz - p2->sz;
1128   }
1129   if (p1->cnt != p2->cnt) {
1130     return p1->cnt - p2->cnt;
1131   }
1132   for (int i = 0; i < p1->cnt; ++i) {
1133     int r = strcmp(p1->n[i], p2->n[i]);
1134     if (r) {
1135       return r;
1136     }
1137   }
1138   return 0;
1139 }
1140 
jbl_ptr_serialize(JBL_PTR ptr,IWXSTR * xstr)1141 iwrc jbl_ptr_serialize(JBL_PTR ptr, IWXSTR *xstr) {
1142   for (int i = 0; i < ptr->cnt; ++i) {
1143     iwrc rc = iwxstr_cat(xstr, "/", 1);
1144     RCRET(rc);
1145     rc = iwxstr_cat(xstr, ptr->n[i], strlen(ptr->n[i]));
1146     RCRET(rc);
1147   }
1148   return 0;
1149 }
1150 
_jbl_visit(binn_iter * iter,int lvl,JBL_VCTX * vctx,JBL_VISITOR visitor)1151 iwrc _jbl_visit(binn_iter *iter, int lvl, JBL_VCTX *vctx, JBL_VISITOR visitor) {
1152   iwrc rc = 0;
1153   binn *bn = vctx->bn;
1154   jbl_visitor_cmd_t cmd;
1155   int idx;
1156   binn bv;
1157 
1158   if (lvl > JBL_MAX_NESTING_LEVEL) {
1159     return JBL_ERROR_MAX_NESTING_LEVEL_EXCEEDED;
1160   }
1161   if (!iter) {
1162     binn_iter it;
1163     if (!BINN_IS_CONTAINER_TYPE(bn->type)) {
1164       return JBL_ERROR_INVALID;
1165     }
1166     if (!binn_iter_init(&it, bn, bn->type)) {
1167       return JBL_ERROR_INVALID;
1168     }
1169     rc = _jbl_visit(&it, 0, vctx, visitor);
1170     return rc;
1171   }
1172 
1173   switch (iter->type) {
1174     case BINN_OBJECT: {
1175       char key[MAX_BIN_KEY_LEN + 1];
1176       while (!vctx->terminate && binn_object_next(iter, key, &bv)) {
1177         cmd = visitor(lvl, &bv, key, -1, vctx, &rc);
1178         RCRET(rc);
1179         if (cmd & JBL_VCMD_TERMINATE) {
1180           vctx->terminate = true;
1181           break;
1182         }
1183         if (!(cmd & JBL_VCMD_SKIP_NESTED) && BINN_IS_CONTAINER_TYPE(bv.type)) {
1184           binn_iter it;
1185           if (!binn_iter_init(&it, &bv, bv.type)) {
1186             return JBL_ERROR_INVALID;
1187           }
1188           rc = _jbl_visit(&it, lvl + 1, vctx, visitor);
1189           RCRET(rc);
1190         }
1191       }
1192       break;
1193     }
1194     case BINN_MAP: {
1195       while (!vctx->terminate && binn_map_next(iter, &idx, &bv)) {
1196         cmd = visitor(lvl, &bv, 0, idx, vctx, &rc);
1197         RCRET(rc);
1198         if (cmd & JBL_VCMD_TERMINATE) {
1199           vctx->terminate = true;
1200           break;
1201         }
1202         if (!(cmd & JBL_VCMD_SKIP_NESTED) && BINN_IS_CONTAINER_TYPE(bv.type)) {
1203           binn_iter it;
1204           if (!binn_iter_init(&it, &bv, bv.type)) {
1205             return JBL_ERROR_INVALID;
1206           }
1207           rc = _jbl_visit(&it, lvl + 1, vctx, visitor);
1208           RCRET(rc);
1209         }
1210       }
1211       break;
1212     }
1213     case BINN_LIST: {
1214       for (idx = 0; !vctx->terminate && binn_list_next(iter, &bv); ++idx) {
1215         cmd = visitor(lvl, &bv, 0, idx, vctx, &rc);
1216         RCRET(rc);
1217         if (cmd & JBL_VCMD_TERMINATE) {
1218           vctx->terminate = true;
1219           break;
1220         }
1221         if (!(cmd & JBL_VCMD_SKIP_NESTED) && BINN_IS_CONTAINER_TYPE(bv.type)) {
1222           binn_iter it;
1223           if (!binn_iter_init(&it, &bv, bv.type)) {
1224             return JBL_ERROR_INVALID;
1225           }
1226           rc = _jbl_visit(&it, lvl + 1, vctx, visitor);
1227           RCRET(rc);
1228         }
1229       }
1230       break;
1231     }
1232   }
1233   return rc;
1234 }
1235 
jbn_visit(JBL_NODE node,int lvl,JBN_VCTX * vctx,JBN_VISITOR visitor)1236 iwrc jbn_visit(JBL_NODE node, int lvl, JBN_VCTX *vctx, JBN_VISITOR visitor) {
1237   iwrc rc = 0;
1238   if (lvl > JBL_MAX_NESTING_LEVEL) {
1239     return JBL_ERROR_MAX_NESTING_LEVEL_EXCEEDED;
1240   }
1241   if (!node) {
1242     node = vctx->root;
1243     lvl = 0;
1244     if (!node) {
1245       return IW_ERROR_INVALID_ARGS;
1246     }
1247   }
1248   JBL_NODE n = node;
1249   switch (node->type) {
1250     case JBV_OBJECT:
1251     case JBV_ARRAY: {
1252       for (n = n->child; !vctx->terminate && n; n = n->next) {
1253         jbn_visitor_cmd_t cmd = visitor(lvl, n, n->key, n->klidx, vctx, &rc);
1254         RCRET(rc);
1255         if (cmd & JBL_VCMD_TERMINATE) {
1256           vctx->terminate = true;
1257         }
1258         if (cmd & JBN_VCMD_DELETE) {
1259           JBL_NODE nn = n->next; // Keep pointer to next
1260           _jbn_remove_item(node, n);
1261           n->next = nn;
1262         } else if (!(cmd & JBL_VCMD_SKIP_NESTED) && (n->type >= JBV_OBJECT)) {
1263           rc = jbn_visit(n, lvl + 1, vctx, visitor);
1264           RCRET(rc);
1265         }
1266       }
1267       break;
1268     }
1269     default:
1270       break;
1271   }
1272   RCRET(rc);
1273   if (lvl == 0) {
1274     visitor(-1, node, 0, 0, vctx, &rc);
1275   }
1276   return rc;
1277 }
1278 
_jbl_visitor_update_jptr_cursor(JBL_VCTX * vctx,int lvl,const char * key,int idx)1279 IW_INLINE bool _jbl_visitor_update_jptr_cursor(JBL_VCTX *vctx, int lvl, const char *key, int idx) {
1280   JBL_PTR jp = vctx->op;
1281   if (lvl < jp->cnt) {
1282     if (vctx->pos >= lvl) {
1283       vctx->pos = lvl - 1;
1284     }
1285     if (vctx->pos + 1 == lvl) {
1286       const char *keyptr;
1287       char buf[IWNUMBUF_SIZE];
1288       if (key) {
1289         keyptr = key;
1290       } else {
1291         iwitoa(idx, buf, IWNUMBUF_SIZE);
1292         keyptr = buf;
1293       }
1294       if (!strcmp(keyptr, jp->n[lvl]) || ((jp->n[lvl][0] == '*') && (jp->n[lvl][1] == '\0'))) {
1295         vctx->pos = lvl;
1296         return (jp->cnt == lvl + 1);
1297       }
1298     }
1299   }
1300   return false;
1301 }
1302 
_jbn_visitor_update_jptr_cursor(JBN_VCTX * vctx,int lvl,const char * key,int idx)1303 IW_INLINE bool _jbn_visitor_update_jptr_cursor(JBN_VCTX *vctx, int lvl, const char *key, int idx) {
1304   JBL_PTR jp = vctx->op;
1305   if (lvl < jp->cnt) {
1306     if (vctx->pos >= lvl) {
1307       vctx->pos = lvl - 1;
1308     }
1309     if (vctx->pos + 1 == lvl) {
1310       const char *keyptr;
1311       char buf[IWNUMBUF_SIZE];
1312       if (key) {
1313         keyptr = key;
1314       } else {
1315         iwitoa(idx, buf, IWNUMBUF_SIZE);
1316         keyptr = buf;
1317         idx = (int) strlen(keyptr);
1318       }
1319       int jplen = (int) strlen(jp->n[lvl]);
1320       if ((  (idx == jplen)
1321           && !strncmp(keyptr, jp->n[lvl], idx)) || ((jp->n[lvl][0] == '*') && (jp->n[lvl][1] == '\0'))) {
1322         vctx->pos = lvl;
1323         return (jp->cnt == lvl + 1);
1324       }
1325     }
1326   }
1327   return false;
1328 }
1329 
_jbl_get_visitor2(int lvl,binn * bv,const char * key,int idx,JBL_VCTX * vctx,iwrc * rc)1330 static jbl_visitor_cmd_t _jbl_get_visitor2(int lvl, binn *bv, const char *key, int idx, JBL_VCTX *vctx, iwrc *rc) {
1331   JBL_PTR jp = vctx->op;
1332   assert(jp);
1333   if (_jbl_visitor_update_jptr_cursor(vctx, lvl, key, idx)) { // Pointer matched
1334     JBL jbl = vctx->result;
1335     memcpy(&jbl->bn, bv, sizeof(*bv));
1336     vctx->found = true;
1337     return JBL_VCMD_TERMINATE;
1338   } else if (jp->cnt < lvl + 1) {
1339     return JBL_VCMD_SKIP_NESTED;
1340   }
1341   return JBL_VCMD_OK;
1342 }
1343 
_jbl_get_visitor(int lvl,binn * bv,const char * key,int idx,JBL_VCTX * vctx,iwrc * rc)1344 static jbl_visitor_cmd_t _jbl_get_visitor(int lvl, binn *bv, const char *key, int idx, JBL_VCTX *vctx, iwrc *rc) {
1345   JBL_PTR jp = vctx->op;
1346   assert(jp);
1347   if (_jbl_visitor_update_jptr_cursor(vctx, lvl, key, idx)) { // Pointer matched
1348     JBL jbl = malloc(sizeof(struct _JBL));
1349     if (!jbl) {
1350       *rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1351       return JBL_VCMD_TERMINATE;
1352     }
1353     memcpy(&jbl->bn, bv, sizeof(*bv));
1354     vctx->result = jbl;
1355     return JBL_VCMD_TERMINATE;
1356   } else if (jp->cnt < lvl + 1) {
1357     return JBL_VCMD_SKIP_NESTED;
1358   }
1359   return JBL_VCMD_OK;
1360 }
1361 
_jbl_at(JBL jbl,JBL_PTR jp,JBL res)1362 bool _jbl_at(JBL jbl, JBL_PTR jp, JBL res) {
1363   JBL_VCTX vctx = {
1364     .bn     = &jbl->bn,
1365     .op     = jp,
1366     .pos    = -1,
1367     .result = res
1368   };
1369   _jbl_visit(0, 0, &vctx, _jbl_get_visitor2);
1370   return vctx.found;
1371 }
1372 
jbl_at2(JBL jbl,JBL_PTR jp,JBL * res)1373 iwrc jbl_at2(JBL jbl, JBL_PTR jp, JBL *res) {
1374   JBL_VCTX vctx = {
1375     .bn  = &jbl->bn,
1376     .op  = jp,
1377     .pos = -1
1378   };
1379   iwrc rc = _jbl_visit(0, 0, &vctx, _jbl_get_visitor);
1380   if (rc) {
1381     *res = 0;
1382   } else {
1383     if (!vctx.result) {
1384       rc = JBL_ERROR_PATH_NOTFOUND;
1385       *res = 0;
1386     } else {
1387       *res = (JBL) vctx.result;
1388     }
1389   }
1390   return rc;
1391 }
1392 
jbl_at(JBL jbl,const char * path,JBL * res)1393 iwrc jbl_at(JBL jbl, const char *path, JBL *res) {
1394   JBL_PTR jp;
1395   iwrc rc = _jbl_ptr_pool(path, &jp, 0);
1396   if (rc) {
1397     *res = 0;
1398     return rc;
1399   }
1400   rc = jbl_at2(jbl, jp, res);
1401   free(jp);
1402   return rc;
1403 }
1404 
_jbn_get_visitor(int lvl,JBL_NODE n,const char * key,int klidx,JBN_VCTX * vctx,iwrc * rc)1405 static jbn_visitor_cmd_t _jbn_get_visitor(int lvl, JBL_NODE n, const char *key, int klidx, JBN_VCTX *vctx, iwrc *rc) {
1406   if (lvl < 0) { // EOF
1407     return JBL_VCMD_OK;
1408   }
1409   JBL_PTR jp = vctx->op;
1410   assert(jp);
1411   if (_jbn_visitor_update_jptr_cursor(vctx, lvl, key, klidx)) { // Pointer matched
1412     vctx->result = n;
1413     return JBL_VCMD_TERMINATE;
1414   } else if (jp->cnt < lvl + 1) {
1415     return JBL_VCMD_SKIP_NESTED;
1416   }
1417   return JBL_VCMD_OK;
1418 }
1419 
jbn_at2(JBL_NODE node,JBL_PTR jp,JBL_NODE * res)1420 iwrc jbn_at2(JBL_NODE node, JBL_PTR jp, JBL_NODE *res) {
1421   JBN_VCTX vctx = {
1422     .root = node,
1423     .op   = jp,
1424     .pos  = -1
1425   };
1426   iwrc rc = jbn_visit(node, 0, &vctx, _jbn_get_visitor);
1427   if (rc) {
1428     *res = 0;
1429   } else {
1430     if (!vctx.result) {
1431       rc = JBL_ERROR_PATH_NOTFOUND;
1432       *res = 0;
1433     } else {
1434       *res = (JBL_NODE) vctx.result;
1435     }
1436   }
1437   return rc;
1438 }
1439 
jbn_at(JBL_NODE node,const char * path,JBL_NODE * res)1440 iwrc jbn_at(JBL_NODE node, const char *path, JBL_NODE *res) {
1441   JBL_PTR jp;
1442   iwrc rc = _jbl_ptr_pool(path, &jp, 0);
1443   if (rc) {
1444     *res = 0;
1445     return rc;
1446   }
1447   rc = jbn_at2(node, jp, res);
1448   free(jp);
1449   return rc;
1450 }
1451 
jbn_paths_compare(JBL_NODE n1,const char * n1path,JBL_NODE n2,const char * n2path,jbl_type_t vtype,iwrc * rcp)1452 int jbn_paths_compare(JBL_NODE n1, const char *n1path, JBL_NODE n2, const char *n2path, jbl_type_t vtype, iwrc *rcp) {
1453   *rcp = 0;
1454   JBL_NODE v1 = 0, v2 = 0;
1455   iwrc rc = jbn_at(n1, n1path, &v1);
1456   if (rc && (rc != JBL_ERROR_PATH_NOTFOUND)) {
1457     *rcp = rc;
1458     return -2;
1459   }
1460   rc = jbn_at(n2, n2path, &v2);
1461   if (rc && (rc != JBL_ERROR_PATH_NOTFOUND)) {
1462     *rcp = rc;
1463     return -2;
1464   }
1465   if (vtype) {
1466     if (((v1 == 0) || (v1->type != vtype)) || ((v2 == 0) || (v2->type != vtype))) {
1467       *rcp = JBL_ERROR_TYPE_MISMATCHED;
1468       return -2;
1469     }
1470   }
1471   return _jbl_compare_nodes(v1, v2, rcp);
1472 }
1473 
jbn_path_compare(JBL_NODE n1,JBL_NODE n2,const char * path,jbl_type_t vtype,iwrc * rcp)1474 int jbn_path_compare(JBL_NODE n1, JBL_NODE n2, const char *path, jbl_type_t vtype, iwrc *rcp) {
1475   return jbn_paths_compare(n1, path, n2, path, vtype, rcp);
1476 }
1477 
jbn_path_compare_str(JBL_NODE n,const char * path,const char * sv,iwrc * rcp)1478 int jbn_path_compare_str(JBL_NODE n, const char *path, const char *sv, iwrc *rcp) {
1479   *rcp = 0;
1480   JBL_NODE v;
1481   iwrc rc = jbn_at(n, path, &v);
1482   if (rc) {
1483     *rcp = rc;
1484     return -2;
1485   }
1486   struct _JBL_NODE cn = {
1487     .type  = JBV_STR,
1488     .vptr  = sv,
1489     .vsize = (int) strlen(sv)
1490   };
1491   return _jbl_compare_nodes(v, &cn, rcp);
1492 }
1493 
jbn_path_compare_i64(JBL_NODE n,const char * path,int64_t iv,iwrc * rcp)1494 int jbn_path_compare_i64(JBL_NODE n, const char *path, int64_t iv, iwrc *rcp) {
1495   *rcp = 0;
1496   JBL_NODE v;
1497   iwrc rc = jbn_at(n, path, &v);
1498   if (rc) {
1499     *rcp = rc;
1500     return -2;
1501   }
1502   struct _JBL_NODE cn = {
1503     .type = JBV_I64,
1504     .vi64 = iv
1505   };
1506   return _jbl_compare_nodes(v, &cn, rcp);
1507 }
1508 
jbn_path_compare_f64(JBL_NODE n,const char * path,double fv,iwrc * rcp)1509 int jbn_path_compare_f64(JBL_NODE n, const char *path, double fv, iwrc *rcp) {
1510   *rcp = 0;
1511   JBL_NODE v;
1512   iwrc rc = jbn_at(n, path, &v);
1513   if (rc) {
1514     *rcp = rc;
1515     return -2;
1516   }
1517   struct _JBL_NODE cn = {
1518     .type = JBV_F64,
1519     .vf64 = fv
1520   };
1521   return _jbl_compare_nodes(v, &cn, rcp);
1522 }
1523 
jbn_path_compare_bool(JBL_NODE n,const char * path,bool bv,iwrc * rcp)1524 int jbn_path_compare_bool(JBL_NODE n, const char *path, bool bv, iwrc *rcp) {
1525   *rcp = 0;
1526   JBL_NODE v;
1527   iwrc rc = jbn_at(n, path, &v);
1528   if (rc) {
1529     *rcp = rc;
1530     return -2;
1531   }
1532   struct _JBL_NODE cn = {
1533     .type  = JBV_BOOL,
1534     .vbool = bv
1535   };
1536   return _jbl_compare_nodes(v, &cn, rcp);
1537 }
1538 
_jbl_node_reset_data(JBL_NODE target)1539 IW_INLINE void _jbl_node_reset_data(JBL_NODE target) {
1540   jbl_type_t t = target->type;
1541   memset(((uint8_t*) target) + offsetof(struct _JBL_NODE, child),
1542          0,
1543          sizeof(struct _JBL_NODE) - offsetof(struct _JBL_NODE, child));
1544   target->type = t;
1545 }
1546 
_jbl_copy_node_data(JBL_NODE target,JBL_NODE value)1547 IW_INLINE void _jbl_copy_node_data(JBL_NODE target, JBL_NODE value) {
1548   memcpy(((uint8_t*) target) + offsetof(struct _JBL_NODE, child),
1549          ((uint8_t*) value) + offsetof(struct _JBL_NODE, child),
1550          sizeof(struct _JBL_NODE) - offsetof(struct _JBL_NODE, child));
1551 }
1552 
_jbl_increment_node_data(JBL_NODE target,JBL_NODE value)1553 iwrc _jbl_increment_node_data(JBL_NODE target, JBL_NODE value) {
1554   if ((value->type != JBV_I64) && (value->type != JBV_F64)) {
1555     return JBL_ERROR_PATCH_INVALID_VALUE;
1556   }
1557   if (target->type == JBV_I64) {
1558     if (value->type == JBV_I64) {
1559       target->vi64 += value->vi64;
1560     } else {
1561       target->vi64 += (int64_t) value->vf64;
1562     }
1563     return 0;
1564   } else if (target->type == JBV_F64) {
1565     if (value->type == JBV_F64) {
1566       target->vf64 += value->vf64;
1567     } else {
1568       target->vf64 += (double) value->vi64;
1569     }
1570     return 0;
1571   } else {
1572     return JBL_ERROR_PATCH_TARGET_INVALID;
1573   }
1574 }
1575 
jbn_data(JBL_NODE node)1576 void jbn_data(JBL_NODE node) {
1577   _jbl_node_reset_data(node);
1578 }
1579 
jbn_length(JBL_NODE node)1580 int jbn_length(JBL_NODE node) {
1581   int ret = 0;
1582   for (JBL_NODE n = node->child; n; n = n->next) {
1583     ++ret;
1584   }
1585   return ret;
1586 }
1587 
_jbn_add_item(JBL_NODE parent,JBL_NODE node)1588 static void _jbn_add_item(JBL_NODE parent, JBL_NODE node) {
1589   assert(parent && node);
1590   node->next = 0;
1591   node->prev = 0;
1592   node->parent = parent;
1593   if (parent->child) {
1594     JBL_NODE prev = parent->child->prev;
1595     parent->child->prev = node;
1596     if (prev) { // -V1051
1597       prev->next = node;
1598       node->prev = prev;
1599     } else {
1600       parent->child->next = node;
1601       node->prev = parent->child;
1602     }
1603   } else {
1604     parent->child = node;
1605   }
1606   if (parent->type == JBV_ARRAY) {
1607     node->key = 0;
1608     if (node->prev) {
1609       node->klidx = node->prev->klidx + 1;
1610     } else {
1611       node->klidx = 0;
1612     }
1613   }
1614 }
1615 
jbn_add_item(JBL_NODE parent,JBL_NODE node)1616 void jbn_add_item(JBL_NODE parent, JBL_NODE node) {
1617   _jbn_add_item(parent, node);
1618 }
1619 
jbn_add_item_str(JBL_NODE parent,const char * key,const char * val,int vlen,JBL_NODE * node_out,IWPOOL * pool)1620 iwrc jbn_add_item_str(JBL_NODE parent, const char *key, const char *val, int vlen, JBL_NODE *node_out, IWPOOL *pool) {
1621   if (!parent || !pool || (parent->type < JBV_OBJECT)) {
1622     return IW_ERROR_INVALID_ARGS;
1623   }
1624   iwrc rc = 0;
1625   JBL_NODE n = iwpool_calloc(sizeof(*n), pool);
1626   if (!n) {
1627     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
1628   }
1629   if (parent->type == JBV_OBJECT) {
1630     if (!key) {
1631       return IW_ERROR_INVALID_ARGS;
1632     }
1633     n->key = iwpool_strdup(pool, key, &rc);
1634     RCGO(rc, finish);
1635     n->klidx = (int) strlen(n->key);
1636   }
1637   n->type = JBV_STR;
1638   if (val) {
1639     if (vlen < 0) {
1640       vlen = (int) strlen(val);
1641     }
1642     n->vptr = iwpool_strndup(pool, val, vlen, &rc);
1643     RCGO(rc, finish);
1644     n->vsize = vlen;
1645   }
1646   jbn_add_item(parent, n);
1647   if (node_out) {
1648     *node_out = n;
1649   }
1650 finish:
1651   return rc;
1652 }
1653 
jbn_add_item_null(JBL_NODE parent,const char * key,IWPOOL * pool)1654 iwrc jbn_add_item_null(JBL_NODE parent, const char *key, IWPOOL *pool) {
1655   if (!parent || !pool || (parent->type < JBV_OBJECT)) {
1656     return IW_ERROR_INVALID_ARGS;
1657   }
1658   iwrc rc = 0;
1659   JBL_NODE n = iwpool_calloc(sizeof(*n), pool);
1660   if (!n) {
1661     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
1662   }
1663   if (parent->type == JBV_OBJECT) {
1664     if (!key) {
1665       return IW_ERROR_INVALID_ARGS;
1666     }
1667     n->key = iwpool_strdup(pool, key, &rc);
1668     RCGO(rc, finish);
1669     n->klidx = (int) strlen(n->key);
1670   }
1671   n->type = JBV_NULL;
1672   jbn_add_item(parent, n);
1673 finish:
1674   return rc;
1675 }
1676 
jbn_add_item_i64(JBL_NODE parent,const char * key,int64_t val,JBL_NODE * node_out,IWPOOL * pool)1677 iwrc jbn_add_item_i64(JBL_NODE parent, const char *key, int64_t val, JBL_NODE *node_out, IWPOOL *pool) {
1678   if (!parent || !pool || (parent->type < JBV_OBJECT)) {
1679     return IW_ERROR_INVALID_ARGS;
1680   }
1681   iwrc rc = 0;
1682   JBL_NODE n = iwpool_calloc(sizeof(*n), pool);
1683   if (!n) {
1684     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
1685   }
1686   if (parent->type == JBV_OBJECT) {
1687     if (!key) {
1688       return IW_ERROR_INVALID_ARGS;
1689     }
1690     n->key = iwpool_strdup(pool, key, &rc);
1691     RCGO(rc, finish);
1692     n->klidx = (int) strlen(n->key);
1693   }
1694   n->type = JBV_I64;
1695   n->vi64 = val;
1696   jbn_add_item(parent, n);
1697   if (node_out) {
1698     *node_out = n;
1699   }
1700 finish:
1701   return rc;
1702 }
1703 
jbn_add_item_f64(JBL_NODE parent,const char * key,double val,JBL_NODE * node_out,IWPOOL * pool)1704 iwrc jbn_add_item_f64(JBL_NODE parent, const char *key, double val, JBL_NODE *node_out, IWPOOL *pool) {
1705   if (!parent || !pool || (parent->type < JBV_OBJECT)) {
1706     return IW_ERROR_INVALID_ARGS;
1707   }
1708   iwrc rc = 0;
1709   JBL_NODE n = iwpool_calloc(sizeof(*n), pool);
1710   if (!n) {
1711     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
1712   }
1713   if (parent->type == JBV_OBJECT) {
1714     if (!key) {
1715       return IW_ERROR_INVALID_ARGS;
1716     }
1717     n->key = iwpool_strdup(pool, key, &rc);
1718     RCGO(rc, finish);
1719     n->klidx = (int) strlen(n->key);
1720   }
1721   n->type = JBV_F64;
1722   n->vf64 = val;
1723   jbn_add_item(parent, n);
1724   if (node_out) {
1725     *node_out = n;
1726   }
1727 finish:
1728   return rc;
1729 }
1730 
jbn_add_item_bool(JBL_NODE parent,const char * key,bool val,JBL_NODE * node_out,IWPOOL * pool)1731 iwrc jbn_add_item_bool(JBL_NODE parent, const char *key, bool val, JBL_NODE *node_out, IWPOOL *pool) {
1732   if (!parent || !pool || (parent->type < JBV_OBJECT)) {
1733     return IW_ERROR_INVALID_ARGS;
1734   }
1735   iwrc rc = 0;
1736   JBL_NODE n = iwpool_calloc(sizeof(*n), pool);
1737   if (!n) {
1738     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
1739   }
1740   if (parent->type == JBV_OBJECT) {
1741     if (!key) {
1742       return IW_ERROR_INVALID_ARGS;
1743     }
1744     n->key = iwpool_strdup(pool, key, &rc);
1745     RCGO(rc, finish);
1746     n->klidx = (int) strlen(n->key);
1747   }
1748   n->type = JBV_BOOL;
1749   n->vbool = val;
1750   jbn_add_item(parent, n);
1751   if (node_out) {
1752     *node_out = n;
1753   }
1754 finish:
1755   return rc;
1756 }
1757 
jbn_add_item_obj(JBL_NODE parent,const char * key,JBL_NODE * out,IWPOOL * pool)1758 iwrc jbn_add_item_obj(JBL_NODE parent, const char *key, JBL_NODE *out, IWPOOL *pool) {
1759   if (!parent || !pool || (parent->type < JBV_OBJECT)) {
1760     return IW_ERROR_INVALID_ARGS;
1761   }
1762   iwrc rc = 0;
1763   JBL_NODE n = iwpool_calloc(sizeof(*n), pool);
1764   if (!n) {
1765     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
1766   }
1767   if (parent->type == JBV_OBJECT) {
1768     if (!key) {
1769       return IW_ERROR_INVALID_ARGS;
1770     }
1771     n->key = iwpool_strdup(pool, key, &rc);
1772     RCGO(rc, finish);
1773     n->klidx = (int) strlen(n->key);
1774   }
1775   n->type = JBV_OBJECT;
1776   jbn_add_item(parent, n);
1777   if (out) {
1778     *out = n;
1779   }
1780 finish:
1781   return rc;
1782 }
1783 
jbn_add_item_arr(JBL_NODE parent,const char * key,JBL_NODE * out,IWPOOL * pool)1784 iwrc jbn_add_item_arr(JBL_NODE parent, const char *key, JBL_NODE *out, IWPOOL *pool) {
1785   if (!parent || !pool || (parent->type < JBV_OBJECT)) {
1786     return IW_ERROR_INVALID_ARGS;
1787   }
1788   iwrc rc = 0;
1789   JBL_NODE n = iwpool_calloc(sizeof(*n), pool);
1790   if (!n) {
1791     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
1792   }
1793   if (parent->type == JBV_OBJECT) {
1794     if (!key) {
1795       return IW_ERROR_INVALID_ARGS;
1796     }
1797     n->key = iwpool_strdup(pool, key, &rc);
1798     RCGO(rc, finish);
1799     n->klidx = (int) strlen(n->key);
1800   }
1801   n->type = JBV_ARRAY;
1802   jbn_add_item(parent, n);
1803   if (out) {
1804     *out = n;
1805   }
1806 finish:
1807   return rc;
1808 }
1809 
jbn_copy_path(JBL_NODE src,const char * src_path,JBL_NODE target,const char * target_path,bool overwrite_on_nulls,bool no_src_clone,IWPOOL * pool)1810 iwrc jbn_copy_path(
1811   JBL_NODE    src,
1812   const char *src_path,
1813   JBL_NODE    target,
1814   const char *target_path,
1815   bool        overwrite_on_nulls,
1816   bool        no_src_clone,
1817   IWPOOL     *pool
1818   ) {
1819   if (!src || !src_path || !target || !target_path || !pool) {
1820     return IW_ERROR_INVALID_ARGS;
1821   }
1822   iwrc rc = 0;
1823   JBL_NODE n1, n2;
1824   jbp_patch_t op = JBP_REPLACE;
1825 
1826   if (strcmp("/", src_path) != 0) { // -V526
1827     rc = jbn_at(src, src_path, &n1);
1828     if (rc == JBL_ERROR_PATH_NOTFOUND) {
1829       return 0;
1830     }
1831     RCRET(rc);
1832   } else {
1833     n1 = src;
1834   }
1835   if (!overwrite_on_nulls && (n1->type <= JBV_NULL)) {
1836     return 0;
1837   }
1838   if (no_src_clone) {
1839     n2 = n1;
1840   } else {
1841     rc = jbn_clone(n1, &n2, pool);
1842     RCRET(rc);
1843   }
1844 
1845   rc = jbn_at(target, target_path, &n1);
1846   if (rc == JBL_ERROR_PATH_NOTFOUND) {
1847     rc = 0;
1848     op = JBP_ADD_CREATE;
1849   }
1850   JBL_PATCH p[] = {
1851     {
1852       .op = op,
1853       .path = target_path,
1854       .vnode = n2
1855     }
1856   };
1857   return jbn_patch(target, p, sizeof(p) / sizeof(p[0]), pool);
1858 }
1859 
jbn_copy_paths(JBL_NODE src,JBL_NODE target,const char ** paths,bool overwrite_on_nulls,bool no_src_clone,IWPOOL * pool)1860 IW_EXPORT iwrc jbn_copy_paths(
1861   JBL_NODE     src,
1862   JBL_NODE     target,
1863   const char **paths,
1864   bool         overwrite_on_nulls,
1865   bool         no_src_clone,
1866   IWPOOL      *pool
1867   ) {
1868   if (!target || !src || !paths || !pool) {
1869     return IW_ERROR_INVALID_ARGS;
1870   }
1871   iwrc rc = 0;
1872   for (const char **p = paths; *p; ++p) {
1873     const char *path = *p;
1874     rc = jbn_copy_path(src, path, target, path, overwrite_on_nulls, no_src_clone, pool);
1875     RCBREAK(rc);
1876   }
1877   return rc;
1878 }
1879 
_jbn_remove_item(JBL_NODE parent,JBL_NODE child)1880 IW_INLINE void _jbn_remove_item(JBL_NODE parent, JBL_NODE child) {
1881   assert(parent->child);
1882   if (parent->child == child) {                 // First element
1883     if (child->next) {
1884       parent->child = child->next;
1885       parent->child->prev = child->prev;
1886       if (child->prev) {
1887         child->prev->next = 0;
1888       }
1889     } else {
1890       parent->child = 0;
1891     }
1892   } else if (parent->child->prev == child) {    // Last element
1893     parent->child->prev = child->prev;
1894     if (child->prev) {
1895       child->prev->next = 0;
1896     }
1897   } else { // Somewhere in middle
1898     if (child->next) {
1899       child->next->prev = child->prev;
1900     }
1901     if (child->prev) {
1902       child->prev->next = child->next;
1903     }
1904   }
1905   child->next = 0;
1906   child->prev = 0;
1907   child->child = 0;
1908   child->parent = 0;
1909 }
1910 
jbn_remove_item(JBL_NODE parent,JBL_NODE child)1911 void jbn_remove_item(JBL_NODE parent, JBL_NODE child) {
1912   _jbn_remove_item(parent, child);
1913 }
1914 
_jbl_create_node(JBLDRCTX * ctx,const binn * bv,JBL_NODE parent,const char * key,int klidx,JBL_NODE * node,bool clone_strings)1915 static iwrc _jbl_create_node(
1916   JBLDRCTX   *ctx,
1917   const binn *bv,
1918   JBL_NODE    parent,
1919   const char *key,
1920   int         klidx,
1921   JBL_NODE   *node,
1922   bool        clone_strings
1923   ) {
1924   iwrc rc = 0;
1925   JBL_NODE n = iwpool_alloc(sizeof(*n), ctx->pool);
1926   if (node) {
1927     *node = 0;
1928   }
1929   if (!n) {
1930     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
1931   }
1932   memset(n, 0, sizeof(*n));
1933   if (key && clone_strings) {
1934     n->key = iwpool_strndup(ctx->pool, key, klidx, &rc);
1935     RCGO(rc, finish);
1936   } else {
1937     n->key = key;
1938   }
1939   n->klidx = klidx;
1940   n->parent = parent;
1941   switch (bv->type) {
1942     case BINN_NULL:
1943       n->type = JBV_NULL;
1944       break;
1945     case BINN_STRING:
1946       n->type = JBV_STR;
1947       if (!clone_strings) {
1948         n->vptr = bv->ptr;
1949         n->vsize = bv->size;
1950       } else {
1951         n->vptr = iwpool_strndup(ctx->pool, bv->ptr, bv->size, &rc);
1952         n->vsize = bv->size;
1953         RCGO(rc, finish);
1954       }
1955       break;
1956     case BINN_OBJECT:
1957     case BINN_MAP:
1958       n->type = JBV_OBJECT;
1959       break;
1960     case BINN_LIST:
1961       n->type = JBV_ARRAY;
1962       break;
1963     case BINN_TRUE:
1964       n->type = JBV_BOOL;
1965       n->vbool = true;
1966       break;
1967     case BINN_FALSE:
1968       n->type = JBV_BOOL;
1969       n->vbool = false;
1970       break;
1971     case BINN_BOOL:
1972       n->type = JBV_BOOL;
1973       n->vbool = bv->vbool;
1974       break;
1975     case BINN_UINT8:
1976       n->vi64 = bv->vuint8;
1977       n->type = JBV_I64;
1978       break;
1979     case BINN_UINT16:
1980       n->vi64 = bv->vuint16;
1981       n->type = JBV_I64;
1982       break;
1983     case BINN_UINT32:
1984       n->vi64 = bv->vuint32;
1985       n->type = JBV_I64;
1986       break;
1987     case BINN_UINT64:
1988       n->vi64 = bv->vuint64;
1989       n->type = JBV_I64;
1990       break;
1991     case BINN_INT8:
1992       n->vi64 = bv->vint8; // NOLINT(bugprone-signed-char-misuse)
1993       n->type = JBV_I64;
1994       break;
1995     case BINN_INT16:
1996       n->vi64 = bv->vint16;
1997       n->type = JBV_I64;
1998       break;
1999     case BINN_INT32:
2000       n->vi64 = bv->vint32;
2001       n->type = JBV_I64;
2002       break;
2003     case BINN_INT64:
2004       n->vi64 = bv->vint64;
2005       n->type = JBV_I64;
2006       break;
2007     case BINN_FLOAT32:
2008     case BINN_FLOAT64:
2009       n->vf64 = bv->vdouble;
2010       n->type = JBV_F64;
2011       break;
2012     default:
2013       rc = JBL_ERROR_CREATION;
2014       goto finish;
2015   }
2016   if (parent) {
2017     _jbn_add_item(parent, n);
2018   }
2019 
2020 finish:
2021   if (rc) {
2022     free(n);
2023   } else {
2024     if (node) {
2025       *node = n;
2026     }
2027   }
2028   return rc;
2029 }
2030 
_jbl_node_from_binn_impl(JBLDRCTX * ctx,const binn * bn,JBL_NODE parent,char * key,int klidx,bool clone_strings)2031 static iwrc _jbl_node_from_binn_impl(
2032   JBLDRCTX   *ctx,
2033   const binn *bn,
2034   JBL_NODE    parent,
2035   char       *key,
2036   int         klidx,
2037   bool        clone_strings
2038   ) {
2039   binn bv;
2040   binn_iter iter;
2041   iwrc rc = 0;
2042 
2043   switch (bn->type) {
2044     case BINN_OBJECT:
2045     case BINN_MAP:
2046       rc = _jbl_create_node(ctx, bn, parent, key, klidx, &parent, clone_strings);
2047       RCRET(rc);
2048       if (!ctx->root) {
2049         ctx->root = parent;
2050       }
2051       if (!binn_iter_init(&iter, (binn*) bn, bn->type)) {
2052         return JBL_ERROR_INVALID;
2053       }
2054       if (bn->type == BINN_OBJECT) {
2055         while (binn_object_next2(&iter, &key, &klidx, &bv)) {
2056           rc = _jbl_node_from_binn_impl(ctx, &bv, parent, key, klidx, clone_strings);
2057           RCRET(rc);
2058         }
2059       } else if (bn->type == BINN_MAP) {
2060         while (binn_map_next(&iter, &klidx, &bv)) {
2061           rc = _jbl_node_from_binn_impl(ctx, &bv, parent, 0, klidx, clone_strings);
2062           RCRET(rc);
2063         }
2064       }
2065       break;
2066     case BINN_LIST:
2067       rc = _jbl_create_node(ctx, bn, parent, key, klidx, &parent, clone_strings);
2068       RCRET(rc);
2069       if (!ctx->root) {
2070         ctx->root = parent;
2071       }
2072       if (!binn_iter_init(&iter, (binn*) bn, bn->type)) {
2073         return JBL_ERROR_INVALID;
2074       }
2075       for (int i = 0; binn_list_next(&iter, &bv); ++i) {
2076         rc = _jbl_node_from_binn_impl(ctx, &bv, parent, 0, i, clone_strings);
2077         RCRET(rc);
2078       }
2079       break;
2080     default: {
2081       rc = _jbl_create_node(ctx, bn, parent, key, klidx, 0, clone_strings);
2082       RCRET(rc);
2083       break;
2084     }
2085   }
2086   return rc;
2087 }
2088 
_jbl_node_from_binn(const binn * bn,JBL_NODE * node,bool clone_strings,IWPOOL * pool)2089 iwrc _jbl_node_from_binn(const binn *bn, JBL_NODE *node, bool clone_strings, IWPOOL *pool) {
2090   JBLDRCTX ctx = {
2091     .pool = pool
2092   };
2093   iwrc rc = _jbl_node_from_binn_impl(&ctx, bn, 0, 0, -1, clone_strings);
2094   if (rc) {
2095     *node = 0;
2096   } else {
2097     *node = ctx.root;
2098   }
2099   return rc;
2100 }
2101 
_jbl_node_find(JBL_NODE node,JBL_PTR ptr,int from,int to)2102 static JBL_NODE _jbl_node_find(JBL_NODE node, JBL_PTR ptr, int from, int to) {
2103   if (!ptr || !node) {
2104     return 0;
2105   }
2106   JBL_NODE n = node;
2107 
2108   for (int i = from; n && i < ptr->cnt && i < to; ++i) {
2109     if (n->type == JBV_OBJECT) {
2110       int ptrnlen = (int) strlen(ptr->n[i]);
2111       for (n = n->child; n; n = n->next) {
2112         if (!strncmp(n->key, ptr->n[i], n->klidx) && (ptrnlen == n->klidx)) {
2113           break;
2114         }
2115       }
2116     } else if (n->type == JBV_ARRAY) {
2117       int64_t idx = iwatoi(ptr->n[i]);
2118       for (n = n->child; n; n = n->next) {
2119         if (idx == n->klidx) {
2120           break;
2121         }
2122       }
2123     } else {
2124       return 0;
2125     }
2126   }
2127   return n;
2128 }
2129 
_jbl_node_find2(JBL_NODE node,JBL_PTR ptr)2130 IW_INLINE JBL_NODE _jbl_node_find2(JBL_NODE node, JBL_PTR ptr) {
2131   if (!node || !ptr || !ptr->cnt) {
2132     return 0;
2133   }
2134   return _jbl_node_find(node, ptr, 0, ptr->cnt - 1);
2135 }
2136 
_jbl_node_detach(JBL_NODE target,JBL_PTR path)2137 static JBL_NODE _jbl_node_detach(JBL_NODE target, JBL_PTR path) {
2138   if (!path) {
2139     return 0;
2140   }
2141   JBL_NODE parent = (path->cnt > 1) ? _jbl_node_find(target, path, 0, path->cnt - 1) : target;
2142   if (!parent) {
2143     return 0;
2144   }
2145   JBL_NODE child = _jbl_node_find(parent, path, path->cnt - 1, path->cnt);
2146   if (!child) {
2147     return 0;
2148   }
2149   _jbn_remove_item(parent, child);
2150   return child;
2151 }
2152 
jbn_detach2(JBL_NODE target,JBL_PTR path)2153 JBL_NODE jbn_detach2(JBL_NODE target, JBL_PTR path) {
2154   return _jbl_node_detach(target, path);
2155 }
2156 
jbn_detach(JBL_NODE target,const char * path)2157 JBL_NODE jbn_detach(JBL_NODE target, const char *path) {
2158   JBL_PTR jp;
2159   iwrc rc = _jbl_ptr_pool(path, &jp, 0);
2160   if (rc) {
2161     return 0;
2162   }
2163   JBL_NODE res = jbn_detach2(target, jp);
2164   free(jp);
2165   return res;
2166 }
2167 
_jbl_cmp_node_keys(const void * o1,const void * o2)2168 static int _jbl_cmp_node_keys(const void *o1, const void *o2) {
2169   JBL_NODE n1 = *((JBL_NODE*) o1);
2170   JBL_NODE n2 = *((JBL_NODE*) o2);
2171   if (!n1 && !n2) {
2172     return 0;
2173   }
2174   if (!n2 || (n1->klidx > n2->klidx)) { // -V522
2175     return 1;
2176   } else if (!n1 || (n1->klidx < n2->klidx)) { // -V522
2177     return -1;
2178   }
2179   return strncmp(n1->key, n2->key, n1->klidx);
2180 }
2181 
_jbl_node_count(JBL_NODE n)2182 static uint32_t _jbl_node_count(JBL_NODE n) {
2183   uint32_t ret = 0;
2184   n = n->child;
2185   while (n) {
2186     ret++;
2187     n = n->next;
2188   }
2189   return ret;
2190 }
2191 
_jbl_compare_objects(JBL_NODE n1,JBL_NODE n2,iwrc * rcp)2192 static int _jbl_compare_objects(JBL_NODE n1, JBL_NODE n2, iwrc *rcp) {
2193   int ret = 0;
2194   uint32_t cnt = _jbl_node_count(n1);
2195   uint32_t i = _jbl_node_count(n2);
2196   if (cnt > i) {
2197     return 1;
2198   } else if (cnt < i) {
2199     return -1;
2200   } else if (cnt == 0) {
2201     return 0;
2202   }
2203   JBL_NODE *s1 = malloc(2 * sizeof(JBL_NODE) * cnt);
2204   if (!s1) {
2205     *rcp = iwrc_set_errno(IW_ERROR_ALLOC, errno);
2206     return 0;
2207   }
2208   JBL_NODE *s2 = s1 + cnt;
2209 
2210   i = 0;
2211   n1 = n1->child;
2212   n2 = n2->child;
2213   while (n1 && n2) {
2214     s1[i] = n1;
2215     s2[i] = n2;
2216     n1 = n1->next;
2217     n2 = n2->next;
2218     ++i;
2219   }
2220   qsort(s1, cnt, sizeof(JBL_NODE), _jbl_cmp_node_keys);
2221   qsort(s2, cnt, sizeof(JBL_NODE), _jbl_cmp_node_keys);
2222   for (i = 0; i < cnt; ++i) {
2223     ret = _jbl_cmp_node_keys(s1 + i, s2 + i);
2224     if (ret) {
2225       goto finish;
2226     }
2227     ret = _jbl_compare_nodes(s1[i], s2[i], rcp);
2228     if (*rcp || ret) {
2229       goto finish;
2230     }
2231   }
2232 
2233 finish:
2234   free(s1);
2235   return ret;
2236 }
2237 
_jbl_compare_nodes(JBL_NODE n1,JBL_NODE n2,iwrc * rcp)2238 int _jbl_compare_nodes(JBL_NODE n1, JBL_NODE n2, iwrc *rcp) {
2239   if (!n1 && !n2) {
2240     return 0;
2241   } else if (!n1) {
2242     return -1;
2243   } else if (!n2) {
2244     return 1;
2245   } else if (n1->type != n2->type) {
2246     return (int) n1->type - (int) n2->type;
2247   }
2248   switch (n1->type) {
2249     case JBV_BOOL:
2250       return n1->vbool - n2->vbool;
2251     case JBV_I64:
2252       return n1->vi64 > n2->vi64 ? 1 : n1->vi64 < n2->vi64 ? -1 : 0;
2253     case JBV_F64: {
2254       size_t sz1, sz2;
2255       char b1[IWNUMBUF_SIZE];
2256       char b2[IWNUMBUF_SIZE];
2257       iwjson_ftoa(n1->vf64, b1, &sz1);
2258       iwjson_ftoa(n2->vf64, b2, &sz2);
2259       return iwafcmp(b1, sz1, b2, sz2);
2260     }
2261     case JBV_STR:
2262       if (n1->vsize != n2->vsize) {
2263         return n1->vsize - n2->vsize;
2264       }
2265       return strncmp(n1->vptr, n2->vptr, n1->vsize);
2266     case JBV_ARRAY:
2267       for (n1 = n1->child, n2 = n2->child; n1 && n2; n1 = n1->next, n2 = n2->next) {
2268         int res = _jbl_compare_nodes(n1, n2, rcp);
2269         if (res) {
2270           return res;
2271         }
2272       }
2273       if (n1) {
2274         return 1;
2275       } else if (n2) {
2276         return -1;
2277       } else {
2278         return 0;
2279       }
2280     case JBV_OBJECT:
2281       return _jbl_compare_objects(n1, n2, rcp);
2282     case JBV_NULL:
2283     case JBV_NONE:
2284       break;
2285   }
2286   return 0;
2287 }
2288 
jbn_compare_nodes(JBL_NODE n1,JBL_NODE n2,iwrc * rcp)2289 int jbn_compare_nodes(JBL_NODE n1, JBL_NODE n2, iwrc *rcp) {
2290   return _jbl_compare_nodes(n1, n2, rcp);
2291 }
2292 
_jbl_target_apply_patch(JBL_NODE target,const JBL_PATCHEXT * ex,IWPOOL * pool)2293 static iwrc _jbl_target_apply_patch(JBL_NODE target, const JBL_PATCHEXT *ex, IWPOOL *pool) {
2294   struct _JBL_NODE *ntmp;
2295   jbp_patch_t op = ex->p->op;
2296   JBL_PTR path = ex->path;
2297   JBL_NODE value = ex->p->vnode;
2298   bool oproot = ex->path->cnt == 1 && *ex->path->n[0] == '\0';
2299 
2300   if (op == JBP_TEST) {
2301     iwrc rc = 0;
2302     if (!value) {
2303       return JBL_ERROR_PATCH_NOVALUE;
2304     }
2305     if (_jbl_compare_nodes(oproot ? target : _jbl_node_find(target, path, 0, path->cnt), value, &rc)) {
2306       RCRET(rc);
2307       return JBL_ERROR_PATCH_TEST_FAILED;
2308     } else {
2309       return rc;
2310     }
2311   }
2312   if (oproot) { // Root operation
2313     if (op == JBP_REMOVE) {
2314       memset(target, 0, sizeof(*target));
2315     } else if ((op == JBP_REPLACE) || (op == JBP_ADD) || (op == JBP_ADD_CREATE)) {
2316       if (!value) {
2317         return JBL_ERROR_PATCH_NOVALUE;
2318       }
2319       memmove(target, value, sizeof(*value));
2320     }
2321   } else { // Not a root
2322     if ((op == JBP_REMOVE) || (op == JBP_REPLACE)) {
2323       _jbl_node_detach(target, ex->path);
2324     }
2325     if (op == JBP_REMOVE) {
2326       return 0;
2327     } else if ((op == JBP_MOVE) || (op == JBP_COPY) || (op == JBP_SWAP)) {
2328       if (op == JBP_MOVE) {
2329         value = _jbl_node_detach(target, ex->from);
2330       } else {
2331         value = _jbl_node_find(target, ex->from, 0, ex->from->cnt);
2332       }
2333       if (!value) {
2334         return JBL_ERROR_PATH_NOTFOUND;
2335       }
2336       if (op == JBP_SWAP) {
2337         ntmp = iwpool_calloc(sizeof(*ntmp), pool);
2338         if (!ntmp) {
2339           return iwrc_set_errno(IW_ERROR_ALLOC, errno);
2340         }
2341       }
2342     } else { // ADD/REPLACE/INCREMENT
2343       if (!value) {
2344         return JBL_ERROR_PATCH_NOVALUE;
2345       }
2346     }
2347     int lastidx = path->cnt - 1;
2348     JBL_NODE parent = (path->cnt > 1) ? _jbl_node_find(target, path, 0, lastidx) : target;
2349     if (!parent) {
2350       if (op == JBP_ADD_CREATE) {
2351         parent = target;
2352         for (int i = 0; i < lastidx; ++i) {
2353           JBL_NODE pn = _jbl_node_find(parent, path, i, i + 1);
2354           if (!pn) {
2355             pn = iwpool_calloc(sizeof(*pn), pool);
2356             if (!pn) {
2357               return iwrc_set_errno(IW_ERROR_ALLOC, errno);
2358             }
2359             pn->type = JBV_OBJECT;
2360             pn->key = path->n[i];
2361             pn->klidx = (int) strlen(pn->key);
2362             _jbn_add_item(parent, pn);
2363           } else if (pn->type != JBV_OBJECT) {
2364             return JBL_ERROR_PATCH_TARGET_INVALID;
2365           }
2366           parent = pn;
2367         }
2368       } else {
2369         return JBL_ERROR_PATCH_TARGET_INVALID;
2370       }
2371     }
2372     if (parent->type == JBV_ARRAY) {
2373       if ((path->n[lastidx][0] == '-') && (path->n[lastidx][1] == '\0')) {
2374         if (op == JBP_SWAP) {
2375           value = _jbl_node_detach(target, ex->from);
2376         }
2377         _jbn_add_item(parent, value); // Add to end of array
2378       } else {                        // Insert into the specified index
2379         int idx = iwatoi(path->n[lastidx]);
2380         int cnt = idx;
2381         JBL_NODE child = parent->child;
2382         while (child && cnt > 0) {
2383           cnt--;
2384           child = child->next;
2385         }
2386         if (cnt > 0) {
2387           return JBL_ERROR_PATCH_INVALID_ARRAY_INDEX;
2388         }
2389         value->klidx = idx;
2390         if (child) {
2391           if (op == JBP_SWAP) {
2392             _jbl_copy_node_data(ntmp, value);
2393             _jbl_copy_node_data(value, child);
2394             _jbl_copy_node_data(child, ntmp);
2395           } else {
2396             value->parent = parent;
2397             value->next = child;
2398             value->prev = child->prev;
2399             child->prev = value;
2400             if (child == parent->child) {
2401               parent->child = value;
2402             } else {
2403               value->prev->next = value;
2404             }
2405             while (child) {
2406               child->klidx++;
2407               child = child->next;
2408             }
2409           }
2410         } else {
2411           if (op == JBP_SWAP) {
2412             value = _jbl_node_detach(target, ex->from);
2413           }
2414           _jbn_add_item(parent, value);
2415         }
2416       }
2417     } else if (parent->type == JBV_OBJECT) {
2418       JBL_NODE child = _jbl_node_find(parent, path, path->cnt - 1, path->cnt);
2419       if (child) {
2420         if (op == JBP_INCREMENT) {
2421           return _jbl_increment_node_data(child, value);
2422         } else {
2423           if (op == JBP_SWAP) {
2424             _jbl_copy_node_data(ntmp, value);
2425             _jbl_copy_node_data(value, child);
2426             _jbl_copy_node_data(child, ntmp);
2427           } else {
2428             _jbl_copy_node_data(child, value);
2429           }
2430         }
2431       } else if (op != JBP_INCREMENT) {
2432         if (op == JBP_SWAP) {
2433           value = _jbl_node_detach(target, ex->from);
2434         }
2435         value->key = path->n[path->cnt - 1];
2436         value->klidx = (int) strlen(value->key);
2437         _jbn_add_item(parent, value);
2438       } else {
2439         return JBL_ERROR_PATCH_TARGET_INVALID;
2440       }
2441     } else {
2442       return JBL_ERROR_PATCH_TARGET_INVALID;
2443     }
2444   }
2445   return 0;
2446 }
2447 
_jbl_from_node_impl(binn * res,JBL_NODE node)2448 static iwrc _jbl_from_node_impl(binn *res, JBL_NODE node) {
2449   iwrc rc = 0;
2450   switch (node->type) {
2451     case JBV_OBJECT:
2452       if (!binn_create(res, BINN_OBJECT, 0, NULL)) {
2453         return JBL_ERROR_CREATION;
2454       }
2455       for (JBL_NODE n = node->child; n; n = n->next) {
2456         binn bv;
2457         rc = _jbl_from_node_impl(&bv, n);
2458         RCRET(rc);
2459         if (!binn_object_set_value2(res, n->key, n->klidx, &bv)) {
2460           rc = JBL_ERROR_CREATION;
2461         }
2462         binn_free(&bv);
2463         RCRET(rc);
2464       }
2465       break;
2466     case JBV_ARRAY:
2467       if (!binn_create(res, BINN_LIST, 0, NULL)) {
2468         return JBL_ERROR_CREATION;
2469       }
2470       for (JBL_NODE n = node->child; n; n = n->next) {
2471         binn bv;
2472         rc = _jbl_from_node_impl(&bv, n);
2473         RCRET(rc);
2474         if (!binn_list_add_value(res, &bv)) {
2475           rc = JBL_ERROR_CREATION;
2476         }
2477         binn_free(&bv);
2478         RCRET(rc);
2479       }
2480       break;
2481     case JBV_STR:
2482       binn_init_item(res);
2483       binn_set_string(res, node->vptr, node->vsize);
2484       break;
2485     case JBV_I64:
2486       binn_init_item(res);
2487       binn_set_int64(res, node->vi64);
2488       break;
2489     case JBV_F64:
2490       binn_init_item(res);
2491       binn_set_double(res, node->vf64);
2492       break;
2493     case JBV_BOOL:
2494       binn_init_item(res);
2495       binn_set_bool(res, node->vbool);
2496       break;
2497     case JBV_NULL:
2498       binn_init_item(res);
2499       binn_set_null(res);
2500       break;
2501     case JBV_NONE:
2502       rc = JBL_ERROR_CREATION;
2503       break;
2504   }
2505   return rc;
2506 }
2507 
_jbl_binn_from_node(binn * res,JBL_NODE node)2508 iwrc _jbl_binn_from_node(binn *res, JBL_NODE node) {
2509   iwrc rc = _jbl_from_node_impl(res, node);
2510   if (!rc) {
2511     if (res->writable && res->dirty) {
2512       binn_save_header(res);
2513     }
2514   }
2515   return rc;
2516 }
2517 
_jbl_from_node(JBL jbl,JBL_NODE node)2518 iwrc _jbl_from_node(JBL jbl, JBL_NODE node) {
2519   jbl->node = node;
2520   return _jbl_binn_from_node(&jbl->bn, node);
2521 }
2522 
_jbl_patch_node(JBL_NODE root,const JBL_PATCH * p,size_t cnt,IWPOOL * pool)2523 static iwrc _jbl_patch_node(JBL_NODE root, const JBL_PATCH *p, size_t cnt, IWPOOL *pool) {
2524   if (cnt < 1) {
2525     return 0;
2526   }
2527   if (!root || !p) {
2528     return IW_ERROR_INVALID_ARGS;
2529   }
2530   iwrc rc = 0;
2531   size_t i = 0;
2532   JBL_PATCHEXT parr[cnt];
2533   memset(parr, 0, cnt * sizeof(JBL_PATCHEXT));
2534   for (i = 0; i < cnt; ++i) {
2535     JBL_PATCHEXT *ext = &parr[i];
2536     ext->p = &p[i];
2537     rc = _jbl_ptr_pool(p[i].path, &ext->path, pool);
2538     RCRET(rc);
2539     if (p[i].from) {
2540       rc = _jbl_ptr_pool(p[i].from, &ext->from, pool);
2541       RCRET(rc);
2542     }
2543   }
2544   for (i = 0; i < cnt; ++i) {
2545     rc = _jbl_target_apply_patch(root, &parr[i], pool);
2546     RCRET(rc);
2547   }
2548   return rc;
2549 }
2550 
_jbl_patch(JBL jbl,const JBL_PATCH * p,size_t cnt,IWPOOL * pool)2551 static iwrc _jbl_patch(JBL jbl, const JBL_PATCH *p, size_t cnt, IWPOOL *pool) {
2552   if (cnt < 1) {
2553     return 0;
2554   }
2555   if (!jbl || !p) {
2556     return IW_ERROR_INVALID_ARGS;
2557   }
2558   binn bv;
2559   binn *bn;
2560   JBL_NODE root;
2561   iwrc rc = _jbl_node_from_binn(&jbl->bn, &root, false, pool);
2562   RCRET(rc);
2563   rc = _jbl_patch_node(root, p, cnt, pool);
2564   RCRET(rc);
2565   if (root->type != JBV_NONE) {
2566     rc = _jbl_from_node_impl(&bv, root);
2567     RCRET(rc);
2568     bn = &bv;
2569   } else {
2570     bn = 0;
2571   }
2572   binn_free(&jbl->bn);
2573   if (bn) {
2574     if (bn->writable && bn->dirty) {
2575       binn_save_header(bn);
2576     }
2577     memcpy(&jbl->bn, bn, sizeof(jbl->bn));
2578     jbl->bn.allocated = 0;
2579   } else {
2580     memset(&jbl->bn, 0, sizeof(jbl->bn));
2581     root->type = JBV_NONE;
2582   }
2583   return rc;
2584 }
2585 
_jbl_cmp_atomic_values(JBL v1,JBL v2)2586 int _jbl_cmp_atomic_values(JBL v1, JBL v2) {
2587   jbl_type_t t1 = jbl_type(v1);
2588   jbl_type_t t2 = jbl_type(v2);
2589   if (t1 != t2) {
2590     return (int) t1 - (int) t2;
2591   }
2592   switch (t1) {
2593     case JBV_BOOL:
2594     case JBV_I64: {
2595       int64_t vv1 = jbl_get_i64(v1);
2596       int64_t vv2 = jbl_get_i64(v2);
2597       return vv1 > vv2 ? 1 : vv1 < vv2 ? -1 : 0;
2598     }
2599     case JBV_STR:
2600       return strcmp(jbl_get_str(v1), jbl_get_str(v2)); // -V575
2601     case JBV_F64: {
2602       double vv1 = jbl_get_f64(v1);
2603       double vv2 = jbl_get_f64(v2);
2604       return vv1 > vv2 ? 1 : vv1 < vv2 ? -1 : 0;
2605     }
2606     default:
2607       return 0;
2608   }
2609 }
2610 
_jbl_is_eq_atomic_values(JBL v1,JBL v2)2611 bool _jbl_is_eq_atomic_values(JBL v1, JBL v2) {
2612   jbl_type_t t1 = jbl_type(v1);
2613   jbl_type_t t2 = jbl_type(v2);
2614   if (t1 != t2) {
2615     return false;
2616   }
2617   switch (t1) {
2618     case JBV_BOOL:
2619     case JBV_I64:
2620       return jbl_get_i64(v1) == jbl_get_i64(v2);
2621     case JBV_STR:
2622       return !strcmp(jbl_get_str(v1), jbl_get_str(v2)); // -V575
2623     case JBV_F64:
2624       return jbl_get_f64(v1) == jbl_get_f64(v2); // -V550
2625     case JBV_OBJECT:
2626     case JBV_ARRAY:
2627       return false;
2628     default:
2629       return true;
2630   }
2631 }
2632 
2633 // --------------------------- Public API
2634 
jbn_apply_from(JBL_NODE target,JBL_NODE from)2635 void jbn_apply_from(JBL_NODE target, JBL_NODE from) {
2636   const int off = offsetof(struct _JBL_NODE, child);
2637   memcpy((char*) target + off,
2638          (char*) from + off,
2639          sizeof(struct _JBL_NODE) - off);
2640 }
2641 
jbl_to_node(JBL jbl,JBL_NODE * node,bool clone_strings,IWPOOL * pool)2642 iwrc jbl_to_node(JBL jbl, JBL_NODE *node, bool clone_strings, IWPOOL *pool) {
2643   if (jbl->node) {
2644     *node = jbl->node;
2645     return 0;
2646   }
2647   return _jbl_node_from_binn(&jbl->bn, node, clone_strings, pool);
2648 }
2649 
jbn_patch(JBL_NODE root,const JBL_PATCH * p,size_t cnt,IWPOOL * pool)2650 iwrc jbn_patch(JBL_NODE root, const JBL_PATCH *p, size_t cnt, IWPOOL *pool) {
2651   return _jbl_patch_node(root, p, cnt, pool);
2652 }
2653 
jbl_patch(JBL jbl,const JBL_PATCH * p,size_t cnt)2654 iwrc jbl_patch(JBL jbl, const JBL_PATCH *p, size_t cnt) {
2655   if (cnt < 1) {
2656     return 0;
2657   }
2658   if (!jbl || !p) {
2659     return IW_ERROR_INVALID_ARGS;
2660   }
2661   IWPOOL *pool = iwpool_create(jbl->bn.size);
2662   if (!pool) {
2663     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
2664   }
2665   iwrc rc = _jbl_patch(jbl, p, cnt, pool);
2666   iwpool_destroy(pool);
2667   return rc;
2668 }
2669 
_jbl_create_patch(JBL_NODE node,JBL_PATCH ** pptr,int * cntp,IWPOOL * pool)2670 static iwrc _jbl_create_patch(JBL_NODE node, JBL_PATCH **pptr, int *cntp, IWPOOL *pool) {
2671   *pptr = 0;
2672   *cntp = 0;
2673   int i = 0;
2674   for (JBL_NODE n = node->child; n; n = n->next) {
2675     if (n->type != JBV_OBJECT) {
2676       return JBL_ERROR_PATCH_INVALID;
2677     }
2678     ++i;
2679   }
2680   JBL_PATCH *p = iwpool_alloc(i * sizeof(*p), pool);
2681   if (!p) {
2682     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
2683   }
2684   memset(p, 0, i * sizeof(*p));
2685   i = 0;
2686   for (JBL_NODE n = node->child; n; n = n->next, ++i) {
2687     JBL_PATCH *pp = p + i;
2688     for (JBL_NODE n2 = n->child; n2; n2 = n2->next) {
2689       if (!strncmp("op", n2->key, n2->klidx)) {
2690         if (n2->type != JBV_STR) {
2691           return JBL_ERROR_PATCH_INVALID;
2692         }
2693         if (!strncmp("add", n2->vptr, n2->vsize)) {
2694           pp->op = JBP_ADD;
2695         } else if (!strncmp("remove", n2->vptr, n2->vsize)) {
2696           pp->op = JBP_REMOVE;
2697         } else if (!strncmp("replace", n2->vptr, n2->vsize)) {
2698           pp->op = JBP_REPLACE;
2699         } else if (!strncmp("copy", n2->vptr, n2->vsize)) {
2700           pp->op = JBP_COPY;
2701         } else if (!strncmp("move", n2->vptr, n2->vsize)) {
2702           pp->op = JBP_MOVE;
2703         } else if (!strncmp("test", n2->vptr, n2->vsize)) {
2704           pp->op = JBP_TEST;
2705         } else if (!strncmp("increment", n2->vptr, n2->vsize)) {
2706           pp->op = JBP_INCREMENT;
2707         } else if (!strncmp("add_create", n2->vptr, n2->vsize)) {
2708           pp->op = JBP_ADD_CREATE;
2709         } else if (!strncmp("swap", n2->vptr, n2->vsize)) {
2710           pp->op = JBP_SWAP;
2711         } else {
2712           return JBL_ERROR_PATCH_INVALID_OP;
2713         }
2714       } else if (!strncmp("value", n2->key, n2->klidx)) {
2715         pp->vnode = n2;
2716       } else if (!strncmp("path", n2->key, n2->klidx)) {
2717         if (n2->type != JBV_STR) {
2718           return JBL_ERROR_PATCH_INVALID;
2719         }
2720         pp->path = n2->vptr;
2721       } else if (!strncmp("from", n2->key, n2->klidx)) {
2722         if (n2->type != JBV_STR) {
2723           return JBL_ERROR_PATCH_INVALID;
2724         }
2725         pp->from = n2->vptr;
2726       }
2727     }
2728   }
2729   *cntp = i;
2730   *pptr = p;
2731   return 0;
2732 }
2733 
jbl_patch_from_json(JBL jbl,const char * patchjson)2734 iwrc jbl_patch_from_json(JBL jbl, const char *patchjson) {
2735   if (!jbl || !patchjson) {
2736     return IW_ERROR_INVALID_ARGS;
2737   }
2738   JBL_PATCH *p;
2739   JBL_NODE patch;
2740   int cnt = (int) strlen(patchjson);
2741   IWPOOL *pool = iwpool_create(MAX(cnt, 1024U));
2742   if (!pool) {
2743     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
2744   }
2745   iwrc rc = jbn_from_json(patchjson, &patch, pool);
2746   RCGO(rc, finish);
2747   if (patch->type == JBV_ARRAY) {
2748     rc = _jbl_create_patch(patch, &p, &cnt, pool);
2749     RCGO(rc, finish);
2750     rc = _jbl_patch(jbl, p, cnt, pool);
2751   } else if (patch->type == JBV_OBJECT) {
2752     // FIXME: Merge patch not implemented
2753     //_jbl_merge_patch_node()
2754     rc = IW_ERROR_NOT_IMPLEMENTED;
2755   } else {
2756     rc = JBL_ERROR_PATCH_INVALID;
2757   }
2758 
2759 finish:
2760   iwpool_destroy(pool);
2761   return rc;
2762 }
2763 
jbl_fill_from_node(JBL jbl,JBL_NODE node)2764 iwrc jbl_fill_from_node(JBL jbl, JBL_NODE node) {
2765   if (!jbl || !node) {
2766     return IW_ERROR_INVALID_ARGS;
2767   }
2768   if (node->type == JBV_NONE) {
2769     memset(jbl, 0, sizeof(*jbl));
2770     return 0;
2771   }
2772   binn bv = { 0 };
2773   iwrc rc = _jbl_binn_from_node(&bv, node);
2774   RCRET(rc);
2775   binn_free(&jbl->bn);
2776   memcpy(&jbl->bn, &bv, sizeof(jbl->bn));
2777   jbl->bn.allocated = 0;
2778   return rc;
2779 }
2780 
jbl_from_node(JBL * jblp,JBL_NODE node)2781 iwrc jbl_from_node(JBL *jblp, JBL_NODE node) {
2782   if (!jblp || !node) {
2783     return IW_ERROR_INVALID_ARGS;
2784   }
2785   iwrc rc = 0;
2786   if (node->type == JBV_OBJECT) {
2787     rc = jbl_create_empty_object(jblp);
2788   } else if (node->type == JBV_ARRAY) {
2789     rc = jbl_create_empty_array(jblp);
2790   } else {
2791     rc = IW_ERROR_INVALID_ARGS;
2792   }
2793   RCRET(rc);
2794   return jbl_fill_from_node(*jblp, node);
2795 }
2796 
_jbl_merge_patch_node(JBL_NODE target,JBL_NODE patch,IWPOOL * pool,iwrc * rcp)2797 static JBL_NODE _jbl_merge_patch_node(JBL_NODE target, JBL_NODE patch, IWPOOL *pool, iwrc *rcp) {
2798   *rcp = 0;
2799   if (!patch) {
2800     return 0;
2801   }
2802   if (patch->type == JBV_OBJECT) {
2803     if (!target) {
2804       target = iwpool_alloc(sizeof(*target), pool);
2805       if (!target) {
2806         *rcp = iwrc_set_errno(IW_ERROR_ALLOC, errno);
2807         return 0;
2808       }
2809       memset(target, 0, sizeof(*target));
2810       target->type = JBV_OBJECT;
2811       target->key = patch->key;
2812       target->klidx = patch->klidx;
2813     } else if (target->type != JBV_OBJECT) {
2814       _jbl_node_reset_data(target);
2815       target->type = JBV_OBJECT;
2816     }
2817     patch = patch->child;
2818     while (patch) {
2819       JBL_NODE patch_next = patch->next;
2820       if (patch->type == JBV_NULL) {
2821         JBL_NODE node = target->child;
2822         while (node) {
2823           if ((node->klidx == patch->klidx) && !strncmp(node->key, patch->key, node->klidx)) {
2824             _jbn_remove_item(target, node);
2825             break;
2826           }
2827           node = node->next;
2828         }
2829       } else {
2830         JBL_NODE node = target->child;
2831         while (node) {
2832           if ((node->klidx == patch->klidx) && !strncmp(node->key, patch->key, node->klidx)) {
2833             _jbl_copy_node_data(node, _jbl_merge_patch_node(node, patch, pool, rcp));
2834             break;
2835           }
2836           node = node->next;
2837         }
2838         if (!node) {
2839           _jbn_add_item(target, _jbl_merge_patch_node(0, patch, pool, rcp));
2840         }
2841       }
2842       patch = patch_next;
2843     }
2844     return target;
2845   } else {
2846     return patch;
2847   }
2848 }
2849 
jbn_merge_patch_from_json(JBL_NODE root,const char * patchjson,IWPOOL * pool)2850 iwrc jbn_merge_patch_from_json(JBL_NODE root, const char *patchjson, IWPOOL *pool) {
2851   if (!root || !patchjson || !pool) {
2852     return IW_ERROR_INVALID_ARGS;
2853   }
2854   JBL_NODE patch, res;
2855   iwrc rc = jbn_from_json(patchjson, &patch, pool);
2856   RCRET(rc);
2857   res = _jbl_merge_patch_node(root, patch, pool, &rc);
2858   RCGO(rc, finish);
2859   if (res != root) {
2860     memcpy(root, res, sizeof(*root)); // -V575
2861   }
2862 
2863 finish:
2864   return rc;
2865 }
2866 
jbl_merge_patch(JBL jbl,const char * patchjson)2867 iwrc jbl_merge_patch(JBL jbl, const char *patchjson) {
2868   if (!jbl || !patchjson) {
2869     return IW_ERROR_INVALID_ARGS;
2870   }
2871   binn bv;
2872   JBL_NODE target;
2873   IWPOOL *pool = iwpool_create(jbl->bn.size * 2);
2874   if (!pool) {
2875     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
2876   }
2877   iwrc rc = _jbl_node_from_binn(&jbl->bn, &target, false, pool);
2878   RCGO(rc, finish);
2879   rc = jbn_merge_patch_from_json(target, patchjson, pool);
2880   RCGO(rc, finish);
2881 
2882   rc = _jbl_binn_from_node(&bv, target);
2883   RCGO(rc, finish);
2884 
2885   binn_free(&jbl->bn);
2886   memcpy(&jbl->bn, &bv, sizeof(jbl->bn));
2887   jbl->bn.allocated = 0;
2888 
2889 finish:
2890   iwpool_destroy(pool);
2891   return 0;
2892 }
2893 
jbl_merge_patch_jbl(JBL jbl,JBL patch)2894 iwrc jbl_merge_patch_jbl(JBL jbl, JBL patch) {
2895   IWXSTR *xstr = iwxstr_new();
2896   if (!xstr) {
2897     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
2898   }
2899   iwrc rc = jbl_as_json(patch, jbl_xstr_json_printer, xstr, 0);
2900   RCGO(rc, finish);
2901   rc = jbl_merge_patch(jbl, iwxstr_ptr(xstr));
2902 finish:
2903   iwxstr_destroy(xstr);
2904   return rc;
2905 }
2906 
jbn_patch_auto(JBL_NODE root,JBL_NODE patch,IWPOOL * pool)2907 iwrc jbn_patch_auto(JBL_NODE root, JBL_NODE patch, IWPOOL *pool) {
2908   if (!root || !patch || !pool) {
2909     return IW_ERROR_INVALID_ARGS;
2910   }
2911   iwrc rc = 0;
2912   if (patch->type == JBV_OBJECT) {
2913     _jbl_merge_patch_node(root, patch, pool, &rc);
2914   } else if (patch->type == JBV_ARRAY) {
2915     int cnt;
2916     JBL_PATCH *p;
2917     rc = _jbl_create_patch(patch, &p, &cnt, pool);
2918     RCRET(rc);
2919     rc = _jbl_patch_node(root, p, cnt, pool);
2920   } else {
2921     return IW_ERROR_INVALID_ARGS;
2922   }
2923   return rc;
2924 }
2925 
jbn_merge_patch(JBL_NODE root,JBL_NODE patch,IWPOOL * pool)2926 iwrc jbn_merge_patch(JBL_NODE root, JBL_NODE patch, IWPOOL *pool) {
2927   if (!root || !patch || !pool || (root->type != JBV_OBJECT)) {
2928     return IW_ERROR_INVALID_ARGS;
2929   }
2930   iwrc rc = 0;
2931   _jbl_merge_patch_node(root, patch, pool, &rc);
2932   return rc;
2933 }
2934 
_jbl_ecodefn(locale_t locale,uint32_t ecode)2935 static const char* _jbl_ecodefn(locale_t locale, uint32_t ecode) {
2936   if (!((ecode > _JBL_ERROR_START) && (ecode < _JBL_ERROR_END))) {
2937     return 0;
2938   }
2939   switch (ecode) {
2940     case JBL_ERROR_INVALID_BUFFER:
2941       return "Invalid JBL buffer (JBL_ERROR_INVALID_BUFFER)";
2942     case JBL_ERROR_CREATION:
2943       return "Cannot create JBL object (JBL_ERROR_CREATION)";
2944     case JBL_ERROR_INVALID:
2945       return "Invalid JBL object (JBL_ERROR_INVALID)";
2946     case JBL_ERROR_PARSE_JSON:
2947       return "Failed to parse JSON string (JBL_ERROR_PARSE_JSON)";
2948     case JBL_ERROR_PARSE_UNQUOTED_STRING:
2949       return "Unquoted JSON string (JBL_ERROR_PARSE_UNQUOTED_STRING)";
2950     case JBL_ERROR_PARSE_INVALID_CODEPOINT:
2951       return "Invalid unicode codepoint/escape sequence (JBL_ERROR_PARSE_INVALID_CODEPOINT)";
2952     case JBL_ERROR_PARSE_INVALID_UTF8:
2953       return "Invalid utf8 string (JBL_ERROR_PARSE_INVALID_UTF8)";
2954     case JBL_ERROR_JSON_POINTER:
2955       return "Invalid JSON pointer (rfc6901) path (JBL_ERROR_JSON_POINTER)";
2956     case JBL_ERROR_PATH_NOTFOUND:
2957       return "JSON object not matched the path specified (JBL_ERROR_PATH_NOTFOUND)";
2958     case JBL_ERROR_PATCH_INVALID:
2959       return "Invalid JSON patch specified (JBL_ERROR_PATCH_INVALID)";
2960     case JBL_ERROR_PATCH_INVALID_OP:
2961       return "Invalid JSON patch operation specified (JBL_ERROR_PATCH_INVALID_OP)";
2962     case JBL_ERROR_PATCH_NOVALUE:
2963       return "No value specified in JSON patch (JBL_ERROR_PATCH_NOVALUE)";
2964     case JBL_ERROR_PATCH_TARGET_INVALID:
2965       return "Could not find target object to set value (JBL_ERROR_PATCH_TARGET_INVALID)";
2966     case JBL_ERROR_PATCH_INVALID_VALUE:
2967       return "Invalid value specified by patch (JBL_ERROR_PATCH_INVALID_VALUE)";
2968     case JBL_ERROR_PATCH_INVALID_ARRAY_INDEX:
2969       return "Invalid array index in JSON patch path (JBL_ERROR_PATCH_INVALID_ARRAY_INDEX)";
2970     case JBL_ERROR_PATCH_TEST_FAILED:
2971       return "JSON patch test operation failed (JBL_ERROR_PATCH_TEST_FAILED)";
2972     case JBL_ERROR_NOT_AN_OBJECT:
2973       return "JBL is not an object (JBL_ERROR_NOT_AN_OBJECT)";
2974     case JBL_ERROR_TYPE_MISMATCHED:
2975       return "Type of JBL object mismatched user type constraints (JBL_ERROR_TYPE_MISMATCHED)";
2976     case JBL_ERROR_MAX_NESTING_LEVEL_EXCEEDED:
2977       return "Exceeded the maximal object nesting level: " _STR(JBL_MAX_NESTING_LEVEL)
2978              " (JBL_ERROR_MAX_NESTING_LEVEL_EXCEEDED)";
2979   }
2980   return 0;
2981 }
2982 
jbl_init(void)2983 iwrc jbl_init(void) {
2984   static int _jbl_initialized = 0;
2985   if (!__sync_bool_compare_and_swap(&_jbl_initialized, 0, 1)) {
2986     return 0;
2987   }
2988   return iwlog_register_ecodefn(_jbl_ecodefn);
2989 }
2990