Bitcoin Core 31.99.0
P2P Digital Currency
univalue_read.cpp
Go to the documentation of this file.
1// Copyright 2014 BitPay Inc.
2// Copyright (c) 2015-present The Bitcoin Core developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or https://opensource.org/licenses/mit-license.php.
5
6#include <univalue.h>
8
9#include <cstdint>
10#include <cstring>
11#include <string>
12#include <string_view>
13#include <utility>
14#include <vector>
15
16/*
17 * According to stackexchange, the original json test suite wanted
18 * to limit depth to 22. Widely-deployed PHP bails at depth 512,
19 * so we will follow PHP's lead, which should be more than sufficient
20 * (further stackexchange comments indicate depth > 32 rarely occurs).
21 */
22static constexpr size_t MAX_JSON_DEPTH = 512;
23
24static bool json_isdigit(int ch)
25{
26 return ((ch >= '0') && (ch <= '9'));
27}
28
29// convert hexadecimal string to unsigned integer
30static const char *hatoui(const char *first, const char *last,
31 unsigned int& out)
32{
33 unsigned int result = 0;
34 for (; first != last; ++first)
35 {
36 int digit;
37 if (json_isdigit(*first))
38 digit = *first - '0';
39
40 else if (*first >= 'a' && *first <= 'f')
41 digit = *first - 'a' + 10;
42
43 else if (*first >= 'A' && *first <= 'F')
44 digit = *first - 'A' + 10;
45
46 else
47 break;
48
49 result = 16 * result + digit;
50 }
51 out = result;
52
53 return first;
54}
55
56enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
57 const char *raw, const char *end)
58{
59 tokenVal.clear();
60 consumed = 0;
61
62 const char *rawStart = raw;
63
64 while (raw < end && (json_isspace(*raw))) // skip whitespace
65 raw++;
66
67 if (raw >= end)
68 return JTOK_NONE;
69
70 switch (*raw) {
71
72 case '{':
73 raw++;
74 consumed = (raw - rawStart);
75 return JTOK_OBJ_OPEN;
76 case '}':
77 raw++;
78 consumed = (raw - rawStart);
79 return JTOK_OBJ_CLOSE;
80 case '[':
81 raw++;
82 consumed = (raw - rawStart);
83 return JTOK_ARR_OPEN;
84 case ']':
85 raw++;
86 consumed = (raw - rawStart);
87 return JTOK_ARR_CLOSE;
88
89 case ':':
90 raw++;
91 consumed = (raw - rawStart);
92 return JTOK_COLON;
93 case ',':
94 raw++;
95 consumed = (raw - rawStart);
96 return JTOK_COMMA;
97
98 case 'n':
99 case 't':
100 case 'f':
101 if (!strncmp(raw, "null", 4)) {
102 raw += 4;
103 consumed = (raw - rawStart);
104 return JTOK_KW_NULL;
105 } else if (!strncmp(raw, "true", 4)) {
106 raw += 4;
107 consumed = (raw - rawStart);
108 return JTOK_KW_TRUE;
109 } else if (!strncmp(raw, "false", 5)) {
110 raw += 5;
111 consumed = (raw - rawStart);
112 return JTOK_KW_FALSE;
113 } else
114 return JTOK_ERR;
115
116 case '-':
117 case '0':
118 case '1':
119 case '2':
120 case '3':
121 case '4':
122 case '5':
123 case '6':
124 case '7':
125 case '8':
126 case '9': {
127 // part 1: int
128 std::string numStr;
129
130 const char *first = raw;
131
132 const char *firstDigit = first;
133 if (!json_isdigit(*firstDigit))
134 firstDigit++;
135 if ((*firstDigit == '0') && json_isdigit(firstDigit[1]))
136 return JTOK_ERR;
137
138 numStr += *raw; // copy first char
139 raw++;
140
141 if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
142 return JTOK_ERR;
143
144 while (raw < end && json_isdigit(*raw)) { // copy digits
145 numStr += *raw;
146 raw++;
147 }
148
149 // part 2: frac
150 if (raw < end && *raw == '.') {
151 numStr += *raw; // copy .
152 raw++;
153
154 if (raw >= end || !json_isdigit(*raw))
155 return JTOK_ERR;
156 while (raw < end && json_isdigit(*raw)) { // copy digits
157 numStr += *raw;
158 raw++;
159 }
160 }
161
162 // part 3: exp
163 if (raw < end && (*raw == 'e' || *raw == 'E')) {
164 numStr += *raw; // copy E
165 raw++;
166
167 if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
168 numStr += *raw;
169 raw++;
170 }
171
172 if (raw >= end || !json_isdigit(*raw))
173 return JTOK_ERR;
174 while (raw < end && json_isdigit(*raw)) { // copy digits
175 numStr += *raw;
176 raw++;
177 }
178 }
179
180 tokenVal = numStr;
181 consumed = (raw - rawStart);
182 return JTOK_NUMBER;
183 }
184
185 case '"': {
186 raw++; // skip "
187
188 std::string valStr;
189 JSONUTF8StringFilter writer(valStr);
190
191 while (true) {
192 if (raw >= end || (unsigned char)*raw < 0x20)
193 return JTOK_ERR;
194
195 else if (*raw == '\\') {
196 raw++; // skip backslash
197
198 if (raw >= end)
199 return JTOK_ERR;
200
201 switch (*raw) {
202 case '"': writer.push_back('\"'); break;
203 case '\\': writer.push_back('\\'); break;
204 case '/': writer.push_back('/'); break;
205 case 'b': writer.push_back('\b'); break;
206 case 'f': writer.push_back('\f'); break;
207 case 'n': writer.push_back('\n'); break;
208 case 'r': writer.push_back('\r'); break;
209 case 't': writer.push_back('\t'); break;
210
211 case 'u': {
212 unsigned int codepoint;
213 if (raw + 1 + 4 >= end ||
214 hatoui(raw + 1, raw + 1 + 4, codepoint) !=
215 raw + 1 + 4)
216 return JTOK_ERR;
217 writer.push_back_u(codepoint);
218 raw += 4;
219 break;
220 }
221 default:
222 return JTOK_ERR;
223
224 }
225
226 raw++; // skip esc'd char
227 }
228
229 else if (*raw == '"') {
230 raw++; // skip "
231 break; // stop scanning
232 }
233
234 else {
235 writer.push_back(static_cast<unsigned char>(*raw));
236 raw++;
237 }
238 }
239
240 if (!writer.finalize())
241 return JTOK_ERR;
242 tokenVal = valStr;
243 consumed = (raw - rawStart);
244 return JTOK_STRING;
245 }
246
247 default:
248 return JTOK_ERR;
249 }
250}
251
252enum expect_bits : unsigned {
253 EXP_OBJ_NAME = (1U << 0),
254 EXP_COLON = (1U << 1),
255 EXP_ARR_VALUE = (1U << 2),
256 EXP_VALUE = (1U << 3),
257 EXP_NOT_VALUE = (1U << 4),
258};
259
260#define expect(bit) (expectMask & (EXP_##bit))
261#define setExpect(bit) (expectMask |= EXP_##bit)
262#define clearExpect(bit) (expectMask &= ~EXP_##bit)
263
264bool UniValue::read(std::string_view json)
265{
266 UniValue parsed;
267 if (!parsed.read_impl(json)) {
268 setNull();
269 return false;
270 }
271 *this = std::move(parsed);
272 return true;
273}
274
275bool UniValue::read_impl(std::string_view str_in)
276{
277 clear();
278
279 uint32_t expectMask = 0;
280 std::vector<UniValue*> stack;
281
282 std::string tokenVal;
283 unsigned int consumed;
284 enum jtokentype tok = JTOK_NONE;
285 enum jtokentype last_tok = JTOK_NONE;
286 const char* raw{str_in.data()};
287 const char* end{raw + str_in.size()};
288 do {
289 last_tok = tok;
290
291 tok = getJsonToken(tokenVal, consumed, raw, end);
292 if (tok == JTOK_NONE || tok == JTOK_ERR)
293 return false;
294 raw += consumed;
295
296 bool isValueOpen = jsonTokenIsValue(tok) ||
297 tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN;
298
299 if (expect(VALUE)) {
300 if (!isValueOpen)
301 return false;
302 clearExpect(VALUE);
303
304 } else if (expect(ARR_VALUE)) {
305 bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE);
306 if (!isArrValue)
307 return false;
308
309 clearExpect(ARR_VALUE);
310
311 } else if (expect(OBJ_NAME)) {
312 bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING);
313 if (!isObjName)
314 return false;
315
316 } else if (expect(COLON)) {
317 if (tok != JTOK_COLON)
318 return false;
319 clearExpect(COLON);
320
321 } else if (!expect(COLON) && (tok == JTOK_COLON)) {
322 return false;
323 }
324
325 if (expect(NOT_VALUE)) {
326 if (isValueOpen)
327 return false;
328 clearExpect(NOT_VALUE);
329 }
330
331 switch (tok) {
332
333 case JTOK_OBJ_OPEN:
334 case JTOK_ARR_OPEN: {
335 VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
336 if (!stack.size()) {
337 if (utyp == VOBJ)
338 setObject();
339 else
340 setArray();
341 stack.push_back(this);
342 } else {
343 UniValue tmpVal(utyp);
344 UniValue *top = stack.back();
345 top->values.push_back(tmpVal);
346
347 UniValue *newTop = &(top->values.back());
348 stack.push_back(newTop);
349 }
350
351 if (stack.size() > MAX_JSON_DEPTH)
352 return false;
353
354 if (utyp == VOBJ)
355 setExpect(OBJ_NAME);
356 else
357 setExpect(ARR_VALUE);
358 break;
359 }
360
361 case JTOK_OBJ_CLOSE:
362 case JTOK_ARR_CLOSE: {
363 if (!stack.size() || (last_tok == JTOK_COMMA))
364 return false;
365
366 VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
367 UniValue *top = stack.back();
368 if (utyp != top->getType())
369 return false;
370
371 stack.pop_back();
372 clearExpect(OBJ_NAME);
373 setExpect(NOT_VALUE);
374 break;
375 }
376
377 case JTOK_COLON: {
378 if (!stack.size())
379 return false;
380
381 UniValue *top = stack.back();
382 if (top->getType() != VOBJ)
383 return false;
384
385 setExpect(VALUE);
386 break;
387 }
388
389 case JTOK_COMMA: {
390 if (!stack.size() ||
391 (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
392 return false;
393
394 UniValue *top = stack.back();
395 if (top->getType() == VOBJ)
396 setExpect(OBJ_NAME);
397 else
398 setExpect(ARR_VALUE);
399 break;
400 }
401
402 case JTOK_KW_NULL:
403 case JTOK_KW_TRUE:
404 case JTOK_KW_FALSE: {
405 UniValue tmpVal;
406 switch (tok) {
407 case JTOK_KW_NULL:
408 // do nothing more
409 break;
410 case JTOK_KW_TRUE:
411 tmpVal.setBool(true);
412 break;
413 case JTOK_KW_FALSE:
414 tmpVal.setBool(false);
415 break;
416 default: /* impossible */ break;
417 }
418
419 if (!stack.size()) {
420 *this = tmpVal;
421 break;
422 }
423
424 UniValue *top = stack.back();
425 top->values.push_back(tmpVal);
426
427 setExpect(NOT_VALUE);
428 break;
429 }
430
431 case JTOK_NUMBER: {
432 UniValue tmpVal(VNUM, tokenVal);
433 if (!stack.size()) {
434 *this = tmpVal;
435 break;
436 }
437
438 UniValue *top = stack.back();
439 top->values.push_back(tmpVal);
440
441 setExpect(NOT_VALUE);
442 break;
443 }
444
445 case JTOK_STRING: {
446 if (expect(OBJ_NAME)) {
447 UniValue *top = stack.back();
448 top->keys.push_back(tokenVal);
449 clearExpect(OBJ_NAME);
450 setExpect(COLON);
451 } else {
452 UniValue tmpVal(VSTR, tokenVal);
453 if (!stack.size()) {
454 *this = tmpVal;
455 break;
456 }
457 UniValue *top = stack.back();
458 top->values.push_back(tmpVal);
459 }
460
461 setExpect(NOT_VALUE);
462 break;
463 }
464
465 default:
466 return false;
467 }
468 } while (!stack.empty ());
469
470 /* Check that nothing follows the initial construct (parsed above). */
471 tok = getJsonToken(tokenVal, consumed, raw, end);
472 if (tok != JTOK_NONE)
473 return false;
474
475 return true;
476}
Filter that generates and validates UTF-8, as well as collates UTF-16 surrogate pairs as specified in...
void push_back(unsigned char ch)
void push_back_u(unsigned int codepoint_)
void push_back(UniValue val)
Definition: univalue.cpp:103
enum VType getType() const
Definition: univalue.h:67
@ VOBJ
Definition: univalue.h:24
@ VSTR
Definition: univalue.h:24
@ VARR
Definition: univalue.h:24
@ VNUM
Definition: univalue.h:24
void setArray()
Definition: univalue.cpp:91
void clear()
Definition: univalue.cpp:17
void setNull()
Definition: univalue.cpp:25
bool read_impl(std::string_view str_in)
Internal parser: May leave this value in an invalid state on failure.
void setBool(bool val)
Definition: univalue.cpp:30
std::vector< UniValue > values
Definition: univalue.h:109
std::vector< std::string > keys
Definition: univalue.h:108
void setObject()
Definition: univalue.cpp:97
bool read(std::string_view json)
Parse JSON; set value to null on failure.
char const * json() noexcept
Template to generate JSON data.
static bool jsonTokenIsValue(enum jtokentype jtt)
Definition: univalue.h:175
static bool json_isspace(int ch)
Definition: univalue.h:192
jtokentype
Definition: univalue.h:155
@ JTOK_OBJ_CLOSE
Definition: univalue.h:159
@ JTOK_STRING
Definition: univalue.h:168
@ JTOK_COLON
Definition: univalue.h:162
@ JTOK_OBJ_OPEN
Definition: univalue.h:158
@ JTOK_NUMBER
Definition: univalue.h:167
@ JTOK_KW_NULL
Definition: univalue.h:164
@ JTOK_COMMA
Definition: univalue.h:163
@ JTOK_ARR_CLOSE
Definition: univalue.h:161
@ JTOK_KW_TRUE
Definition: univalue.h:165
@ JTOK_ARR_OPEN
Definition: univalue.h:160
@ JTOK_KW_FALSE
Definition: univalue.h:166
@ JTOK_ERR
Definition: univalue.h:156
@ JTOK_NONE
Definition: univalue.h:157
static bool json_isdigit(int ch)
#define clearExpect(bit)
static constexpr size_t MAX_JSON_DEPTH
enum jtokentype getJsonToken(std::string &tokenVal, unsigned int &consumed, const char *raw, const char *end)
#define expect(bit)
expect_bits
@ EXP_ARR_VALUE
@ EXP_NOT_VALUE
@ EXP_COLON
@ EXP_VALUE
@ EXP_OBJ_NAME
static const char * hatoui(const char *first, const char *last, unsigned int &out)
#define setExpect(bit)