LCOV - code coverage report
Current view: top level - mjson - mjson.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 5.7 % 651 37
Test Date: 2025-11-11 10:26:08 Functions: 7.3 % 55 4

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

Generated by: LCOV version 2.0-1