Branch data 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 <string.h>
# 6 : : #include <vector>
# 7 : : #include <stdio.h>
# 8 : : #include "univalue.h"
# 9 : : #include "univalue_utffilter.h"
# 10 : :
# 11 : : /*
# 12 : : * According to stackexchange, the original json test suite wanted
# 13 : : * to limit depth to 22. Widely-deployed PHP bails at depth 512,
# 14 : : * so we will follow PHP's lead, which should be more than sufficient
# 15 : : * (further stackexchange comments indicate depth > 32 rarely occurs).
# 16 : : */
# 17 : : static const size_t MAX_JSON_DEPTH = 512;
# 18 : :
# 19 : : static bool json_isdigit(int ch)
# 20 : 4529552 : {
# 21 [ + + ][ + + ]: 4529552 : return ((ch >= '0') && (ch <= '9'));
# 22 : 4529552 : }
# 23 : :
# 24 : : // convert hexadecimal string to unsigned integer
# 25 : : static const char *hatoui(const char *first, const char *last,
# 26 : : unsigned int& out)
# 27 : 341 : {
# 28 : 341 : unsigned int result = 0;
# 29 [ + + ]: 1705 : for (; first != last; ++first)
# 30 : 1364 : {
# 31 : 1364 : int digit;
# 32 [ + + ]: 1364 : if (json_isdigit(*first))
# 33 : 685 : digit = *first - '0';
# 34 : :
# 35 [ + - ][ + - ]: 679 : else if (*first >= 'a' && *first <= 'f')
# 36 : 679 : digit = *first - 'a' + 10;
# 37 : :
# 38 [ # # ][ # # ]: 0 : else if (*first >= 'A' && *first <= 'F')
# 39 : 0 : digit = *first - 'A' + 10;
# 40 : :
# 41 : 0 : else
# 42 : 0 : break;
# 43 : :
# 44 : 1364 : result = 16 * result + digit;
# 45 : 1364 : }
# 46 : 341 : out = result;
# 47 : :
# 48 : 341 : return first;
# 49 : 341 : }
# 50 : :
# 51 : : enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
# 52 : : const char *raw, const char *end)
# 53 : 4172533 : {
# 54 : 4172533 : tokenVal.clear();
# 55 : 4172533 : consumed = 0;
# 56 : :
# 57 : 4172533 : const char *rawStart = raw;
# 58 : :
# 59 [ + + ][ + + ]: 5409173 : while (raw < end && (json_isspace(*raw))) // skip whitespace
# 60 : 1236640 : raw++;
# 61 : :
# 62 [ + + ]: 4172533 : if (raw >= end)
# 63 : 140122 : return JTOK_NONE;
# 64 : :
# 65 : 4032411 : switch (*raw) {
# 66 : :
# 67 [ + + ]: 225405 : case '{':
# 68 : 225405 : raw++;
# 69 : 225405 : consumed = (raw - rawStart);
# 70 : 225405 : return JTOK_OBJ_OPEN;
# 71 [ + + ]: 225403 : case '}':
# 72 : 225403 : raw++;
# 73 : 225403 : consumed = (raw - rawStart);
# 74 : 225403 : return JTOK_OBJ_CLOSE;
# 75 [ + + ]: 87346 : case '[':
# 76 : 87346 : raw++;
# 77 : 87346 : consumed = (raw - rawStart);
# 78 : 87346 : return JTOK_ARR_OPEN;
# 79 [ + + ]: 87322 : case ']':
# 80 : 87322 : raw++;
# 81 : 87322 : consumed = (raw - rawStart);
# 82 : 87322 : return JTOK_ARR_CLOSE;
# 83 : :
# 84 [ + + ]: 627854 : case ':':
# 85 : 627854 : raw++;
# 86 : 627854 : consumed = (raw - rawStart);
# 87 : 627854 : return JTOK_COLON;
# 88 [ + + ]: 544153 : case ',':
# 89 : 544153 : raw++;
# 90 : 544153 : consumed = (raw - rawStart);
# 91 : 544153 : return JTOK_COMMA;
# 92 : :
# 93 [ + + ]: 5080 : case 'n':
# 94 [ + + ]: 9468 : case 't':
# 95 [ + + ]: 11610 : case 'f':
# 96 [ + + ]: 11610 : if (!strncmp(raw, "null", 4)) {
# 97 : 5076 : raw += 4;
# 98 : 5076 : consumed = (raw - rawStart);
# 99 : 5076 : return JTOK_KW_NULL;
# 100 [ + + ]: 6534 : } else if (!strncmp(raw, "true", 4)) {
# 101 : 4386 : raw += 4;
# 102 : 4386 : consumed = (raw - rawStart);
# 103 : 4386 : return JTOK_KW_TRUE;
# 104 [ + + ]: 4386 : } else if (!strncmp(raw, "false", 5)) {
# 105 : 2138 : raw += 5;
# 106 : 2138 : consumed = (raw - rawStart);
# 107 : 2138 : return JTOK_KW_FALSE;
# 108 : 2138 : } else
# 109 : 10 : return JTOK_ERR;
# 110 : :
# 111 [ + + ]: 22156 : case '-':
# 112 [ + + ]: 217561 : case '0':
# 113 [ + + ]: 538199 : case '1':
# 114 [ + + ]: 758408 : case '2':
# 115 [ + + ]: 868891 : case '3':
# 116 [ + + ]: 916366 : case '4':
# 117 [ + + ]: 949466 : case '5':
# 118 [ + + ]: 1031869 : case '6':
# 119 [ + + ]: 1067856 : case '7':
# 120 [ + + ]: 1130773 : case '8':
# 121 [ + + ]: 1193987 : case '9': {
# 122 : : // part 1: int
# 123 : 1193987 : std::string numStr;
# 124 : :
# 125 : 1193987 : const char *first = raw;
# 126 : :
# 127 : 1193987 : const char *firstDigit = first;
# 128 [ + + ]: 1193987 : if (!json_isdigit(*firstDigit))
# 129 : 22156 : firstDigit++;
# 130 [ + + ][ - + ]: 1193987 : if ((*firstDigit == '0') && json_isdigit(firstDigit[1]))
# 131 : 0 : return JTOK_ERR;
# 132 : :
# 133 : 1193987 : numStr += *raw; // copy first char
# 134 : 1193987 : raw++;
# 135 : :
# 136 [ + + ][ + - ]: 1193987 : if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
# [ - + ]
# 137 : 0 : return JTOK_ERR;
# 138 : :
# 139 [ + + ][ + + ]: 3720880 : while (raw < end && json_isdigit(*raw)) { // copy digits
# 140 : 2526893 : numStr += *raw;
# 141 : 2526893 : raw++;
# 142 : 2526893 : }
# 143 : :
# 144 : : // part 2: frac
# 145 [ + + ][ + + ]: 1193987 : if (raw < end && *raw == '.') {
# 146 : 43171 : numStr += *raw; // copy .
# 147 : 43171 : raw++;
# 148 : :
# 149 [ - + ][ - + ]: 43171 : if (raw >= end || !json_isdigit(*raw))
# 150 : 0 : return JTOK_ERR;
# 151 [ + + ][ + + ]: 297440 : while (raw < end && json_isdigit(*raw)) { // copy digits
# 152 : 254269 : numStr += *raw;
# 153 : 254269 : raw++;
# 154 : 254269 : }
# 155 : 43171 : }
# 156 : :
# 157 : : // part 3: exp
# 158 [ + + ][ + + ]: 1193987 : if (raw < end && (*raw == 'e' || *raw == 'E')) {
# [ - + ]
# 159 : 9214 : numStr += *raw; // copy E
# 160 : 9214 : raw++;
# 161 : :
# 162 [ + - ][ + + ]: 9214 : if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
# [ + + ]
# 163 : 9210 : numStr += *raw;
# 164 : 9210 : raw++;
# 165 : 9210 : }
# 166 : :
# 167 [ - + ][ - + ]: 9214 : if (raw >= end || !json_isdigit(*raw))
# 168 : 0 : return JTOK_ERR;
# 169 [ + + ][ + + ]: 27628 : while (raw < end && json_isdigit(*raw)) { // copy digits
# 170 : 18414 : numStr += *raw;
# 171 : 18414 : raw++;
# 172 : 18414 : }
# 173 : 9214 : }
# 174 : :
# 175 : 1193987 : tokenVal = numStr;
# 176 : 1193987 : consumed = (raw - rawStart);
# 177 : 1193987 : return JTOK_NUMBER;
# 178 : 1193987 : }
# 179 : :
# 180 [ + + ]: 1029314 : case '"': {
# 181 : 1029314 : raw++; // skip "
# 182 : :
# 183 : 1029314 : std::string valStr;
# 184 : 1029314 : JSONUTF8StringFilter writer(valStr);
# 185 : :
# 186 : 287096164 : while (true) {
# 187 [ - + ][ - + ]: 287096164 : if (raw >= end || (unsigned char)*raw < 0x20)
# 188 : 0 : return JTOK_ERR;
# 189 : :
# 190 [ + + ]: 287096164 : else if (*raw == '\\') {
# 191 : 382 : raw++; // skip backslash
# 192 : :
# 193 [ - + ]: 382 : if (raw >= end)
# 194 : 0 : return JTOK_ERR;
# 195 : :
# 196 : 382 : switch (*raw) {
# 197 [ + + ]: 5 : case '"': writer.push_back('\"'); break;
# 198 [ + + ]: 1 : case '\\': writer.push_back('\\'); break;
# 199 [ - + ]: 0 : case '/': writer.push_back('/'); break;
# 200 [ + + ]: 1 : case 'b': writer.push_back('\b'); break;
# 201 [ + + ]: 1 : case 'f': writer.push_back('\f'); break;
# 202 [ + + ]: 31 : case 'n': writer.push_back('\n'); break;
# 203 [ + + ]: 1 : case 'r': writer.push_back('\r'); break;
# 204 [ + + ]: 1 : case 't': writer.push_back('\t'); break;
# 205 : :
# 206 [ + + ]: 341 : case 'u': {
# 207 : 341 : unsigned int codepoint;
# 208 [ - + ]: 341 : if (raw + 1 + 4 >= end ||
# 209 [ - + ]: 341 : hatoui(raw + 1, raw + 1 + 4, codepoint) !=
# 210 : 341 : raw + 1 + 4)
# 211 : 0 : return JTOK_ERR;
# 212 : 341 : writer.push_back_u(codepoint);
# 213 : 341 : raw += 4;
# 214 : 341 : break;
# 215 : 341 : }
# 216 [ - + ]: 0 : default:
# 217 : 0 : return JTOK_ERR;
# 218 : :
# 219 : 382 : }
# 220 : :
# 221 : 382 : raw++; // skip esc'd char
# 222 : 382 : }
# 223 : :
# 224 [ + + ]: 287095782 : else if (*raw == '"') {
# 225 : 1029314 : raw++; // skip "
# 226 : 1029314 : break; // stop scanning
# 227 : 1029314 : }
# 228 : :
# 229 : 286066468 : else {
# 230 : 286066468 : writer.push_back(static_cast<unsigned char>(*raw));
# 231 : 286066468 : raw++;
# 232 : 286066468 : }
# 233 : 287096164 : }
# 234 : :
# 235 [ - + ]: 1029314 : if (!writer.finalize())
# 236 : 0 : return JTOK_ERR;
# 237 : 1029314 : tokenVal = valStr;
# 238 : 1029314 : consumed = (raw - rawStart);
# 239 : 1029314 : return JTOK_STRING;
# 240 : 1029314 : }
# 241 : :
# 242 [ + + ]: 17 : default:
# 243 : 17 : return JTOK_ERR;
# 244 : 4032411 : }
# 245 : 4032411 : }
# 246 : :
# 247 : : enum expect_bits : unsigned {
# 248 : : EXP_OBJ_NAME = (1U << 0),
# 249 : : EXP_COLON = (1U << 1),
# 250 : : EXP_ARR_VALUE = (1U << 2),
# 251 : : EXP_VALUE = (1U << 3),
# 252 : : EXP_NOT_VALUE = (1U << 4),
# 253 : : };
# 254 : :
# 255 : 15246482 : #define expect(bit) (expectMask & (EXP_##bit))
# 256 : 3678538 : #define setExpect(bit) (expectMask |= EXP_##bit)
# 257 : 3794097 : #define clearExpect(bit) (expectMask &= ~EXP_##bit)
# 258 : :
# 259 : : bool UniValue::read(const char *raw, size_t size)
# 260 : 140151 : {
# 261 : 140151 : clear();
# 262 : :
# 263 : 140151 : uint32_t expectMask = 0;
# 264 : 140151 : std::vector<UniValue*> stack;
# 265 : :
# 266 : 140151 : std::string tokenVal;
# 267 : 140151 : unsigned int consumed;
# 268 : 140151 : enum jtokentype tok = JTOK_NONE;
# 269 : 140151 : enum jtokentype last_tok = JTOK_NONE;
# 270 : 140151 : const char* end = raw + size;
# 271 : 3050718 : do {
# 272 : 3050718 : last_tok = tok;
# 273 : :
# 274 : 3050718 : tok = getJsonToken(tokenVal, consumed, raw, end);
# 275 [ + + ][ + + ]: 3050718 : if (tok == JTOK_NONE || tok == JTOK_ERR)
# 276 : 31 : return false;
# 277 : 3050687 : raw += consumed;
# 278 : :
# 279 [ + + ]: 3050687 : bool isValueOpen = jsonTokenIsValue(tok) ||
# 280 [ + + ][ + + ]: 3050687 : tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN;
# 281 : :
# 282 [ + + ]: 3050687 : if (expect(VALUE)) {
# 283 [ - + ]: 627854 : if (!isValueOpen)
# 284 : 0 : return false;
# 285 : 627854 : clearExpect(VALUE);
# 286 : :
# 287 [ + + ]: 2422833 : } else if (expect(ARR_VALUE)) {
# 288 [ + + ][ + - ]: 172011 : bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE);
# 289 [ - + ]: 172011 : if (!isArrValue)
# 290 : 0 : return false;
# 291 : :
# 292 : 172011 : clearExpect(ARR_VALUE);
# 293 : :
# 294 [ + + ]: 2250822 : } else if (expect(OBJ_NAME)) {
# 295 [ + + ][ + - ]: 684873 : bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING);
# 296 [ - + ]: 684873 : if (!isObjName)
# 297 : 0 : return false;
# 298 : :
# 299 [ + + ]: 1565949 : } else if (expect(COLON)) {
# 300 [ - + ]: 627854 : if (tok != JTOK_COLON)
# 301 : 0 : return false;
# 302 : 627854 : clearExpect(COLON);
# 303 : :
# 304 [ + - ][ - + ]: 938095 : } else if (!expect(COLON) && (tok == JTOK_COLON)) {
# 305 : 0 : return false;
# 306 : 0 : }
# 307 : :
# 308 [ + + ]: 3050687 : if (expect(NOT_VALUE)) {
# 309 [ - + ]: 1425801 : if (isValueOpen)
# 310 : 0 : return false;
# 311 : 1425801 : clearExpect(NOT_VALUE);
# 312 : 1425801 : }
# 313 : :
# 314 : 3050687 : switch (tok) {
# 315 : :
# 316 [ + + ]: 225405 : case JTOK_OBJ_OPEN:
# 317 [ + + ]: 312751 : case JTOK_ARR_OPEN: {
# 318 [ + + ]: 312751 : VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
# 319 [ + + ]: 312751 : if (!stack.size()) {
# 320 [ + + ]: 140145 : if (utyp == VOBJ)
# 321 : 139603 : setObject();
# 322 : 542 : else
# 323 : 542 : setArray();
# 324 : 140145 : stack.push_back(this);
# 325 : 172606 : } else {
# 326 : 172606 : UniValue tmpVal(utyp);
# 327 : 172606 : UniValue *top = stack.back();
# 328 : 172606 : top->values.push_back(tmpVal);
# 329 : :
# 330 : 172606 : UniValue *newTop = &(top->values.back());
# 331 : 172606 : stack.push_back(newTop);
# 332 : 172606 : }
# 333 : :
# 334 [ - + ]: 312751 : if (stack.size() > MAX_JSON_DEPTH)
# 335 : 0 : return false;
# 336 : :
# 337 [ + + ]: 312751 : if (utyp == VOBJ)
# 338 : 225405 : setExpect(OBJ_NAME);
# 339 : 87346 : else
# 340 : 87346 : setExpect(ARR_VALUE);
# 341 : 312751 : break;
# 342 : 312751 : }
# 343 : :
# 344 [ + + ]: 225403 : case JTOK_OBJ_CLOSE:
# 345 [ + + ]: 312723 : case JTOK_ARR_CLOSE: {
# 346 [ - + ][ - + ]: 312723 : if (!stack.size() || (last_tok == JTOK_COMMA))
# 347 : 0 : return false;
# 348 : :
# 349 [ + + ]: 312723 : VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
# 350 : 312723 : UniValue *top = stack.back();
# 351 [ - + ]: 312723 : if (utyp != top->getType())
# 352 : 0 : return false;
# 353 : :
# 354 : 312723 : stack.pop_back();
# 355 : 312723 : clearExpect(OBJ_NAME);
# 356 : 312723 : setExpect(NOT_VALUE);
# 357 : 312723 : break;
# 358 : 312723 : }
# 359 : :
# 360 [ + + ]: 627854 : case JTOK_COLON: {
# 361 [ - + ]: 627854 : if (!stack.size())
# 362 : 0 : return false;
# 363 : :
# 364 : 627854 : UniValue *top = stack.back();
# 365 [ - + ]: 627854 : if (top->getType() != VOBJ)
# 366 : 0 : return false;
# 367 : :
# 368 : 627854 : setExpect(VALUE);
# 369 : 627854 : break;
# 370 : 627854 : }
# 371 : :
# 372 [ + + ]: 544153 : case JTOK_COMMA: {
# 373 [ - + ]: 544153 : if (!stack.size() ||
# 374 [ - + ][ - + ]: 544153 : (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
# 375 : 0 : return false;
# 376 : :
# 377 : 544153 : UniValue *top = stack.back();
# 378 [ + + ]: 544153 : if (top->getType() == VOBJ)
# 379 : 459470 : setExpect(OBJ_NAME);
# 380 : 84683 : else
# 381 : 84683 : setExpect(ARR_VALUE);
# 382 : 544153 : break;
# 383 : 544153 : }
# 384 : :
# 385 [ + + ]: 5076 : case JTOK_KW_NULL:
# 386 [ + + ]: 9462 : case JTOK_KW_TRUE:
# 387 [ + + ]: 11600 : case JTOK_KW_FALSE: {
# 388 : 11600 : UniValue tmpVal;
# 389 : 11600 : switch (tok) {
# 390 [ + + ]: 5076 : case JTOK_KW_NULL:
# 391 : : // do nothing more
# 392 : 5076 : break;
# 393 [ + + ]: 4386 : case JTOK_KW_TRUE:
# 394 : 4386 : tmpVal.setBool(true);
# 395 : 4386 : break;
# 396 [ + + ]: 2138 : case JTOK_KW_FALSE:
# 397 : 2138 : tmpVal.setBool(false);
# 398 : 2138 : break;
# 399 [ - + ]: 0 : default: /* impossible */ break;
# 400 : 11600 : }
# 401 : :
# 402 [ - + ]: 11600 : if (!stack.size()) {
# 403 : 0 : *this = tmpVal;
# 404 : 0 : break;
# 405 : 0 : }
# 406 : :
# 407 : 11600 : UniValue *top = stack.back();
# 408 : 11600 : top->values.push_back(tmpVal);
# 409 : :
# 410 : 11600 : setExpect(NOT_VALUE);
# 411 : 11600 : break;
# 412 : 11600 : }
# 413 : :
# 414 [ + + ]: 212292 : case JTOK_NUMBER: {
# 415 : 212292 : UniValue tmpVal(VNUM, tokenVal);
# 416 [ - + ]: 212292 : if (!stack.size()) {
# 417 : 0 : *this = tmpVal;
# 418 : 0 : break;
# 419 : 0 : }
# 420 : :
# 421 : 212292 : UniValue *top = stack.back();
# 422 : 212292 : top->values.push_back(tmpVal);
# 423 : :
# 424 : 212292 : setExpect(NOT_VALUE);
# 425 : 212292 : break;
# 426 : 212292 : }
# 427 : :
# 428 [ + + ]: 1029314 : case JTOK_STRING: {
# 429 [ + + ]: 1029314 : if (expect(OBJ_NAME)) {
# 430 : 627854 : UniValue *top = stack.back();
# 431 : 627854 : top->keys.push_back(tokenVal);
# 432 : 627854 : clearExpect(OBJ_NAME);
# 433 : 627854 : setExpect(COLON);
# 434 : 627854 : } else {
# 435 : 401460 : UniValue tmpVal(VSTR, tokenVal);
# 436 [ + + ]: 401460 : if (!stack.size()) {
# 437 : 3 : *this = tmpVal;
# 438 : 3 : break;
# 439 : 3 : }
# 440 : 401457 : UniValue *top = stack.back();
# 441 : 401457 : top->values.push_back(tmpVal);
# 442 : 401457 : }
# 443 : :
# 444 : 1029311 : setExpect(NOT_VALUE);
# 445 : 1029311 : break;
# 446 : 1029314 : }
# 447 : :
# 448 [ - + ]: 0 : default:
# 449 : 0 : return false;
# 450 : 3050687 : }
# 451 [ + + ]: 3050687 : } while (!stack.empty ());
# 452 : :
# 453 : : /* Check that nothing follows the initial construct (parsed above). */
# 454 : 140120 : tok = getJsonToken(tokenVal, consumed, raw, end);
# 455 [ + + ]: 140120 : if (tok != JTOK_NONE)
# 456 : 2 : return false;
# 457 : :
# 458 : 140118 : return true;
# 459 : 140120 : }
# 460 : :
|