Merge commit '982709199f1b4e9e35211c419a81938f9f1dd4ed' into bitcoin

This commit is contained in:
MarcoFalke 2015-12-02 12:28:14 +01:00
commit fad4ea836d
16 changed files with 205 additions and 83 deletions

View File

@ -18,6 +18,7 @@ univalue-config.h*
test-driver test-driver
libtool libtool
ltmain.sh ltmain.sh
test-suite.log
*.a *.a
*.la *.la

View File

@ -36,7 +36,7 @@ script:
- ./configure --cache-file=config.cache $UNIVALUE_CONFIG_ALL $UNIVALUE_CONFIG || ( cat config.log && false) - ./configure --cache-file=config.cache $UNIVALUE_CONFIG_ALL $UNIVALUE_CONFIG || ( cat config.log && false)
- make -s $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL ; false ) - make -s $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL ; false )
- export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib
- if [ "$RUN_TESTS" = "true" ]; then make check; fi - if [ "$RUN_TESTS" = "true" ]; then make $MAKEJOBS distcheck; fi
matrix: matrix:
fast_finish: true fast_finish: true

View File

@ -70,6 +70,9 @@ TEST_FILES = \
$(TEST_DATA_DIR)/fail32.json \ $(TEST_DATA_DIR)/fail32.json \
$(TEST_DATA_DIR)/fail33.json \ $(TEST_DATA_DIR)/fail33.json \
$(TEST_DATA_DIR)/fail34.json \ $(TEST_DATA_DIR)/fail34.json \
$(TEST_DATA_DIR)/fail35.json \
$(TEST_DATA_DIR)/fail36.json \
$(TEST_DATA_DIR)/fail37.json \
$(TEST_DATA_DIR)/fail3.json \ $(TEST_DATA_DIR)/fail3.json \
$(TEST_DATA_DIR)/fail4.json \ $(TEST_DATA_DIR)/fail4.json \
$(TEST_DATA_DIR)/fail5.json \ $(TEST_DATA_DIR)/fail5.json \
@ -79,6 +82,7 @@ TEST_FILES = \
$(TEST_DATA_DIR)/fail9.json \ $(TEST_DATA_DIR)/fail9.json \
$(TEST_DATA_DIR)/pass1.json \ $(TEST_DATA_DIR)/pass1.json \
$(TEST_DATA_DIR)/pass2.json \ $(TEST_DATA_DIR)/pass2.json \
$(TEST_DATA_DIR)/pass3.json $(TEST_DATA_DIR)/pass3.json \
$(TEST_DATA_DIR)/round1.json
EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS) EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS)

View File

