Browse Source

Squashed 'src/univalue/' content from commit 87d9045

git-subtree-dir: src/univalue
git-subtree-split: 87d90455ff
tags/v0.15.1
Jonas Schnelli 6 years ago
commit
2f9f082b5e

+ 22
- 0
.gitignore View File

@@ -0,0 +1,22 @@
.deps/
INSTALL
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
compile
config.log
config.status
config.guess
config.sub
configure
depcomp
install-sh
missing
stamp-h1
univalue-config.h*
test-driver
libtool
ltmain.sh

*.o

+ 52
- 0
.travis.yml View File

@@ -0,0 +1,52 @@

language: cpp

compiler:
- clang
- gcc

os:
- linux
- osx

sudo: false

env:
global:
- MAKEJOBS=-j3
- RUN_TESTS=true
- BASE_OUTDIR=$TRAVIS_BUILD_DIR/out

cache:
apt: true

addons:
apt:
packages:
- pkg-config

before_script:
- if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi
- test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh

script:
- if [ -n "$UNIVALUE_CONFIG" ]; then unset CC; unset CXX; fi
- OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST
- UNIVALUE_CONFIG_ALL="--prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib"
- ./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 )
- export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib
- if [ "$RUN_TESTS" = "true" ]; then make check; fi

matrix:
fast_finish: true
include:
- os: linux
compiler: gcc
env: UNIVALUE_CONFIG=--host=x86_64-w64-mingw32 RUN_TESTS=false
addons:
apt:
packages:
- g++-mingw-w64-x86-64
- gcc-mingw-w64-x86-64
- binutils-mingw-w64-x86-64

+ 19
- 0
COPYING View File

@@ -0,0 +1,19 @@

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


+ 84
- 0
Makefile.am View File

@@ -0,0 +1,84 @@
ACLOCAL_AMFLAGS = -I build-aux/m4
.PHONY: gen
.INTERMEDIATE: $(GENBIN)

include_HEADERS = include/univalue.h
noinst_HEADERS = lib/univalue_escapes.h

lib_LTLIBRARIES = lib/libunivalue.la

pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = pc/libunivalue.pc

lib_libunivalue_la_SOURCES = \
lib/univalue.cpp \
lib/univalue_read.cpp \
lib/univalue_write.cpp

lib_libunivalue_la_LDFLAGS = \
-version-info $(LIBUNIVALUE_CURRENT):$(LIBUNIVALUE_REVISION):$(LIBUNIVALUE_AGE) \
-no-undefined
lib_libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include

TESTS = test/unitester

GENBIN = gen/gen$(BUILD_EXEEXT)
GEN_SRCS = gen/gen.cpp

$(GENBIN): $(GEN_SRCS)
@echo Building $@
$(AM_V_at)c++ -I$(top_srcdir)/include -o $@ $<

gen: lib/univalue_escapes.h $(GENBIN)
@echo Updating $<
$(AM_V_at)$(GENBIN) > lib/univalue_escapes.h

noinst_PROGRAMS = $(TESTS)

TEST_DATA_DIR=test

test_unitester_SOURCES = test/unitester.cpp
test_unitester_LDADD = lib/libunivalue.la
test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\"
test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)

TEST_FILES = \
$(TEST_DATA_DIR)/fail10.json \
$(TEST_DATA_DIR)/fail11.json \
$(TEST_DATA_DIR)/fail12.json \
$(TEST_DATA_DIR)/fail13.json \
$(TEST_DATA_DIR)/fail14.json \
$(TEST_DATA_DIR)/fail15.json \
$(TEST_DATA_DIR)/fail16.json \
$(TEST_DATA_DIR)/fail17.json \
$(TEST_DATA_DIR)/fail18.json \
$(TEST_DATA_DIR)/fail19.json \
$(TEST_DATA_DIR)/fail1.json \
$(TEST_DATA_DIR)/fail20.json \
$(TEST_DATA_DIR)/fail21.json \
$(TEST_DATA_DIR)/fail22.json \
$(TEST_DATA_DIR)/fail23.json \
$(TEST_DATA_DIR)/fail24.json \
$(TEST_DATA_DIR)/fail25.json \
$(TEST_DATA_DIR)/fail26.json \
$(TEST_DATA_DIR)/fail27.json \
$(TEST_DATA_DIR)/fail28.json \
$(TEST_DATA_DIR)/fail29.json \
$(TEST_DATA_DIR)/fail2.json \
$(TEST_DATA_DIR)/fail30.json \
$(TEST_DATA_DIR)/fail31.json \
$(TEST_DATA_DIR)/fail32.json \
$(TEST_DATA_DIR)/fail33.json \
$(TEST_DATA_DIR)/fail34.json \
$(TEST_DATA_DIR)/fail3.json \
$(TEST_DATA_DIR)/fail4.json \
$(TEST_DATA_DIR)/fail5.json \
$(TEST_DATA_DIR)/fail6.json \
$(TEST_DATA_DIR)/fail7.json \
$(TEST_DATA_DIR)/fail8.json \
$(TEST_DATA_DIR)/fail9.json \
$(TEST_DATA_DIR)/pass1.json \
$(TEST_DATA_DIR)/pass2.json \
$(TEST_DATA_DIR)/pass3.json

EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS)

+ 7
- 0
README View File

@@ -0,0 +1,7 @@

UniValue

A universal value object, with JSON encoding (output) and decoding (input).

Built as a single dynamic RAII C++ object class, and no templates.


+ 10
- 0
TODO View File

@@ -0,0 +1,10 @@

Rearrange tree for easier 'git subtree' style use

Move towards C++11 etc.

Namespace support - must come up with useful shorthand, avoiding
long Univalue::Univalue::Univalue usages forced upon library users.

Improve test suite


+ 9
- 0
autogen.sh View File

@@ -0,0 +1,9 @@
#!/bin/sh
set -e
srcdir="$(dirname $0)"
cd "$srcdir"
if [ -z ${LIBTOOLIZE} ] && GLIBTOOLIZE="`which glibtoolize 2>/dev/null`"; then
LIBTOOLIZE="${GLIBTOOLIZE}"
export LIBTOOLIZE
fi
autoreconf --install --force

+ 0
- 0
build-aux/m4/.empty View File


+ 69
- 0
configure.ac View File

@@ -0,0 +1,69 @@
m4_define([libunivalue_major_version], [1])
m4_define([libunivalue_minor_version], [1])
m4_define([libunivalue_micro_version], [1])
m4_define([libunivalue_interface_age], [1])
# If you need a modifier for the version number.
# Normally empty, but can be used to make "fixup" releases.
m4_define([libunivalue_extraversion], [])

dnl libtool versioning from libunivalue
m4_define([libunivalue_current], [m4_eval(100 * libunivalue_minor_version + libunivalue_micro_version - libunivalue_interface_age)])
m4_define([libunivalue_binary_age], [m4_eval(100 * libunivalue_minor_version + libunivalue_micro_version)])
m4_define([libunivalue_revision], [libunivalue_interface_age])
m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_interface_age)])
m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()])


AC_INIT([univalue], [1.0.0],
[http://github.com/jgarzik/univalue/])

dnl make the compilation flags quiet unless V=1 is used
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])

AC_PREREQ(2.60)
AC_CONFIG_SRCDIR([lib/univalue.cpp])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([build-aux/m4])
AC_CONFIG_HEADERS([univalue-config.h])
AM_INIT_AUTOMAKE([subdir-objects foreign])

LIBUNIVALUE_MAJOR_VERSION=libunivalue_major_version
LIBUNIVALUE_MINOR_VERSION=libunivalue_minor_version
LIBUNIVALUE_MICRO_VERSION=libunivalue_micro_version
LIBUNIVALUE_INTERFACE_AGE=libunivalue_interface_age

# ABI version
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
LIBUNIVALUE_CURRENT=libunivalue_current
LIBUNIVALUE_REVISION=libunivalue_revision
LIBUNIVALUE_AGE=libunivalue_age

AC_SUBST(LIBUNIVALUE_CURRENT)
AC_SUBST(LIBUNIVALUE_REVISION)
AC_SUBST(LIBUNIVALUE_AGE)

LT_INIT
LT_LANG([C++])

case $host in
*mingw*)
LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"
;;
esac

BUILD_EXEEXT=
case $build in
*mingw*)
BUILD_EXEEXT=".exe"
;;
esac

AC_CONFIG_FILES([
Makefile
pc/libunivalue.pc
pc/libunivalue-uninstalled.pc])

AC_SUBST(LIBTOOL_APP_LDFLAGS)
AC_SUBST(BUILD_EXEEXT)
AC_OUTPUT


+ 77
- 0
gen/gen.cpp View File

@@ -0,0 +1,77 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

//
// To re-create univalue_escapes.h:
// $ g++ -o gen gen.cpp
// $ ./gen > univalue_escapes.h
//

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "univalue.h"

using namespace std;

static bool initEscapes;
static const char *escapes[256];

static void initJsonEscape()
{
escapes[(int)'"'] = "\\\"";
escapes[(int)'\\'] = "\\\\";
escapes[(int)'\b'] = "\\b";
escapes[(int)'\f'] = "\\f";
escapes[(int)'\n'] = "\\n";
escapes[(int)'\r'] = "\\r";
escapes[(int)'\t'] = "\\t";

initEscapes = true;
}

static void outputEscape()
{
printf( "// Automatically generated file. Do not modify.\n"
"#ifndef BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H\n"
"#define BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H\n"
"static const char *escapes[256] = {\n");

for (unsigned int i = 0; i < 256; i++) {
if (!escapes[i]) {
printf("\tNULL,\n");
} else {
printf("\t\"");

unsigned int si;
for (si = 0; si < strlen(escapes[i]); si++) {
char ch = escapes[i][si];
switch (ch) {
case '"':
printf("\\\"");
break;
case '\\':
printf("\\\\");
break;
default:
printf("%c", escapes[i][si]);
break;
}
}

printf("\",\n");
}
}

printf( "};\n"
"#endif // BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H\n");
}

