diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index 225533ce3..ba749196d 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -1,306 +1,310 @@ // Copyright 2014 BitPay Inc. // Copyright 2015 Bitcoin Core Developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://opensource.org/licenses/mit-license.php. #ifndef __UNIVALUE_H__ #define __UNIVALUE_H__ #include #include #include #include #include #include #include // std::pair +namespace { + struct UniValueStreamWriter; +} + class UniValue { + friend struct ::UniValueStreamWriter; + 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_write.cpp b/src/univalue/lib/univalue_write.cpp index db039fcb0..14e3965d3 100644 --- a/src/univalue/lib/univalue_write.cpp +++ b/src/univalue/lib/univalue_write.cpp @@ -1,112 +1,154 @@ // Copyright 2014 BitPay Inc. // Distributed under the MIT software license, see the accompanying // file COPYING or https://opensource.org/licenses/mit-license.php. -#include +#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); +namespace { +struct UniValueStreamWriter { + std::string str; - for (unsigned int i = 0; i < inS.size(); i++) { - unsigned char ch = inS[i]; - const char *escStr = escapes[ch]; + UniValueStreamWriter() { + str.reserve(1024); + } - if (escStr) - outS += escStr; - else - outS += ch; + std::string getString() { + return std::move(str); } - return outS; -} + void put(char c) { + str.push_back(c); + } + void put(char c, size_t nFill) { + str.append(nFill, c); + } + void write(const char *s) { + str.append(s); + } + void write(const std::string &s) { + str.append(s); + } + + void indentStr(unsigned int prettyIndent, unsigned int indentLevel) { + put(' ', prettyIndent * indentLevel); + } -std::string UniValue::write(unsigned int prettyIndent, - unsigned int indentLevel) const -{ - std::string s; - s.reserve(1024); + void escapeJson(const std::string &inS); + void writeAny(unsigned int prettyIndent, unsigned int indentLevel, const UniValue &obj); + void writeArray(unsigned int prettyIndent, unsigned int indentLevel, const UniValue &obj); + void writeObject(unsigned int prettyIndent, unsigned int indentLevel, const UniValue &obj); +}; +void UniValueStreamWriter::escapeJson(const std::string &inS) { + for (const auto ch : inS) { + const char * const escStr = escapes[uint8_t(ch)]; + + if (escStr) { + write(escStr); + } else { + put(ch); + } + } +} + +void UniValueStreamWriter::writeAny(unsigned int prettyIndent, unsigned int indentLevel, const UniValue &obj) { unsigned int modIndent = indentLevel; - if (modIndent == 0) + if (modIndent == 0) { modIndent = 1; + } - switch (typ) { - case VNULL: - s += "null"; + switch (obj.typ) { + case UniValue::VNULL: + write("null"); break; - case VOBJ: - writeObject(prettyIndent, modIndent, s); + case UniValue::VOBJ: + writeObject(prettyIndent, modIndent, obj); break; - case VARR: - writeArray(prettyIndent, modIndent, s); + case UniValue::VARR: + writeArray(prettyIndent, modIndent, obj); break; - case VSTR: - s += "\"" + json_escape(val) + "\""; + case UniValue::VSTR: + put('"'); + escapeJson(obj.val); + put('"'); break; - case VNUM: - s += val; + case UniValue::VNUM: + write(obj.val); break; - case VBOOL: - s += (val == "1" ? "true" : "false"); + case UniValue::VBOOL: + write(obj.val == "1" ? "true" : "false"); break; } - - return s; } -static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) -{ - s.append(prettyIndent * indentLevel, ' '); -} +void UniValueStreamWriter::writeArray(unsigned int prettyIndent, unsigned int indentLevel, const UniValue &obj) { + put('['); + if (prettyIndent) { + put('\n'); + } -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 += ","; + const size_t nValues = obj.values.size(); + for (size_t i = 0; i < nValues; ++i) { + if (prettyIndent) { + indentStr(prettyIndent, indentLevel); + } + writeAny(prettyIndent, indentLevel + 1, obj.values[i]); + if (i != nValues - 1) { + put(','); + } + if (prettyIndent) { + put('\n'); } - if (prettyIndent) - s += "\n"; } - if (prettyIndent) - indentStr(prettyIndent, indentLevel - 1, s); - s += "]"; + if (prettyIndent) { + indentStr(prettyIndent, indentLevel - 1); + } + put(']'); } -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 += "}"; +void UniValueStreamWriter::writeObject(unsigned int prettyIndent, unsigned int indentLevel, const UniValue &obj) { + put('{'); + if (prettyIndent) { + put('\n'); + } + + // Note: if typ == VOBJ, then keys.size() == values.size() always, so we can + // use the non-bounds-checking operator[]() for both keys and values here safely. + const size_t nItems = obj.keys.size(); + for (size_t i = 0; i < nItems; ++i) { + if (prettyIndent) { + indentStr(prettyIndent, indentLevel); + } + put('"'); + escapeJson(obj.keys[i]); + write("\":"); + + if (prettyIndent) { + put(' '); + } + writeAny(prettyIndent, indentLevel + 1, obj.values[i]); + if (i != nItems - 1) { + put(','); + } + if (prettyIndent) { + put('\n'); + } + } + + if (prettyIndent) { + indentStr(prettyIndent, indentLevel - 1); + } + put('}'); +} } +std::string UniValue::write(unsigned int prettyIndent, unsigned int indentLevel) const { + UniValueStreamWriter ss; + ss.writeAny(prettyIndent, indentLevel, *this); + return ss.getString(); +}