LCOV - code coverage report
Current view: top level - src/univalue/lib - univalue_read.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 260 270 96.3 %
Date: 2026-06-25 07:23:43 Functions: 4 4 100.0 %

          Line data    Source code
       1             : // Copyright 2014 BitPay Inc.
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or https://opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <univalue.h>
       6             : #include <univalue_utffilter.h>
       7             : 
       8             : #include <cstdio>
       9             : #include <cstdint>
      10             : #include <cstring>
      11             : #include <string>
      12             : #include <vector>
      13             : 
      14             : /*
      15             :  * According to stackexchange, the original json test suite wanted
      16             :  * to limit depth to 22.  Widely-deployed PHP bails at depth 512,
      17             :  * so we will follow PHP's lead, which should be more than sufficient
      18             :  * (further stackexchange comments indicate depth > 32 rarely occurs).
      19             :  */
      20             : static constexpr size_t MAX_JSON_DEPTH = 512;
      21             : 
      22    25113125 : static bool json_isdigit(int ch)
      23             : {
      24    25113125 :     return ((ch >= '0') && (ch <= '9'));
      25             : }
      26             : 
      27             : // convert hexadecimal string to unsigned integer
      28        1163 : static const char *hatoui(const char *first, const char *last,
      29             :                           unsigned int& out)
      30             : {
      31        1163 :     unsigned int result = 0;
      32        5815 :     for (; first != last; ++first)
      33             :     {
      34             :         int digit;
      35        4652 :         if (json_isdigit(*first))
      36        2792 :             digit = *first - '0';
      37             : 
      38        1860 :         else if (*first >= 'a' && *first <= 'f')
      39        1838 :             digit = *first - 'a' + 10;
      40             : 
      41          22 :         else if (*first >= 'A' && *first <= 'F')
      42          22 :             digit = *first - 'A' + 10;
      43             : 
      44             :         else
      45           0 :             break;
      46             : 
      47        4652 :         result = 16 * result + digit;
      48        4652 :     }
      49        1163 :     out = result;
      50             : 
      51        1163 :     return first;
      52             : }
      53             : 
      54    21046235 : enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
      55             :                             const char *raw, const char *end)
      56             : {
      57    21046235 :     tokenVal.clear();
      58    21046235 :     consumed = 0;
      59             : 
      60    21046235 :     const char *rawStart = raw;
      61             : 
      62    26118056 :     while (raw < end && (json_isspace(*raw)))          // skip whitespace
      63     5071821 :         raw++;
      64             : 
      65    21046235 :     if (raw >= end)
      66      512825 :         return JTOK_NONE;
      67             : 
      68    20533410 :     switch (*raw) {
      69             : 
      70             :     case '{':
      71      571615 :         raw++;
      72      571615 :         consumed = (raw - rawStart);
      73      571615 :         return JTOK_OBJ_OPEN;
      74             :     case '}':
      75      571599 :         raw++;
      76      571599 :         consumed = (raw - rawStart);
      77      571599 :         return JTOK_OBJ_CLOSE;
      78             :     case '[':
      79      485260 :         raw++;
      80      485260 :         consumed = (raw - rawStart);
      81      485260 :         return JTOK_ARR_OPEN;
      82             :     case ']':
      83      484724 :         raw++;
      84      484724 :         consumed = (raw - rawStart);
      85      484724 :         return JTOK_ARR_CLOSE;
      86             : 
      87             :     case ':':
      88     2221569 :         raw++;
      89     2221569 :         consumed = (raw - rawStart);
      90     2221569 :         return JTOK_COLON;
      91             :     case ',':
      92     2841076 :         raw++;
      93     2841076 :         consumed = (raw - rawStart);
      94     2841076 :         return JTOK_COMMA;
      95             : 
      96             :     case 'n':
      97             :     case 't':
      98             :     case 'f':
      99       40237 :         if (!strncmp(raw, "null", 4)) {
     100        8192 :             raw += 4;
     101        8192 :             consumed = (raw - rawStart);
     102        8192 :             return JTOK_KW_NULL;
     103       32045 :         } else if (!strncmp(raw, "true", 4)) {
     104       20727 :             raw += 4;
     105       20727 :             consumed = (raw - rawStart);
     106       20727 :             return JTOK_KW_TRUE;
     107       11318 :         } else if (!strncmp(raw, "false", 5)) {
     108       11305 :             raw += 5;
     109       11305 :             consumed = (raw - rawStart);
     110       11305 :             return JTOK_KW_FALSE;
     111             :         } else
     112          13 :             return JTOK_ERR;
     113             : 
     114             :     case '-':
     115             :     case '0':
     116             :     case '1':
     117             :     case '2':
     118             :     case '3':
     119             :     case '4':
     120             :     case '5':
     121             :     case '6':
     122             :     case '7':
     123             :     case '8':
     124             :     case '9': {
     125             :         // part 1: int
     126     8775372 :         std::string numStr;
     127             : 
     128     8775372 :         const char *first = raw;
     129             : 
     130     8775372 :         const char *firstDigit = first;
     131     8775372 :         if (!json_isdigit(*firstDigit))
     132      128000 :             firstDigit++;
     133     7203112 :         if ((*firstDigit == '0') && json_isdigit(firstDigit[1]))
     134           1 :             return JTOK_ERR;
     135             : 
     136     7203111 :         numStr += *raw;                       // copy first char
     137     7203111 :         raw++;
     138             : 
     139     7203111 :         if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
     140           0 :             return JTOK_ERR;
     141             : 
     142    37592206 :         while (raw < end && json_isdigit(*raw)) {  // copy digits
     143    14770461 :             numStr += *raw;
     144    14770461 :             raw++;
     145             :         }
     146             : 
     147             :         // part 2: frac
     148     7203111 :         if (raw < end && *raw == '.') {
     149       72132 :             numStr += *raw;                   // copy .
     150       72132 :             raw++;
     151             : 
     152       72132 :             if (raw >= end || !json_isdigit(*raw))
     153           0 :                 return JTOK_ERR;
     154     1189615 :             while (raw < end && json_isdigit(*raw)) { // copy digits
     155      533128 :                 numStr += *raw;
     156      533128 :                 raw++;
     157             :             }
     158       72132 :         }
     159             : 
     160             :         // part 3: exp
     161     7203111 :         if (raw < end && (*raw == 'e' || *raw == 'E')) {
     162      827268 :             numStr += *raw;                   // copy E
     163       41138 :             raw++;
     164             : 
     165       41138 :             if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
     166       41131 :                 numStr += *raw;
     167       41131 :                 raw++;
     168       41131 :             }
     169             : 
     170       41138 :             if (raw >= end || !json_isdigit(*raw))
     171           3 :                 return JTOK_ERR;
     172      220332 :             while (raw < end && json_isdigit(*raw)) { // copy digits
     173       82260 :                 numStr += *raw;
     174       82260 :                 raw++;
     175             :             }
     176       41135 :         }
     177             : 
     178     6416978 :         tokenVal = numStr;
     179     7203108 :         consumed = (raw - rawStart);
     180     7203108 :         return JTOK_NUMBER;
     181    10347632 :         }
     182             : 
     183             :     case '"': {
     184     4541938 :         raw++;                                // skip "
     185             : 
     186     4541938 :         std::string valStr;
     187     4541938 :         JSONUTF8StringFilter writer(valStr);
     188             : 
     189   308529659 :         while (true) {
     190   308529659 :             if (raw >= end || (unsigned char)*raw < 0x20)
     191           4 :                 return JTOK_ERR;
     192             : 
     193   308529655 :             else if (*raw == '\\') {
     194        1238 :                 raw++;                        // skip backslash
     195             : 
     196        1238 :                 if (raw >= end)
     197           0 :                     return JTOK_ERR;
     198             : 
     199        1238 :                 switch (*raw) {
     200          27 :                 case '"':  writer.push_back('\"'); break;
     201           7 :                 case '\\': writer.push_back('\\'); break;
     202           2 :                 case '/':  writer.push_back('/'); break;
     203           7 :                 case 'b':  writer.push_back('\b'); break;
     204           7 :                 case 'f':  writer.push_back('\f'); break;
     205           7 :                 case 'n':  writer.push_back('\n'); break;
     206           7 :                 case 'r':  writer.push_back('\r'); break;
     207           7 :                 case 't':  writer.push_back('\t'); break;
     208             : 
     209             :                 case 'u': {
     210             :                     unsigned int codepoint;
     211        2326 :                     if (raw + 1 + 4 >= end ||
     212        1163 :                         hatoui(raw + 1, raw + 1 + 4, codepoint) !=
     213        1163 :                                raw + 1 + 4)
     214           0 :                         return JTOK_ERR;
     215        1163 :                     writer.push_back_u(codepoint);
     216        1163 :                     raw += 4;
     217        1163 :                     break;
     218             :                     }
     219             :                 default:
     220           4 :                     return JTOK_ERR;
     221             : 
     222             :                 }
     223             : 
     224        1234 :                 raw++;                        // skip esc'd char
     225        1234 :             }
     226             : 
     227   308528417 :             else if (*raw == '"') {
     228     4541930 :                 raw++;                        // skip "
     229     4541930 :                 break;                        // stop scanning
     230             :             }
     231             : 
     232             :             else {
     233   303986487 :                 writer.push_back(static_cast<unsigned char>(*raw));
     234   303986487 :                 raw++;
     235             :             }
     236             :         }
     237             : 
     238     4541930 :         if (!writer.finalize())
     239           4 :             return JTOK_ERR;
     240     4541926 :         tokenVal = valStr;
     241     4541926 :         consumed = (raw - rawStart);
     242     4541926 :         return JTOK_STRING;
     243     4541938 :         }
     244             : 
     245             :     default:
     246          20 :         return JTOK_ERR;
     247             :     }
     248    22618495 : }
     249             : 
     250             : enum expect_bits : unsigned {
     251             :     EXP_OBJ_NAME = (1U << 0),
     252             :     EXP_COLON = (1U << 1),
     253             :     EXP_ARR_VALUE = (1U << 2),
     254             :     EXP_VALUE = (1U << 3),
     255             :     EXP_NOT_VALUE = (1U << 4),
     256             : };
     257             : 
     258             : #define expect(bit) (expectMask & (EXP_##bit))
     259             : #define setExpect(bit) (expectMask |= EXP_##bit)
     260             : #define clearExpect(bit) (expectMask &= ~EXP_##bit)
     261             : 
     262      512897 : bool UniValue::read(const char *raw, size_t size)
     263             : {
     264      512897 :     clear();
     265             : 
     266      512897 :     uint32_t expectMask = 0;
     267      512897 :     std::vector<UniValue*> stack;
     268             : 
     269      512897 :     std::string tokenVal;
     270             :     unsigned int consumed;
     271      512897 :     enum jtokentype tok = JTOK_NONE;
     272      512897 :     enum jtokentype last_tok = JTOK_NONE;
     273      512897 :     const char* end = raw + size;
     274      512897 :     do {
     275    12559004 :         last_tok = tok;
     276             : 
     277    12559004 :         tok = getJsonToken(tokenVal, consumed, raw, end);
     278    12559002 :         if (tok == JTOK_NONE || tok == JTOK_ERR)
     279          46 :             return false;
     280    12558956 :         raw += consumed;
     281             : 
     282    19734794 :         bool isValueOpen = jsonTokenIsValue(tok) ||
     283     7175838 :             tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN;
     284             : 
     285    12558958 :         if (expect(VALUE)) {
     286     2221565 :             if (!isValueOpen)
     287           2 :                 return false;
     288     2221563 :             clearExpect(VALUE);
     289             : 
     290    12558956 :         } else if (expect(ARR_VALUE)) {
     291     1675444 :             bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE);
     292     1675444 :             if (!isArrValue)
     293           2 :                 return false;
     294             : 
     295     1675442 :             clearExpect(ARR_VALUE);
     296             : 
     297    10337391 :         } else if (expect(OBJ_NAME)) {
     298     2222479 :             bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING);
     299     2222479 :             if (!isObjName)
     300           4 :                 return false;
     301             : 
     302     8661945 :         } else if (expect(COLON)) {
     303     2221569 :             if (tok != JTOK_COLON)
     304           2 :                 return false;
     305     2221567 :             clearExpect(COLON);
     306             : 
     307     6439468 :         } else if (!expect(COLON) && (tok == JTOK_COLON)) {
     308           1 :             return false;
     309             :         }
     310             : 
     311    12558947 :         if (expect(NOT_VALUE)) {
     312     5926591 :             if (isValueOpen)
     313           2 :                 return false;
     314     5926589 :             clearExpect(NOT_VALUE);
     315     5926589 :         }
     316             : 
     317    12558945 :         switch (tok) {
     318             : 
     319             :         case JTOK_OBJ_OPEN:
     320             :         case JTOK_ARR_OPEN: {
     321     1056871 :             VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
     322     1056871 :             if (!stack.size()) {
     323      512210 :                 if (utyp == VOBJ)
     324      511905 :                     setObject();
     325             :                 else
     326         305 :                     setArray();
     327      512210 :                 stack.push_back(this);
     328      512210 :             } else {
     329      544661 :                 UniValue tmpVal(utyp);
     330      544661 :                 UniValue *top = stack.back();
     331      544661 :                 top->values.push_back(tmpVal);
     332             : 
     333      544661 :                 UniValue *newTop = &(top->values.back());
     334      544661 :                 stack.push_back(newTop);
     335      544661 :             }
     336             : 
     337     1056871 :             if (stack.size() > MAX_JSON_DEPTH)
     338           1 :                 return false;
     339             : 
     340     1056870 :             if (utyp == VOBJ)
     341      571613 :                 setExpect(OBJ_NAME);
     342             :             else
     343      485257 :                 setExpect(ARR_VALUE);
     344     1056870 :             break;
     345             :             }
     346             : 
     347             :         case JTOK_OBJ_CLOSE:
     348             :         case JTOK_ARR_CLOSE: {
     349     1056320 :             if (!stack.size() || (last_tok == JTOK_COMMA))
     350           2 :                 return false;
     351             : 
     352     1056318 :             VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
     353     1056318 :             UniValue *top = stack.back();
     354     1056318 :             if (utyp != top->getType())
     355           1 :                 return false;
     356             : 
     357     1056317 :             stack.pop_back();
     358     1056317 :             clearExpect(OBJ_NAME);
     359     1056317 :             setExpect(NOT_VALUE);
     360     1056317 :             break;
     361             :             }
     362             : 
     363             :         case JTOK_COLON: {
     364     2221567 :             if (!stack.size())
     365           0 :                 return false;
     366             : 
     367     2221567 :             UniValue *top = stack.back();
     368     2221567 :             if (top->getType() != VOBJ)
     369           0 :                 return false;
     370             : 
     371     2221567 :             setExpect(VALUE);
     372     2221567 :             break;
     373             :             }
     374             : 
     375             :         case JTOK_COMMA: {
     376     5682144 :             if (!stack.size() ||
     377     2841072 :                 (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
     378           0 :                 return false;
     379             : 
     380     2841072 :             UniValue *top = stack.back();
     381     2841072 :             if (top->getType() == VOBJ)
     382     1650869 :                 setExpect(OBJ_NAME);
     383             :             else
     384     1190203 :                 setExpect(ARR_VALUE);
     385     2841072 :             break;
     386             :             }
     387             : 
     388             :         case JTOK_KW_NULL:
     389             :         case JTOK_KW_TRUE:
     390             :         case JTOK_KW_FALSE: {
     391       40221 :             UniValue tmpVal;
     392       40221 :             switch (tok) {
     393             :             case JTOK_KW_NULL:
     394             :                 // do nothing more
     395        8191 :                 break;
     396             :             case JTOK_KW_TRUE:
     397       20725 :                 tmpVal.setBool(true);
     398       20725 :                 break;
     399             :             case JTOK_KW_FALSE:
     400       11305 :                 tmpVal.setBool(false);
     401       11305 :                 break;
     402           0 :             default: /* impossible */ break;
     403             :             }
     404             : 
     405       40221 :             if (!stack.size()) {
     406         492 :                 *this = tmpVal;
     407         492 :                 break;
     408             :             }
     409             : 
     410       39729 :             UniValue *top = stack.back();
     411       39729 :             top->values.push_back(tmpVal);
     412             : 
     413       39729 :             setExpect(NOT_VALUE);
     414       39729 :             break;
     415       40221 :             }
     416             : 
     417             :         case JTOK_NUMBER: {
     418      800970 :             UniValue tmpVal(VNUM, tokenVal);
     419      800970 :             if (!stack.size()) {
     420         170 :                 *this = tmpVal;
     421         170 :                 break;
     422             :             }
     423             : 
     424      800800 :             UniValue *top = stack.back();
     425      800800 :             top->values.push_back(tmpVal);
     426             : 
     427      800800 :             setExpect(NOT_VALUE);
     428      800800 :             break;
     429      800970 :             }
     430             : 
     431             :         case JTOK_STRING: {
     432     4541924 :             if (expect(OBJ_NAME)) {
     433     2221569 :                 UniValue *top = stack.back();
     434     2221569 :                 top->keys.push_back(tokenVal);
     435     2221569 :                 clearExpect(OBJ_NAME);
     436     2221569 :                 setExpect(COLON);
     437     2221569 :             } else {
     438     2320355 :                 UniValue tmpVal(VSTR, tokenVal);
     439     2320355 :                 if (!stack.size()) {
     440           4 :                     *this = tmpVal;
     441           4 :                     break;
     442             :                 }
     443     2320351 :                 UniValue *top = stack.back();
     444     2320351 :                 top->values.push_back(tmpVal);
     445     2320355 :             }
     446             : 
     447     4541920 :             setExpect(NOT_VALUE);
     448     4541920 :             break;
     449             :             }
     450             : 
     451             :         default:
     452           0 :             return false;
     453             :         }
     454    12558941 :     } while (!stack.empty ());
     455             : 
     456             :     /* Check that nothing follows the initial construct (parsed above).  */
     457      512834 :     tok = getJsonToken(tokenVal, consumed, raw, end);
     458      512834 :     if (tok != JTOK_NONE)
     459          13 :         return false;
     460             : 
     461      512821 :     return true;
     462      512901 : }
     463             : 

Generated by: LCOV version 1.16