diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac index 8298332ac1..f313128399 100644 --- a/src/univalue/configure.ac +++ b/src/univalue/configure.ac @@ -1,69 +1,69 @@ m4_define([libunivalue_major_version], [1]) m4_define([libunivalue_minor_version], [1]) -m4_define([libunivalue_micro_version], [3]) -m4_define([libunivalue_interface_age], [3]) +m4_define([libunivalue_micro_version], [4]) +m4_define([libunivalue_interface_age], [4]) # 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.3], +AC_INIT([univalue], [1.0.4], [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 diff --git a/src/univalue/gen/gen.cpp b/src/univalue/gen/gen.cpp index 85fe20924a..be3d4ccf72 100644 --- a/src/univalue/gen/gen.cpp +++ b/src/univalue/gen/gen.cpp @@ -1,82 +1,82 @@ // Copyright 2014 BitPay Inc. // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://opensource.org/licenses/mit-license.php. // // To re-create univalue_escapes.h: // $ g++ -o gen gen.cpp // $ ./gen > univalue_escapes.h // #include #include #include "univalue.h" static bool initEscapes; static std::string escapes[256]; static void initJsonEscape() { // Escape all lower control characters (some get overridden with smaller sequences below) for (int ch=0x00; ch<0x20; ++ch) { char tmpbuf[20]; snprintf(tmpbuf, sizeof(tmpbuf), "\\u%04x", ch); escapes[ch] = std::string(tmpbuf); } escapes[(int)'"'] = "\\\""; escapes[(int)'\\'] = "\\\\"; escapes[(int)'\b'] = "\\b"; escapes[(int)'\f'] = "\\f"; escapes[(int)'\n'] = "\\n"; escapes[(int)'\r'] = "\\r"; escapes[(int)'\t'] = "\\t"; escapes[(int)'\x7f'] = "\\u007f"; // U+007F DELETE 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].empty()) { printf("\tNULL,\n"); } else { printf("\t\""); unsigned int si; for (si = 0; si < escapes[i].size(); 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; } diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index 4fd2223b30..10ce39aec8 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -1,303 +1,307 @@ // 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. +// file COPYING or https://opensource.org/licenses/mit-license.php. #ifndef __UNIVALUE_H__ #define __UNIVALUE_H__ #include #include #include #include #include #include #include // .get_int64() #include // 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(); } void getObjMap(std::map& kv) const; bool checkObject(const std::map& memberTypes) const; const UniValue& operator[](const std::string& key) const; const UniValue& operator[](size_t index) const; bool exists(const std::string& key) const { size_t i; return findKey(key, i); } 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_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& 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 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, bool val_) { + UniValue tmpVal((bool)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, size_t len); bool read(const char *raw) { return read(raw, strlen(raw)); } bool read(const std::string& rawStr) { return read(rawStr.data(), rawStr.size()); } private: UniValue::VType typ; std::string val; // numbers are stored as C++ strings std::vector keys; std::vector values; bool findKey(const std::string& key, size_t& retIdx) 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 const std::vector& getKeys() const; const std::vector& getValues() const; bool get_bool() const; 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 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 Pair(const char *cKey, const char *cVal) { std::string key(cKey); UniValue uVal(cVal); return std::make_pair(key, uVal); } static inline std::pair Pair(const char *cKey, std::string strVal) { std::string key(cKey); UniValue uVal(strVal); return std::make_pair(key, uVal); } static inline std::pair Pair(const char *cKey, uint64_t u64Val) { std::string key(cKey); UniValue uVal(u64Val); return std::make_pair(key, uVal); } static inline std::pair Pair(const char *cKey, int64_t i64Val) { std::string key(cKey); UniValue uVal(i64Val); return std::make_pair(key, uVal); } static inline std::pair Pair(const char *cKey, bool iVal) { std::string key(cKey); UniValue uVal(iVal); return std::make_pair(key, uVal); } static inline std::pair Pair(const char *cKey, int iVal) { std::string key(cKey); UniValue uVal(iVal); return std::make_pair(key, uVal); } static inline std::pair Pair(const char *cKey, double dVal) { std::string key(cKey); UniValue uVal(dVal); return std::make_pair(key, uVal); } static inline std::pair Pair(const char *cKey, const UniValue& uVal) { std::string key(cKey); return std::make_pair(key, uVal); } static inline std::pair 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, const char *end); extern const char *uvTypeName(UniValue::VType t); static inline bool jsonTokenIsValue(enum jtokentype jtt) { switch (jtt) { case JTOK_KW_NULL: case JTOK_KW_TRUE: case JTOK_KW_FALSE: case JTOK_NUMBER: case JTOK_STRING: return true; default: return false; } // not reached } static inline bool json_isspace(int ch) { switch (ch) { case 0x20: case 0x09: case 0x0a: case 0x0d: return true; default: return false; } // not reached } extern const UniValue NullUniValue; const UniValue& find_value( const UniValue& obj, const std::string& name); #endif // __UNIVALUE_H__ diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp index 4c9c15d63e..2b966ad859 100644 --- a/src/univalue/lib/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -1,242 +1,242 @@ // 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. +// file COPYING or https://opensource.org/licenses/mit-license.php. #include #include #include #include #include "univalue.h" 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 std::string& s) { std::string tokenVal; unsigned int consumed; enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size()); return (tt == JTOK_NUMBER); } bool UniValue::setNumStr(const std::string& val_) { if (!validNumStr(val_)) return false; clear(); typ = VNUM; val = val_; return true; } bool UniValue::setInt(uint64_t val_) { std::ostringstream oss; oss << val_; return setNumStr(oss.str()); } bool UniValue::setInt(int64_t val_) { std::ostringstream oss; oss << val_; return setNumStr(oss.str()); } bool UniValue::setFloat(double val_) { std::ostringstream oss; oss << std::setprecision(16) << val_; bool ret = setNumStr(oss.str()); typ = VNUM; return ret; } bool UniValue::setStr(const std::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& vec) { if (typ != VARR) return false; values.insert(values.end(), vec.begin(), vec.end()); 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_) { if (typ != VOBJ) return false; size_t idx; if (findKey(key, idx)) values[idx] = val_; else __pushKV(key, val_); return true; } bool UniValue::pushKVs(const UniValue& obj) { if (typ != VOBJ || obj.typ != VOBJ) return false; for (size_t i = 0; i < obj.keys.size(); i++) __pushKV(obj.keys[i], obj.values.at(i)); return true; } void UniValue::getObjMap(std::map& 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 (size_t i = 0; i < keys.size(); i++) { if (keys[i] == key) { retIdx = i; return true; } } return false; } bool UniValue::checkObject(const std::map& t) const { if (typ != VOBJ) return false; for (std::map::const_iterator it = t.begin(); it != t.end(); ++it) { size_t idx = 0; if (!findKey(it->first, idx)) 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; size_t index = 0; if (!findKey(key, index)) return NullUniValue; return values.at(index); } const UniValue& UniValue::operator[](size_t 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; } diff --git a/src/univalue/lib/univalue_get.cpp b/src/univalue/lib/univalue_get.cpp index eabcf2dad1..19ba5bbc3e 100644 --- a/src/univalue/lib/univalue_get.cpp +++ b/src/univalue/lib/univalue_get.cpp @@ -1,147 +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. +// file COPYING or https://opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #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::min() && n <= std::numeric_limits::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::min() && n <= std::numeric_limits::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& UniValue::getKeys() const { if (typ != VOBJ) throw std::runtime_error("JSON value is not an object as expected"); return keys; } const std::vector& 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; } diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp index 14834db24d..c29821e597 100644 --- a/src/univalue/lib/univalue_read.cpp +++ b/src/univalue/lib/univalue_read.cpp @@ -1,449 +1,449 @@ // Copyright 2014 BitPay Inc. // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://opensource.org/licenses/mit-license.php. #include #include #include #include "univalue.h" #include "univalue_utffilter.h" 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(std::string& tokenVal, unsigned int& consumed, const char *raw, const char *end) { tokenVal.clear(); consumed = 0; const char *rawStart = raw; while (raw < end && (json_isspace(*raw))) // skip whitespace raw++; if (raw >= end) return JTOK_NONE; switch (*raw) { 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 std::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 == '-') && (raw < end) && (!json_isdigit(*raw))) return JTOK_ERR; while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } // part 2: frac if (raw < end && *raw == '.') { numStr += *raw; // copy . raw++; if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } } // part 3: exp if (raw < end && (*raw == 'e' || *raw == 'E')) { numStr += *raw; // copy E raw++; if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/- numStr += *raw; raw++; } if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } } tokenVal = numStr; consumed = (raw - rawStart); return JTOK_NUMBER; } case '"': { raw++; // skip " std::string valStr; JSONUTF8StringFilter writer(valStr); while (true) { if (raw >= end || (unsigned char)*raw < 0x20) return JTOK_ERR; else if (*raw == '\\') { raw++; // skip backslash if (raw >= end) return JTOK_ERR; switch (*raw) { case '"': writer.push_back('\"'); break; case '\\': writer.push_back('\\'); break; case '/': writer.push_back('/'); break; case 'b': writer.push_back('\b'); break; case 'f': writer.push_back('\f'); break; case 'n': writer.push_back('\n'); break; case 'r': writer.push_back('\r'); break; case 't': writer.push_back('\t'); break; case 'u': { unsigned int codepoint; if (raw + 1 + 4 >= end || hatoui(raw + 1, raw + 1 + 4, codepoint) != raw + 1 + 4) return JTOK_ERR; writer.push_back_u(codepoint); raw += 4; break; } default: return JTOK_ERR; } raw++; // skip esc'd char } else if (*raw == '"') { raw++; // skip " break; // stop scanning } else { writer.push_back(*raw); raw++; } } if (!writer.finalize()) return JTOK_ERR; 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, size_t size) { clear(); uint32_t expectMask = 0; std::vector stack; std::string tokenVal; unsigned int consumed; enum jtokentype tok = JTOK_NONE; enum jtokentype last_tok = JTOK_NONE; const char* end = raw + size; do { last_tok = tok; tok = getJsonToken(tokenVal, consumed, raw, end); 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: { 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; } if (!stack.size()) { *this = tmpVal; break; } UniValue *top = stack.back(); top->values.push_back(tmpVal); setExpect(NOT_VALUE); break; } case JTOK_NUMBER: { UniValue tmpVal(VNUM, tokenVal); if (!stack.size()) { *this = tmpVal; break; } UniValue *top = stack.back(); top->values.push_back(tmpVal); setExpect(NOT_VALUE); break; } case JTOK_STRING: { if (expect(OBJ_NAME)) { UniValue *top = stack.back(); top->keys.push_back(tokenVal); clearExpect(OBJ_NAME); setExpect(COLON); } else { UniValue tmpVal(VSTR, tokenVal); if (!stack.size()) { *this = tmpVal; break; } UniValue *top = stack.back(); 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, end); if (tok != JTOK_NONE) return false; return true; } diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/lib/univalue_utffilter.h index 20d4043009..c24ac58eaf 100644 --- a/src/univalue/lib/univalue_utffilter.h +++ b/src/univalue/lib/univalue_utffilter.h @@ -1,119 +1,119 @@ // Copyright 2016 Wladimir J. van der Laan // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://opensource.org/licenses/mit-license.php. #ifndef UNIVALUE_UTFFILTER_H #define UNIVALUE_UTFFILTER_H #include /** * Filter that generates and validates UTF-8, as well as collates UTF-16 * surrogate pairs as specified in RFC4627. */ class JSONUTF8StringFilter { public: explicit JSONUTF8StringFilter(std::string &s): str(s), is_valid(true), codepoint(0), state(0), surpair(0) { } // Write single 8-bit char (may be part of UTF-8 sequence) void push_back(unsigned char ch) { if (state == 0) { if (ch < 0x80) // 7-bit ASCII, fast direct pass-through str.push_back(ch); else if (ch < 0xc0) // Mid-sequence character, invalid in this state is_valid = false; else if (ch < 0xe0) { // Start of 2-byte sequence codepoint = (ch & 0x1f) << 6; state = 6; } else if (ch < 0xf0) { // Start of 3-byte sequence codepoint = (ch & 0x0f) << 12; state = 12; } else if (ch < 0xf8) { // Start of 4-byte sequence codepoint = (ch & 0x07) << 18; state = 18; } else // Reserved, invalid is_valid = false; } else { if ((ch & 0xc0) != 0x80) // Not a continuation, invalid is_valid = false; state -= 6; codepoint |= (ch & 0x3f) << state; if (state == 0) push_back_u(codepoint); } } // Write codepoint directly, possibly collating surrogate pairs void push_back_u(unsigned int codepoint_) { if (state) // Only accept full codepoints in open state is_valid = false; if (codepoint_ >= 0xD800 && codepoint_ < 0xDC00) { // First half of surrogate pair if (surpair) // Two subsequent surrogate pair openers - fail is_valid = false; else surpair = codepoint_; } else if (codepoint_ >= 0xDC00 && codepoint_ < 0xE000) { // Second half of surrogate pair if (surpair) { // Open surrogate pair, expect second half // Compute code point from UTF-16 surrogate pair append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint_ - 0xDC00)); surpair = 0; } else // Second half doesn't follow a first half - fail is_valid = false; } else { if (surpair) // First half of surrogate pair not followed by second - fail is_valid = false; else append_codepoint(codepoint_); } } // Check that we're in a state where the string can be ended // No open sequences, no open surrogate pairs, etc bool finalize() { if (state || surpair) is_valid = false; return is_valid; } private: std::string &str; bool is_valid; // Current UTF-8 decoding state unsigned int codepoint; int state; // Top bit to be filled in for next UTF-8 byte, or 0 // Keep track of the following state to handle the following section of // RFC4627: // // To escape an extended character that is not in the Basic Multilingual // Plane, the character is represented as a twelve-character sequence, // encoding the UTF-16 surrogate pair. So, for example, a string // containing only the G clef character (U+1D11E) may be represented as // "\uD834\uDD1E". // // 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 void append_codepoint(unsigned int codepoint_) { if (codepoint_ <= 0x7f) str.push_back((char)codepoint_); else if (codepoint_ <= 0x7FF) { str.push_back((char)(0xC0 | (codepoint_ >> 6))); str.push_back((char)(0x80 | (codepoint_ & 0x3F))); } else if (codepoint_ <= 0xFFFF) { str.push_back((char)(0xE0 | (codepoint_ >> 12))); str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); str.push_back((char)(0x80 | (codepoint_ & 0x3F))); } else if (codepoint_ <= 0x1FFFFF) { str.push_back((char)(0xF0 | (codepoint_ >> 18))); str.push_back((char)(0x80 | ((codepoint_ >> 12) & 0x3F))); str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); str.push_back((char)(0x80 | (codepoint_ & 0x3F))); } } }; #endif diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp index 827eb9b271..d31414edde 100644 --- a/src/univalue/lib/univalue_write.cpp +++ b/src/univalue/lib/univalue_write.cpp @@ -1,113 +1,113 @@ // Copyright 2014 BitPay Inc. // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://opensource.org/licenses/mit-license.php. #include #include #include #include "univalue.h" #include "univalue_escapes.h" static std::string json_escape(const std::string& inS) { std::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 outS += ch; } return outS; } std::string UniValue::write(unsigned int prettyIndent, unsigned int indentLevel) const { std::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, std::string& s) { s.append(prettyIndent * indentLevel, ' '); } void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::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 += "\n"; } if (prettyIndent) indentStr(prettyIndent, indentLevel - 1, s); s += "]"; } void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::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 += "}"; } diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp index 02446292a1..15b043d0eb 100644 --- a/src/univalue/test/object.cpp +++ b/src/univalue/test/object.cpp @@ -1,395 +1,407 @@ // 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. +// file COPYING or https://opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #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 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 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)); + strKey = "moon"; + BOOST_CHECK(obj.pushKV(strKey, true)); + + strKey = "spoon"; + BOOST_CHECK(obj.pushKV(strKey, false)); + 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.size(), 11); 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["moon"].getValStr(), "1"); + BOOST_CHECK_EQUAL(obj["spoon"].getValStr(), ""); 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("moon")); + BOOST_CHECK(obj.exists("spoon")); BOOST_CHECK(obj.exists("cat1")); BOOST_CHECK(obj.exists("cat2")); BOOST_CHECK(!obj.exists("nyuknyuknyuk")); std::map 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["moon"] = UniValue::VBOOL; + objTypes["spoon"] = UniValue::VBOOL; 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 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; } diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp index 75c0dc225a..8915714200 100644 --- a/src/univalue/test/unitester.cpp +++ b/src/univalue/test/unitester.cpp @@ -1,169 +1,169 @@ // 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. +// file COPYING or https://opensource.org/licenses/mit-license.php. #include #include #include #include #include #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 std::string srcdir(JSON_TEST_SRC); static bool test_failed = false; #define d_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", filename.c_str()); } } #define f_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", __func__); } } static std::string rtrim(std::string s) { s.erase(s.find_last_not_of(" \n\r\t")+1); return s; } static void runtest(std::string filename, const std::string& jdata) { std::string prefix = filename.substr(0, 4); bool wantPass = (prefix == "pass") || (prefix == "roun"); bool wantFail = (prefix == "fail"); bool wantRoundTrip = (prefix == "roun"); assert(wantPass || wantFail); UniValue val; bool testResult = val.read(jdata); if (wantPass) { d_assert(testResult == true); } else { d_assert(testResult == false); } if (wantRoundTrip) { std::string odata = val.write(0, 0); assert(odata == rtrim(jdata)); } } static void runtest_file(const char *filename_) { std::string basename(filename_); std::string filename = srcdir + "/" + basename; FILE *f = fopen(filename.c_str(), "r"); assert(f != NULL); std::string jdata; char buf[4096]; while (!feof(f)) { int bread = fread(buf, 1, sizeof(buf), f); assert(!ferror(f)); std::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", "fail35.json", "fail36.json", "fail37.json", "fail38.json", // invalid unicode: only first half of surrogate pair "fail39.json", // invalid unicode: only second half of surrogate pair "fail40.json", // invalid unicode: broken 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", "fail4.json", // extra comma "fail5.json", "fail6.json", "fail7.json", "fail8.json", "fail9.json", // extra comma "pass1.json", "pass2.json", "pass3.json", "round1.json", // round-trip test "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 void unescape_unicode_test() { UniValue val; bool testResult; // Escaped ASCII (quote) testResult = val.read("[\"\\u0022\"]"); f_assert(testResult); f_assert(val[0].get_str() == "\""); // Escaped Basic Plane character, two-byte UTF-8 testResult = val.read("[\"\\u0191\"]"); f_assert(testResult); f_assert(val[0].get_str() == "\xc6\x91"); // Escaped Basic Plane character, three-byte UTF-8 testResult = val.read("[\"\\u2191\"]"); f_assert(testResult); f_assert(val[0].get_str() == "\xe2\x86\x91"); // Escaped Supplementary Plane character U+1d161 testResult = val.read("[\"\\ud834\\udd61\"]"); f_assert(testResult); f_assert(val[0].get_str() == "\xf0\x9d\x85\xa1"); } int main (int argc, char *argv[]) { for (unsigned int fidx = 0; fidx < ARRAY_SIZE(filenames); fidx++) { runtest_file(filenames[fidx]); } unescape_unicode_test(); return test_failed ? 1 : 0; }