Files
thehub/hub/3rdparty/univalue/lib/univalue_read.cpp
tomFlowee c63519fa47 Move 3rdparty libraries around.
The leveldb and univalue 3rd party libraries are not installed and
not needed by anyone outside of the Hub.
So move them there, making it easier for 3rd party usage.
2022-12-13 11:34:54 +01:00

447 lines
11 KiB
C++

// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <string.h>
#include <vector>
#include <stdio.h>
#include "univalue.h"
static bool json_isdigit(int ch)
{
return ((ch >= '0') && (ch <= '9'));
}
// convert hexadecimal string to unsigned integer
static const char *hatoui(const char *first, const char *last,
unsigned int& out)
{
unsigned int result = 0;
for (; first != last; ++first)
{
int digit;
if (json_isdigit(*first))
digit = *first - '0';
else if (*first >= 'a' && *first <= 'f')
digit = *first - 'a' + 10;
else if (*first >= 'A' && *first <= 'F')
digit = *first - 'A' + 10;
else
break;
result = 16 * result + digit;
}
out = result;
return first;
}
enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
const char *raw)
{
tokenVal.clear();
consumed = 0;
const char *rawStart = raw;
while ((*raw) && (json_isspace(*raw))) // skip whitespace
raw++;
switch (*raw) {
case 0:
return JTOK_NONE;
case '{':
raw++;
consumed = (raw - rawStart);
return JTOK_OBJ_OPEN;
case '}':
raw++;
consumed = (raw - rawStart);
return JTOK_OBJ_CLOSE;
case '[':
raw++;
consumed = (raw - rawStart);
return JTOK_ARR_OPEN;
case ']':
raw++;
consumed = (raw - rawStart);
return JTOK_ARR_CLOSE;
case ':':
raw++;
consumed = (raw - rawStart);
return JTOK_COLON;
case ',':
raw++;
consumed = (raw - rawStart);
return JTOK_COMMA;
case 'n':
case 't':
case 'f':
if (!strncmp(raw, "null", 4)) {
raw += 4;
consumed = (raw - rawStart);
return JTOK_KW_NULL;
} else if (!strncmp(raw, "true", 4)) {
raw += 4;
consumed = (raw - rawStart);
return JTOK_KW_TRUE;
} else if (!strncmp(raw, "false", 5)) {
raw += 5;
consumed = (raw - rawStart);
return JTOK_KW_FALSE;
} else
return JTOK_ERR;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
// part 1: int
std::string numStr;
const char *first = raw;
const char *firstDigit = first;
if (!json_isdigit(*firstDigit))
firstDigit++;
if ((*firstDigit == '0') && json_isdigit(firstDigit[1]))
return JTOK_ERR;
numStr += *raw; // copy first char
raw++;
if ((*first == '-') && (!json_isdigit(*raw)))
return JTOK_ERR;
while ((*raw) && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
// part 2: frac
if (*raw == '.') {
numStr += *raw; // copy .
raw++;
if (!json_isdigit(*raw))
return JTOK_ERR;
while ((*raw) && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
}
// part 3: exp
if (*raw == 'e' || *raw == 'E') {
numStr += *raw; // copy E
raw++;
if (*raw == '-' || *raw == '+') { // copy +/-
numStr += *raw;
raw++;
}
if (!json_isdigit(*raw))
return JTOK_ERR;
while ((*raw) && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
}
tokenVal = numStr;
consumed = (raw - rawStart);
return JTOK_NUMBER;
}
case '"': {
raw++; // skip "
std::string valStr;
while (*raw) {
if (*raw < 0x20)
return JTOK_ERR;
else if (*raw == '\\') {
raw++; // skip backslash
switch (*raw) {
case '"': valStr += "\""; break;
case '\\': valStr += "\\"; break;
case '/': valStr += "/"; break;
case 'b': valStr += "\b"; break;
case 'f': valStr += "\f"; break;
case 'n': valStr += "\n"; break;
case 'r': valStr += "\r"; break;
case 't': valStr += "\t"; break;
case 'u': {
unsigned int codepoint;
if (hatoui(raw + 1, raw + 1 + 4, codepoint) !=
raw + 1 + 4)
return JTOK_ERR;
if (codepoint <= 0x7f)
valStr.push_back((char)codepoint);
else if (codepoint <= 0x7FF) {
valStr.push_back((char)(0xC0 | (codepoint >> 6)));
valStr.push_back((char)(0x80 | (codepoint & 0x3F)));
} else if (codepoint <= 0xFFFF) {
valStr.push_back((char)(0xE0 | (codepoint >> 12)));
valStr.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F)));
valStr.push_back((char)(0x80 | (codepoint & 0x3F)));
}
raw += 4;
break;
}
default:
return JTOK_ERR;
}
raw++; // skip esc'd char
}
else if (*raw == '"') {
raw++; // skip "
break; // stop scanning
}
else {
valStr += *raw;
raw++;
}
}
tokenVal = valStr;
consumed = (raw - rawStart);
return JTOK_STRING;
}
default:
return JTOK_ERR;
}
}
enum expect_bits {
EXP_OBJ_NAME = (1U << 0),
EXP_COLON = (1U << 1),
EXP_ARR_VALUE = (1U << 2),
EXP_VALUE = (1U << 3),
EXP_NOT_VALUE = (1U << 4),
};
#define expect(bit) (expectMask & (EXP_##bit))
#define setExpect(bit) (expectMask |= EXP_##bit)
#define clearExpect(bit) (expectMask &= ~EXP_##bit)
bool UniValue::read(const char *raw)
{
clear();
uint32_t expectMask = 0;
std::vector<UniValue*> stack;
std::string tokenVal;
unsigned int consumed;
enum jtokentype tok = JTOK_NONE;
enum jtokentype last_tok = JTOK_NONE;
do {
last_tok = tok;
tok = getJsonToken(tokenVal, consumed, raw);
if (tok == JTOK_NONE || tok == JTOK_ERR)
return false;
raw += consumed;
bool isValueOpen = jsonTokenIsValue(tok) ||
tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN;
if (expect(VALUE)) {
if (!isValueOpen)
return false;
clearExpect(VALUE);
} else if (expect(ARR_VALUE)) {
bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE);
if (!isArrValue)
return false;
clearExpect(ARR_VALUE);
} else if (expect(OBJ_NAME)) {
bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING);
if (!isObjName)
return false;
} else if (expect(COLON)) {
if (tok != JTOK_COLON)
return false;
clearExpect(COLON);
} else if (!expect(COLON) && (tok == JTOK_COLON)) {
return false;
}
if (expect(NOT_VALUE)) {
if (isValueOpen)
return false;
clearExpect(NOT_VALUE);
}
switch (tok) {
case JTOK_OBJ_OPEN:
case JTOK_ARR_OPEN: {
VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
if (!stack.size()) {
if (utyp == VOBJ)
setObject();
else
setArray();
stack.push_back(this);
} else {
UniValue tmpVal(utyp);
UniValue *top = stack.back();
top->values.push_back(tmpVal);
UniValue *newTop = &(top->values.back());
stack.push_back(newTop);
}
if (utyp == VOBJ)
setExpect(OBJ_NAME);
else
setExpect(ARR_VALUE);
break;
}
case JTOK_OBJ_CLOSE:
case JTOK_ARR_CLOSE: {
if (!stack.size() || (last_tok == JTOK_COMMA))
return false;
VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
UniValue *top = stack.back();
if (utyp != top->getType())
return false;
stack.pop_back();
clearExpect(OBJ_NAME);
setExpect(NOT_VALUE);
break;
}
case JTOK_COLON: {
if (!stack.size())
return false;
UniValue *top = stack.back();
if (top->getType() != VOBJ)
return false;
setExpect(VALUE);
break;
}
case JTOK_COMMA: {
if (!stack.size() ||
(last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
return false;
UniValue *top = stack.back();
if (top->getType() == VOBJ)
setExpect(OBJ_NAME);
else
setExpect(ARR_VALUE);
break;
}
case JTOK_KW_NULL:
case JTOK_KW_TRUE:
case JTOK_KW_FALSE: {
if (!stack.size())
return false;
UniValue tmpVal;
switch (tok) {
case JTOK_KW_NULL:
// do nothing more
break;
case JTOK_KW_TRUE:
tmpVal.setBool(true);
break;
case JTOK_KW_FALSE:
tmpVal.setBool(false);
break;
default: /* impossible */ break;
}
UniValue *top = stack.back();
top->values.push_back(tmpVal);
setExpect(NOT_VALUE);
break;
}
case JTOK_NUMBER: {
if (!stack.size())
return false;
UniValue tmpVal(VNUM, tokenVal);
UniValue *top = stack.back();
top->values.push_back(tmpVal);
setExpect(NOT_VALUE);
break;
}
case JTOK_STRING: {
if (!stack.size())
return false;
UniValue *top = stack.back();
if (expect(OBJ_NAME)) {
top->keys.push_back(tokenVal);
clearExpect(OBJ_NAME);
setExpect(COLON);
} else {
UniValue tmpVal(VSTR, tokenVal);
top->values.push_back(tmpVal);
}
setExpect(NOT_VALUE);
break;
}
default:
return false;
}
} while (!stack.empty ());
/* Check that nothing follows the initial construct (parsed above). */
tok = getJsonToken(tokenVal, consumed, raw);
if (tok != JTOK_NONE)
return false;
return true;
}