Coverage Report

Created: 2026-07-14 18:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/bitcoin/src/univalue/lib/univalue_read.cpp
Line
Count
Source
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 <cstdint>
9
#include <cstring>
10
#include <string>
11
#include <string_view>
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
static bool json_isdigit(int ch)
23
2.11M
{
24
2.11M
    return ((ch >= '0') && (ch <= '9'));
  Branch (24:13): [True: 1.77M, False: 342k]
  Branch (24:28): [True: 1.68M, False: 93.7k]
25
2.11M
}
26
27
// convert hexadecimal string to unsigned integer
28
static const char *hatoui(const char *first, const char *last,
29
                          unsigned int& out)
30
0
{
31
0
    unsigned int result = 0;
32
0
    for (; first != last; ++first)
  Branch (32:12): [True: 0, False: 0]
33
0
    {
34
0
        int digit;
35
0
        if (json_isdigit(*first))
  Branch (35:13): [True: 0, False: 0]
36
0
            digit = *first - '0';
37
38
0
        else if (*first >= 'a' && *first <= 'f')
  Branch (38:18): [True: 0, False: 0]
  Branch (38:35): [True: 0, False: 0]
39
0
            digit = *first - 'a' + 10;
40
41
0
        else if (*first >= 'A' && *first <= 'F')
  Branch (41:18): [True: 0, False: 0]
  Branch (41:35): [True: 0, False: 0]
42
0
            digit = *first - 'A' + 10;
43
44
0
        else
45
0
            break;
46
47
0
        result = 16 * result + digit;
48
0
    }
49
0
    out = result;
50
51
0
    return first;
52
0
}
53
54
enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
55
                            const char *raw, const char *end)