@ -14,7 +14,7 @@ m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_inter
m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()]) m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()])
AC_INIT([univalue], [1.0.0], AC_INIT([univalue], [1.0.1],
[http://github.com/jgarzik/univalue/]) [http://github.com/jgarzik/univalue/])
dnl make the compilation flags quiet unless V=1 is used dnl make the compilation flags quiet unless V=1 is used

View File

@ -8,7 +8,6 @@
// $ ./gen > univalue_escapes.h // $ ./gen > univalue_escapes.h
// //
#include <ctype.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "univalue.h" #include "univalue.h"
@ -16,10 +15,17 @@
using namespace std; using namespace std;
static bool initEscapes; static bool initEscapes;
static const char *escapes[256]; static std::string escapes[256];
static void initJsonEscape() static void initJsonEscape()
{ {
// Escape all lower control characters (some get overridden with smaller sequences below)
for (int ch=0x00; ch<0x20; ++ch) {
char tmpbuf[20];
snprintf(tmpbuf, sizeof(tmpbuf), "\\u%04x", ch);
escapes[ch] = std::string(tmpbuf);
}
escapes[(int)'"'] = "\\\""; escapes[(int)'"'] = "\\\"";
escapes[(int)'\\'] = "\\\\"; escapes[(int)'\\'] = "\\\\";
escapes[(int)'\b'] = "\\b"; escapes[(int)'\b'] = "\\b";
@ -27,6 +33,7 @@ static void initJsonEscape()
escapes[(int)'\n'] = "\\n"; escapes[(int)'\n'] = "\\n";
escapes[(int)'\r'] = "\\r"; escapes[(int)'\r'] = "\\r";
escapes[(int)'\t'] = "\\t"; escapes[(int)'\t'] = "\\t";
escapes[(int)'\x7f'] = "\\u007f"; // U+007F DELETE
initEscapes = true; initEscapes = true;
} }
@ -39,13 +46,13 @@ static void outputEscape()
"static const char *escapes[256] = {\n"); "static const char *escapes[256] = {\n");
for (unsigned int i = 0; i < 256; i++) { for (unsigned int i = 0; i < 256; i++) {
if (!escapes[i]) { if (escapes[i].empty()) {
printf("\tNULL,\n"); printf("\tNULL,\n");
} else { } else {
printf("\t\""); printf("\t\"");
unsigned int si; unsigned int si;
for (si = 0; si < strlen(escapes[i]); si++) { for (si = 0; si < escapes[i].size(); si++) {
char ch = escapes[i][si]; char ch = escapes[i][si];
switch (ch) { switch (ch) {
case '"': case '"':

View File

@ -243,6 +243,39 @@ extern enum jtokentype getJsonToken(std::string& tokenVal,
unsigned int& consumed, const char *raw); unsigned int& consumed, const char *raw);
extern const char *uvTypeName(UniValue::VType t); extern const char *uvTypeName(UniValue::VType t);
static inline bool jsonTokenIsValue(enum jtokentype jtt)
{
switch (jtt) {
case JTOK_KW_NULL:
case JTOK_KW_TRUE:
case JTOK_KW_FALSE:
case JTOK_NUMBER:
case JTOK_STRING:
return true;
default:
return false;
}
// not reached
}
static inline bool json_isspace(int ch)
{
switch (ch) {
case 0x20:
case 0x09:
case 0x0a:
case 0x0d:
return true;
default:
return false;
}
// not reached
}
extern const UniValue NullUniValue; extern const UniValue NullUniValue;
const UniValue& find_value( const UniValue& obj, const std::string& name); const UniValue& find_value( const UniValue& obj, const std::string& name);

View File

@ -4,7 +4,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <stdint.h> #include <stdint.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <iomanip> #include <iomanip>
#include <limits> #include <limits>
@ -21,7 +20,7 @@ static bool ParsePrechecks(const std::string& str)
{ {
if (str.empty()) // No empty string allowed if (str.empty()) // No empty string allowed
return false; return false;
if (str.size() >= 1 && (isspace(str[0]) || isspace(str[str.size()-1]))) // No padding allowed if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed
return false; return false;
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
return false; return false;
@ -210,7 +209,7 @@ bool UniValue::pushKVs(const UniValue& obj)
for (unsigned int i = 0; i < obj.keys.size(); i++) { for (unsigned int i = 0; i < obj.keys.size(); i++) {
keys.push_back(obj.keys[i]); keys.push_back(obj.keys[i]);
values.push_back(obj.values[i]); values.push_back(obj.values.at(i));
} }
return true; return true;
@ -234,7 +233,7 @@ bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t)
if (idx < 0) if (idx < 0)
return false; return false;
if (values[idx].getType() != it->second) if (values.at(idx).getType() != it->second)
return false; return false;
} }
@ -250,7 +249,7 @@ const UniValue& UniValue::operator[](const std::string& key) const
if (index < 0) if (index < 0)
return NullUniValue; return NullUniValue;
return values[index]; return values.at(index);
} }
const UniValue& UniValue::operator[](unsigned int index) const const UniValue& UniValue::operator[](unsigned int index) const
@ -260,7 +259,7 @@ const UniValue& UniValue::operator[](unsigned int index) const
if (index >= values.size()) if (index >= values.size())
return NullUniValue; return NullUniValue;
return values[index]; return values.at(index);
} }
const char *uvTypeName(UniValue::VType t) const char *uvTypeName(UniValue::VType t)
@ -278,15 +277,11 @@ const char *uvTypeName(UniValue::VType t)
return NULL; return NULL;
} }
const UniValue& find_value( const UniValue& obj, const std::string& name) const UniValue& find_value(const UniValue& obj, const std::string& name)
{ {
for (unsigned int i = 0; i < obj.keys.size(); i++) for (unsigned int i = 0; i < obj.keys.size(); i++)
{ if (obj.keys[i] == name)
if( obj.keys[i] == name ) return obj.values.at(i);
{
return obj.values[i];
}
}
return NullUniValue; return NullUniValue;
} }

View File

@ -2,38 +2,38 @@
#ifndef BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H #ifndef BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H
#define BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H #define BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H
static const char *escapes[256] = { static const char *escapes[256] = {
NULL, "\\u0000",
NULL, "\\u0001",
NULL, "\\u0002",
NULL, "\\u0003",
NULL, "\\u0004",
NULL, "\\u0005",
NULL, "\\u0006",
NULL, "\\u0007",
"\\b", "\\b",
"\\t", "\\t",
"\\n", "\\n",
NULL, "\\u000b",
"\\f", "\\f",
"\\r", "\\r",
NULL, "\\u000e",
NULL, "\\u000f",
NULL, "\\u0010",
NULL, "\\u0011",
NULL, "\\u0012",
NULL, "\\u0013",
NULL, "\\u0014",
NULL, "\\u0015",
NULL, "\\u0016",
NULL, "\\u0017",
NULL, "\\u0018",
NULL, "\\u0019",
NULL, "\\u001a",
NULL, "\\u001b",
NULL, "\\u001c",
NULL, "\\u001d",
NULL, "\\u001e",
NULL, "\\u001f",
NULL, NULL,
NULL, NULL,
"\\\"", "\\\"",
@ -129,7 +129,7 @@ static const char *escapes[256] = {
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL, "\\u007f",
NULL, NULL,
NULL, NULL,
NULL, NULL,

View File

@ -9,6 +9,11 @@
using namespace std; using namespace std;
static bool json_isdigit(int ch)
{
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)
@ -17,7 +22,7 @@ static const char *hatoui(const char *first, const char *last,
for (; first != last; ++first) for (; first != last; ++first)
{ {
int digit; int digit;
if (isdigit(*first)) if (json_isdigit(*first))
digit = *first - '0'; digit = *first - '0';
else if (*first >= 'a' && *first <= 'f') else if (*first >= 'a' && *first <= 'f')
@ -44,7 +49,7 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
const char *rawStart = raw; const char *rawStart = raw;
while ((*raw) && (isspace(*raw))) // skip whitespace while ((*raw) && (json_isspace(*raw))) // skip whitespace
raw++; raw++;
switch (*raw) { switch (*raw) {
@ -113,18 +118,18 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
const char *first = raw; const char *first = raw;
const char *firstDigit = first; const char *firstDigit = first;
if (!isdigit(*firstDigit)) if (!json_isdigit(*firstDigit))
firstDigit++; firstDigit++;
if ((*firstDigit == '0') && 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 == '-') && (!isdigit(*raw))) if ((*first == '-') && (!json_isdigit(*raw)))
return JTOK_ERR; return JTOK_ERR;
while ((*raw) && isdigit(*raw)) { // copy digits while ((*raw) && json_isdigit(*raw)) { // copy digits
numStr += *raw; numStr += *raw;
raw++; raw++;
} }
@ -134,9 +139,9 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
numStr += *raw; // copy . numStr += *raw; // copy .
raw++; raw++;
if (!isdigit(*raw)) if (!json_isdigit(*raw))
return JTOK_ERR; return JTOK_ERR;
while ((*raw) && isdigit(*raw)) { // copy digits while ((*raw) && json_isdigit(*raw)) { // copy digits
numStr += *raw; numStr += *raw;
raw++; raw++;
} }
@ -152,9 +157,9 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
raw++; raw++;
} }
if (!isdigit(*raw)) if (!json_isdigit(*raw))
return JTOK_ERR; return JTOK_ERR;
while ((*raw) && isdigit(*raw)) { // copy digits while ((*raw) && json_isdigit(*raw)) { // copy digits
numStr += *raw; numStr += *raw;
raw++; raw++;
} }
@ -236,12 +241,23 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
} }
} }
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) bool UniValue::read(const char *raw)
{ {
clear(); clear();
bool expectName = false; uint32_t expectMask = 0;
bool expectColon = false;
vector<UniValue*> stack; vector<UniValue*> stack;
string tokenVal; string tokenVal;
@ -256,6 +272,41 @@ bool UniValue::read(const char *raw)
return false; return false;
raw += consumed; 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) { switch (tok) {
case JTOK_OBJ_OPEN: case JTOK_OBJ_OPEN:
@ -277,13 +328,15 @@ bool UniValue::read(const char *raw)
} }
if (utyp == VOBJ) if (utyp == VOBJ)
expectName = true; setExpect(OBJ_NAME);
else
setExpect(ARR_VALUE);
break; break;
} }
case JTOK_OBJ_CLOSE: case JTOK_OBJ_CLOSE:
case JTOK_ARR_CLOSE: { case JTOK_ARR_CLOSE: {
if (!stack.size() || expectColon || (last_tok == JTOK_COMMA)) if (!stack.size() || (last_tok == JTOK_COMMA))
return false; return false;
VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR); VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
@ -292,37 +345,40 @@ bool UniValue::read(const char *raw)
return false; return false;
stack.pop_back(); stack.pop_back();
expectName = false; clearExpect(OBJ_NAME);
setExpect(NOT_VALUE);
break; break;
} }
case JTOK_COLON: { case JTOK_COLON: {
if (!stack.size() || expectName || !expectColon) if (!stack.size())
return false; return false;
UniValue *top = stack.back(); UniValue *top = stack.back();
if (top->getType() != VOBJ) if (top->getType() != VOBJ)
return false; return false;
expectColon = false; setExpect(VALUE);
break; break;
} }
case JTOK_COMMA: { case JTOK_COMMA: {
if (!stack.size() || expectName || expectColon || if (!stack.size() ||
(last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN)) (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
return false; return false;
UniValue *top = stack.back(); UniValue *top = stack.back();
if (top->getType() == VOBJ) if (top->getType() == VOBJ)
expectName = true; setExpect(OBJ_NAME);
else
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() || expectName || expectColon) if (!stack.size())
return false; return false;
UniValue tmpVal; UniValue tmpVal;
@ -342,17 +398,19 @@ bool UniValue::read(const char *raw)
UniValue *top = stack.back(); UniValue *top = stack.back();
top->values.push_back(tmpVal); top->values.push_back(tmpVal);
setExpect(NOT_VALUE);
break; break;
} }
case JTOK_NUMBER: { case JTOK_NUMBER: {
if (!stack.size() || expectName || expectColon) if (!stack.size())
return false; return false;
UniValue tmpVal(VNUM, tokenVal); UniValue tmpVal(VNUM, tokenVal);
UniValue *top = stack.back(); UniValue *top = stack.back();
top->values.push_back(tmpVal); top->values.push_back(tmpVal);
setExpect(NOT_VALUE);
break; break;
} }
@ -362,15 +420,16 @@ bool UniValue::read(const char *raw)
UniValue *top = stack.back(); UniValue *top = stack.back();
if (expectName) { if (expect(OBJ_NAME)) {
top->keys.push_back(tokenVal); top->keys.push_back(tokenVal);
expectName = false; clearExpect(OBJ_NAME);
expectColon = true; setExpect(COLON);
} else { } else {
UniValue tmpVal(VSTR, tokenVal); UniValue tmpVal(VSTR, tokenVal);
top->values.push_back(tmpVal); top->values.push_back(tmpVal);
} }
setExpect(NOT_VALUE);
break; break;
} }