int main (int argc, char *argv[])
{
initJsonEscape();
outputEscape();
return 0;
}


+ 250
- 0
include/univalue.h View File

@@ -0,0 +1,250 @@
// 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.

#ifndef __UNIVALUE_H__
#define __UNIVALUE_H__

#include <stdint.h>

#include <string>
#include <vector>
#include <map>
#include <cassert>

#include <sstream> // .get_int64()
#include <utility> // std::pair

class UniValue {
public:
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, };

UniValue() { typ = VNULL; }
UniValue(UniValue::VType initialType, const std::string& initialStr = "") {
typ = initialType;
val = initialStr;
}
UniValue(uint64_t val_) {
setInt(val_);
}
UniValue(int64_t val_) {
setInt(val_);
}
UniValue(bool val_) {
setBool(val_);
}
UniValue(int val_) {
setInt(val_);
}
UniValue(double val_) {
setFloat(val_);
}
UniValue(const std::string& val_) {
setStr(val_);
}
UniValue(const char *val_) {
std::string s(val_);
setStr(s);
}
~UniValue() {}

void clear();

bool setNull();
bool setBool(bool val);
bool setNumStr(const std::string& val);
bool setInt(uint64_t val);
bool setInt(int64_t val);
bool setInt(int val) { return setInt((int64_t)val); }
bool setFloat(double val);
bool setStr(const std::string& val);
bool setArray();
bool setObject();

enum VType getType() const { return typ; }
const std::string& getValStr() const { return val; }
bool empty() const { return (values.size() == 0); }

size_t size() const { return values.size(); }

bool getBool() const { return isTrue(); }
bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes);
const UniValue& operator[](const std::string& key) const;
const UniValue& operator[](unsigned int index) const;
bool exists(const std::string& key) const { return (findKey(key) >= 0); }

bool isNull() const { return (typ == VNULL); }
bool isTrue() const { return (typ == VBOOL) && (val == "1"); }
bool isFalse() const { return (typ == VBOOL) && (val != "1"); }
bool isBool() const { return (typ == VBOOL); }
bool isStr() const { return (typ == VSTR); }
bool isNum() const { return (typ == VNUM); }
bool isArray() const { return (typ == VARR); }
bool isObject() const { return (typ == VOBJ); }

bool push_back(const UniValue& val);
bool push_back(const std::string& val_) {
UniValue tmpVal(VSTR, val_);
return push_back(tmpVal);
}
bool push_back(const char *val_) {
std::string s(val_);
return push_back(s);
}
bool push_backV(const std::vector<UniValue>& vec);

bool pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const std::string& val) {
UniValue tmpVal(VSTR, val);
return pushKV(key, tmpVal);
}
bool pushKV(const std::string& key, const char *val_) {
std::string val(val_);
return pushKV(key, val);
}
bool pushKV(const std::string& key, int64_t val) {
UniValue tmpVal(val);
return pushKV(key, tmpVal);
}
bool pushKV(const std::string& key, uint64_t val) {
UniValue tmpVal(val);
return pushKV(key, tmpVal);
}
bool pushKV(const std::string& key, int val) {
UniValue tmpVal((int64_t)val);
return pushKV(key, tmpVal);
}
bool pushKV(const std::string& key, double val) {
UniValue tmpVal(val);
return pushKV(key, tmpVal);
}
bool pushKVs(const UniValue& obj);

std::string write(unsigned int prettyIndent = 0,
unsigned int indentLevel = 0) const;

bool read(const char *raw);
bool read(const std::string& rawStr) {
return read(rawStr.c_str());
}

private:
UniValue::VType typ;
std::string val; // numbers are stored as C++ strings
std::vector<std::string> keys;
std::vector<UniValue> values;

int findKey(const std::string& key) 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;

public:
// Strict type-specific getters, these throw std::runtime_error if the
// value is of unexpected type
std::vector<std::string> getKeys() const;
std::vector<UniValue> getValues() const;
bool get_bool() const;
std::string get_str() const;
int get_int() const;
int64_t get_int64() const;
double get_real() const;
const UniValue& get_obj() const;
const UniValue& get_array() const;

enum VType type() const { return getType(); }
bool push_back(std::pair<std::string,UniValue> pear) {
return pushKV(pear.first, pear.second);
}
friend const UniValue& find_value( const UniValue& obj, const std::string& name);
};

//
// The following were added for compatibility with json_spirit.
// Most duplicate other methods, and should be removed.
//
static inline std::pair<std::string,UniValue> Pair(const char *cKey, const char *cVal)
{
std::string key(cKey);
UniValue uVal(cVal);
return std::make_pair(key, uVal);
}

static inline std::pair<std::string,UniValue> Pair(const char *cKey, std::string strVal)
{
std::string key(cKey);
UniValue uVal(strVal);
return std::make_pair(key, uVal);
}

