Merge #11420: Bump univalue subtree and fix json formatting in tests

619bb05 Squashed 'src/univalue/' changes from 16a1f7f6e..fe805ea74 (MarcoFalke)

Pull request description:

  The subtree-merge commit also fixes the whitespace for failing tests, such that bisect doesn't break.

  Finally, the bump also includes the changes that accidentally modified our subtree, such that the subtree check should work fine now:

  ```sh
  ./contrib/devtools/git-subtree-check.sh src/univalue

Tree-SHA512: 3009d1e52b6f41ef89ecc8a000649f08e44395538703f294995a6e913e3fbfb7813d6bd31fdb4acb6127fd4af99c095bf980a12f1f026bb27cacc66e1487cd1e
This commit is contained in:
Wladimir J. van der Laan 2017-10-11 18:12:18 +02:00 committed by Pasta
parent 7f2ebba617
commit 36bb302f80
31 changed files with 852 additions and 298 deletions

View File

@ -12,6 +12,7 @@ pkgconfig_DATA = pc/libunivalue.pc
libunivalue_la_SOURCES = \ libunivalue_la_SOURCES = \
lib/univalue.cpp \ lib/univalue.cpp \
lib/univalue_get.cpp \
lib/univalue_read.cpp \ lib/univalue_read.cpp \
lib/univalue_write.cpp lib/univalue_write.cpp
@ -20,7 +21,7 @@ libunivalue_la_LDFLAGS = \
-no-undefined -no-undefined
libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include
TESTS = test/unitester TESTS = test/object test/unitester test/no_nul
GENBIN = gen/gen$(BUILD_EXEEXT) GENBIN = gen/gen$(BUILD_EXEEXT)
GEN_SRCS = gen/gen.cpp GEN_SRCS = gen/gen.cpp
@ -33,7 +34,7 @@ gen: lib/univalue_escapes.h $(GENBIN)
@echo Updating $< @echo Updating $<
$(AM_V_at)$(GENBIN) > lib/univalue_escapes.h $(AM_V_at)$(GENBIN) > lib/univalue_escapes.h
noinst_PROGRAMS = $(TESTS) noinst_PROGRAMS = $(TESTS) test/test_json
TEST_DATA_DIR=test TEST_DATA_DIR=test
@ -42,6 +43,21 @@ test_unitester_LDADD = libunivalue.la
test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\" test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\"
test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
test_test_json_SOURCES = test/test_json.cpp
test_test_json_LDADD = libunivalue.la
test_test_json_CXXFLAGS = -I$(top_srcdir)/include
test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
test_no_nul_SOURCES = test/no_nul.cpp
test_no_nul_LDADD = libunivalue.la
test_no_nul_CXXFLAGS = -I$(top_srcdir)/include
test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
test_object_SOURCES = test/object.cpp
test_object_LDADD = libunivalue.la
test_object_CXXFLAGS = -I$(top_srcdir)/include
test_object_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
TEST_FILES = \ TEST_FILES = \
$(TEST_DATA_DIR)/fail10.json \ $(TEST_DATA_DIR)/fail10.json \
$(TEST_DATA_DIR)/fail11.json \ $(TEST_DATA_DIR)/fail11.json \
@ -77,6 +93,8 @@ TEST_FILES = \
$(TEST_DATA_DIR)/fail39.json \ $(TEST_DATA_DIR)/fail39.json \
$(TEST_DATA_DIR)/fail40.json \ $(TEST_DATA_DIR)/fail40.json \
$(TEST_DATA_DIR)/fail41.json \ $(TEST_DATA_DIR)/fail41.json \
$(TEST_DATA_DIR)/fail42.json \
$(TEST_DATA_DIR)/fail44.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 \
@ -88,6 +106,11 @@ TEST_FILES = \
$(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 \ $(TEST_DATA_DIR)/round1.json \
$(TEST_DATA_DIR)/round2.json $(TEST_DATA_DIR)/round2.json \
$(TEST_DATA_DIR)/round3.json \
$(TEST_DATA_DIR)/round4.json \
$(TEST_DATA_DIR)/round5.json \
$(TEST_DATA_DIR)/round6.json \
$(TEST_DATA_DIR)/round7.json
EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS) EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS)

View File

@ -1,7 +0,0 @@
UniValue
A universal value object, with JSON encoding (output) and decoding (input).
Built as a single dynamic RAII C++ object class, and no templates.

32
src/univalue/README.md Normal file
View File

@ -0,0 +1,32 @@
# UniValue
## Summary
A universal value class, with JSON encoding and decoding.
UniValue is an abstract data type that may be a null, boolean, string,
number, array container, or a key/value dictionary container, nested to
an arbitrary depth.
This class is aligned with the JSON standard, [RFC
7159](https://tools.ietf.org/html/rfc7159.html).
## Installation
This project is a standard GNU
[autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html)
project. Build and install instructions are available in the `INSTALL`
file provided with GNU autotools.
```
$ ./autogen.sh
$ ./configure
$ make
```
## Design
UniValue provides a single dynamic RAII C++ object class,
and minimizes template use (contra json_spirit).

View File

@ -1,7 +1,7 @@
m4_define([libunivalue_major_version], [1]) m4_define([libunivalue_major_version], [1])
m4_define([libunivalue_minor_version], [1]) m4_define([libunivalue_minor_version], [1])
m4_define([libunivalue_micro_version], [2]) m4_define([libunivalue_micro_version], [3])
m4_define([libunivalue_interface_age], [2]) m4_define([libunivalue_interface_age], [3])
# If you need a modifier for the version number. # If you need a modifier for the version number.
# Normally empty, but can be used to make "fixup" releases. # Normally empty, but can be used to make "fixup" releases.
m4_define([libunivalue_extraversion], []) m4_define([libunivalue_extraversion], [])
@ -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.2], AC_INIT([univalue], [1.0.3],
[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

@ -7,6 +7,7 @@
#define __UNIVALUE_H__ #define __UNIVALUE_H__
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <string> #include <string>
#include <vector> #include <vector>
@ -69,10 +70,11 @@ public:
size_t size() const { return values.size(); } size_t size() const { return values.size(); }
bool getBool() const { return isTrue(); } bool getBool() const { return isTrue(); }
bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes); void getObjMap(std::map<std::string,UniValue>& kv) const;
bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes) const;
const UniValue& operator[](const std::string& key) const; const UniValue& operator[](const std::string& key) const;
const UniValue& operator[](unsigned int index) const; const UniValue& operator[](size_t index) const;
bool exists(const std::string& key) const { return (findKey(key) >= 0); } bool exists(const std::string& key) const { size_t i; return findKey(key, i); }
bool isNull() const { return (typ == VNULL); } bool isNull() const { return (typ == VNULL); }
bool isTrue() const { return (typ == VBOOL) && (val == "1"); } bool isTrue() const { return (typ == VBOOL) && (val == "1"); }
@ -92,8 +94,25 @@ public:
std::string s(val_); std::string s(val_);
return push_back(s); return push_back(s);
} }
bool push_back(uint64_t val_) {
UniValue tmpVal(val_);
return push_back(tmpVal);
}
bool push_back(int64_t val_) {
UniValue tmpVal(val_);
return push_back(tmpVal);
}
bool push_back(int val_) {
UniValue tmpVal(val_);
return push_back(tmpVal);
}
bool push_back(double val_) {
UniValue tmpVal(val_);
return push_back(tmpVal);
}
bool push_backV(const std::vector<UniValue>& vec); bool push_backV(const std::vector<UniValue>& vec);
void __pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const UniValue& val); bool pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const std::string& val_) { bool pushKV(const std::string& key, const std::string& val_) {
UniValue tmpVal(VSTR, val_); UniValue tmpVal(VSTR, val_);
@ -124,9 +143,10 @@ public:
std::string write(unsigned int prettyIndent = 0, std::string write(unsigned int prettyIndent = 0,
unsigned int indentLevel = 0) const; unsigned int indentLevel = 0) const;
bool read(const char *raw); bool read(const char *raw, size_t len);
bool read(const char *raw) { return read(raw, strlen(raw)); }
bool read(const std::string& rawStr) { bool read(const std::string& rawStr) {
return read(rawStr.c_str()); return read(rawStr.data(), rawStr.size());
} }
private: private:
@ -135,7 +155,7 @@ private:
std::vector<std::string> keys; std::vector<std::string> keys;
std::vector<UniValue> values; std::vector<UniValue> values;
int findKey(const std::string& key) const; bool findKey(const std::string& key, size_t& retIdx) const;
void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
@ -240,7 +260,7 @@ enum jtokentype {
}; };
extern enum jtokentype getJsonToken(std::string& tokenVal, extern enum jtokentype getJsonToken(std::string& tokenVal,
unsigned int& consumed, const char *raw); unsigned int& consumed, const char *raw, const char *end);
extern const char *uvTypeName(UniValue::VType t); extern const char *uvTypeName(UniValue::VType t);
static inline bool jsonTokenIsValue(enum jtokentype jtt) static inline bool jsonTokenIsValue(enum jtokentype jtt)

View File

@ -4,75 +4,12 @@
// 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 <errno.h>
#include <iomanip> #include <iomanip>
#include <limits>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "univalue.h" #include "univalue.h"
namespace
{
static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed
return false;
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
return false;
return true;
}
bool ParseInt32(const std::string& str, int32_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = NULL;
errno = 0; // strtol will not set errno if valid
long int n = strtol(str.c_str(), &endp, 10);
if(out) *out = (int32_t)n;
// Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
// platforms the size of these types may be different.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int32_t>::min() &&
n <= std::numeric_limits<int32_t>::max();
}
bool ParseInt64(const std::string& str, int64_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = NULL;
errno = 0; // strtoll will not set errno if valid
long long int n = strtoll(str.c_str(), &endp, 10);
if(out) *out = (int64_t)n;
// Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int64_t*.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int64_t>::min() &&
n <= std::numeric_limits<int64_t>::max();
}
bool ParseDouble(const std::string& str, double *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
return false;
std::istringstream text(str);
text.imbue(std::locale::classic());
double result;
text >> result;
if(out) *out = result;
return text.eof() && !text.fail();
}
}
using namespace std; using namespace std;
const UniValue NullUniValue; const UniValue NullUniValue;
@ -104,7 +41,7 @@ static bool validNumStr(const string& s)
{ {
string tokenVal; string tokenVal;
unsigned int consumed; unsigned int consumed;
enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str()); enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size());
return (tt == JTOK_NUMBER); return (tt == JTOK_NUMBER);
} }
@ -189,13 +126,22 @@ bool UniValue::push_backV(const std::vector<UniValue>& vec)
return true; return true;
} }
void UniValue::__pushKV(const std::string& key, const UniValue& val_)
{
keys.push_back(key);
values.push_back(val_);
}
bool UniValue::pushKV(const std::string& key, const UniValue& val_) bool UniValue::pushKV(const std::string& key, const UniValue& val_)
{ {
if (typ != VOBJ) if (typ != VOBJ)
return false; return false;
keys.push_back(key); size_t idx;
values.push_back(val_); if (findKey(key, idx))
values[idx] = val_;
else
__pushKV(key, val_);
return true; return true;
} }
@ -204,30 +150,43 @@ bool UniValue::pushKVs(const UniValue& obj)
if (typ != VOBJ || obj.typ != VOBJ) if (typ != VOBJ || obj.typ != VOBJ)
return false; return false;
for (unsigned int i = 0; i < obj.keys.size(); i++) { for (size_t i = 0; i < obj.keys.size(); i++)
keys.push_back(obj.keys[i]); __pushKV(obj.keys[i], obj.values.at(i));
values.push_back(obj.values.at(i));
}
return true; return true;
} }
int UniValue::findKey(const std::string& key) const void UniValue::getObjMap(std::map<std::string,UniValue>& kv) const
{ {
for (unsigned int i = 0; i < keys.size(); i++) { if (typ != VOBJ)
if (keys[i] == key) return;
return (int) i;
}
return -1; kv.clear();
for (size_t i = 0; i < keys.size(); i++)
kv[keys[i]] = values[i];
} }
bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) bool UniValue::findKey(const std::string& key, size_t& retIdx) const
{ {
for (size_t i = 0; i < keys.size(); i++) {
if (keys[i] == key) {
retIdx = i;
return true;
}
}
return false;
}
bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) const
{
if (typ != VOBJ)
return false;
for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin(); for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin();
it != t.end(); ++it) { it != t.end(); ++it) {
int idx = findKey(it->first); size_t idx = 0;
if (idx < 0) if (!findKey(it->first, idx))
return false; return false;
if (values.at(idx).getType() != it->second) if (values.at(idx).getType() != it->second)
@ -242,14 +201,14 @@ const UniValue& UniValue::operator[](const std::string& key) const
if (typ != VOBJ) if (typ != VOBJ)
return NullUniValue; return NullUniValue;
int index = findKey(key); size_t index = 0;
if (index < 0) if (!findKey(key, index))
return NullUniValue; return NullUniValue;
return values.at(index); return values.at(index);
} }
const UniValue& UniValue::operator[](unsigned int index) const const UniValue& UniValue::operator[](size_t index) const
{ {
if (typ != VOBJ && typ != VARR) if (typ != VOBJ && typ != VARR)
return NullUniValue; return NullUniValue;
@ -283,75 +242,3 @@ const UniValue& find_value(const UniValue& obj, const std::string& name)
return NullUniValue; return NullUniValue;
} }
const std::vector<std::string>& UniValue::getKeys() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return keys;
}
const std::vector<UniValue>& UniValue::getValues() const
{
if (typ != VOBJ && typ != VARR)
throw std::runtime_error("JSON value is not an object or array as expected");
return values;
}
bool UniValue::get_bool() const
{
if (typ != VBOOL)
throw std::runtime_error("JSON value is not a boolean as expected");
return getBool();
}
const std::string& UniValue::get_str() const
{
if (typ != VSTR)
throw std::runtime_error("JSON value is not a string as expected");
return getValStr();
}
int UniValue::get_int() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not an integer as expected");
int32_t retval;
if (!ParseInt32(getValStr(), &retval))
throw std::runtime_error("JSON integer out of range");
return retval;
}
int64_t UniValue::get_int64() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not an integer as expected");
int64_t retval;
if (!ParseInt64(getValStr(), &retval))
throw std::runtime_error("JSON integer out of range");
return retval;
}
double UniValue::get_real() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not a number as expected");
double retval;
if (!ParseDouble(getValStr(), &retval))
throw std::runtime_error("JSON double out of range");
return retval;
}
const UniValue& UniValue::get_obj() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return *this;
}
const UniValue& UniValue::get_array() const
{
if (typ != VARR)
throw std::runtime_error("JSON value is not an array as expected");
return *this;
}

View File

@ -0,0 +1,147 @@
// Copyright 2014 BitPay Inc.
// Copyright 2015 Bitcoin Core Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdexcept>
#include <vector>
#include <limits>
#include <string>
#include "univalue.h"
namespace
{
static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed
return false;
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
return false;
return true;
}
bool ParseInt32(const std::string& str, int32_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = NULL;
errno = 0; // strtol will not set errno if valid
long int n = strtol(str.c_str(), &endp, 10);
if(out) *out = (int32_t)n;
// Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
// platforms the size of these types may be different.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int32_t>::min() &&
n <= std::numeric_limits<int32_t>::max();
}
bool ParseInt64(const std::string& str, int64_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = NULL;
errno = 0; // strtoll will not set errno if valid
long long int n = strtoll(str.c_str(), &endp, 10);
if(out) *out = (int64_t)n;
// Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int64_t*.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int64_t>::min() &&
n <= std::numeric_limits<int64_t>::max();
}
bool ParseDouble(const std::string& str, double *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
return false;
std::istringstream text(str);
text.imbue(std::locale::classic());
double result;
text >> result;
if(out) *out = result;
return text.eof() && !text.fail();
}
}
const std::vector<std::string>& UniValue::getKeys() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return keys;
}
const std::vector<UniValue>& UniValue::getValues() const
{
if (typ != VOBJ && typ != VARR)
throw std::runtime_error("JSON value is not an object or array as expected");
return values;
}
bool UniValue::get_bool() const
{
if (typ != VBOOL)
throw std::runtime_error("JSON value is not a boolean as expected");
return getBool();
}
const std::string& UniValue::get_str() const
{
if (typ != VSTR)
throw std::runtime_error("JSON value is not a string as expected");
return getValStr();
}
int UniValue::get_int() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not an integer as expected");
int32_t retval;
if (!ParseInt32(getValStr(), &retval))
throw std::runtime_error("JSON integer out of range");
return retval;
}
int64_t UniValue::get_int64() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not an integer as expected");
int64_t retval;
if (!ParseInt64(getValStr(), &retval))
throw std::runtime_error("JSON integer out of range");
return retval;
}
double UniValue::get_real() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not a number as expected");
double retval;
if (!ParseDouble(getValStr(), &retval))
throw std::runtime_error("JSON double out of range");
return retval;
}
const UniValue& UniValue::get_obj() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return *this;
}
const UniValue& UniValue::get_array() const
{
if (typ != VARR)
throw std::runtime_error("JSON value is not an array as expected");
return *this;
}

View File

@ -43,21 +43,21 @@ static const char *hatoui(const char *first, const char *last,
} }
enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, enum jtokentype getJsonToken(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);
@ -127,40 +127,40 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
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++;
} }
@ -177,13 +177,16 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
string valStr; 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;
@ -196,7 +199,8 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
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);
@ -246,7 +250,7 @@ enum expect_bits {
#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();
@ -257,10 +261,11 @@ bool UniValue::read(const char *raw)
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;
@ -371,9 +376,6 @@ bool UniValue::read(const char *raw)
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:
@ -388,6 +390,11 @@ bool UniValue::read(const char *raw)
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);
@ -396,10 +403,12 @@ bool UniValue::read(const char *raw)
} }
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);
@ -408,17 +417,18 @@ bool UniValue::read(const char *raw)
} }
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);
} }
@ -432,7 +442,7 @@ bool UniValue::read(const char *raw)
} 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;

View File

@ -42,20 +42,20 @@ public:
} }
} }
// Write codepoint directly, possibly collating surrogate pairs // Write codepoint directly, possibly collating surrogate pairs
void push_back_u(unsigned int codepoint) void push_back_u(unsigned int codepoint_)
{ {
// Only accept full codepoints in open state // Only accept full codepoints in open state
if (state) if (state)
is_valid = false; is_valid = false;
if (codepoint >= 0xD800 && codepoint < 0xDC00) { // First half of surrogate pair if (codepoint_ >= 0xD800 && codepoint_ < 0xDC00) { // First half of surrogate pair
if (surpair) // Two subsequent surrogate pair openers - fail if (surpair) // Two subsequent surrogate pair openers - fail
is_valid = false; is_valid = false;
else else
surpair = codepoint; surpair = codepoint_;
} else if (codepoint >= 0xDC00 && codepoint < 0xE000) { // Second half of surrogate pair } else if (codepoint_ >= 0xDC00 && codepoint_ < 0xE000) { // Second half of surrogate pair
if (surpair) { // Open surrogate pair, expect second half if (surpair) { // Open surrogate pair, expect second half
// Compute code point from UTF-16 surrogate pair // Compute code point from UTF-16 surrogate pair
append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint - 0xDC00)); append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint_ - 0xDC00));
surpair = 0; surpair = 0;
} else // First half of surrogate pair not followed by second } else // First half of surrogate pair not followed by second
is_valid = false; is_valid = false;
@ -63,7 +63,7 @@ public:
if (surpair) // First half of surrogate pair not followed by second if (surpair) // First half of surrogate pair not followed by second
is_valid = false; is_valid = false;
else else
append_codepoint(codepoint); append_codepoint(codepoint_);
} }
} }
// Check that we're in a state where the string can be ended // Check that we're in a state where the string can be ended
@ -91,22 +91,22 @@ private:
// Two subsequent \u.... may have to be replaced with one actual codepoint. // Two subsequent \u.... may have to be replaced with one actual codepoint.
unsigned int surpair; // First of UTF-16 surrogate pair unsigned int surpair; // First of UTF-16 surrogate pair
void append_codepoint(unsigned int codepoint) void append_codepoint(unsigned int codepoint_)
{ {
if (codepoint <= 0x7f) if (codepoint_ <= 0x7f)
str.push_back((char)codepoint); str.push_back((char)codepoint_);
else if (codepoint <= 0x7FF) { else if (codepoint_ <= 0x7FF) {
str.push_back((char)(0xC0 | (codepoint >> 6))); str.push_back((char)(0xC0 | (codepoint_ >> 6)));
str.push_back((char)(0x80 | (codepoint & 0x3F))); str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
} else if (codepoint <= 0xFFFF) { } else if (codepoint_ <= 0xFFFF) {
str.push_back((char)(0xE0 | (codepoint >> 12))); str.push_back((char)(0xE0 | (codepoint_ >> 12)));
str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F)));
str.push_back((char)(0x80 | (codepoint & 0x3F))); str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
} else if (codepoint <= 0x1FFFFF) { } else if (codepoint_ <= 0x1FFFFF) {
str.push_back((char)(0xF0 | (codepoint >> 18))); str.push_back((char)(0xF0 | (codepoint_ >> 18)));
str.push_back((char)(0x80 | ((codepoint >> 12) & 0x3F))); str.push_back((char)(0x80 | ((codepoint_ >> 12) & 0x3F)));
str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F)));
str.push_back((char)(0x80 | (codepoint & 0x3F))); str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
} }
} }
}; };

View File

@ -79,8 +79,6 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s
s += values[i].write(prettyIndent, indentLevel + 1); s += values[i].write(prettyIndent, indentLevel + 1);
if (i != (values.size() - 1)) { if (i != (values.size() - 1)) {
s += ","; s += ",";
if (prettyIndent)
s += " ";
} }
if (prettyIndent) if (prettyIndent)
s += "\n"; s += "\n";

View File

@ -1,4 +1,8 @@
object
unitester unitester
test_json
no_nul
*.trs *.trs
*.log *.log

View File

@ -1 +1 @@
"A JSON payload should be an object or array, not a string." "This is a string that never ends, yes it goes on and on, my friends.

Binary file not shown.

View File

@ -0,0 +1 @@
"This file ends without a newline or close-quote.

View File

@ -0,0 +1,8 @@
#include "univalue.h"
int main (int argc, char *argv[])
{
char buf[] = "___[1,2,3]___";
UniValue val;
return val.read(buf + 3, 7) ? 0 : 1;
}

View File

@ -0,0 +1,395 @@
// Copyright (c) 2014 BitPay Inc.
// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <stdint.h>
#include <vector>
#include <string>
#include <map>
#include <cassert>
#include <stdexcept>
#include <univalue.h>
#define BOOST_FIXTURE_TEST_SUITE(a, b)
#define BOOST_AUTO_TEST_CASE(funcName) void funcName()
#define BOOST_AUTO_TEST_SUITE_END()
#define BOOST_CHECK(expr) assert(expr)
#define BOOST_CHECK_EQUAL(v1, v2) assert((v1) == (v2))
#define BOOST_CHECK_THROW(stmt, excMatch) { \
try { \
(stmt); \
} catch (excMatch & e) { \
} catch (...) { \
assert(0); \
} \
}
#define BOOST_CHECK_NO_THROW(stmt) { \
try { \
(stmt); \
} catch (...) { \
assert(0); \
} \
}
BOOST_FIXTURE_TEST_SUITE(univalue_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(univalue_constructor)
{
UniValue v1;
BOOST_CHECK(v1.isNull());
UniValue v2(UniValue::VSTR);
BOOST_CHECK(v2.isStr());
UniValue v3(UniValue::VSTR, "foo");
BOOST_CHECK(v3.isStr());
BOOST_CHECK_EQUAL(v3.getValStr(), "foo");
UniValue numTest;
BOOST_CHECK(numTest.setNumStr("82"));
BOOST_CHECK(numTest.isNum());
BOOST_CHECK_EQUAL(numTest.getValStr(), "82");
uint64_t vu64 = 82;
UniValue v4(vu64);
BOOST_CHECK(v4.isNum());
BOOST_CHECK_EQUAL(v4.getValStr(), "82");
int64_t vi64 = -82;
UniValue v5(vi64);
BOOST_CHECK(v5.isNum());
BOOST_CHECK_EQUAL(v5.getValStr(), "-82");
int vi = -688;
UniValue v6(vi);
BOOST_CHECK(v6.isNum());
BOOST_CHECK_EQUAL(v6.getValStr(), "-688");
double vd = -7.21;
UniValue v7(vd);
BOOST_CHECK(v7.isNum());
BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21");
std::string vs("yawn");
UniValue v8(vs);
BOOST_CHECK(v8.isStr());
BOOST_CHECK_EQUAL(v8.getValStr(), "yawn");
const char *vcs = "zappa";
UniValue v9(vcs);
BOOST_CHECK(v9.isStr());
BOOST_CHECK_EQUAL(v9.getValStr(), "zappa");
}
BOOST_AUTO_TEST_CASE(univalue_typecheck)
{
UniValue v1;
BOOST_CHECK(v1.setNumStr("1"));
BOOST_CHECK(v1.isNum());
BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error);
UniValue v2;
BOOST_CHECK(v2.setBool(true));
BOOST_CHECK_EQUAL(v2.get_bool(), true);
BOOST_CHECK_THROW(v2.get_int(), std::runtime_error);
UniValue v3;
BOOST_CHECK(v3.setNumStr("32482348723847471234"));
BOOST_CHECK_THROW(v3.get_int64(), std::runtime_error);
BOOST_CHECK(v3.setNumStr("1000"));
BOOST_CHECK_EQUAL(v3.get_int64(), 1000);
UniValue v4;
BOOST_CHECK(v4.setNumStr("2147483648"));
BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648);
BOOST_CHECK_THROW(v4.get_int(), std::runtime_error);
BOOST_CHECK(v4.setNumStr("1000"));
BOOST_CHECK_EQUAL(v4.get_int(), 1000);
BOOST_CHECK_THROW(v4.get_str(), std::runtime_error);
BOOST_CHECK_EQUAL(v4.get_real(), 1000);
BOOST_CHECK_THROW(v4.get_array(), std::runtime_error);
BOOST_CHECK_THROW(v4.getKeys(), std::runtime_error);
BOOST_CHECK_THROW(v4.getValues(), std::runtime_error);
BOOST_CHECK_THROW(v4.get_obj(), std::runtime_error);
UniValue v5;
BOOST_CHECK(v5.read("[true, 10]"));
BOOST_CHECK_NO_THROW(v5.get_array());
std::vector<UniValue> vals = v5.getValues();
BOOST_CHECK_THROW(vals[0].get_int(), std::runtime_error);
BOOST_CHECK_EQUAL(vals[0].get_bool(), true);
BOOST_CHECK_EQUAL(vals[1].get_int(), 10);
BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error);
}
BOOST_AUTO_TEST_CASE(univalue_set)
{
UniValue v(UniValue::VSTR, "foo");
v.clear();
BOOST_CHECK(v.isNull());
BOOST_CHECK_EQUAL(v.getValStr(), "");
BOOST_CHECK(v.setObject());
BOOST_CHECK(v.isObject());
BOOST_CHECK_EQUAL(v.size(), 0);
BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ);
BOOST_CHECK(v.empty());
BOOST_CHECK(v.setArray());
BOOST_CHECK(v.isArray());
BOOST_CHECK_EQUAL(v.size(), 0);
BOOST_CHECK(v.setStr("zum"));
BOOST_CHECK(v.isStr());
BOOST_CHECK_EQUAL(v.getValStr(), "zum");
BOOST_CHECK(v.setFloat(-1.01));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "-1.01");
BOOST_CHECK(v.setInt((int)1023));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "1023");
BOOST_CHECK(v.setInt((int64_t)-1023LL));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "-1023");
BOOST_CHECK(v.setInt((uint64_t)1023ULL));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "1023");
BOOST_CHECK(v.setNumStr("-688"));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "-688");
BOOST_CHECK(v.setBool(false));
BOOST_CHECK_EQUAL(v.isBool(), true);
BOOST_CHECK_EQUAL(v.isTrue(), false);
BOOST_CHECK_EQUAL(v.isFalse(), true);
BOOST_CHECK_EQUAL(v.getBool(), false);
BOOST_CHECK(v.setBool(true));
BOOST_CHECK_EQUAL(v.isBool(), true);
BOOST_CHECK_EQUAL(v.isTrue(), true);
BOOST_CHECK_EQUAL(v.isFalse(), false);
BOOST_CHECK_EQUAL(v.getBool(), true);
BOOST_CHECK(!v.setNumStr("zombocom"));
BOOST_CHECK(v.setNull());
BOOST_CHECK(v.isNull());
}
BOOST_AUTO_TEST_CASE(univalue_array)
{
UniValue arr(UniValue::VARR);
UniValue v((int64_t)1023LL);
BOOST_CHECK(arr.push_back(v));
std::string vStr("zippy");
BOOST_CHECK(arr.push_back(vStr));
const char *s = "pippy";
BOOST_CHECK(arr.push_back(s));
std::vector<UniValue> vec;
v.setStr("boing");
vec.push_back(v);
v.setStr("going");
vec.push_back(v);
BOOST_CHECK(arr.push_backV(vec));
BOOST_CHECK(arr.push_back((uint64_t) 400ULL));
BOOST_CHECK(arr.push_back((int64_t) -400LL));
BOOST_CHECK(arr.push_back((int) -401));
BOOST_CHECK(arr.push_back(-40.1));
BOOST_CHECK_EQUAL(arr.empty(), false);
BOOST_CHECK_EQUAL(arr.size(), 9);
BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023");
BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy");
BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy");
BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing");
BOOST_CHECK_EQUAL(arr[4].getValStr(), "going");
BOOST_CHECK_EQUAL(arr[5].getValStr(), "400");
BOOST_CHECK_EQUAL(arr[6].getValStr(), "-400");
BOOST_CHECK_EQUAL(arr[7].getValStr(), "-401");
BOOST_CHECK_EQUAL(arr[8].getValStr(), "-40.1");
BOOST_CHECK_EQUAL(arr[999].getValStr(), "");
arr.clear();
BOOST_CHECK(arr.empty());
BOOST_CHECK_EQUAL(arr.size(), 0);
}
BOOST_AUTO_TEST_CASE(univalue_object)
{
UniValue obj(UniValue::VOBJ);
std::string strKey, strVal;
UniValue v;
strKey = "age";
v.setInt(100);
BOOST_CHECK(obj.pushKV(strKey, v));
strKey = "first";
strVal = "John";
BOOST_CHECK(obj.pushKV(strKey, strVal));
strKey = "last";
const char *cVal = "Smith";
BOOST_CHECK(obj.pushKV(strKey, cVal));
strKey = "distance";
BOOST_CHECK(obj.pushKV(strKey, (int64_t) 25));
strKey = "time";
BOOST_CHECK(obj.pushKV(strKey, (uint64_t) 3600));
strKey = "calories";
BOOST_CHECK(obj.pushKV(strKey, (int) 12));
strKey = "temperature";
BOOST_CHECK(obj.pushKV(strKey, (double) 90.012));
UniValue obj2(UniValue::VOBJ);
BOOST_CHECK(obj2.pushKV("cat1", 9000));
BOOST_CHECK(obj2.pushKV("cat2", 12345));
BOOST_CHECK(obj.pushKVs(obj2));
BOOST_CHECK_EQUAL(obj.empty(), false);
BOOST_CHECK_EQUAL(obj.size(), 9);
BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100");
BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John");
BOOST_CHECK_EQUAL(obj["last"].getValStr(), "Smith");
BOOST_CHECK_EQUAL(obj["distance"].getValStr(), "25");
BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600");
BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12");
BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012");
BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000");
BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345");
BOOST_CHECK_EQUAL(obj["nyuknyuknyuk"].getValStr(), "");
BOOST_CHECK(obj.exists("age"));
BOOST_CHECK(obj.exists("first"));
BOOST_CHECK(obj.exists("last"));
BOOST_CHECK(obj.exists("distance"));
BOOST_CHECK(obj.exists("time"));
BOOST_CHECK(obj.exists("calories"));
BOOST_CHECK(obj.exists("temperature"));
BOOST_CHECK(obj.exists("cat1"));
BOOST_CHECK(obj.exists("cat2"));
BOOST_CHECK(!obj.exists("nyuknyuknyuk"));
std::map<std::string, UniValue::VType> objTypes;
objTypes["age"] = UniValue::VNUM;
objTypes["first"] = UniValue::VSTR;
objTypes["last"] = UniValue::VSTR;
objTypes["distance"] = UniValue::VNUM;
objTypes["time"] = UniValue::VNUM;
objTypes["calories"] = UniValue::VNUM;
objTypes["temperature"] = UniValue::VNUM;
objTypes["cat1"] = UniValue::VNUM;
objTypes["cat2"] = UniValue::VNUM;
BOOST_CHECK(obj.checkObject(objTypes));
objTypes["cat2"] = UniValue::VSTR;
BOOST_CHECK(!obj.checkObject(objTypes));
obj.clear();
BOOST_CHECK(obj.empty());
BOOST_CHECK_EQUAL(obj.size(), 0);
BOOST_CHECK_EQUAL(obj.getType(), UniValue::VNULL);
BOOST_CHECK_EQUAL(obj.setObject(), true);
UniValue uv;
uv.setInt(42);
obj.__pushKV("age", uv);
BOOST_CHECK_EQUAL(obj.size(), 1);
BOOST_CHECK_EQUAL(obj["age"].getValStr(), "42");
uv.setInt(43);
obj.pushKV("age", uv);
BOOST_CHECK_EQUAL(obj.size(), 1);
BOOST_CHECK_EQUAL(obj["age"].getValStr(), "43");
obj.pushKV("name", "foo bar");
std::map<std::string,UniValue> kv;
obj.getObjMap(kv);
BOOST_CHECK_EQUAL(kv["age"].getValStr(), "43");
BOOST_CHECK_EQUAL(kv["name"].getValStr(), "foo bar");
}
static const char *json1 =
"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian http://test.com\"}}]";
BOOST_AUTO_TEST_CASE(univalue_readwrite)
{
UniValue v;
BOOST_CHECK(v.read(json1));
std::string strJson1(json1);
BOOST_CHECK(v.read(strJson1));
BOOST_CHECK(v.isArray());
BOOST_CHECK_EQUAL(v.size(), 2);
BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000");
UniValue obj = v[1];
BOOST_CHECK(obj.isObject());
BOOST_CHECK_EQUAL(obj.size(), 3);
BOOST_CHECK(obj["key1"].isStr());
std::string correctValue("str");
correctValue.push_back('\0');
BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue);
BOOST_CHECK(obj["key2"].isNum());
BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800");
BOOST_CHECK(obj["key3"].isObject());
BOOST_CHECK_EQUAL(strJson1, v.write());
/* Check for (correctly reporting) a parsing error if the initial
JSON construct is followed by more stuff. Note that whitespace
is, of course, exempt. */
BOOST_CHECK(v.read(" {}\n "));
BOOST_CHECK(v.isObject());
BOOST_CHECK(v.read(" []\n "));
BOOST_CHECK(v.isArray());
BOOST_CHECK(!v.read("@{}"));
BOOST_CHECK(!v.read("{} garbage"));
BOOST_CHECK(!v.read("[]{}"));
BOOST_CHECK(!v.read("{}[]"));
BOOST_CHECK(!v.read("{} 42"));
}
BOOST_AUTO_TEST_SUITE_END()
int main (int argc, char *argv[])
{
univalue_constructor();
univalue_typecheck();
univalue_set();
univalue_array();
univalue_object();
univalue_readwrite();
return 0;
}

View File

@ -0,0 +1 @@
"abcdefghijklmnopqrstuvwxyz"

View File

@ -0,0 +1 @@
7

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
false

View File

@ -0,0 +1 @@
null

View File

@ -0,0 +1,24 @@
// Test program that can be called by the JSON test suite at
// https://github.com/nst/JSONTestSuite.
//
// It reads JSON input from stdin and exits with code 0 if it can be parsed
// successfully. It also pretty prints the parsed JSON value to stdout.
#include <iostream>
#include <string>
#include "univalue.h"
using namespace std;
int main (int argc, char *argv[])
{
UniValue val;
if (val.read(string(istreambuf_iterator<char>(cin),
istreambuf_iterator<char>()))) {
cout << val.write(1 /* prettyIndent */, 4 /* indentLevel */) << endl;
return 0;
} else {
cerr << "JSON Parse Error." << endl;
return 1;
}
}

View File

@ -113,6 +113,8 @@ static const char *filenames[] = {
"fail39.json", // invalid unicode: only second half of surrogate pair "fail39.json", // invalid unicode: only second half of surrogate pair
"fail40.json", // invalid unicode: broken UTF-8 "fail40.json", // invalid unicode: broken UTF-8
"fail41.json", // invalid unicode: unfinished UTF-8 "fail41.json", // invalid unicode: unfinished UTF-8
"fail42.json", // valid json with garbage following a nul byte
"fail44.json", // unterminated string
"fail3.json", "fail3.json",
"fail4.json", // extra comma "fail4.json", // extra comma
"fail5.json", "fail5.json",
@ -125,6 +127,11 @@ static const char *filenames[] = {
"pass3.json", "pass3.json",
"round1.json", // round-trip test "round1.json", // round-trip test
"round2.json", // unicode "round2.json", // unicode
"round3.json", // bare string
"round4.json", // bare number
"round5.json", // bare true
"round6.json", // bare false
"round7.json", // bare null
}; };
// Test \u handling // Test \u handling