Browse Source

Squashed 'src/univalue/' changes from 16a1f7f6e..fe805ea74

fe805ea74 Declare single-argument (non-converting) constructors "explicit"
8a2d6f1e3 Merge pull request #41 from jgarzik/get-obj-map
ba341a20d Add getObjMap() helper method.  Also, constify checkObject().
ceb119413 Handle .pushKV() and .checkObject() edge cases.
107db9829 Add ::push_back(double) method for feature parity.
d41530031 Move one-line implementation of UniValue::read() to header.
52e85b35b Move exception-throwing get_* methods into separate implementation module.
dac529675 README.md: update code quotes
3e31dcffb README.md: close code quote
d09b8429d Update README.md
f1b86edb4 Convert README to markdown style.
1dfe464ef Import UniValue class unit tests from bitcoin project.
0d3e74dd1 operator[] takes size_t index parameter (versus unsigned int)
640158fa2 Private findKey() method becomes size_t clean, and returns bool on failure.
709913585 Merge pull request #36 from ryanofsky/pr/end-str
a31231b51 Version 1.0.3
4fd5444d1 Reject unterminated strings
81eba332b Merge pull request #26 from isle2983/pushBackHelpers
36405413e Merge PR #32 from branch 'nul-not-special' of git://github.com/ryanofsky/univalue into merge
89bb07322 Merge pull request #31 from ryanofsky/raw-literals
511008c36 Merge pull request #30 from ryanofsky/test-driver
77974f3a9 Merge pull request #34 from paveljanik/20161116_Wshadow_codepoint
a38fcd355 Do not shadow member variable codepoint.
fd32d1ab8 Don't require nul-terminated string inputs
0bb1439d0 Support parsing raw literals in UniValue
28876d045 Merge pull request #29 from btcdrak/exportspace
839ccd71f Add test driver for JSONTestSuite
26ef3fff1 Remove trailing whitespace from JSON export
cfa0384d6 Convenience wrappers for push_back-ing integer types
REVERT: 16a1f7f6e Merge #3: Pull upstream
REVERT: daf1285af Merge pull request #2 from jgarzik/master
REVERT: f32df99e9 Merge branch '2016_04_unicode' into bitcoin
REVERT: 280b191cb Merge remote-tracking branch 'jgarzik/master' into bitcoin
REVERT: 2740c4f71 Merge branch '2015_11_escape_plan' into bitcoin

git-subtree-dir: src/univalue
git-subtree-split: fe805ea74f8919382720b09a905a14e81311b3ad
0.16
MarcoFalke 7 years ago
parent
commit
619bb05037
  1. 29
      Makefile.am
  2. 7
      README
  3. 32
      README.md
  4. 6
      configure.ac
  5. 34
      include/univalue.h
  6. 193
      lib/univalue.cpp
  7. 147
      lib/univalue_get.cpp
  8. 72
      lib/univalue_read.cpp
  9. 44
      lib/univalue_utffilter.h
  10. 2
      lib/univalue_write.cpp
  11. 4
      test/.gitignore
  12. 2
      test/fail1.json
  13. BIN
      test/fail42.json
  14. 1
      test/fail44.json
  15. 8
      test/no_nul.cpp
  16. 395
      test/object.cpp
  17. 1
      test/round3.json
  18. 1
      test/round4.json
  19. 1
      test/round5.json
  20. 1
      test/round6.json
  21. 1
      test/round7.json
  22. 24
      test/test_json.cpp
  23. 7
      test/unitester.cpp

29
Makefile.am

@ -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)

7
README

@ -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
README.md

@ -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).

6
configure.ac

@ -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

34
include/univalue.h

@ -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)

193
lib/univalue.cpp

@ -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
{
if (typ != VOBJ)
return;
kv.clear();
for (size_t i = 0; i < keys.size(); i++)
kv[keys[i]] = values[i];
}
bool UniValue::findKey(const std::string& key, size_t& retIdx) const
{ {
for (unsigned int i = 0; i < keys.size(); i++) { for (size_t i = 0; i < keys.size(); i++) {
if (keys[i] == key) if (keys[i] == key) {
return (int) i; retIdx = i;
return true;
}
} }
return -1; return false;
} }
bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) 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;
}

147
lib/univalue_get.cpp

@ -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;
}

72
lib/univalue_read.cpp

@ -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;

44
lib/univalue_utffilter.h

@ -13,7 +13,7 @@
class JSONUTF8StringFilter class JSONUTF8StringFilter
{ {
public: public:
JSONUTF8StringFilter(std::string &s): explicit JSONUTF8StringFilter(std::string &s):
str(s), is_valid(true), codepoint(0), state(0), surpair(0) str(s), is_valid(true), codepoint(0), state(0), surpair(0)
{ {
} }
@ -46,19 +46,19 @@ 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_)
{ {
if (state) // Only accept full codepoints in open state if (state) // Only accept full codepoints in open 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 // Second half doesn't follow a first half - fail } else // Second half doesn't follow a first half - fail
is_valid = false; is_valid = false;
@ -66,7 +66,7 @@ public:
if (surpair) // First half of surrogate pair not followed by second - fail if (surpair) // First half of surrogate pair not followed by second - fail
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
@ -96,22 +96,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 half of open UTF-16 surrogate pair, or 0 unsigned int surpair; // First half of open UTF-16 surrogate pair, or 0
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)));
} }
} }
}; };

2
lib/univalue_write.cpp

@ -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";

4
test/.gitignore vendored

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

2
test/fail1.json

@ -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.

BIN
test/fail42.json

Binary file not shown.

1
test/fail44.json

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

8
test/no_nul.cpp

@ -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;
}

395
test/object.cpp

@ -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;
}

1
test/round3.json

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

1
test/round4.json

@ -0,0 +1 @@
7

1
test/round5.json

@ -0,0 +1 @@
true

1
test/round6.json

@ -0,0 +1 @@
false

1
test/round7.json

@ -0,0 +1 @@
null

24
test/test_json.cpp

@ -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;
}
}

7
test/unitester.cpp

@ -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

Loading…
Cancel
Save