static inline std::pair<std::string,UniValue> Pair(const char *cKey, uint64_t u64Val)
{
std::string key(cKey);
UniValue uVal(u64Val);
return std::make_pair(key, uVal);
}

static inline std::pair<std::string,UniValue> Pair(const char *cKey, int64_t i64Val)
{
std::string key(cKey);
UniValue uVal(i64Val);
return std::make_pair(key, uVal);
}

static inline std::pair<std::string,UniValue> Pair(const char *cKey, bool iVal)
{
std::string key(cKey);
UniValue uVal(iVal);
return std::make_pair(key, uVal);
}

static inline std::pair<std::string,UniValue> Pair(const char *cKey, int iVal)
{
std::string key(cKey);
UniValue uVal(iVal);
return std::make_pair(key, uVal);
}

static inline std::pair<std::string,UniValue> Pair(const char *cKey, double dVal)
{
std::string key(cKey);
UniValue uVal(dVal);
return std::make_pair(key, uVal);
}

static inline std::pair<std::string,UniValue> Pair(const char *cKey, const UniValue& uVal)
{
std::string key(cKey);
return std::make_pair(key, uVal);
}

static inline std::pair<std::string,UniValue> Pair(std::string key, const UniValue& uVal)
{
return std::make_pair(key, uVal);
}

enum jtokentype {
JTOK_ERR = -1,
JTOK_NONE = 0, // eof
JTOK_OBJ_OPEN,
JTOK_OBJ_CLOSE,
JTOK_ARR_OPEN,
JTOK_ARR_CLOSE,
JTOK_COLON,
JTOK_COMMA,
JTOK_KW_NULL,
JTOK_KW_TRUE,
JTOK_KW_FALSE,
JTOK_NUMBER,
JTOK_STRING,
};

extern enum jtokentype getJsonToken(std::string& tokenVal,
unsigned int& consumed, const char *raw);
extern const char *uvTypeName(UniValue::VType t);

extern const UniValue NullUniValue;

const UniValue& find_value( const UniValue& obj, const std::string& name);

#endif // __UNIVALUE_H__

+ 10
- 0
lib/.gitignore View File

@@ -0,0 +1,10 @@

libunivalue-uninstalled.pc
libunivalue.pc
libunivalue.a
gen

.libs
*.lo
*.la


+ 365
- 0
lib/univalue.cpp View File

@@ -0,0 +1,365 @@
// 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 <ctype.h>
#include <errno.h>
#include <iomanip>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <stdlib.h>
#include <string.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 && (isspace(str[0]) || 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;

const UniValue NullUniValue;

void UniValue::clear()
{
typ = VNULL;
val.clear();
keys.clear();
values.clear();
}

bool UniValue::setNull()
{
clear();
return true;
}

bool UniValue::setBool(bool val_)
{
clear();
typ = VBOOL;
if (val_)
val = "1";
return true;
}

static bool validNumStr(const string& s)
{
string tokenVal;
unsigned int consumed;
enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str());
return (tt == JTOK_NUMBER);
}

bool UniValue::setNumStr(const string& val_)
{
if (!validNumStr(val_))
return false;

clear();
typ = VNUM;
val = val_;
return true;
}

bool UniValue::setInt(uint64_t val)
{
string s;
ostringstream oss;

oss << val;

return setNumStr(oss.str());
}

bool UniValue::setInt(int64_t val)
{
string s;
ostringstream oss;

oss << val;

return setNumStr(oss.str());
}

bool UniValue::setFloat(double val)
{
string s;
ostringstream oss;

oss << std::setprecision(16) << val;

bool ret = setNumStr(oss.str());
typ = VNUM;
return ret;
}

bool UniValue::setStr(const string& val_)
{
clear();
typ = VSTR;
val = val_;
return true;
}

bool UniValue::setArray()
{
clear();
typ = VARR;
return true;
}

bool UniValue::setObject()
{
clear();
typ = VOBJ;
return true;
}

bool UniValue::push_back(const UniValue& val)
{
if (typ != VARR)
return false;

values.push_back(val);
return true;
}

bool UniValue::push_backV(const std::vector<UniValue>& vec)
{
if (typ != VARR)
return false;

values.insert(values.end(), vec.begin(), vec.end());

return true;
}

bool UniValue::pushKV(const std::string& key, const UniValue& val)
{
if (typ != VOBJ)
return false;

keys.push_back(key);
values.push_back(val);
return true;
}

bool UniValue::pushKVs(const UniValue& obj)
{
if (typ != VOBJ || obj.typ != VOBJ)
return false;

for (unsigned int i = 0; i < obj.keys.size(); i++) {
keys.push_back(obj.keys[i]);
values.push_back(obj.values[i]);
}

return true;
}

int UniValue::findKey(const std::string& key) const
{
for (unsigned int i = 0; i < keys.size(); i++) {
if (keys[i] == key)
return (int) i;
}

return -1;
}

bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t)
{
for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin();
it != t.end(); it++) {
int idx = findKey(it->first);
if (idx < 0)
return false;

if (values[idx].getType() != it->second)
return false;
}

return true;
}

const UniValue& UniValue::operator[](const std::string& key) const
{
if (typ != VOBJ)
return NullUniValue;

int index = findKey(key);
if (index < 0)
return NullUniValue;

return values[index];
}

const UniValue& UniValue::operator[](unsigned int index) const
{
if (typ != VOBJ && typ != VARR)
return NullUniValue;
if (index >= values.size())
return NullUniValue;

return values[index];
}

const char *uvTypeName(UniValue::VType t)
{
switch (t) {
case UniValue::VNULL: return "null";
case UniValue::VBOOL: return "bool";
case UniValue::VOBJ: return "object";
case UniValue::VARR: return "array";
case UniValue::VSTR: return "string";
case UniValue::VNUM: return "number";
}

// not reached
return NULL;
}

const UniValue& find_value( const UniValue& obj, const std::string& name)
{
for (unsigned int i = 0; i < obj.keys.size(); i++)
{
if( obj.keys[i] == name )
{
return obj.values[i];
}
}

return NullUniValue;
}

std::vector<std::string> UniValue::getKeys() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return keys;
}

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();
}

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


+ 262
- 0
lib/univalue_escapes.h View File

@@ -0,0 +1,262 @@
// Automatically generated file. Do not modify.
#ifndef BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H
#define BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H
static const char *escapes[256] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"\\b",
"\\t",
"\\n",
NULL,
"\\f",
"\\r",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"\\\"",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"\\\\",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
#endif // BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H

+ 389
- 0
lib/univalue_read.cpp View File

@@ -0,0 +1,389 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <string.h>
#include <vector>
#include <stdio.h>
#include "univalue.h"

using namespace std;

// convert hexadecimal string to unsigned integer
static const char *hatoui(const char *first, const char *last,
unsigned int& out)
{
unsigned int result = 0;
for (; first != last; ++first)
{
int digit;
if (isdigit(*first))
digit = *first - '0';

else if (*first >= 'a' && *first <= 'f')
digit = *first - 'a' + 10;

else if (*first >= 'A' && *first <= 'F')
digit = *first - 'A' + 10;

else
break;

result = 16 * result + digit;
}
out = result;

return first;
}

enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
const char *raw)
{
tokenVal.clear();
consumed = 0;

const char *rawStart = raw;

while ((*raw) && (isspace(*raw))) // skip whitespace
raw++;

switch (*raw) {

case 0:
return JTOK_NONE;

case '{':
raw++;
consumed = (raw - rawStart);
return JTOK_OBJ_OPEN;
case '}':
raw++;
consumed = (raw - rawStart);
return JTOK_OBJ_CLOSE;
case '[':
raw++;
consumed = (raw - rawStart);
return JTOK_ARR_OPEN;
case ']':
raw++;
consumed = (raw - rawStart);
return JTOK_ARR_CLOSE;

case ':':
raw++;
consumed = (raw - rawStart);
return JTOK_COLON;
case ',':
raw++;
consumed = (raw - rawStart);
return JTOK_COMMA;

case 'n':
case 't':
case 'f':
if (!strncmp(raw, "null", 4)) {
raw += 4;
consumed = (raw - rawStart);
return JTOK_KW_NULL;
} else if (!strncmp(raw, "true", 4)) {
raw += 4;
consumed = (raw - rawStart);
return JTOK_KW_TRUE;
} else if (!strncmp(raw, "false", 5)) {
raw += 5;
consumed = (raw - rawStart);
return JTOK_KW_FALSE;
} else
return JTOK_ERR;

case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
// part 1: int
string numStr;

const char *first = raw;

const char *firstDigit = first;
if (!isdigit(*firstDigit))
firstDigit++;
if ((*firstDigit == '0') && isdigit(firstDigit[1]))
return JTOK_ERR;

numStr += *raw; // copy first char
raw++;

if ((*first == '-') && (!isdigit(*raw)))
return JTOK_ERR;

while ((*raw) && isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}

// part 2: frac
if (*raw == '.') {
numStr += *raw; // copy .
raw++;

if (!isdigit(*raw))
return JTOK_ERR;
while ((*raw) && isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
}

// part 3: exp
if (*raw == 'e' || *raw == 'E') {
numStr += *raw; // copy E
raw++;

if (*raw == '-' || *raw == '+') { // copy +/-
numStr += *raw;
raw++;
}

if (!isdigit(*raw))
return JTOK_ERR;
while ((*raw) && isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
}

tokenVal = numStr;
consumed = (raw - rawStart);
return JTOK_NUMBER;
}

case '"': {
raw++; // skip "

string valStr;

while (*raw) {
if (*raw < 0x20)
return JTOK_ERR;

else if (*raw == '\\') {
raw++; // skip backslash

switch (*raw) {
case '"': valStr += "\""; break;
case '\\': valStr += "\\"; break;
case '/': valStr += "/"; break;
case 'b': valStr += "\b"; break;
case 'f': valStr += "\f"; break;
case 'n': valStr += "\n"; break;
case 'r': valStr += "\r"; break;
case 't': valStr += "\t"; break;

case 'u': {
unsigned int codepoint;
if (hatoui(raw + 1, raw + 1 + 4, codepoint) !=
raw + 1 + 4)
return JTOK_ERR;

if (codepoint <= 0x7f)
valStr.push_back((char)codepoint);
else if (codepoint <= 0x7FF) {
valStr.push_back((char)(0xC0 | (codepoint >> 6)));
valStr.push_back((char)(0x80 | (codepoint & 0x3F)));
} else if (codepoint <= 0xFFFF) {
valStr.push_back((char)(0xE0 | (codepoint >> 12)));
valStr.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F)));
valStr.push_back((char)(0x80 | (codepoint & 0x3F)));
}

raw += 4;
break;
}
default:
return JTOK_ERR;

}

raw++; // skip esc'd char
}

else if (*raw == '"') {
raw++; // skip "
break; // stop scanning
}

else {
valStr += *raw;
raw++;
}
}

tokenVal = valStr;
consumed = (raw - rawStart);
return JTOK_STRING;
}