View File

@ -2,7 +2,6 @@
// 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 <ctype.h>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <stdio.h> #include <stdio.h>
@ -25,10 +24,10 @@ static string json_escape(const string& inS)
if (escStr) if (escStr)
outS += escStr; outS += escStr;
else if (isprint(ch)) else if (ch < 0x80)
outS += ch; outS += ch;
else { else { // TODO handle UTF-8 properly
char tmpesc[16]; char tmpesc[16];
sprintf(tmpesc, "\\u%04x", ch); sprintf(tmpesc, "\\u%04x", ch);
outS += tmpesc; outS += tmpesc;
@ -113,7 +112,7 @@ void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel,
s += "\"" + json_escape(keys[i]) + "\":"; s += "\"" + json_escape(keys[i]) + "\":";
if (prettyIndent) if (prettyIndent)
s += " "; s += " ";
s += values[i].write(prettyIndent, indentLevel + 1); s += values.at(i).write(prettyIndent, indentLevel + 1);
if (i != (values.size() - 1)) if (i != (values.size() - 1))
s += ","; s += ",";
if (prettyIndent) if (prettyIndent)

View File

@ -1 +1,4 @@
unitester unitester
*.trs
*.log

View File

@ -0,0 +1 @@
[ true true true [] [] [] ]

View File

@ -0,0 +1 @@
{"a":}

View File

@ -0,0 +1 @@
{"a":1 "b":2}

View File

@ -0,0 +1 @@
["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f"]

View File

@ -19,24 +19,37 @@
using namespace std; using namespace std;
string srcdir(JSON_TEST_SRC); string srcdir(JSON_TEST_SRC);
static bool test_failed = false;
#define d_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", filename.c_str()); } }
static std::string rtrim(std::string s)
{
s.erase(s.find_last_not_of(" \n\r\t")+1);
return s;
}
static void runtest(string filename, const string& jdata) static void runtest(string filename, const string& jdata)
{ {
fprintf(stderr, "test %s\n", filename.c_str());
string prefix = filename.substr(0, 4); string prefix = filename.substr(0, 4);
bool wantPass = (prefix == "pass"); bool wantPass = (prefix == "pass") || (prefix == "roun");
bool wantFail = (prefix == "fail"); bool wantFail = (prefix == "fail");
bool wantRoundTrip = (prefix == "roun");
assert(wantPass || wantFail); assert(wantPass || wantFail);
UniValue val; UniValue val;
bool testResult = val.read(jdata); bool testResult = val.read(jdata);
if (wantPass) { if (wantPass) {
assert(testResult == true); d_assert(testResult == true);
} else { } else {
assert(testResult == false); d_assert(testResult == false);
}
if (wantRoundTrip) {
std::string odata = val.write(0, 0);
assert(odata == rtrim(jdata));
} }
} }
@ -92,6 +105,9 @@ static const char *filenames[] = {
"fail32.json", "fail32.json",
"fail33.json", "fail33.json",
"fail34.json", "fail34.json",
"fail35.json",
"fail36.json",
"fail37.json",
"fail3.json", "fail3.json",
"fail4.json", // extra comma "fail4.json", // extra comma
"fail5.json", "fail5.json",
@ -102,6 +118,7 @@ static const char *filenames[] = {
"pass1.json", "pass1.json",
"pass2.json", "pass2.json",
"pass3.json", "pass3.json",
"round1.json", // round-trip test
}; };
int main (int argc, char *argv[]) int main (int argc, char *argv[])
@ -110,6 +127,6 @@ int main (int argc, char *argv[])
runtest_file(filenames[fidx]); runtest_file(filenames[fidx]);
} }
return 0; return test_failed ? 1 : 0;
} }