LCOV - code coverage report
Current view: top level - src/univalue/lib - univalue_read.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 260 270 96.3 %
Date: 2026-06-25 07:23:51 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       22513 : static bool json_isdigit(int ch)
      23             : {
      24       22513 :     return ((ch >= '0') && (ch <= '9'));
      25             : }
      26             : 
      27             : // convert hexadecimal string to unsigned integer
      28          50 : static const char *hatoui(const char *first, const char *last,
      29             :                           unsigned int& out)
      30             : {
      31          50 :     unsigned int result = 0;
      32         250 :     for (; first != last; ++first)
      33             :     {
      34             :         int digit;
      35         200 :         if (json_isdigit(*first))
      36         150 :             digit = *first - '0';
      37             : 
      38          50 :         else if (*first >= 'a' && *first <= 'f')
      39          28 :             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         200 :         result = 16 * result + digit;
      48         200 :     }
      49          50 :     out = result;
      50             : 
      51          50 :     return first;
      52             : }
      53             : 
      54       53609 : enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
      55             :                             const char *raw, const char *end)
      56             : {
      57       53609 :     tokenVal.clear();
      58       53609 :     consumed = 0;
      59             : 
      60       53609 :     const char *rawStart = raw;
      61             : 
      62       88649 :     while (raw < end && (json_isspace(*raw)))          // skip whitespace
      63       35040 :         raw++;
      64             : 
      65       53609 :     if (raw >= end)
      66         217 :         return JTOK_NONE;
      67             : 
      68       53392 :     switch (*raw) {
      69             : 
      70             :     case '{':
      71         279 :         raw++;
      72         279 :         consumed = (raw - rawStart);
      73         279 :         return JTOK_OBJ_OPEN;
      74             :     case '}':
      75         263 :         raw++;
      76         263 :         consumed = (raw - rawStart);
      77         263 :         return JTOK_OBJ_CLOSE;
      78             :     case '[':
      79        5359 :         raw++;
      80        5359 :         consumed = (raw - rawStart);
      81        5359 :         return JTOK_ARR_OPEN;
      82             :     case ']':
      83        4823 :         raw++;
      84        4823 :         consumed = (raw - rawStart);
      85        4823 :         return JTOK_ARR_CLOSE;
      86             : 
      87             :     case ':':
      88        1092 :         raw++;
      89        1092 :         consumed = (raw - rawStart);
      90        1092 :         return JTOK_COLON;
      91             :     case ',':
      92       16770 :         raw++;
      93       16770 :         consumed = (raw - rawStart);
      94       16770 :         return JTOK_COMMA;
      95             : 
      96             :     case 'n':
      97             :     case 't':
      98             :     case 'f':
      99         195 :         if (!strncmp(raw, "null", 4)) {
     100           7 :             raw += 4;
     101           7 :             consumed = (raw - rawStart);
     102           7 :             return JTOK_KW_NULL;
     103         188 :         } else if (!strncmp(raw, "true", 4)) {
     104          94 :             raw += 4;
     105          94 :             consumed = (raw - rawStart);
     106          94 :             return JTOK_KW_TRUE;
     107          94 :         } else if (!strncmp(raw, "false", 5)) {
     108          89 :             raw += 5;
     109          89 :             consumed = (raw - rawStart);
     110          89 :             return JTOK_KW_FALSE;
     111             :         } else
     112           5 :             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        8399 :         std::string numStr;
     127             : 
     128        8399 :         const char *first = raw;
     129             : 
     130        8399 :         const char *firstDigit = first;
     131        8399 :         if (!json_isdigit(*firstDigit))
     132         766 :             firstDigit++;
     133        5061 :         if ((*firstDigit == '0') && json_isdigit(firstDigit[1]))
     134           1 :             return JTOK_ERR;
     135             : 
     136        5060 :         numStr += *raw;                       // copy first char
     137        5060 :         raw++;
     138             : 
     139        5060 :         if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
     140           0 :             return JTOK_ERR;
     141             : 
     142       30433 :         while (raw < end && json_isdigit(*raw)) {  // copy digits
     143       11783 :             numStr += *raw;
     144       11783 :             raw++;
     145             :         }
     146             : 
     147             :         // part 2: frac
     148        5060 :         if (raw < end && *raw == '.') {
     149         227 :             numStr += *raw;                   // copy .
     150         227 :             raw++;
     151             : 
     152         227 :             if (raw >= end || !json_isdigit(*raw))
     153           0 :                 return JTOK_ERR;
     154        1941 :             while (raw < end && json_isdigit(*raw)) { // copy digits
     155         800 :                 numStr += *raw;
     156         800 :                 raw++;
     157             :             }
     158         227 :         }
     159             : 
     160             :         // part 3: exp
     161        5060 :         if (raw < end && (*raw == 'e' || *raw == 'E')) {
     162        1694 :             numStr += *raw;                   // copy E
     163          25 :             raw++;
     164             : 
     165          25 :             if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
     166          18 :                 numStr += *raw;
     167          18 :                 raw++;
     168          18 :             }
     169             : 
     170          25 :             if (raw >= end || !json_isdigit(*raw))
     171           3 :                 return JTOK_ERR;
     172         100 :             while (raw < end && json_isdigit(*raw)) { // copy digits
     173          34 :                 numStr += *raw;
     174          34 :                 raw++;
     175             :             }
     176          22 :         }
     177             : 
     178        3388 :         tokenVal = numStr;
     179        5057 :         consumed = (raw - rawStart);
     180        5057 :         return JTOK_NUMBER;
     181       11737 :         }
     182             : 
     183             :     case '"': {
     184       16194 :         raw++;                                // skip "
     185             : 
     186       16194 :         std::string valStr;
     187       16194 :         JSONUTF8StringFilter writer(valStr);
     188             : 
     189      898806 :         while (true) {
     190      898806 :             if (raw >= end || (unsigned char)*raw < 0x20)
     191           4 :                 return JTOK_ERR;
     192             : 
     193      898802 :             else if (*raw == '\\') {
     194          81 :                 raw++;                        // skip backslash
     195             : 
     196          81 :                 if (raw >= end)
     197           0 :                     return JTOK_ERR;
     198             : 
     199          81 :                 switch (*raw) {
     200           7 :                 case '"':  writer.push_back('\"'); break;
     201           3 :                 case '\\': writer.push_back('\\'); break;
     202           2 :                 case '/':  writer.push_back('/'); break;
     203           3 :                 case 'b':  writer.push_back('\b'); break;
     204           3 :                 case 'f':  writer.push_back('\f'); break;
     205           3 :                 case 'n':  writer.push_back('\n'); break;
     206           3 :                 case 'r':  writer.push_back('\r'); break;
     207           3 :                 case 't':  writer.push_back('\t'); break;
     208             : 
     209             :                 case 'u': {
     210             :                     unsigned int codepoint;
     211         100 :                     if (raw + 1 + 4 >= end ||
     212          50 :                         hatoui(raw + 1, raw + 1 + 4, codepoint) !=
     213          50 :                                raw + 1 + 4)
     214           0 :                         return JTOK_ERR;
     215          50 :                     writer.push_back_u(codepoint);
     216          50 :                     raw += 4;
     217          50 :                     break;
     218             :                     }
     219             :                 default:
     220           4 :                     return JTOK_ERR;
     221             : 
     222             :                 }
     223             : 
     224          77 :                 raw++;                        // skip esc'd char
     225          77 :             }
     226             : 
     227      898721 :             else if (*raw == '"') {
     228       16186 :                 raw++;                        // skip "
     229       16186 :                 break;                        // stop scanning
     230             :             }
     231             : 
     232             :             else {
     233      882535 :                 writer.push_back(static_cast<unsigned char>(*raw));
     234      882535 :                 raw++;
     235             :             }
     236             :         }
     237             : 
     238       16186 :         if (!writer.finalize())
     239           4 :             return JTOK_ERR;
     240       16182 :         tokenVal = valStr;
     241       16182 :         consumed = (raw - rawStart);
     242       16182 :         return JTOK_STRING;
     243       16194 :         }
     244             : 
     245             :     default:
     246          18 :         return JTOK_ERR;
     247             :     }
     248       56947 : }
     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         279 : bool UniValue::read(const char *raw, size_t size)
     263             : {
     264         279 :     clear();
     265             : 
     266         279 :     uint32_t expectMask = 0;
     267         279 :     std::vector<UniValue*> stack;
     268             : 
     269         279 :     std::string tokenVal;
     270             :     unsigned int consumed;
     271         279 :     enum jtokentype tok = JTOK_NONE;
     272         279 :     enum jtokentype last_tok = JTOK_NONE;
     273         279 :     const char* end = raw + size;
     274         279 :     do {
     275       46687 :         last_tok = tok;
     276             : 
     277       46687 :         tok = getJsonToken(tokenVal, consumed, raw, end);
     278       46687 :         if (tok == JTOK_NONE || tok == JTOK_ERR)
     279          36 :             return false;
     280       46651 :         raw += consumed;
     281             : 
     282       75232 :         bool isValueOpen = jsonTokenIsValue(tok) ||
     283       28581 :             tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN;
     284             : 
     285       46651 :         if (expect(VALUE)) {
     286        1088 :             if (!isValueOpen)
     287           2 :                 return false;
     288        1086 :             clearExpect(VALUE);
     289             : 
     290       46649 :         } else if (expect(ARR_VALUE)) {
     291       21272 :             bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE);
     292       21272 :             if (!isArrValue)
     293           2 :                 return false;
     294             : 
     295       21270 :             clearExpect(ARR_VALUE);
     296             : 
     297       45561 :         } else if (expect(OBJ_NAME)) {
     298        1108 :             bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING);
     299        1108 :             if (!isObjName)
     300           4 :                 return false;
     301             : 
     302       24287 :         } else if (expect(COLON)) {
     303        1092 :             if (tok != JTOK_COLON)
     304           2 :                 return false;
     305        1090 :             clearExpect(COLON);
     306             : 
     307       23181 :         } else if (!expect(COLON) && (tok == JTOK_COLON)) {
     308           1 :             return false;
     309             :         }
     310             : 
     311       46640 :         if (expect(NOT_VALUE)) {
     312       22912 :             if (isValueOpen)
     313           2 :                 return false;
     314       22910 :             clearExpect(NOT_VALUE);
     315       22910 :         }
     316             : 
     317       46638 :         switch (tok) {
     318             : 
     319             :         case JTOK_OBJ_OPEN:
     320             :         case JTOK_ARR_OPEN: {
     321        5634 :             VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
     322        5634 :             if (!stack.size()) {
     323         224 :                 if (utyp == VOBJ)
     324          81 :                     setObject();
     325             :                 else
     326         143 :                     setArray();
     327         224 :                 stack.push_back(this);
     328         224 :             } else {
     329        5410 :                 UniValue tmpVal(utyp);
     330        5410 :                 UniValue *top = stack.back();
     331        5410 :                 top->values.push_back(tmpVal);
     332             : 
     333        5410 :                 UniValue *newTop = &(top->values.back());
     334        5410 :                 stack.push_back(newTop);
     335        5410 :             }
     336             : 
     337        5634 :             if (stack.size() > MAX_JSON_DEPTH)
     338           1 :                 return false;
     339             : 
     340        5633 :             if (utyp == VOBJ)
     341         277 :                 setExpect(OBJ_NAME);
     342             :             else
     343        5356 :                 setExpect(ARR_VALUE);
     344        5633 :             break;
     345             :             }
     346             : 
     347             :         case JTOK_OBJ_CLOSE:
     348             :         case JTOK_ARR_CLOSE: {
     349        5083 :             if (!stack.size() || (last_tok == JTOK_COMMA))
     350           2 :                 return false;
     351             : 
     352        5081 :             VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
     353        5081 :             UniValue *top = stack.back();
     354        5081 :             if (utyp != top->getType())
     355           1 :                 return false;
     356             : 
     357        5080 :             stack.pop_back();
     358        5080 :             clearExpect(OBJ_NAME);
     359        5080 :             setExpect(NOT_VALUE);
     360        5080 :             break;
     361             :             }
     362             : 
     363             :         case JTOK_COLON: {
     364        1090 :             if (!stack.size())
     365           0 :                 return false;
     366             : 
     367        1090 :             UniValue *top = stack.back();
     368        1090 :             if (top->getType() != VOBJ)
     369           0 :                 return false;
     370             : 
     371        1090 :             setExpect(VALUE);
     372        1090 :             break;
     373             :             }
     374             : 
     375             :         case JTOK_COMMA: {
     376       33532 :             if (!stack.size() ||
     377       16766 :                 (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
     378           0 :                 return false;
     379             : 
     380       16766 :             UniValue *top = stack.back();
     381       16766 :             if (top->getType() == VOBJ)
     382         834 :                 setExpect(OBJ_NAME);
     383             :             else
     384       15932 :                 setExpect(ARR_VALUE);
     385       16766 :             break;
     386             :             }
     387             : 
     388             :         case JTOK_KW_NULL:
     389             :         case JTOK_KW_TRUE:
     390             :         case JTOK_KW_FALSE: {
     391         187 :             UniValue tmpVal;
     392         187 :             switch (tok) {
     393             :             case JTOK_KW_NULL:
     394             :                 // do nothing more
     395           6 :                 break;
     396             :             case JTOK_KW_TRUE:
     397          92 :                 tmpVal.setBool(true);
     398          92 :                 break;
     399             :             case JTOK_KW_FALSE:
     400          89 :                 tmpVal.setBool(false);
     401          89 :                 break;
     402           0 :             default: /* impossible */ break;
     403             :             }
     404             : 
     405         187 :             if (!stack.size()) {
     406          16 :                 *this = tmpVal;
     407          16 :                 break;
     408             :             }
     409             : 
     410         171 :             UniValue *top = stack.back();
     411         171 :             top->values.push_back(tmpVal);
     412             : 
     413         171 :             setExpect(NOT_VALUE);
     414         171 :             break;
     415         187 :             }
     416             : 
     417             :         case JTOK_NUMBER: {
     418        1698 :             UniValue tmpVal(VNUM, tokenVal);
     419        1698 :             if (!stack.size()) {
     420          26 :                 *this = tmpVal;
     421          26 :                 break;
     422             :             }
     423             : 
     424        1672 :             UniValue *top = stack.back();
     425        1672 :             top->values.push_back(tmpVal);
     426             : 
     427        1672 :             setExpect(NOT_VALUE);
     428        1672 :             break;
     429        1698 :             }
     430             : 
     431             :         case JTOK_STRING: {
     432       16180 :             if (expect(OBJ_NAME)) {
     433        1092 :                 UniValue *top = stack.back();
     434        1092 :                 top->keys.push_back(tokenVal);
     435        1092 :                 clearExpect(OBJ_NAME);
     436        1092 :                 setExpect(COLON);
     437        1092 :             } else {
     438       15088 :                 UniValue tmpVal(VSTR, tokenVal);
     439       15088 :                 if (!stack.size()) {
     440           2 :                     *this = tmpVal;
     441           2 :                     break;
     442             :                 }
     443       15086 :                 UniValue *top = stack.back();
     444       15086 :                 top->values.push_back(tmpVal);
     445       15088 :             }
     446             : 
     447       16178 :             setExpect(NOT_VALUE);
     448       16178 :             break;
     449             :             }
     450             : 
     451             :         default:
     452           0 :             return false;
     453             :         }
     454       46634 :     } while (!stack.empty ());
     455             : 
     456             :     /* Check that nothing follows the initial construct (parsed above).  */
     457         226 :     tok = getJsonToken(tokenVal, consumed, raw, end);
     458         226 :     if (tok != JTOK_NONE)
     459          13 :         return false;
     460             : 
     461         213 :     return true;
     462         279 : }
     463             : 

Generated by: LCOV version 1.16