56
6.64M
{
57
6.64M
    tokenVal.clear();
58
6.64M
    consumed = 0;
59
60
6.64M
    const char *rawStart = raw;
61
62
6.64M
    while (raw < end && (json_isspace(*raw)))          // skip whitespace
  Branch (62:12): [True: 6.30M, False: 341k]
  Branch (62:25): [True: 0, False: 6.30M]
63
0
        raw++;
64
65
6.64M
    if (raw >= end)
  Branch (65:9): [True: 337k, False: 6.31M]
66
337k
        return JTOK_NONE;
67
68
6.31M
    switch (*raw) {
69
70
337k
    case '{':
  Branch (70:5): [True: 337k, False: 5.97M]
71
337k
        raw++;
72
337k
        consumed = (raw - rawStart);
73
337k
        return JTOK_OBJ_OPEN;
74
336k
    case '}':
  Branch (74:5): [True: 336k, False: 5.97M]
75
336k
        raw++;
76
336k
        consumed = (raw - rawStart);
77
336k
        return JTOK_OBJ_CLOSE;
78
336k
    case '[':
  Branch (78:5): [True: 336k, False: 5.97M]
79
336k
        raw++;
80
336k
        consumed = (raw - rawStart);
81
336k
        return JTOK_ARR_OPEN;
82
337k
    case ']':
  Branch (82:5): [True: 337k, False: 5.97M]
83
337k
        raw++;
84
337k
        consumed = (raw - rawStart);
85
337k
        return JTOK_ARR_CLOSE;
86
87
1.34M
    case ':':
  Branch (87:5): [True: 1.34M, False: 4.96M]
88
1.34M
        raw++;
89
1.34M
        consumed = (raw - rawStart);
90
1.34M
        return JTOK_COLON;
91
1.01M
    case ',':
  Branch (91:5): [True: 1.01M, False: 5.29M]
92
1.01M
        raw++;
93
1.01M
        consumed = (raw - rawStart);
94
1.01M
        return JTOK_COMMA;
95
96
0
    case 'n':
  Branch (96:5): [True: 0, False: 6.31M]
97
0
    case 't':
  Branch (97:5): [True: 0, False: 6.31M]
98
3.08k
    case 'f':
  Branch (98:5): [True: 3.08k, False: 6.30M]
99
3.08k
        if (!strncmp(raw, "null", 4)) {
  Branch (99:13): [True: 0, False: 3.08k]
100
0
            raw += 4;
101
0
            consumed = (raw - rawStart);
102
0
            return JTOK_KW_NULL;
103
3.08k
        } else if (!strncmp(raw, "true", 4)) {
  Branch (103:20): [True: 0, False: 3.08k]
104
0
            raw += 4;
105
0
            consumed = (raw - rawStart);
106
0
            return JTOK_KW_TRUE;
107
3.08k
        } else if (!strncmp(raw, "false", 5)) {
  Branch (107:20): [True: 3.08k, False: 0]
108
3.08k
            raw += 5;
109
3.08k
            consumed = (raw - rawStart);
110
3.08k
            return JTOK_KW_FALSE;
111
3.08k
        } else
112
0
            return JTOK_ERR;
113
114
458
    case '-':
  Branch (114:5): [True: 458, False: 6.31M]
115
1.43k
    case '0':
  Branch (115:5): [True: 972, False: 6.31M]
116
55.5k
    case '1':
  Branch (116:5): [True: 54.0k, False: 6.25M]
117
59.2k
    case '2':
  Branch (117:5): [True: 3.75k, False: 6.30M]
118
92.4k
    case '3':
  Branch (118:5): [True: 33.1k, False: 6.27M]
119
429k
    case '4':
  Branch (119:5): [True: 336k, False: 5.97M]
120
430k
    case '5':
  Branch (120:5): [True: 1.01k, False: 6.31M]
121
431k
    case '6':
  Branch (121:5): [True: 1.33k, False: 6.31M]
122
431k
    case '7':
  Branch (122:5): [True: 43, False: 6.31M]
123
433k
    case '8':
  Branch (123:5): [True: 1.44k, False: 6.31M]
124
433k
    case '9': {
  Branch (124:5): [True: 82, False: 6.31M]
125
        // part 1: int
126
433k
        std::string numStr;
127
128
433k
        const char *first = raw;
129
130
433k
        const char *firstDigit = first;
131
433k
        if (!json_isdigit(*firstDigit))
  Branch (131:13): [True: 458, False: 432k]
132
458
            firstDigit++;
133
433k
        if ((*firstDigit == '0') && json_isdigit(firstDigit[1]))
  Branch (133:13): [True: 972, False: 432k]
  Branch (133:37): [True: 0, False: 972]
134
0
            return JTOK_ERR;
135
136
433k
        numStr += *raw;                       // copy first char
137
433k
        raw++;
138
139
433k
        if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
  Branch (139:13): [True: 458, False: 432k]
  Branch (139:32): [True: 458, False: 0]
  Branch (139:47): [True: 0, False: 458]
140
0
            return JTOK_ERR;
141
142
1.68M
        while (raw < end && json_isdigit(*raw)) {  // copy digits
  Branch (142:16): [True: 1.68M, False: 2.04k]
  Branch (142:29): [True: 1.25M, False: 431k]
143
1.25M
            numStr += *raw;
144
1.25M
            raw++;
145
1.25M
        }
146
147
        // part 2: frac
148
433k
        if (raw < end && *raw == '.') {
  Branch (148:13): [True: 430k, False: 2.85k]
  Branch (148:26): [True: 54, False: 430k]
149
54
            numStr += *raw;                   // copy .
150
54
            raw++;
151
152
54
            if (raw >= end || !json_isdigit(*raw))
  Branch (152:17): [True: 0, False: 54]
  Branch (152:31): [True: 0, False: 54]
153
0
                return JTOK_ERR;
154
162
            while (raw < end && json_isdigit(*raw)) { // copy digits
  Branch (154:20): [True: 108, False: 54]
  Branch (154:33): [True: 108, False: 0]
155
108
                numStr += *raw;
156
108
                raw++;
157
108
            }
158
54
        }
159
160
        // part 3: exp
161
433k
        if (raw < end && (*raw == 'e' || *raw == 'E')) {
  Branch (161:13): [True: 430k, False: 2.91k]
  Branch (161:27): [True: 0, False: 430k]
  Branch (161:42): [True: 0, False: 430k]
162
0
            numStr += *raw;                   // copy E
163
0
            raw++;
164
165
0
            if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
  Branch (165:17): [True: 0, False: 0]
  Branch (165:31): [True: 0, False: 0]
  Branch (165:46): [True: 0, False: 0]
166
0
                numStr += *raw;
167
0
                raw++;
168
0
            }
169
170
0
            if (raw >= end || !json_isdigit(*raw))
  Branch (170:17): [True: 0, False: 0]
  Branch (170:31): [True: 0, False: 0]
171
0
                return JTOK_ERR;
172
0
            while (raw < end && json_isdigit(*raw)) { // copy digits
  Branch (172:20): [True: 0, False: 0]
  Branch (172:33): [True: 0, False: 0]
173
0
                numStr += *raw;
174
0
                raw++;
175
0
            }
176
0
        }
177
178
433k
        tokenVal = numStr;
179
433k
        consumed = (raw - rawStart);
180
433k
        return JTOK_NUMBER;
181
433k
        }
182
183
2.08M
    case '"': {
  Branch (183:5): [True: 2.08M, False: 4.22M]
184
2.08M
        raw++;                                // skip "
185
186
2.08M
        std::string valStr;
187
2.08M
        JSONUTF8StringFilter writer(valStr);
188
189
22.2M
        while (true) {
  Branch (189:16): [Folded - Ignored]
190
22.2M
            if (raw >= end || (unsigned char)*raw < 0x20)
  Branch (190:17): [True: 0, False: 22.2M]
  Branch (190:31): [True: 0, False: 22.2M]
191
0
                return JTOK_ERR;
192
193
22.2M
            else if (*raw == '\\') {
  Branch (193:22): [True: 250k, False: 22.0M]
194
250k
                raw++;                        // skip backslash
195
196
250k
                if (raw >= end)
  Branch (196:21): [True: 0, False: 250k]
197
0
                    return JTOK_ERR;
198
199
250k
                switch (*raw) {
200
0
                case '"':  writer.push_back('\"'); break;
  Branch (200:17): [True: 0, False: 250k]
201
0
                case '\\': writer.push_back('\\'); break;
  Branch (201:17): [True: 0, False: 250k]
202
0
                case '/':  writer.push_back('/'); break;
  Branch (202:17): [True: 0, False: 250k]
203
0
                case 'b':  writer.push_back('\b'); break;
  Branch (203:17): [True: 0, False: 250k]
204
0
                case 'f':  writer.push_back('\f'); break;
  Branch (204:17): [True: 0, False: 250k]
205
250k
                case 'n':  writer.push_back('\n'); break;
  Branch (205:17): [True: 250k, False: 0]
206
0
                case 'r':  writer.push_back('\r'); break;
  Branch (206:17): [True: 0, False: 250k]
207
0
                case 't':  writer.push_back('\t'); break;
  Branch (207:17): [True: 0, False: 250k]
208
209
0
                case 'u': {
  Branch (209:17): [True: 0, False: 250k]
210
0
                    unsigned int codepoint;
211
0
                    if (raw + 1 + 4 >= end ||
  Branch (211:25): [True: 0, False: 0]
212
0
                        hatoui(raw + 1, raw + 1 + 4, codepoint) !=
  Branch (212:25): [True: 0, False: 0]
213
0
                               raw + 1 + 4)
214
0
                        return JTOK_ERR;
215
0
                    writer.push_back_u(codepoint);
216
0
                    raw += 4;
217
0
                    break;
218
0
                    }
219
0
                default:
  Branch (219:17): [True: 0, False: 250k]
220
0
                    return JTOK_ERR;
221
222
250k
                }
223
224
250k
                raw++;                        // skip esc'd char
225
250k
            }
226
227
22.0M
            else if (*raw == '"') {
  Branch (227:22): [True: 2.07M, False: 19.9M]
228
2.07M
                raw++;                        // skip "
229
2.07M
                break;                        // stop scanning
230
2.07M
            }
231
232
19.9M
            else {
233
19.9M
                writer.push_back(static_cast<unsigned char>(*raw));
234
19.9M
                raw++;
235
19.9M
            }
236
22.2M
        }
237
238
2.08M
        if (!writer.finalize())
  Branch (238:13): [True: 0, False: 2.08M]
239
0
            return JTOK_ERR;
240
2.08M
        tokenVal = valStr;
241
2.08M
        consumed = (raw - rawStart);
242
2.08M
        return JTOK_STRING;
243
2.08M
        }
244
245
0
    default:
  Branch (245:5): [True: 0, False: 6.31M]
246
0
        return JTOK_ERR;
247
6.31M
    }
