aboutsummaryrefslogtreecommitdiff
path: root/src/univalue/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/univalue/lib')
-rw-r--r--src/univalue/lib/.gitignore2
-rw-r--r--src/univalue/lib/univalue.cpp360
-rw-r--r--src/univalue/lib/univalue_escapes.h262
-rw-r--r--src/univalue/lib/univalue_read.cpp448
-rw-r--r--src/univalue/lib/univalue_write.cpp126
5 files changed, 1198 insertions, 0 deletions
diff --git a/src/univalue/lib/.gitignore b/src/univalue/lib/.gitignore
new file mode 100644
index 0000000000..ee7fc2851c
--- /dev/null
+++ b/src/univalue/lib/.gitignore
@@ -0,0 +1,2 @@
+gen
+.libs
diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp
new file mode 100644
index 0000000000..0076d6678e
--- /dev/null
+++ b/src/univalue/lib/univalue.cpp
@@ -0,0 +1,360 @@
+// 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 <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 && (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;
+
+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.at(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.at(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.at(index);
+}
+
+const UniValue& UniValue::operator[](unsigned int index) const
+{
+ if (typ != VOBJ && typ != VARR)
+ return NullUniValue;
+ if (index >= values.size())
+ return NullUniValue;
+
+ return values.at(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.at(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;
+}
+
diff --git a/src/univalue/lib/univalue_escapes.h b/src/univalue/lib/univalue_escapes.h
new file mode 100644
index 0000000000..74596aab6d
--- /dev/null
+++ b/src/univalue/lib/univalue_escapes.h
@@ -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] = {
+ "\\u0000",
+ "\\u0001",
+ "\\u0002",
+ "\\u0003",
+ "\\u0004",
+ "\\u0005",
+ "\\u0006",
+ "\\u0007",
+ "\\b",
+ "\\t",
+ "\\n",
+ "\\u000b",
+ "\\f",
+ "\\r",
+ "\\u000e",
+ "\\u000f",
+ "\\u0010",
+ "\\u0011",
+ "\\u0012",
+ "\\u0013",
+ "\\u0014",
+ "\\u0015",
+ "\\u0016",
+ "\\u0017",
+ "\\u0018",
+ "\\u0019",
+ "\\u001a",
+ "\\u001b",
+ "\\u001c",
+ "\\u001d",
+ "\\u001e",
+ "\\u001f",
+ 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,
+ "\\u007f",
+ 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
diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp
new file mode 100644
index 0000000000..c7516b9628
--- /dev/null
+++ b/src/univalue/lib/univalue_read.cpp
@@ -0,0 +1,448 @@
+// 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;
+
+static bool json_isdigit(int ch)
+{
+ return ((ch >= '0') && (ch <= '9'));
+}
+
+// convert hexadecimal string to unsigned integer
+static const char *hatoui(const char *first, const char *last,
+ unsigned int& out)
+{
+ unsigned int result = 0;
+ for (; first != last; ++first)
+ {
+ int digit;
+ if (json_isdigit(*first))
+ digit = *first - '0';
+
+ else if (*first >= 'a' && *first <= 'f')
+ digit = *first - 'a' + 10;
+
+ else if (*first >= 'A' && *first <= 'F')
+ digit = *first - 'A' + 10;
+
+ else
+ break;
+
+ result = 16 * result + digit;
+ }
+ out = result;
+
+ return first;
+}
+
+enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
+ const char *raw)
+{
+ tokenVal.clear();
+ consumed = 0;
+
+ const char *rawStart = raw;
+
+ while ((*raw) && (json_isspace(*raw))) // skip whitespace
+ raw++;
+
+ switch (*raw) {
+
+ case 0:
+ return JTOK_NONE;
+
+ case '{':
+ raw++;
+ consumed = (raw - rawStart);
+ return JTOK_OBJ_OPEN;
+ case '}':
+ raw++;
+ consumed = (raw - rawStart);
+ return JTOK_OBJ_CLOSE;
+ case '[':
+ raw++;
+ consumed = (raw - rawStart);
+ return JTOK_ARR_OPEN;
+ case ']':
+ raw++;
+ consumed = (raw - rawStart);
+ return JTOK_ARR_CLOSE;
+
+ case ':':
+ raw++;
+ consumed = (raw - rawStart);
+ return JTOK_COLON;
+ case ',':
+ raw++;
+ consumed = (raw - rawStart);
+ return JTOK_COMMA;
+
+ case 'n':
+ case 't':
+ case 'f':
+ if (!strncmp(raw, "null", 4)) {
+ raw += 4;
+ consumed = (raw - rawStart);
+ return JTOK_KW_NULL;
+ } else if (!strncmp(raw, "true", 4)) {
+ raw += 4;
+ consumed = (raw - rawStart);
+ return JTOK_KW_TRUE;
+ } else if (!strncmp(raw, "false", 5)) {
+ raw += 5;
+ consumed = (raw - rawStart);
+ return JTOK_KW_FALSE;
+ } else
+ return JTOK_ERR;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ // part 1: int
+ string numStr;
+
+ const char *first = raw;
+
+ const char *firstDigit = first;
+ if (!json_isdigit(*firstDigit))
+ firstDigit++;
+ if ((*firstDigit == '0') && json_isdigit(firstDigit[1]))
+ return JTOK_ERR;
+
+ numStr += *raw; // copy first char
+ raw++;
+
+ if ((*first == '-') && (!json_isdigit(*raw)))
+ return JTOK_ERR;
+
+ while ((*raw) && json_isdigit(*raw)) { // copy digits
+ numStr += *raw;
+ raw++;
+ }
+
+ // part 2: frac
+ if (*raw == '.') {
+ numStr += *raw; // copy .
+ raw++;
+
+ if (!json_isdigit(*raw))
+ return JTOK_ERR;
+ while ((*raw) && json_isdigit(*raw)) { // copy digits
+ numStr += *raw;
+ raw++;
+ }
+ }
+
+ // part 3: exp
+ if (*raw == 'e' || *raw == 'E') {
+ numStr += *raw; // copy E
+ raw++;
+
+ if (*raw == '-' || *raw == '+') { // copy +/-
+ numStr += *raw;
+ raw++;
+ }
+
+ if (!json_isdigit(*raw))
+ return JTOK_ERR;
+ while ((*raw) && json_isdigit(*raw)) { // copy digits
+ numStr += *raw;
+ raw++;
+ }
+ }
+
+ tokenVal = numStr;
+ consumed = (raw - rawStart);
+ return JTOK_NUMBER;
+ }
+
+ case '"': {
+ raw++; // skip "
+
+ string valStr;
+
+ while (*raw) {
+ if (*raw < 0x20)
+ return JTOK_ERR;
+
+ else if (*raw == '\\') {
+ raw++; // skip backslash
+
+ switch (*raw) {
+ case '"': valStr += "\""; break;
+ case '\\': valStr += "\\"; break;
+ case '/': valStr += "/"; break;
+ case 'b': valStr += "\b"; break;
+ case 'f': valStr += "\f"; break;
+ case 'n': valStr += "\n"; break;
+ case 'r': valStr += "\r"; break;
+ case 't': valStr += "\t"; break;
+
+ case 'u': {
+ unsigned int codepoint;
+ if (hatoui(raw + 1, raw + 1 + 4, codepoint) !=
+ raw + 1 + 4)
+ return JTOK_ERR;
+
+ if (codepoint <= 0x7f)
+ valStr.push_back((char)codepoint);
+ else if (codepoint <= 0x7FF) {
+ valStr.push_back((char)(0xC0 | (codepoint >> 6)));
+ valStr.push_back((char)(0x80 | (codepoint & 0x3F)));
+ } else if (codepoint <= 0xFFFF) {
+ valStr.push_back((char)(0xE0 | (codepoint >> 12)));
+ valStr.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F)));
+ valStr.push_back((char)(0x80 | (codepoint & 0x3F)));
+ }
+
+ raw += 4;
+ break;
+ }
+ default:
+ return JTOK_ERR;
+
+ }
+
+ raw++; // skip esc'd char
+ }
+
+ else if (*raw == '"') {
+ raw++; // skip "
+ break; // stop scanning
+ }
+
+ else {
+ valStr += *raw;
+ raw++;
+ }
+ }
+
+ tokenVal = valStr;
+ consumed = (raw - rawStart);
+ return JTOK_STRING;
+ }
+
+ default:
+ return JTOK_ERR;
+ }
+}
+
+enum expect_bits {
+ EXP_OBJ_NAME = (1U << 0),
+ EXP_COLON = (1U << 1),
+ EXP_ARR_VALUE = (1U << 2),
+ EXP_VALUE = (1U << 3),
+ EXP_NOT_VALUE = (1U << 4),
+};
+
+#define expect(bit) (expectMask & (EXP_##bit))
+#define setExpect(bit) (expectMask |= EXP_##bit)
+#define clearExpect(bit) (expectMask &= ~EXP_##bit)
+
+bool UniValue::read(const char *raw)
+{
+ clear();
+
+ uint32_t expectMask = 0;
+ 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;
+
+ bool isValueOpen = jsonTokenIsValue(tok) ||
+ tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN;
+
+ if (expect(VALUE)) {
+ if (!isValueOpen)
+ return false;
+ clearExpect(VALUE);
+
+ } else if (expect(ARR_VALUE)) {
+ bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE);
+ if (!isArrValue)
+ return false;
+
+ clearExpect(ARR_VALUE);
+
+ } else if (expect(OBJ_NAME)) {
+ bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING);
+ if (!isObjName)
+ return false;
+
+ } else if (expect(COLON)) {
+ if (tok != JTOK_COLON)
+ return false;
+ clearExpect(COLON);
+
+ } else if (!expect(COLON) && (tok == JTOK_COLON)) {
+ return false;
+ }
+
+ if (expect(NOT_VALUE)) {
+ if (isValueOpen)
+ return false;
+ clearExpect(NOT_VALUE);
+ }
+
+ switch (tok) {
+
+ case JTOK_OBJ_OPEN:
+ case JTOK_ARR_OPEN: {
+ VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
+ if (!stack.size()) {
+ if (utyp == VOBJ)
+ setObject();
+ else
+ setArray();
+ stack.push_back(this);
+ } else {
+ UniValue tmpVal(utyp);
+ UniValue *top = stack.back();
+ top->values.push_back(tmpVal);
+
+ UniValue *newTop = &(top->values.back());
+ stack.push_back(newTop);
+ }
+
+ if (utyp == VOBJ)
+ setExpect(OBJ_NAME);
+ else
+ setExpect(ARR_VALUE);
+ break;
+ }
+
+ case JTOK_OBJ_CLOSE:
+ case JTOK_ARR_CLOSE: {
+ if (!stack.size() || (last_tok == JTOK_COMMA))
+ return false;
+
+ VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
+ UniValue *top = stack.back();
+ if (utyp != top->getType())
+ return false;
+
+ stack.pop_back();
+ clearExpect(OBJ_NAME);
+ setExpect(NOT_VALUE);
+ break;
+ }
+
+ case JTOK_COLON: {
+ if (!stack.size())
+ return false;
+
+ UniValue *top = stack.back();
+ if (top->getType() != VOBJ)
+ return false;
+
+ setExpect(VALUE);
+ break;
+ }
+
+ case JTOK_COMMA: {
+ if (!stack.size() ||
+ (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
+ return false;
+
+ UniValue *top = stack.back();
+ if (top->getType() == VOBJ)
+ setExpect(OBJ_NAME);
+ else
+ setExpect(ARR_VALUE);
+ break;
+ }
+
+ case JTOK_KW_NULL:
+ case JTOK_KW_TRUE:
+ case JTOK_KW_FALSE: {
+ if (!stack.size())
+ return false;
+
+ UniValue tmpVal;
+ switch (tok) {
+ case JTOK_KW_NULL:
+ // do nothing more
+ break;
+ case JTOK_KW_TRUE:
+ tmpVal.setBool(true);
+ break;
+ case JTOK_KW_FALSE:
+ tmpVal.setBool(false);
+ break;
+ default: /* impossible */ break;
+ }
+
+ UniValue *top = stack.back();
+ top->values.push_back(tmpVal);
+
+ setExpect(NOT_VALUE);
+ break;
+ }
+
+ case JTOK_NUMBER: {
+ if (!stack.size())
+ return false;
+
+ UniValue tmpVal(VNUM, tokenVal);
+ UniValue *top = stack.back();
+ top->values.push_back(tmpVal);
+
+ setExpect(NOT_VALUE);
+ break;
+ }
+
+ case JTOK_STRING: {
+ if (!stack.size())
+ return false;
+
+ UniValue *top = stack.back();
+
+ if (expect(OBJ_NAME)) {
+ top->keys.push_back(tokenVal);
+ clearExpect(OBJ_NAME);
+ setExpect(COLON);
+ } else {
+ UniValue tmpVal(VSTR, tokenVal);
+ top->values.push_back(tmpVal);
+ }
+
+ setExpect(NOT_VALUE);
+ break;
+ }
+
+ default:
+ return false;
+ }
+ } while (!stack.empty ());
+
+ /* Check that nothing follows the initial construct (parsed above). */
+ tok = getJsonToken(tokenVal, consumed, raw);
+ if (tok != JTOK_NONE)
+ return false;
+
+ return true;
+}
+
diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp
new file mode 100644
index 0000000000..ceb4cc9166
--- /dev/null
+++ b/src/univalue/lib/univalue_write.cpp
@@ -0,0 +1,126 @@
+// 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 <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 (ch < 0x80)
+ outS += ch;
+
+ else { // TODO handle UTF-8 properly
+ 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.at(i).write(prettyIndent, indentLevel + 1);
+ if (i != (values.size() - 1))
+ s += ",";
+ if (prettyIndent)
+ s += "\n";
+ }
+
+ if (prettyIndent)
+ indentStr(prettyIndent, indentLevel - 1, s);
+ s += "}";
+}
+