default:
return JTOK_ERR;
}
}

bool UniValue::read(const char *raw)
{
clear();

bool expectName = false;
bool expectColon = false;
vector<UniValue*> stack;

string tokenVal;
unsigned int consumed;
enum jtokentype tok = JTOK_NONE;
enum jtokentype last_tok = JTOK_NONE;
do {
last_tok = tok;

tok = getJsonToken(tokenVal, consumed, raw);
if (tok == JTOK_NONE || tok == JTOK_ERR)
return false;
raw += consumed;

switch (tok) {

case JTOK_OBJ_OPEN:
case JTOK_ARR_OPEN: {
VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
if (!stack.size()) {
if (utyp == VOBJ)
setObject();
else
setArray();
stack.push_back(this);
} else {
UniValue tmpVal(utyp);
UniValue *top = stack.back();
top->values.push_back(tmpVal);

UniValue *newTop = &(top->values.back());
stack.push_back(newTop);
}

if (utyp == VOBJ)
expectName = true;
break;
}

case JTOK_OBJ_CLOSE:
case JTOK_ARR_CLOSE: {
if (!stack.size() || expectColon || (last_tok == JTOK_COMMA))
return false;

VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
UniValue *top = stack.back();
if (utyp != top->getType())
return false;

stack.pop_back();
expectName = false;
break;
}

case JTOK_COLON: {
if (!stack.size() || expectName || !expectColon)
return false;

UniValue *top = stack.back();
if (top->getType() != VOBJ)
return false;

expectColon = false;
break;
}

case JTOK_COMMA: {
if (!stack.size() || expectName || expectColon ||
(last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
return false;

UniValue *top = stack.back();
if (top->getType() == VOBJ)
expectName = true;
break;
}

case JTOK_KW_NULL:
case JTOK_KW_TRUE:
case JTOK_KW_FALSE: {
if (!stack.size() || expectName || expectColon)
return false;

UniValue tmpVal;
switch (tok) {
case JTOK_KW_NULL:
// do nothing more
break;
case JTOK_KW_TRUE:
tmpVal.setBool(true);
break;
case JTOK_KW_FALSE:
tmpVal.setBool(false);
break;
default: /* impossible */ break;
}

UniValue *top = stack.back();
top->values.push_back(tmpVal);

break;
}

case JTOK_NUMBER: {
if (!stack.size() || expectName || expectColon)
return false;

UniValue tmpVal(VNUM, tokenVal);
UniValue *top = stack.back();
top->values.push_back(tmpVal);

break;
}

case JTOK_STRING: {
if (!stack.size())
return false;

UniValue *top = stack.back();

if (expectName) {
top->keys.push_back(tokenVal);
expectName = false;
expectColon = true;
} else {
UniValue tmpVal(VSTR, tokenVal);
top->values.push_back(tmpVal);
}

break;
}

default:
return false;
}
} while (!stack.empty ());

/* Check that nothing follows the initial construct (parsed above). */
tok = getJsonToken(tokenVal, consumed, raw);
if (tok != JTOK_NONE)
return false;

return true;
}


+ 127
- 0
lib/univalue_write.cpp View File

@@ -0,0 +1,127 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <ctype.h>
#include <iomanip>
#include <sstream>
#include <stdio.h>
#include "univalue.h"
#include "univalue_escapes.h"

// TODO: Using UTF8

using namespace std;

static string json_escape(const string& inS)
{
string outS;
outS.reserve(inS.size() * 2);

for (unsigned int i = 0; i < inS.size(); i++) {
unsigned char ch = inS[i];
const char *escStr = escapes[ch];

if (escStr)
outS += escStr;

else if (isprint(ch))
outS += ch;

else {
char tmpesc[16];
sprintf(tmpesc, "\\u%04x", ch);
outS += tmpesc;
}
}

return outS;
}

string UniValue::write(unsigned int prettyIndent,
unsigned int indentLevel) const
{
string s;
s.reserve(1024);

unsigned int modIndent = indentLevel;
if (modIndent == 0)
modIndent = 1;

switch (typ) {
case VNULL:
s += "null";
break;
case VOBJ:
writeObject(prettyIndent, modIndent, s);
break;
case VARR:
writeArray(prettyIndent, modIndent, s);
break;
case VSTR:
s += "\"" + json_escape(val) + "\"";
break;
case VNUM:
s += val;
break;
case VBOOL:
s += (val == "1" ? "true" : "false");
break;
}

return s;
}

static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, string& s)
{
s.append(prettyIndent * indentLevel, ' ');
}

void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
{
s += "[";
if (prettyIndent)
s += "\n";

for (unsigned int i = 0; i < values.size(); i++) {
if (prettyIndent)
indentStr(prettyIndent, indentLevel, s);
s += values[i].write(prettyIndent, indentLevel + 1);
if (i != (values.size() - 1)) {
s += ",";
if (prettyIndent)
s += " ";
}
if (prettyIndent)
s += "\n";
}

if (prettyIndent)
indentStr(prettyIndent, indentLevel - 1, s);
s += "]";
}

void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
{
s += "{";
if (prettyIndent)
s += "\n";

for (unsigned int i = 0; i < keys.size(); i++) {
if (prettyIndent)
indentStr(prettyIndent, indentLevel, s);
s += "\"" + json_escape(keys[i]) + "\":";
if (prettyIndent)
s += " ";
s += values[i].write(prettyIndent, indentLevel + 1);
if (i != (values.size() - 1))
s += ",";
if (prettyIndent)
s += "\n";
}

if (prettyIndent)
indentStr(prettyIndent, indentLevel - 1, s);
s += "}";
}


+ 9
- 0
pc/libunivalue-uninstalled.pc.in View File

@@ -0,0 +1,9 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@

Name: libunivalue
Description: libunivalue, C++ universal value object and JSON library
Version: @VERSION@
Libs: ${pc_top_builddir}/${pcfiledir}/libunivalue.la

+ 10
- 0
pc/libunivalue.pc.in View File

@@ -0,0 +1,10 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@

Name: libunivalue
Description: libunivalue, C++ universal value object and JSON library
Version: @VERSION@
Libs: -L${libdir} -lunivalue
Cflags: -I${includedir}

+ 7
- 0
test/.gitignore View File

@@ -0,0 +1,7 @@

unitester

*.log
*.trs

.libs

+ 1
- 0
test/fail1.json View File

@@ -0,0 +1 @@
"A JSON payload should be an object or array, not a string."

+ 1
- 0
test/fail10.json View File

@@ -0,0 +1 @@
{"Extra value after close": true} "misplaced quoted value"

+ 1
- 0
test/fail11.json View File

@@ -0,0 +1 @@
{"Illegal expression": 1 + 2}

+ 1
- 0
test/fail12.json View File

@@ -0,0 +1 @@
{"Illegal invocation": alert()}

+ 1
- 0
test/fail13.json View File

@@ -0,0 +1 @@
{"Numbers cannot have leading zeroes": 013}

+ 1
- 0
test/fail14.json View File

@@ -0,0 +1 @@
{"Numbers cannot be hex": 0x14}

+ 1
- 0
test/fail15.json View File

@@ -0,0 +1 @@
["Illegal backslash escape: \x15"]

+ 1
- 0
test/fail16.json View File

@@ -0,0 +1 @@
[\naked]

+ 1
- 0
test/fail17.json View File

@@ -0,0 +1 @@
["Illegal backslash escape: \017"]

+ 1
- 0
test/fail18.json View File

@@ -0,0 +1 @@
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]

