ROOTANA
Loading...
Searching...
No Matches
mjson.cxx
Go to the documentation of this file.
1/********************************************************************\
2
3 Name: mjson.cxx
4 Created by: Konstantin Olchanski
5
6 Contents: JSON encoder and decoder
7
8 The JSON parser is written to the specifications at:
9 http://www.json.org/
10 http://www.ietf.org/rfc/rfc4627.txt
11
12\********************************************************************/
13
14#undef NDEBUG // midas required assert() to be always enabled
15
16#include "mjson.h"
17
18#include <stdio.h>
19#include <assert.h>
20#include <math.h>
21#include <string.h>
22#include <limits.h>
23#include <stdlib.h>
24#include <cerrno>
25
26static const char* skip_spaces(const char* s)
27{
28 while (1) {
29 // per RFC 4627, "Insignificant whitespace"
30 switch (*s) {
31 default: return s;
32 case ' ': s++; break;
33 case '\t': s++; break;
34 case '\n': s++; break;
35 case '\r': s++; break;
36 }
37 }
38 // NOT REACHED
39}
40
41static int hexToInt(char c)
42{
43 if (c == 0)
44 return -1;
45 if (c >= '0' && c <= '9')
46 return c-'0';
47 if (c >= 'a' && c <= 'f')
48 return c-'a'+10;
49 if (c >= 'A' && c <= 'F')
50 return c-'A'+10;
51 return -1;
52}
53
54static int xparse_unicode(const char* s, const char** sout)
55{
56 int unicode = 0;
57
58 for (int i=0; i<4; i++) {
59 int v = hexToInt(*s);
60 if (v < 0) {
61 *sout = s;
62 return -1;
63 }
64 unicode = unicode*16 + v;
65 s++;
66 }
67
68 *sout = s;
69 return unicode;
70}
71
72static std::string xoutput_unicode(int unicode, bool* error)
73{
74 // see http://en.wikipedia.org/wiki/UTF-8
75 if (unicode >= 0 && unicode <= 0x7F) { // 7 bits
76 char buf[2];
77 buf[0] = unicode & 0x7F;
78 buf[1] = 0;
79 return buf;
80 }
81
82 // FIXME: does this unicode gibberish work right?
83
84 if (unicode >= 0x80 && unicode <= 0x7FF) { // 11 bits
85 char buf[3];
86 buf[0] = 0x80|0x40|((unicode>>6)&0x1F); // 5 bits
87 buf[1] = 0x80|((unicode>>0)&0x3F); // 6 bits
88 buf[2] = 0;
89 return buf;
90 }
91
92 if (unicode >= 0x800 && unicode <= 0xFFFF) { // 16 bits
93 char buf[4];
94 buf[0] = 0x80|0x40|0x20|((unicode>>12)&0xF); // 4 bits
95 buf[1] = 0x80|((unicode>>6)&0x3F); // 6 bits
96 buf[2] = 0x80|((unicode>>0)&0x3F); // 6 bits
97 buf[3] = 0;
98 return buf;
99 }
100
101 *error = true;
102 return "";
103}
104
105static std::string xparse_string(const char* s, const char** sout, bool *error)
106{
107 //printf("xstring-->%s\n", s);
108
109 std::string v;
110
111 while (1) {
112 if (*s == 0) {
113 // error
114 *sout = s;
115 *error = true;
116 return "";
117 } else if (*s == '\"') {
118 // end of string
119 *sout = s+1;
120 return v;
121 } else if (*s == '\\') {
122 // escape sequence
123 s++;
124 //printf("escape %d (%c)\n", *s, *s);
125 switch (*s) {
126 case 0:
127 // maybe error - unexpected end of string
128 *sout = s;
129 *error = true;
130 return v;
131 default:
132 // error - unknown escape
133 *sout = s;
134 *error = true;
135 return v;
136 case '\"': v += '\"'; s++; break;
137 case '\\': v += '\\'; s++; break;
138 case '/': v += '/'; s++; break;
139 case 'b': v += '\b'; s++; break;
140 case 'f': v += '\f'; s++; break;
141 case 'n': v += '\n'; s++; break;
142 case 'r': v += '\r'; s++; break;
143 case 't': v += '\t'; s++; break;
144 case 'u': {
145 s++;
146 int unicode = xparse_unicode(s, sout);
147 //printf("unicode %d (0x%x), next %c\n", unicode, unicode, **sout);
148 if (unicode < 0) {
149 // error - bad unicode
150 *sout = s;
151 *error = true;
152 return v;
153 }
154 v += xoutput_unicode(unicode, error);
155 if (*error) {
156 // error - bad unicode
157 //*sout = s; // stay pointing at the bad unicode
158 *error = true;
159 return v;
160 }
161 s = *sout;
162 break;
163 }
164 }
165 } else {
166 v += *s;
167 s++;
168 }
169 }
170
171 // NOT REACHED
172}
173
174static MJsonNode* parse_something(const char* sin, const char* s, const char** sout);
175
176static MJsonNode* parse_array(const char* sin, const char* s, const char** sout)
177{
178 //printf("array-->%s\n", s);
180
181 s = skip_spaces(s);
182
183 if (*s == ']') {
184 // empty array
185 *sout = s+1;
186 return n;
187 }
188
189 while (1) {
190 s = skip_spaces(s);
191
192 if (*s == 0) {
193 *sout = s;
194 return MJsonNode::MakeError(n, "unexpected end of string while parsing array", sin, s);
195 }
196
197 MJsonNode *p = parse_something(sin, s, sout);
198 if (p == NULL) {
199 // sout set by parse_something()
200 return MJsonNode::MakeError(n, "cannot parse array element", sin, *sout);
201 }
202 if (p->GetType() == MJSON_ERROR) {
203 // sout set by parse_something()
204 return MJsonNode::MakeError(n, "error parsing array element", sin, *sout);
205 }
206
207 n->AddToArray(p);
208
209 s = skip_spaces(*sout);
210
211 if (*s == ']') {
212 // end of array
213 *sout = s+1;
214 return n;
215 }
216
217 if (*s == ',') {
218 s++;
219 continue;
220 }
221
222 *sout = s;
223 return MJsonNode::MakeError(n, "unexpected char after array element, should be \',\' or \']\'", sin, s);
224 }
225
226 // NOT REACHED
227}
228
229static MJsonNode* parse_object(const char* sin, const char* s, const char** sout)
230{
231 //printf("object-->%s\n", s);
232
234
235 s = skip_spaces(s);
236
237 if (*s == '}') {
238 // empty object
239 *sout = s+1;
240 return n;
241 }
242
243 while (1) {
244 s = skip_spaces(s);
245
246 //printf("xobject-->%s\n", s);
247
248 if (*s == 0) {
249 *sout = s;
250 return MJsonNode::MakeError(n, "unexpected end of string while parsing object", sin, s);
251 } else if (*s != '\"') {
252 *sout = s;
253 return MJsonNode::MakeError(n, "unexpected char while parsing object, should be \"\"\"", sin, s);
254 }
255
256 bool error = false;
257 std::string name = xparse_string(s+1, sout, &error);
258 if (error || name.length() < 1) {
259 // sout set by parse_something()
260 return MJsonNode::MakeError(n, "cannot parse name of object element", sin, *sout);
261 }
262
263 s = skip_spaces(*sout);
264
265 if (*s == 0) {
266 *sout = s;
267 return MJsonNode::MakeError(n, "unexpected end of string after name of object element", sin, s);
268 } else if (*s != ':') {
269 *sout = s;
270 return MJsonNode::MakeError(n, "unexpected char after name of object element, should be \":\"", sin, s);
271 }
272
273 MJsonNode *p = parse_something(sin, s+1, sout);
274 if (p == NULL) {
275 // sout set by parse_something()
276 return MJsonNode::MakeError(n, "cannot parse object element", sin, *sout);
277 }
278 if (p->GetType() == MJSON_ERROR) {
279 // sout set by parse_something()
280 return MJsonNode::MakeError(n, "error parsing object element", sin, *sout);
281 }
282
283 n->AddToObject(name.c_str(), p);
284
285 s = skip_spaces(*sout);
286
287 //printf("xobject-->%s\n", s);
288
289 if (*s == '}') {
290 // end of object
291 *sout = s+1;
292 return n;
293 }
294
295 if (*s == ',') {
296 s++;
297 continue;
298 }
299
300 // error
301 *sout = s;
302 return MJsonNode::MakeError(n, "unexpected char after object element, should be \"}\" or \",\"", sin, s);
303 }
304
305 // NOT REACHED
306}
307
308static MJsonNode* parse_string(const char* sin, const char* s, const char** sout)
309{
310 //printf("string-->%s\n", s);
311
312 bool error = false;
313 std::string v = xparse_string(s, sout, &error);
314
315 if (error)
316 return MJsonNode::MakeError(NULL, "cannot parse string", sin, *sout);
317
318 return MJsonNode::MakeString(v.c_str());
319}
320
321static std::string parse_digits(const char* s, const char** sout)
322{
323 std::string v;
324 v.reserve(32); // allocate space for a longish number
325
326 while (*s) {
327 if (*s < '0')
328 break;
329 if (*s > '9')
330 break;
331
332 v += *s;
333 s++;
334 }
335
336 *sout = s;
337 return v;
338}
339
340bool atoll_with_overflow(const char *str, long long& number)
341{
342 char *end = nullptr;
343 errno = 0;
344
345 number = std::strtoll(str, &end, 10);
346
347 if ((number == LLONG_MAX || number == LLONG_MIN) && errno == ERANGE)
348 // iff stroll sets errno to ERANGE, an over- or underflow occurred
349 return false;
350
351 if (end == str)
352 // if no characters were converted, the input was invalid
353 return false;
354
355 return true;
356}
357
358static void test_atoll_with_overflow_value(const char*s, long long v, bool flag)
359{
360 long long vv;
361
362 bool ff = atoll_with_overflow(s, vv);
363
364 //printf("atoll test: [%s] -> %lld (0x%llx) should be %lld (0x%llx)\n", s, vv, vv, v, v);
365
366 if (vv == v)
367 return;
368
369 if (ff == flag)
370 return;
371
372 printf("atoll test failed: [%s] -> %lld (0x%llx) != %lld (0x%llx)\n", s, vv, vv, v, v);
373 assert(!"mjson self test: my atoll() is broken, bye!");
374 abort();
375 // DOES NOT RETURN
376}
377
379{
380 // test positive values
381 test_atoll_with_overflow_value("0", 0, true);
382 test_atoll_with_overflow_value("1", 1, true);
383 test_atoll_with_overflow_value("12", 12, true);
384 test_atoll_with_overflow_value("1234", 1234, true);
385
386 // check overflow of 64-bit integers
387 test_atoll_with_overflow_value("9223372036854775806", 9223372036854775806, true);
388 test_atoll_with_overflow_value("9223372036854775807", 9223372036854775807, true);
389 test_atoll_with_overflow_value("9223372036854775808", 0, false);
390 test_atoll_with_overflow_value("9223372036854775809", 0, false);
391
392 test_atoll_with_overflow_value("999999999999999999999999999999999999999999999999999999", 0, false);
393
394 // test negative
395 test_atoll_with_overflow_value("-0", 0, true);
396 test_atoll_with_overflow_value("-1", -1, true);
397 test_atoll_with_overflow_value("-12", -12, true);
398 test_atoll_with_overflow_value("-1234", -1234, true);
399
400 test_atoll_with_overflow_value("-9223372036854775807", 0x8000000000000000+1, true);
401 test_atoll_with_overflow_value("-9223372036854775808", 0x8000000000000000, true);
402 test_atoll_with_overflow_value("-9223372036854775809", 0, false);
403 test_atoll_with_overflow_value("-9223372036854775810", 0, false);
404
405 test_atoll_with_overflow_value("-999999999999999999999999999999999999999999999999999999", 0, false);
406
407 //printf("sizeof(int): %zu\n", sizeof(int));
408 //printf("sizeof(long long): %zu\n", sizeof(long long));
409}
410
412{
413public:
418};
419
421
422static MJsonNode* parse_number(const char* sin, const char* s, const char** sout)
423{
424 //printf("number-->%s\n", s);
425
426 // thread sanitizer complains about this. run the test
427 // function on program startup (before main()) from
428 // contructor of static object. K.O.
429 //
430 //static int once = 1;
431 //if (once) {
432 // once = 0;
433 // test_atoll_with_overflow();
434 //}
435
436 // per RFC 4627
437 // A number contains an integer component that
438 // may be prefixed with an optional minus sign, which may be followed by
439 // a fraction part and/or an exponent part.
440 //
441 // number = [ minus ] int [ frac ] [ exp ]
442 // decimal-point = %x2E ; .
443 // digit1-9 = %x31-39 ; 1-9
444 // e = %x65 / %x45 ; e E
445 // exp = e [ minus / plus ] 1*DIGIT
446 // frac = decimal-point 1*DIGIT
447 // int = zero / ( digit1-9 *DIGIT )
448 // minus = %x2D ; -
449 // plus = %x2B ; +
450 // zero = %x30 ; 0
451
452 int sign = 1;
453 std::string sint;
454 std::string sfrac;
455 int expsign = 1;
456 std::string sexp;
457
458 if (*s == '-') {
459 sign = -1;
460 s++;
461 }
462
463 if (*s == '0') {
464 sint += *s;
465 s++;
466 } else {
467 sint = parse_digits(s, sout);
468 s = *sout;
469 }
470
471 if (*s == '.') {
472 s++;
473 sfrac = parse_digits(s, sout);
474 s = *sout;
475 }
476
477 if (*s == 'e' || *s == 'E') {
478 s++;
479
480 if (*s == '-') {
481 expsign = -1;
482 s++;
483 }
484
485 if (*s == '+') {
486 expsign = +1;
487 s++;
488 }
489
490 sexp = parse_digits(s, sout);
491 s = *sout;
492 }
493
494 //printf("number: sign %d, sint [%s], sfrac [%s], expsign %d, sexp [%s]\n", sign, sint.c_str(), sfrac.c_str(), expsign, sexp.c_str());
495
496 // check for floating point
497
498 bool flag;
499 long long e;
500
501 if (expsign < 0 || sfrac.length() > 0) {
502 // definitely floating point number
503 double v1 = atof(sint.c_str());
504 double v2 = 0;
505 double vm = 0.1;
506 const char* p = sfrac.c_str();
507 for ( ; *p != 0; p++, vm/=10.0) {
508 v2 += (*p-'0')*vm;
509 }
510
511 flag = atoll_with_overflow(sexp.c_str(), e);
512
513 if (flag && (e < 0 || e > 400)) {
514 // overflow or exponent will not fit into IEEE754 double precision number
515 // convert to 0 or +/- infinity
516 printf("overflow!\n");
517 if (expsign > 0) {
518 *sout = s;
519 double one = 1;
520 double zero = 0;
521 double inf = one/zero; // IEEE-754 1.0/0.0 is "+infinity", +infinity*(-1) => -infinity
522 return MJsonNode::MakeNumber(sign*inf);
523 } else {
524 *sout = s;
525 return MJsonNode::MakeNumber(sign*0.0);
526 }
527 }
528
529 double ee = 1.0;
530 if (e != 0)
531 ee = pow(10, (double)(expsign*e));
532 double v = sign*(v1+v2)*ee;
533 //printf("v1: %f, v2: %f, e: %d, ee: %g, v: %g\n", v1, v2, e, ee, v);
534
535 *sout = s;
536 return MJsonNode::MakeNumber(v);
537 } else {
538 // no sfrac, expsign is positive, so this is an integer, unless it overflows
539
540 flag = atoll_with_overflow(sexp.c_str(), e); // may overflow
541
542 if (flag && (e < 0 || e > 400)) {
543 // overflow or exponent will not fit into IEEE754 double precision number
544 // convert to +/- infinity
545 //printf("overflow!\n");
546 *sout = s;
547 double one = 1;
548 double zero = 0;
549 double inf = one/zero; // IEEE-754 1.0/0.0 is "+infinity", +infinity*(-1) => -infinity
550 return MJsonNode::MakeNumber(sign*inf);
551 }
552
553 // this is stupid but quicker than calling pow(). Unless they feed us stupid exponents that are not really integers anyway
554 for (int ee=0; ee<e; ee++)
555 sint += "0";
556
557 int overflow = 0;
558 long long v = 0;
559
560 std::string sstr = sign == 1 ? sint : "-" + sint;
561
562 flag = atoll_with_overflow(sstr.c_str(), v);
563 if (!flag)
564 overflow = 1;
565
566 if (overflow) {
567 // overflow, convert to double
568 //printf("integer overflow: sign %d, int: [%s], frac [%s], expsign %d, exp [%s]\n", sign, sint.c_str(), sfrac.c_str(), expsign, sexp.c_str());
569
570 double vv = atof(sint.c_str());
571 *sout = s;
572 return MJsonNode::MakeNumber(sign*vv);
573 }
574
575 *sout = s;
576 return MJsonNode::MakeInt(v);
577 }
578
579 /* code will never be executed
580 *sout = s;
581 return MJsonNode::MakeError(NULL, "cannot parse number", sin, s);
582 */
583}
584
585static MJsonNode* parse_null(const char* sin, const char* s, const char** sout)
586{
587 if (s[0] == 'n' && s[1] == 'u' && s[2] == 'l' && s[3] == 'l') {
588 *sout = s+4;
589 return MJsonNode::MakeNull();
590 }
591
592 *sout = s;
593 return MJsonNode::MakeError(NULL, "cannot parse \"null\"", sin, s);
594}
595
596static MJsonNode* parse_true(const char* sin, const char* s, const char** sout)
597{
598 if (s[0] == 't' && s[1] == 'r' && s[2] == 'u' && s[3] == 'e') {
599 *sout = s+4;
600 return MJsonNode::MakeBool(true);
601 }
602
603 *sout = s;
604 return MJsonNode::MakeError(NULL, "cannot parse \"true\"", sin, s);
605}
606
607static MJsonNode* parse_false(const char* sin, const char* s, const char** sout)
608{
609 if (s[0] == 'f' && s[1] == 'a' && s[2] == 'l' && s[3] == 's' && s[4] == 'e') {
610 *sout = s+5;
611 return MJsonNode::MakeBool(false);
612 }
613
614 *sout = s;
615 return MJsonNode::MakeError(NULL, "cannot parse \"false\"", sin, s);
616}
617
618
619static MJsonNode* parse_something(const char* sin, const char* s, const char** sout)
620{
621 s = skip_spaces(s);
622
623 if (*s == '[') {
624 return parse_array(sin, s+1, sout);
625 } else if (*s == '{') {
626 return parse_object(sin, s+1, sout);
627 } else if (*s == '\"') {
628 return parse_string(sin, s+1, sout);
629 } else if (*s == '-') {
630 return parse_number(sin, s, sout);
631 } else if (*s >= '0' && *s <= '9') {
632 return parse_number(sin, s, sout);
633 } else if (*s == 'n') {
634 return parse_null(sin, s, sout);
635 } else if (*s == 't') {
636 return parse_true(sin, s, sout);
637 } else if (*s == 'f') {
638 return parse_false(sin, s, sout);
639 }
640
641 *sout = s;
642 return MJsonNode::MakeError(NULL, "unexpected char at top level", sin, s);
643}
644
645MJsonNode* MJsonNode::Parse(const char* jsonstring)
646{
647 const char*sout;
648 return parse_something(jsonstring, jsonstring, &sout);
649}
650
652{
653 //printf("MJsonNode dtor!\n");
654 //this->Dump();
655
656 for (unsigned i=0; i<subnodes.size(); i++)
657 delete subnodes[i];
658 subnodes.clear();
659
660 if (arraybuffer_ptr) {
661 free(arraybuffer_ptr);
662 }
663
664 if (arraybuffer_vec) {
665 delete arraybuffer_vec;
666 }
667
669 arraybuffer_ptr = NULL;
670 arraybuffer_vec = NULL;
671
672 // poison deleted nodes
674}
675
676static char toHexChar(int c)
677{
678 assert(c>=0);
679 assert(c<=15);
680 if (c <= 9)
681 return '0' + c;
682 else
683 return 'A' + c - 10;
684}
685
686std::string MJsonNode::Encode(const char* s)
687{
688 std::string v;
689 while (*s) {
690 switch (*s) {
691 case '\"': v += "\\\""; s++; break;
692 case '\\': v += "\\\\"; s++; break;
693 //case '/': v += "\\/"; s++; break;
694 case '\b': v += "\\b"; s++; break;
695 case '\f': v += "\\f"; s++; break;
696 case '\n': v += "\\n"; s++; break;
697 case '\r': v += "\\r"; s++; break;
698 case '\t': v += "\\t"; s++; break;
699 default: {
700 if (iscntrl(*s)) {
701 v += "\\u";
702 v += "0";
703 v += "0";
704 v += toHexChar(((*s)>>4) & 0xF);
705 v += toHexChar(((*s)>>0) & 0xF);
706 s++;
707 break;
708 } else {
709 v += *s; s++;
710 break;
711 }
712 }
713 }
714 }
715 return v;
716}
717
718std::string MJsonNode::EncodeLL(long long value)
719{
720 char buf[256];
721 snprintf(buf, sizeof(buf), "%lld", value);
722 return buf;
723}
724
725std::string MJsonNode::EncodeDouble(double numbervalue)
726{
727 if (isfinite(numbervalue)) {
728 char buf[256];
729 snprintf(buf, sizeof(buf), "%.16e", numbervalue);
730 return buf;
731 } else if (isnan(numbervalue)) {
732 return "\"NaN\"";
733 } else if (isinf(numbervalue)) {
734 if (numbervalue > 0)
735 return "\"Infinity\"";
736 else
737 return "\"-Infinity\"";
738 } else {
739 assert(!"this cannot happen!");
740 }
741 return "";
742}
743
744std::string MJsonNode::Stringify(int flags) const
745{
746 switch (type) {
747 case MJSON_ARRAY: {
748 std::string v;
749 v += "[";
750 for (size_t i=0; i<subnodes.size(); i++) {
751 if (i > 0)
752 v += ",";
753 v += subnodes[i]->Stringify(flags);
754 }
755 v += "]";
756 return v;
757 }
758 case MJSON_OBJECT: {
759 std::string v;
760 v += "{";
761 for (size_t i=0; i<object_names.size(); i++) {
762 if (i > 0)
763 v += ",";
764 v += std::string("\"") + Encode(object_names[i].c_str()) + "\"";
765 v += ":";
766 v += subnodes[i]->Stringify(flags);
767 }
768 v += "}";
769 return v;
770 }
771 case MJSON_STRING: {
772 return std::string("\"") + Encode(string_value.c_str()) + "\"";
773 }
774 case MJSON_INT: {
775 return EncodeLL(ll_value);
776 }
777 case MJSON_NUMBER: {
779 }
780 case MJSON_BOOL:
781 if (ll_value)
782 return "true";
783 else
784 return "false";
785 case MJSON_NULL:
786 return "null";
787 case MJSON_JSON:
788 return string_value;
790 return "arraybuffer";
791 case MJSON_ERROR:
792 return std::string("json parse error: ") + string_value;
793 default:
794 assert(!"should not come here");
795 return ""; // NOT REACHED
796 }
797}
798
799MJsonNode* MJsonNode::MakeError(MJsonNode* errornode, const char* errormessage, const char* sin, const char* serror)
800{
802 if (errornode)
803 n->subnodes.push_back(errornode);
804 n->string_value = errormessage;
805 if (sin && serror) {
806 char msg[256];
807 char sample[32];
808 strncpy(sample, serror, 31);
809 sample[31] = 0;
810 int offset = serror-sin;
811 int lineno = 1;
812 int lineoff = 0;
813 for (const char* s = sin; s != serror; s++) {
814 if (*s == 0)
815 break;
816 if (*s == '\n') {
817 lineno++;
818 lineoff=0;
819 } else {
820 lineoff++;
821 }
822 }
823 snprintf(msg, sizeof(msg), " at char \"%c\" file offset %d, line %d position %d, around text \"%s\"", *serror, offset, lineno, lineoff, sample);
824 n->string_value += msg;
825 }
826 return n;
827}
828
833
838
840{
842 n->string_value = value;
843 return n;
844}
845
847{
848 MJsonNode* n = new MJsonNode(MJSON_INT);
849 n->ll_value = value;
850 return n;
851}
852
854{
856 n->double_value = value;
857 return n;
858}
859
861{
863 if (value)
864 n->ll_value = 1;
865 else
866 n->ll_value = 0;
867 return n;
868}
869
871{
872 return new MJsonNode(MJSON_NULL);
873}
874
876{
878 n->string_value = json;
879 return n;
880}
881
882MJsonNode* MJsonNode::MakeArrayBuffer(char* ptr, size_t size)
883{
885 n->arraybuffer_ptr = ptr;
886 n->arraybuffer_size = size;
887 assert(n->arraybuffer_vec == NULL);
888 return n;
889}
890
891MJsonNode* MJsonNode::MakeArrayBuffer(std::vector<char>* pvec)
892{
893 assert(pvec != NULL);
895 n->arraybuffer_vec = pvec;
896 assert(n->arraybuffer_ptr == NULL);
897 return n;
898}
899
901{
902 if (type == MJSON_ARRAY) {
903 subnodes.push_back(node);
904 return;
905 }
906
907 assert(!"not an array");
908}
909
910void MJsonNode::AddToObject(const char* name, MJsonNode* node) /// add node to an object
911{
912 if (type == MJSON_OBJECT) {
913 object_names.push_back(name);
914 subnodes.push_back(node);
915 //objectvalue[name] = node;
916 return;
917 }
918
919 assert(!"not an object");
920}
921
922int MJsonNode::GetType() const /// get node type: MJSON_xxx
923{
924 return type;
925}
926
928{
929 if (type == MJSON_ARRAY || type == MJSON_NULL)
930 return &subnodes;
931 else
932 return NULL;
933}
934
936{
937 if (type == MJSON_OBJECT || type == MJSON_NULL)
938 return &object_names;
939 else
940 return NULL;
941}
942
944{
945 if (type == MJSON_OBJECT || type == MJSON_NULL)
946 return &subnodes;
947 else
948 return NULL;
949}
950
951const MJsonNode* MJsonNode::FindObjectNode(const char* name) const
952{
953 if (type != MJSON_OBJECT)
954 return NULL;
955 for (unsigned i=0; i<object_names.size(); i++) {
956 if (strcmp(object_names[i].c_str(), name) == 0)
957 return subnodes[i];
958 }
959 return NULL;
960}
961
962void MJsonNode::DeleteObjectNode(const char* name)
963{
964 if (type != MJSON_OBJECT)
965 return;
966 for (unsigned i=0; i<object_names.size(); i++) {
967 if (strcmp(object_names[i].c_str(), name) == 0) {
968 object_names[i] = "";
969 delete subnodes[i];
970 subnodes[i] = NULL;
971
972 object_names.erase(object_names.begin()+i);
973 subnodes.erase(subnodes.begin()+i);
974 return;
975 }
976 }
977}
978
979std::string MJsonNode::GetString() const
980{
981 if (type == MJSON_STRING)
982 return string_value;
983 else
984 return "";
985}
986
987long long MJsonNode::GetInt() const
988{
989 if (type == MJSON_INT)
990 return ll_value;
991 else
992 return 0;
993}
994
995long long MJsonNode::GetLL() const
996{
997 if (type == MJSON_INT)
998 return ll_value;
999 else
1000 return 0;
1001}
1002
1004{
1005 if (type == MJSON_INT) {
1006 return ll_value;
1007 } else if (type == MJSON_NUMBER) {
1008 return double_value;
1009 } else if (type == MJSON_STRING) {
1010 if (string_value == "NaN") {
1011 double zero1 = 0;
1012 double zero2 = 0;
1013 return zero1/zero2; // IEEE-754 0.0/0.0 is a NaN
1014 } else if (string_value == "Infinity") {
1015 double zero = 0;
1016 double one = 1;
1017 return one/zero; // IEEE-754 1.0/0.0 is +infinity
1018 } else if (string_value == "-Infinity") {
1019 double zero = 0;
1020 double one = -1;
1021 return one/zero; // IEEE-754 -1.0/0.0 is -infinity
1022 }
1023 return 0;
1024 } else {
1025 return 0;
1026 }
1027}
1028
1029bool MJsonNode::GetBool() const /// get boolean value, false if not a boolean or value is JSON "null"
1030{
1031 if (type == MJSON_BOOL)
1032 return (ll_value != 0);
1033 else
1034 return false;
1035}
1036
1037void MJsonNode::GetArrayBuffer(const char** pptr, size_t* psize) const
1038{
1039 if (type == MJSON_ARRAYBUFFER) {
1040 if (arraybuffer_vec) {
1041 if (pptr)
1042 *pptr = arraybuffer_vec->data();
1043 if (psize)
1044 *psize = arraybuffer_vec->size();
1045 } else {
1046 if (pptr)
1047 *pptr = arraybuffer_ptr;
1048 if (psize)
1049 *psize = arraybuffer_size;
1050 }
1051 } else {
1052 if (pptr)
1053 *pptr = NULL;
1054 if (psize)
1055 *psize = 0;
1056 }
1057}
1058
1059std::string MJsonNode::GetError() const
1060{
1061 if (type == MJSON_ERROR)
1062 return string_value;
1063 else
1064 return "";
1065}
1066
1067MJsonNode::MJsonNode(int xtype) // default constructor
1068{
1069 type = xtype;
1070}
1071
1072const char* MJsonNode::TypeToString(int type)
1073{
1074 switch (type) {
1075 default: return "UNKNOWN";
1076 case MJSON_ERROR: return "ERROR";
1077 case MJSON_NONE: return "NONE";
1078 case MJSON_ARRAY: return "ARRAY";
1079 case MJSON_OBJECT: return "OBJECT";
1080 case MJSON_STRING: return "STRING";
1081 case MJSON_INT: return "INT";
1082 case MJSON_NUMBER: return "NUMBER";
1083 case MJSON_BOOL: return "BOOL";
1084 case MJSON_NULL: return "NULL";
1085 case MJSON_JSON: return "JSON";
1086 case MJSON_ARRAYBUFFER: return "ARRAYBUFFER";
1087 }
1088}
1089
1090static void pnest(int nest)
1091{
1092 for (int i=0; i<nest; i++)
1093 printf(" ");
1094}
1095
1096void MJsonNode::Dump(int nest) const // debug
1097{
1098 printf("Node type %d (%s)", type, TypeToString(type));
1099 switch (type) {
1100 default: printf("\n"); break;
1101 case MJSON_STRING: printf(", value [%s]\n", string_value.c_str()); break;
1102 case MJSON_INT: printf(", value %lld\n", ll_value); break;
1103 case MJSON_NUMBER: printf(", value %g\n", double_value); break;
1104 case MJSON_BOOL: printf(", value %lld\n", ll_value); break;
1105 case MJSON_NULL: printf(", null\n"); break;
1106 case MJSON_JSON: printf(", json [%s]\n", string_value.c_str()); break;
1107 case MJSON_ARRAYBUFFER:
1108 {
1109 printf(", arraybuffer size %zu (%p)", arraybuffer_size, arraybuffer_ptr);
1110 if (arraybuffer_vec)
1111 printf(", arraybuffer_vec size %zu (%p)\n", arraybuffer_vec->size(), arraybuffer_vec);
1112 printf("\n");
1113 }
1114 break;
1115
1116 case MJSON_ARRAY:
1117 printf("\n");
1118 for (size_t i=0; i<subnodes.size(); i++) {
1119 pnest(nest);
1120 printf("element %zu: ", i);
1121 subnodes[i]->Dump(nest+1);
1122 }
1123 break;
1124 case MJSON_OBJECT:
1125 printf("\n");
1126 for (size_t i=0; i<object_names.size(); i++) {
1127 pnest(nest);
1128 printf("%s: ", object_names[i].c_str());
1129 subnodes[i]->Dump(nest+1);
1130 }
1131 break;
1132 case MJSON_ERROR:
1133 printf(": %s\n", string_value.c_str());
1134 for (size_t i=0; i<subnodes.size(); i++) {
1135 pnest(nest);
1136 printf("errorelement %zu: ", i);
1137 subnodes[i]->Dump(nest+1);
1138 }
1139 break;
1140 }
1141}
1142
1144{
1145 MJsonNode* n = new MJsonNode(*this);
1146 assert(n->object_names.size() == object_names.size());
1147 assert(n->subnodes.size() == subnodes.size());
1148 for (size_t i=0; i<n->subnodes.size(); i++) {
1149 n->subnodes[i] = subnodes[i]->Copy();
1150 }
1151 if (arraybuffer_size > 0) { // normal buffer
1153 n->arraybuffer_ptr = (char*)malloc(arraybuffer_size);
1154 assert(n->arraybuffer_ptr != NULL);
1156 } else if (arraybuffer_ptr != NULL) { // zero-size buffer
1157 assert(arraybuffer_size == 0);
1158 n->arraybuffer_size = 0;
1159 n->arraybuffer_ptr = (char*)malloc(1); // NB: malloc(0) is not portable, may return NULL. K.O. Jun 2025
1160 assert(n->arraybuffer_ptr != NULL);
1161 } else { // NULL buffer
1162 n->arraybuffer_size = 0;
1163 n->arraybuffer_ptr = NULL;
1164 }
1165 if (arraybuffer_vec) {
1166 n->arraybuffer_vec = new std::vector<char>(*arraybuffer_vec); // copy constructor
1167 }
1168 return n;
1169}
1170
1171/* emacs
1172 * Local Variables:
1173 * tab-width: 8
1174 * c-basic-offset: 3
1175 * indent-tabs-mode: nil
1176 * End:
1177 */
std::string GetString() const
find subnode with given name, NULL if not object, NULL is name not found
Definition mjson.cxx:979
static MJsonNode * MakeJSON(const char *json)
Definition mjson.cxx:875
#define MJSON_STRING
Definition mjson.h:23
void Dump(int nest=0) const
return node type as string
Definition mjson.cxx:1096
#define MJSON_ARRAYBUFFER
Definition mjson.h:29
static MJsonNode * MakeArray()
Definition mjson.cxx:829
MJsonNode * Copy() const
dump the subtree to standard output
Definition mjson.cxx:1143
const MJsonNodeVector * GetArray() const
get node type: MJSON_xxx
Definition mjson.cxx:927
long long GetLL() const
get integer value, 0 if not an integer or value is JSON "null"
Definition mjson.cxx:995
MJsonStringVector object_names
Definition mjson.h:40
int GetType() const
delete a node from an object
Definition mjson.cxx:922
void AddToArray(MJsonNode *node)
Definition mjson.cxx:900
static MJsonNode * MakeNumber(double value)
Definition mjson.cxx:853
static std::string Encode(const char *s)
Definition mjson.cxx:686
#define MJSON_ERROR
Definition mjson.h:19
#define MJSON_JSON
Definition mjson.h:28
double double_value
Definition mjson.h:43
std::vector< MJsonNode * > MJsonNodeVector
Definition mjson.h:34
std::vector< std::string > MJsonStringVector
Definition mjson.h:33
long long GetInt() const
get string value, "" if not string or value is JSON "null"
Definition mjson.cxx:987
MJsonNode(int type)
make a copy of the json tree
Definition mjson.cxx:1067
static MJsonNode * MakeObject()
Definition mjson.cxx:834
void AddToObject(const char *name, MJsonNode *node)
add node to an array. the array takes ownership of this node
Definition mjson.cxx:910
double GetDouble() const
get 64-bit long long value, 0 if not an integer or value is JSON "null"
Definition mjson.cxx:1003
int type
Definition mjson.h:38
#define MJSON_BOOL
Definition mjson.h:26
const MJsonNodeVector * GetObjectNodes() const
get array of object names, NULL if not object, empty array if value is JSON "null"
Definition mjson.cxx:943
#define MJSON_OBJECT
Definition mjson.h:22
static MJsonNode * MakeBool(bool value)
Definition mjson.cxx:860
size_t arraybuffer_size
Definition mjson.h:44
std::string Stringify(int flags=0) const
Definition mjson.cxx:744
#define MJSON_ARRAY
Definition mjson.h:21
static MJsonNode * MakeString(const char *value)
Definition mjson.cxx:839
void DeleteObjectNode(const char *name)
add node to an object. the object takes ownership of this node
Definition mjson.cxx:962
#define MJSON_NULL
Definition mjson.h:27
bool GetBool() const
get number or integer value, 0 if not a number or value is JSON "null"
Definition mjson.cxx:1029
static MJsonNode * MakeInt(long long value)
Definition mjson.cxx:846
static std::string EncodeDouble(double v)
Definition mjson.cxx:725
~MJsonNode()
Definition mjson.cxx:651
void GetArrayBuffer(const char **pptr, size_t *psize) const
get boolean value, false if not a boolean or value is JSON "null"
Definition mjson.cxx:1037
std::vector< char > * arraybuffer_vec
Definition mjson.h:46
long long ll_value
Definition mjson.h:42
std::string string_value
Definition mjson.h:41
#define MJSON_NONE
Definition mjson.h:20
const MJsonStringVector * GetObjectNames() const
get array value, NULL if not array, empty array if value is JSON "null"
Definition mjson.cxx:935
char * arraybuffer_ptr
Definition mjson.h:45
static MJsonNode * MakeError(MJsonNode *errornode, const char *errormessage, const char *sin, const char *serror)
the node takes ownership of the buffer
Definition mjson.cxx:799
const MJsonNode * FindObjectNode(const char *name) const
get array of object subnodes, NULL if not object, empty array if value is JSON "null"
Definition mjson.cxx:951
#define MJSON_INT
Definition mjson.h:24
static MJsonNode * MakeArrayBuffer(char *ptr, size_t size)
Definition mjson.cxx:882
static std::string EncodeLL(long long v)
Definition mjson.cxx:718
static MJsonNode * MakeNull()
Definition mjson.cxx:870
std::string GetError() const
Definition mjson.cxx:1059
static MJsonNode * Parse(const char *jsonstring)
Definition mjson.cxx:645
#define MJSON_NUMBER
Definition mjson.h:25
MJsonNodeVector subnodes
Definition mjson.h:39
static const char * TypeToString(int type)
get error message from MJSON_ERROR nodes
Definition mjson.cxx:1072
static char toHexChar(int c)
Definition mjson.cxx:676
static std::string xoutput_unicode(int unicode, bool *error)
Definition mjson.cxx:72
bool atoll_with_overflow(const char *str, long long &number)
Definition mjson.cxx:340
static int xparse_unicode(const char *s, const char **sout)
Definition mjson.cxx:54
static void test_atoll_with_overflow_value(const char *s, long long v, bool flag)
Definition mjson.cxx:358
static MJsonNode * parse_something(const char *sin, const char *s, const char **sout)
Definition mjson.cxx:619
static MJsonNode * parse_string(const char *sin, const char *s, const char **sout)
Definition mjson.cxx:308
static MJsonNode * parse_false(const char *sin, const char *s, const char **sout)
Definition mjson.cxx:607
static MJsonNode * parse_true(const char *sin, const char *s, const char **sout)
Definition mjson.cxx:596
static MJsonNode * parse_object(const char *sin, const char *s, const char **sout)
Definition mjson.cxx:229
static TestAtollWithOverflow runme
Definition mjson.cxx:420
static std::string xparse_string(const char *s, const char **sout, bool *error)
Definition mjson.cxx:105
static int hexToInt(char c)
Definition mjson.cxx:41
static MJsonNode * parse_null(const char *sin, const char *s, const char **sout)
Definition mjson.cxx:585
static void pnest(int nest)
Definition mjson.cxx:1090
static const char * skip_spaces(const char *s)
Definition mjson.cxx:26
static std::string parse_digits(const char *s, const char **sout)
Definition mjson.cxx:321
static MJsonNode * parse_number(const char *sin, const char *s, const char **sout)
Definition mjson.cxx:422
static void test_atoll_with_overflow()
Definition mjson.cxx:378
static MJsonNode * parse_array(const char *sin, const char *s, const char **sout)
Definition mjson.cxx:176
static const int one
Definition xxhash.cxx:179