Changeset View
Changeset View
Standalone View
Standalone View
src/univalue/lib/univalue_read.cpp
// Copyright 2014 BitPay Inc. | // Copyright 2014 BitPay Inc. | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <string.h> | #include <string.h> | ||||
#include <vector> | #include <vector> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include "univalue.h" | #include "univalue.h" | ||||
#include "univalue_utffilter.h" | #include "univalue_utffilter.h" | ||||
using namespace std; | |||||
static bool json_isdigit(int ch) | static bool json_isdigit(int ch) | ||||
{ | { | ||||
return ((ch >= '0') && (ch <= '9')); | return ((ch >= '0') && (ch <= '9')); | ||||
} | } | ||||
// convert hexadecimal string to unsigned integer | // convert hexadecimal string to unsigned integer | ||||
static const char *hatoui(const char *first, const char *last, | static const char *hatoui(const char *first, const char *last, | ||||
unsigned int& out) | unsigned int& out) | ||||
Show All 16 Lines | for (; first != last; ++first) | ||||
result = 16 * result + digit; | result = 16 * result + digit; | ||||
} | } | ||||
out = result; | out = result; | ||||
return first; | return first; | ||||
} | } | ||||
enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, | enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed, | ||||
const char *raw) | const char *raw, const char *end) | ||||
{ | { | ||||
tokenVal.clear(); | tokenVal.clear(); | ||||
consumed = 0; | consumed = 0; | ||||
const char *rawStart = raw; | const char *rawStart = raw; | ||||
while ((*raw) && (json_isspace(*raw))) // skip whitespace | while (raw < end && (json_isspace(*raw))) // skip whitespace | ||||
raw++; | raw++; | ||||
switch (*raw) { | if (raw >= end) | ||||
case 0: | |||||
return JTOK_NONE; | return JTOK_NONE; | ||||
switch (*raw) { | |||||
case '{': | case '{': | ||||
raw++; | raw++; | ||||
consumed = (raw - rawStart); | consumed = (raw - rawStart); | ||||
return JTOK_OBJ_OPEN; | return JTOK_OBJ_OPEN; | ||||
case '}': | case '}': | ||||
raw++; | raw++; | ||||
consumed = (raw - rawStart); | consumed = (raw - rawStart); | ||||
return JTOK_OBJ_CLOSE; | return JTOK_OBJ_CLOSE; | ||||
Show All 40 Lines | enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed, | ||||
case '3': | case '3': | ||||
case '4': | case '4': | ||||
case '5': | case '5': | ||||
case '6': | case '6': | ||||
case '7': | case '7': | ||||
case '8': | case '8': | ||||
case '9': { | case '9': { | ||||
// part 1: int | // part 1: int | ||||
string numStr; | std::string numStr; | ||||
const char *first = raw; | const char *first = raw; | ||||
const char *firstDigit = first; | const char *firstDigit = first; | ||||
if (!json_isdigit(*firstDigit)) | if (!json_isdigit(*firstDigit)) | ||||
firstDigit++; | firstDigit++; | ||||
if ((*firstDigit == '0') && json_isdigit(firstDigit[1])) | if ((*firstDigit == '0') && json_isdigit(firstDigit[1])) | ||||
return JTOK_ERR; | return JTOK_ERR; | ||||
numStr += *raw; // copy first char | numStr += *raw; // copy first char | ||||
raw++; | raw++; | ||||
if ((*first == '-') && (!json_isdigit(*raw))) | if ((*first == '-') && (raw < end) && (!json_isdigit(*raw))) | ||||
return JTOK_ERR; | return JTOK_ERR; | ||||
while ((*raw) && json_isdigit(*raw)) { // copy digits | while (raw < end && json_isdigit(*raw)) { // copy digits | ||||
numStr += *raw; | numStr += *raw; | ||||
raw++; | raw++; | ||||
} | } | ||||
// part 2: frac | // part 2: frac | ||||
if (*raw == '.') { | if (raw < end && *raw == '.') { | ||||
numStr += *raw; // copy . | numStr += *raw; // copy . | ||||
raw++; | raw++; | ||||
if (!json_isdigit(*raw)) | if (raw >= end || !json_isdigit(*raw)) | ||||
return JTOK_ERR; | return JTOK_ERR; | ||||
while ((*raw) && json_isdigit(*raw)) { // copy digits | while (raw < end && json_isdigit(*raw)) { // copy digits | ||||
numStr += *raw; | numStr += *raw; | ||||
raw++; | raw++; | ||||
} | } | ||||
} | } | ||||
// part 3: exp | // part 3: exp | ||||
if (*raw == 'e' || *raw == 'E') { | if (raw < end && (*raw == 'e' || *raw == 'E')) { | ||||
numStr += *raw; // copy E | numStr += *raw; // copy E | ||||
raw++; | raw++; | ||||
if (*raw == '-' || *raw == '+') { // copy +/- | if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/- | ||||
numStr += *raw; | numStr += *raw; | ||||
raw++; | raw++; | ||||
} | } | ||||
if (!json_isdigit(*raw)) | if (raw >= end || !json_isdigit(*raw)) | ||||
return JTOK_ERR; | return JTOK_ERR; | ||||
while ((*raw) && json_isdigit(*raw)) { // copy digits | while (raw < end && json_isdigit(*raw)) { // copy digits | ||||
numStr += *raw; | numStr += *raw; | ||||
raw++; | raw++; | ||||
} | } | ||||
} | } | ||||
tokenVal = numStr; | tokenVal = numStr; | ||||
consumed = (raw - rawStart); | consumed = (raw - rawStart); | ||||
return JTOK_NUMBER; | return JTOK_NUMBER; | ||||
} | } | ||||
case '"': { | case '"': { | ||||
raw++; // skip " | raw++; // skip " | ||||
string valStr; | std::string valStr; | ||||
JSONUTF8StringFilter writer(valStr); | JSONUTF8StringFilter writer(valStr); | ||||
while (*raw) { | while (true) { | ||||
if ((unsigned char)*raw < 0x20) | if (raw >= end || (unsigned char)*raw < 0x20) | ||||
return JTOK_ERR; | return JTOK_ERR; | ||||
else if (*raw == '\\') { | else if (*raw == '\\') { | ||||
raw++; // skip backslash | raw++; // skip backslash | ||||
if (raw >= end) | |||||
return JTOK_ERR; | |||||
switch (*raw) { | switch (*raw) { | ||||
case '"': writer.push_back('\"'); break; | case '"': writer.push_back('\"'); break; | ||||
case '\\': writer.push_back('\\'); break; | case '\\': writer.push_back('\\'); break; | ||||
case '/': writer.push_back('/'); break; | case '/': writer.push_back('/'); break; | ||||
case 'b': writer.push_back('\b'); break; | case 'b': writer.push_back('\b'); break; | ||||
case 'f': writer.push_back('\f'); break; | case 'f': writer.push_back('\f'); break; | ||||
case 'n': writer.push_back('\n'); break; | case 'n': writer.push_back('\n'); break; | ||||
case 'r': writer.push_back('\r'); break; | case 'r': writer.push_back('\r'); break; | ||||
case 't': writer.push_back('\t'); break; | case 't': writer.push_back('\t'); break; | ||||
case 'u': { | case 'u': { | ||||
unsigned int codepoint; | unsigned int codepoint; | ||||
if (hatoui(raw + 1, raw + 1 + 4, codepoint) != | if (raw + 1 + 4 >= end || | ||||
hatoui(raw + 1, raw + 1 + 4, codepoint) != | |||||
raw + 1 + 4) | raw + 1 + 4) | ||||
return JTOK_ERR; | return JTOK_ERR; | ||||
writer.push_back_u(codepoint); | writer.push_back_u(codepoint); | ||||
raw += 4; | raw += 4; | ||||
break; | break; | ||||
} | } | ||||
default: | default: | ||||
return JTOK_ERR; | return JTOK_ERR; | ||||
Show All 33 Lines | enum expect_bits { | ||||
EXP_VALUE = (1U << 3), | EXP_VALUE = (1U << 3), | ||||
EXP_NOT_VALUE = (1U << 4), | EXP_NOT_VALUE = (1U << 4), | ||||
}; | }; | ||||
#define expect(bit) (expectMask & (EXP_##bit)) | #define expect(bit) (expectMask & (EXP_##bit)) | ||||
#define setExpect(bit) (expectMask |= EXP_##bit) | #define setExpect(bit) (expectMask |= EXP_##bit) | ||||
#define clearExpect(bit) (expectMask &= ~EXP_##bit) | #define clearExpect(bit) (expectMask &= ~EXP_##bit) | ||||
bool UniValue::read(const char *raw) | bool UniValue::read(const char *raw, size_t size) | ||||
{ | { | ||||
clear(); | clear(); | ||||
uint32_t expectMask = 0; | uint32_t expectMask = 0; | ||||
vector<UniValue*> stack; | std::vector<UniValue*> stack; | ||||
string tokenVal; | std::string tokenVal; | ||||
unsigned int consumed; | unsigned int consumed; | ||||
enum jtokentype tok = JTOK_NONE; | enum jtokentype tok = JTOK_NONE; | ||||
enum jtokentype last_tok = JTOK_NONE; | enum jtokentype last_tok = JTOK_NONE; | ||||
const char* end = raw + size; | |||||
do { | do { | ||||
last_tok = tok; | last_tok = tok; | ||||
tok = getJsonToken(tokenVal, consumed, raw); | tok = getJsonToken(tokenVal, consumed, raw, end); | ||||
if (tok == JTOK_NONE || tok == JTOK_ERR) | if (tok == JTOK_NONE || tok == JTOK_ERR) | ||||
return false; | return false; | ||||
raw += consumed; | raw += consumed; | ||||
bool isValueOpen = jsonTokenIsValue(tok) || | bool isValueOpen = jsonTokenIsValue(tok) || | ||||
tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN; | tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN; | ||||
if (expect(VALUE)) { | if (expect(VALUE)) { | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | do { | ||||
else | else | ||||
setExpect(ARR_VALUE); | setExpect(ARR_VALUE); | ||||
break; | break; | ||||
} | } | ||||
case JTOK_KW_NULL: | case JTOK_KW_NULL: | ||||
case JTOK_KW_TRUE: | case JTOK_KW_TRUE: | ||||
case JTOK_KW_FALSE: { | case JTOK_KW_FALSE: { | ||||
if (!stack.size()) | |||||
return false; | |||||
UniValue tmpVal; | UniValue tmpVal; | ||||
switch (tok) { | switch (tok) { | ||||
case JTOK_KW_NULL: | case JTOK_KW_NULL: | ||||
// do nothing more | // do nothing more | ||||
break; | break; | ||||
case JTOK_KW_TRUE: | case JTOK_KW_TRUE: | ||||
tmpVal.setBool(true); | tmpVal.setBool(true); | ||||
break; | break; | ||||
case JTOK_KW_FALSE: | case JTOK_KW_FALSE: | ||||
tmpVal.setBool(false); | tmpVal.setBool(false); | ||||
break; | break; | ||||
default: /* impossible */ break; | default: /* impossible */ break; | ||||
} | } | ||||
if (!stack.size()) { | |||||
*this = tmpVal; | |||||
break; | |||||
} | |||||
UniValue *top = stack.back(); | UniValue *top = stack.back(); | ||||
top->values.push_back(tmpVal); | top->values.push_back(tmpVal); | ||||
setExpect(NOT_VALUE); | setExpect(NOT_VALUE); | ||||
break; | break; | ||||
} | } | ||||
case JTOK_NUMBER: { | case JTOK_NUMBER: { | ||||
if (!stack.size()) | |||||
return false; | |||||
UniValue tmpVal(VNUM, tokenVal); | UniValue tmpVal(VNUM, tokenVal); | ||||
if (!stack.size()) { | |||||
*this = tmpVal; | |||||
break; | |||||
} | |||||
UniValue *top = stack.back(); | UniValue *top = stack.back(); | ||||
top->values.push_back(tmpVal); | top->values.push_back(tmpVal); | ||||
setExpect(NOT_VALUE); | setExpect(NOT_VALUE); | ||||
break; | break; | ||||
} | } | ||||
case JTOK_STRING: { | case JTOK_STRING: { | ||||
if (!stack.size()) | |||||
return false; | |||||
UniValue *top = stack.back(); | |||||
if (expect(OBJ_NAME)) { | if (expect(OBJ_NAME)) { | ||||
UniValue *top = stack.back(); | |||||
top->keys.push_back(tokenVal); | top->keys.push_back(tokenVal); | ||||
clearExpect(OBJ_NAME); | clearExpect(OBJ_NAME); | ||||
setExpect(COLON); | setExpect(COLON); | ||||
} else { | } else { | ||||
UniValue tmpVal(VSTR, tokenVal); | UniValue tmpVal(VSTR, tokenVal); | ||||
if (!stack.size()) { | |||||
*this = tmpVal; | |||||
break; | |||||
} | |||||
UniValue *top = stack.back(); | |||||
top->values.push_back(tmpVal); | top->values.push_back(tmpVal); | ||||
} | } | ||||
setExpect(NOT_VALUE); | setExpect(NOT_VALUE); | ||||
break; | break; | ||||
} | } | ||||
default: | default: | ||||
return false; | return false; | ||||
} | } | ||||
} while (!stack.empty ()); | } while (!stack.empty ()); | ||||
/* Check that nothing follows the initial construct (parsed above). */ | /* Check that nothing follows the initial construct (parsed above). */ | ||||
tok = getJsonToken(tokenVal, consumed, raw); | tok = getJsonToken(tokenVal, consumed, raw, end); | ||||
if (tok != JTOK_NONE) | if (tok != JTOK_NONE) | ||||
return false; | return false; | ||||
return true; | return true; | ||||
} | } | ||||