+ 1
- 0
test/fail19.json View File

@@ -0,0 +1 @@
{"Missing colon" null}

+ 1
- 0
test/fail2.json View File

@@ -0,0 +1 @@
["Unclosed array"

+ 1
- 0
test/fail20.json View File

@@ -0,0 +1 @@
{"Double colon":: null}

+ 1
- 0
test/fail21.json View File

@@ -0,0 +1 @@
{"Comma instead of colon", null}

+ 1
- 0
test/fail22.json View File

@@ -0,0 +1 @@
["Colon instead of comma": false]

+ 1
- 0
test/fail23.json View File

@@ -0,0 +1 @@
["Bad value", truth]

+ 1
- 0
test/fail24.json View File

@@ -0,0 +1 @@
['single quote']

+ 1
- 0
test/fail25.json View File

@@ -0,0 +1 @@
[" tab character in string "]

+ 1
- 0
test/fail26.json View File

@@ -0,0 +1 @@
["tab\ character\ in\ string\ "]

+ 2
- 0
test/fail27.json View File

@@ -0,0 +1,2 @@
["line
break"]

+ 2
- 0
test/fail28.json View File

@@ -0,0 +1,2 @@
["line\
break"]

+ 1
- 0
test/fail29.json View File

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

+ 1
- 0
test/fail3.json View File

@@ -0,0 +1 @@
{unquoted_key: "keys must be quoted"}

+ 1
- 0
test/fail30.json View File

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

+ 1
- 0
test/fail31.json View File

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

+ 1
- 0
test/fail32.json View File

@@ -0,0 +1 @@
{"Comma instead if closing brace": true,

+ 1
- 0
test/fail33.json View File

@@ -0,0 +1 @@
["mismatch"}

+ 1
- 0
test/fail34.json View File

@@ -0,0 +1 @@
{} garbage

+ 1
- 0
test/fail4.json View File

@@ -0,0 +1 @@
["extra comma",]

+ 1
- 0
test/fail5.json View File

@@ -0,0 +1 @@
["double extra comma",,]

+ 1
- 0
test/fail6.json View File

@@ -0,0 +1 @@
[ , "<-- missing value"]

+ 1
- 0
test/fail7.json View File

@@ -0,0 +1 @@
["Comma after the close"],

+ 1
- 0
test/fail8.json View File

@@ -0,0 +1 @@
["Extra close"]]

+ 1
- 0
test/fail9.json View File

@@ -0,0 +1 @@
{"Extra comma": true,}

+ 58
- 0
test/pass1.json View File

@@ -0,0 +1,58 @@
[
"JSON Test Pattern pass1",
{"object with 1 member":["array with 1 element"]},
{},
[],
-42,
true,
false,
null,
{
"integer": 1234567890,
"real": -9876.543210,
"e": 0.123456789e-12,
"E": 1.234567890E+34,
"": 23456789012E66,
"zero": 0,
"one": 1,
"space": " ",
"quote": "\"",
"backslash": "\\",
"controls": "\b\f\n\r\t",
"slash": "/ & \/",
"alpha": "abcdefghijklmnopqrstuvwyz",
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
"digit": "0123456789",
"0123456789": "digit",
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
"true": true,
"false": false,
"null": null,
"array":[ ],
"object":{ },
"address": "50 St. James Street",
"url": "http://www.JSON.org/",
"comment": "// /* <!-- --",
"# -- --> */": " ",
" s p a c e d " :[1,2 , 3

,

4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
"quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
: "A key can be any string"
},
0.5 ,98.6
,
99.44
,

1066,
1e1,
0.1e1,
1e-1,
1e00,2e+00,2e-00
,"rosebud"]

+ 1
- 0
test/pass2.json View File

@@ -0,0 +1 @@
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]

+ 6
- 0
test/pass3.json View File

@@ -0,0 +1,6 @@
{
"JSON Test Pattern pass3": {
"The outermost value": "must be an object or array.",
"In this test": "It is an object."
}
}

+ 115
- 0
test/unitester.cpp View File

@@ -0,0 +1,115 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cassert>
#include <string>
#include "univalue.h"

#ifndef JSON_TEST_SRC
#error JSON_TEST_SRC must point to test source directory
#endif

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif

using namespace std;
string srcdir(JSON_TEST_SRC);

static void runtest(string filename, const string& jdata)
{
fprintf(stderr, "test %s\n", filename.c_str());

string prefix = filename.substr(0, 4);

bool wantPass = (prefix == "pass");
bool wantFail = (prefix == "fail");
assert(wantPass || wantFail);

UniValue val;
bool testResult = val.read(jdata);

if (wantPass) {
assert(testResult == true);
} else {
assert(testResult == false);
}
}

static void runtest_file(const char *filename_)
{
string basename(filename_);
string filename = srcdir + "/" + basename;
FILE *f = fopen(filename.c_str(), "r");
assert(f != NULL);

string jdata;

char buf[4096];
while (!feof(f)) {
int bread = fread(buf, 1, sizeof(buf), f);
assert(!ferror(f));

string s(buf, bread);
jdata += s;
}

assert(!ferror(f));
fclose(f);

runtest(basename, jdata);
}

static const char *filenames[] = {
"fail10.json",
"fail11.json",
"fail12.json",
"fail13.json",
"fail14.json",
"fail15.json",
"fail16.json",
"fail17.json",
//"fail18.json", // investigate
"fail19.json",
"fail1.json",
"fail20.json",
"fail21.json",
"fail22.json",
"fail23.json",
"fail24.json",
"fail25.json",
"fail26.json",
"fail27.json",
"fail28.json",
"fail29.json",
"fail2.json",
"fail30.json",
"fail31.json",
"fail32.json",
"fail33.json",
"fail34.json",
"fail3.json",
"fail4.json", // extra comma
"fail5.json",
"fail6.json",
"fail7.json",
"fail8.json",
"fail9.json", // extra comma
"pass1.json",
"pass2.json",
"pass3.json",
};

int main (int argc, char *argv[])
{
for (unsigned int fidx = 0; fidx < ARRAY_SIZE(filenames); fidx++) {
runtest_file(filenames[fidx]);
}

return 0;
}


Loading…
Cancel
Save