248
6.31M
}
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
31.3M
#define expect(bit) (expectMask & (EXP_##bit))
259
7.57M
#define setExpect(bit) (expectMask |= EXP_##bit)
260
7.94M
#define clearExpect(bit) (expectMask &= ~EXP_##bit)
261
262
bool UniValue::read(std::string_view str_in)
263
336k
{
264
336k
    clear();
265
266
336k
    uint32_t expectMask = 0;
267
336k
    std::vector<UniValue*> stack;
268
269
336k
    std::string tokenVal;
270
336k
    unsigned int consumed;
271
336k
    enum jtokentype tok = JTOK_NONE;
272
336k
    enum jtokentype last_tok = JTOK_NONE;
273
336k
    const char* raw{str_in.data()};
274
336k
    const char* end{raw + str_in.size()};
275
6.30M
    do {
276
6.30M
        last_tok = tok;
277
278
6.30M
        tok = getJsonToken(tokenVal, consumed, raw, end);
279
6.30M
        if (tok == JTOK_NONE || tok == JTOK_ERR)
  Branch (279:13): [True: 3.73k, False: 6.29M]
  Branch (279:33): [True: 18.4E, False: 6.31M]
280
0
            return false;
281
6.30M
        raw += consumed;
282
283
6.30M
        bool isValueOpen = jsonTokenIsValue(tok) ||
  Branch (283:28): [True: 2.54M, False: 3.75M]
284
6.30M
            tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN;
  Branch (284:13): [True: 348k, False: 3.40M]
  Branch (284:37): [True: 339k, False: 3.06M]
285
286
6.30M
        if (expect(VALUE)) {
287
1.34M
            if (!isValueOpen)
  Branch (287:17): [True: 0, False: 1.34M]
288
0
                return false;
289
1.34M
            clearExpect(VALUE);
290
291
4.95M
        } else if (expect(ARR_VALUE)) {
292
343k
            bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE);
  Branch (292:31): [True: 153k, False: 189k]
  Branch (292:46): [True: 189k, False: 0]
293
343k
            if (!isArrValue)
  Branch (293:17): [True: 0, False: 343k]
294
0
                return false;
295
296
343k
            clearExpect(ARR_VALUE);
297
298
4.61M
        } else if (expect(OBJ_NAME)) {
299
1.34M
            bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING);
  Branch (299:31): [True: 0, False: 1.34M]
  Branch (299:56): [True: 1.34M, False: 0]
300
1.34M
            if (!isObjName)
  Branch (300:17): [True: 0, False: 1.34M]
301
0
                return false;
302
303
3.26M
        } else if (expect(COLON)) {
304
1.35M
            if (tok != JTOK_COLON)
  Branch (304:17): [True: 0, False: 1.35M]
305
0
                return false;
306
1.35M
            clearExpect(COLON);
307
308
1.90M
        } else if (!expect(COLON) && (tok == JTOK_COLON)) {
  Branch (308:20): [True: 1.84M, False: 61.5k]
  Branch (308:38): [True: 0, False: 1.84M]
309
0
            return false;
310
0
        }
311
312
6.30M
        if (expect(NOT_VALUE)) {
313
2.87M
            if (isValueOpen)
  Branch (313:17): [True: 0, False: 2.87M]
314
0
                return false;
315
2.87M
            clearExpect(NOT_VALUE);
316
2.87M
        }
317
318
6.30M
        switch (tok) {
319
320
337k
        case JTOK_OBJ_OPEN:
  Branch (320:9): [True: 337k, False: 5.96M]
321
674k
        case JTOK_ARR_OPEN: {
  Branch (321:9): [True: 336k, False: 5.96M]
322
674k
            VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
  Branch (322:27): [True: 336k, False: 337k]
323
674k
            if (!stack.size()) {
  Branch (323:17): [True: 336k, False: 337k]
324
336k
                if (utyp == VOBJ)
  Branch (324:21): [True: 336k, False: 0]
325
336k
                    setObject();
326
0
                else
327
0
                    setArray();
328
336k
                stack.push_back(this);
329
337k
            } else {
330
337k
                UniValue tmpVal(utyp);
331
337k
                UniValue *top = stack.back();
332
337k
                top->values.push_back(tmpVal);
333
334
337k
                UniValue *newTop = &(top->values.back());
335
337k
                stack.push_back(newTop);
336
337k
            }
337
338
674k
            if (stack.size() > MAX_JSON_DEPTH)
  Branch (338:17): [True: 0, False: 674k]
339
0
                return false;
340
341
674k
            if (utyp == VOBJ)
  Branch (341:17): [True: 336k, False: 337k]
342
336k
                setExpect(OBJ_NAME);
343
337k
            else
344
337k
                setExpect(ARR_VALUE);
345
674k
            break;
346
674k
            }
347
348
337k
        case JTOK_OBJ_CLOSE:
  Branch (348:9): [True: 337k, False: 5.96M]
349
674k
        case JTOK_ARR_CLOSE: {
  Branch (349:9): [True: 337k, False: 5.96M]
350
675k
            if (!stack.size() || (last_tok == JTOK_COMMA))
  Branch (350:17): [True: 18.4E, False: 675k]
  Branch (350:34): [True: 0, False: 675k]
351
0
                return false;
352
353
674k
            VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
  Branch (353:27): [True: 337k, False: 336k]
354
674k
            UniValue *top = stack.back();
355
674k
            if (utyp != top->getType())
  Branch (355:17): [True: 0, False: 674k]
356
0
                return false;
357
358
674k
            stack.pop_back();
359
674k
            clearExpect(OBJ_NAME);
360
674k
            setExpect(NOT_VALUE);
361
674k
            break;
362
674k
            }
363
364
1.35M
        case JTOK_COLON: {
  Branch (364:9): [True: 1.35M, False: 4.94M]
365
1.35M
            if (!stack.size())
  Branch (365:17): [True: 0, False: 1.35M]
366
0
                return false;
367
368
1.35M
            UniValue *top = stack.back();
369
1.35M
            if (top->getType() != VOBJ)
  Branch (369:17): [True: 0, False: 1.35M]
370
0
                return false;
371
372
1.35M
            setExpect(VALUE);
373
1.35M
            break;
374
1.35M
            }
375
376
1.01M
        case JTOK_COMMA: {
  Branch (376:9): [True: 1.01M, False: 5.28M]
377
1.01M
            if (!stack.size() ||
  Branch (377:17): [True: 0, False: 1.01M]
378
1.01M
                (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
  Branch (378:17): [True: 0, False: 1.01M]
  Branch (378:45): [True: 0, False: 1.01M]
379
0
                return false;
380
381
1.01M
            UniValue *top = stack.back();
382
1.01M
            if (top->getType() == VOBJ)
  Branch (382:17): [True: 1.01M, False: 6.17k]
383
1.01M
                setExpect(OBJ_NAME);
384
6.17k
            else
385
6.17k
                setExpect(ARR_VALUE);
386
1.01M
            break;
387
1.01M
            }
388
389
0
        case JTOK_KW_NULL:
  Branch (389:9): [True: 0, False: 6.30M]
390
0
        case JTOK_KW_TRUE:
  Branch (390:9): [True: 0, False: 6.30M]
391
3.08k
        case JTOK_KW_FALSE: {
  Branch (391:9): [True: 3.08k, False: 6.29M]
392
3.08k
            UniValue tmpVal;
393
3.08k
            switch (tok) {
394
0
            case JTOK_KW_NULL:
  Branch (394:13): [True: 0, False: 3.08k]
395
                // do nothing more
396
0
                break;
397
0
            case JTOK_KW_TRUE:
  Branch (397:13): [True: 0, False: 3.08k]
398
0
                tmpVal.setBool(true);
399
0
                break;
400
3.08k
            case JTOK_KW_FALSE:
  Branch (400:13): [True: 3.08k, False: 0]
401
3.08k
                tmpVal.setBool(false);
402
3.08k
                break;
403
0
            default: /* impossible */ break;
  Branch (403:13): [True: 0, False: 3.08k]
404
3.08k
            }
405
406
3.08k
            if (!stack.size()) {
  Branch (406:17): [True: 0, False: 3.08k]
407
0
                *this = tmpVal;
408
0
                break;
409
0
            }
410
411
3.08k
            UniValue *top = stack.back();
412
3.08k
            top->values.push_back(tmpVal);
413
414
3.08k
            setExpect(NOT_VALUE);
415
3.08k
            break;
416
3.08k
            }
417
418
430k
        case JTOK_NUMBER: {
  Branch (418:9): [True: 430k, False: 5.86M]
419
430k
            UniValue tmpVal(VNUM, tokenVal);
420
430k
            if (!stack.size()) {
  Branch (420:17): [True: 0, False: 430k]
421
0
                *this = tmpVal;
422
0
                break;
423
0
            }
424
425
430k
            UniValue *top = stack.back();
426
430k
            top->values.push_back(tmpVal);
427
428
430k
            setExpect(NOT_VALUE);
429
430k
            break;
430
430k
            }
431
432
2.07M
        case JTOK_STRING: {
  Branch (432:9): [True: 2.07M, False: 4.22M]
433
2.07M
            if (expect(OBJ_NAME)) {
434
1.34M
                UniValue *top = stack.back();
435
1.34M
                top->keys.push_back(tokenVal);
436
1.34M
                clearExpect(OBJ_NAME);
437
1.34M
                setExpect(COLON);
438
1.34M
            } else {
439
729k
                UniValue tmpVal(VSTR, tokenVal);
440
729k
                if (!stack.size()) {
  Branch (440:21): [True: 0, False: 729k]
441
0
                    *this = tmpVal;
442
0
                    break;
443
0
                }
444
729k
                UniValue *top = stack.back();
445
729k
                top->values.push_back(tmpVal);
446
729k
            }
447
448
2.07M
            setExpect(NOT_VALUE);
449
2.07M
            break;
450
2.07M
            }
451
452
0
        default:
  Branch (452:9): [True: 0, False: 6.30M]
453
0
            return false;
454
6.30M
        }
455
6.30M
    } while (!stack.empty ());
  Branch (455:14): [True: 5.96M, False: 341k]
456
457
    /* Check that nothing follows the initial construct (parsed above).  */
458
341k
    tok = getJsonToken(tokenVal, consumed, raw, end);
459
341k
    if (tok != JTOK_NONE)
  Branch (459:9): [True: 0, False: 341k]
460
0
        return false;
461
462
341k
    return true;
463
341k
}
464