diff options
Diffstat (limited to 'lib/jsoncpp/jsoncpp.cpp')
-rw-r--r-- | lib/jsoncpp/jsoncpp.cpp | 2762 |
1 files changed, 1351 insertions, 1411 deletions
diff --git a/lib/jsoncpp/jsoncpp.cpp b/lib/jsoncpp/jsoncpp.cpp index 507a1c6ad..30e0a69f7 100644 --- a/lib/jsoncpp/jsoncpp.cpp +++ b/lib/jsoncpp/jsoncpp.cpp @@ -92,6 +92,9 @@ license you like. #ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED #define LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#if !defined(JSON_IS_AMALGAMATION) +#include <json/config.h> +#endif // Also support old flag NO_LOCALE_SUPPORT #ifdef NO_LOCALE_SUPPORT @@ -109,7 +112,7 @@ license you like. */ namespace Json { -static char getDecimalPoint() { +static inline char getDecimalPoint() { #ifdef JSONCPP_NO_LOCALE_SUPPORT return '\0'; #else @@ -119,8 +122,8 @@ static char getDecimalPoint() { } /// Converts a unicode code-point to UTF-8. -static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) { - JSONCPP_STRING result; +static inline String codePointToUTF8(unsigned int cp) { + String result; // based on description from http://en.wikipedia.org/wiki/UTF-8 @@ -154,7 +157,7 @@ enum { }; // Defines a char buffer for use with uintToString(). -typedef char UIntToStringBuffer[uintToStringBufferSize]; +using UIntToStringBuffer = char[uintToStringBufferSize]; /** Converts an unsigned integer to string. * @param value Unsigned integer to convert to string @@ -174,28 +177,45 @@ static inline void uintToString(LargestUInt value, char*& current) { * We had a sophisticated way, but it did not work in WinCE. * @see https://github.com/open-source-parsers/jsoncpp/pull/9 */ -static inline void fixNumericLocale(char* begin, char* end) { - while (begin < end) { +template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) { + for (; begin != end; ++begin) { if (*begin == ',') { *begin = '.'; } - ++begin; } + return begin; } -static inline void fixNumericLocaleInput(char* begin, char* end) { +template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) { char decimalPoint = getDecimalPoint(); - if (decimalPoint != '\0' && decimalPoint != '.') { - while (begin < end) { - if (*begin == '.') { - *begin = decimalPoint; - } - ++begin; + if (decimalPoint == '\0' || decimalPoint == '.') { + return; + } + for (; begin != end; ++begin) { + if (*begin == '.') { + *begin = decimalPoint; } } } -} // namespace Json { +/** + * Return iterator that would be the new end of the range [begin,end), if we + * were to delete zeros in the end of string, but not the last zero before '.'. + */ +template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) { + for (; begin != end; --end) { + if (*(end - 1) != '0') { + return end; + } + // Don't delete the last zero before the decimal point. + if (begin != (end - 1) && *(end - 2) == '.') { + return end; + } + } + return end; +} + +} // namespace Json #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED @@ -219,69 +239,65 @@ static inline void fixNumericLocaleInput(char* begin, char* end) { // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) +#include "json_tool.h" #include <json/assertions.h> #include <json/reader.h> #include <json/value.h> -#include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include <utility> -#include <cstdio> +#include <algorithm> #include <cassert> #include <cstring> +#include <iostream> #include <istream> -#include <sstream> +#include <limits> #include <memory> #include <set> -#include <limits> +#include <sstream> +#include <utility> -#if defined(_MSC_VER) -#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above -#define snprintf sprintf_s -#elif _MSC_VER >= 1900 // VC++ 14.0 and above -#define snprintf std::snprintf -#else -#define snprintf _snprintf -#endif -#elif defined(__ANDROID__) || defined(__QNXNTO__) -#define snprintf snprintf -#elif __cplusplus >= 201103L -#if !defined(__MINGW32__) && !defined(__CYGWIN__) -#define snprintf std::snprintf -#endif -#endif +#include <cstdio> +#if __cplusplus >= 201103L -#if defined(__QNXNTO__) +#if !defined(sscanf) #define sscanf std::sscanf #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +#endif //__cplusplus + +#if defined(_MSC_VER) +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES +#endif //_MSC_VER + +#if defined(_MSC_VER) // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif -// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile time to change the stack limit +// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile +// time to change the stack limit #if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) #define JSONCPP_DEPRECATED_STACK_LIMIT 1000 #endif -static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() +static size_t const stackLimit_g = + JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() namespace Json { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr<CharReader> CharReaderPtr; +using CharReaderPtr = std::unique_ptr<CharReader>; #else -typedef std::auto_ptr<CharReader> CharReaderPtr; +using CharReaderPtr = std::auto_ptr<CharReader>; #endif // Implementation of class Features // //////////////////////////////// -Features::Features() - : allowComments_(true), strictRoot_(false), - allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} +Features::Features() = default; -Features Features::all() { return Features(); } +Features Features::all() { return {}; } Features Features::strictMode() { Features features; @@ -296,49 +312,38 @@ Features Features::strictMode() { // //////////////////////////////// bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { - for (; begin < end; ++begin) - if (*begin == '\n' || *begin == '\r') - return true; - return false; + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); } // Class Reader // ////////////////////////////////////////////////////////////////// -Reader::Reader() - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(Features::all()), - collectComments_() {} +Reader::Reader() : features_(Features::all()) {} -Reader::Reader(const Features& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(features), collectComments_() { -} +Reader::Reader(const Features& features) : features_(features) {} -bool -Reader::parse(const std::string& document, Value& root, bool collectComments) { +bool Reader::parse(const std::string& document, Value& root, + bool collectComments) { document_.assign(document.begin(), document.end()); const char* begin = document_.c_str(); const char* end = begin + document_.length(); return parse(begin, end, root, collectComments); } -bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { - // std::istream_iterator<char> begin(sin); +bool Reader::parse(std::istream& is, Value& root, bool collectComments) { + // std::istream_iterator<char> begin(is); // std::istream_iterator<char> end; // Those would allow streamed input from a file, if parse() were a // template function. - // Since JSONCPP_STRING is reference-counted, this at least does not + // Since String is reference-counted, this at least does not // create an extra copy. - JSONCPP_STRING doc; - std::getline(sin, doc, (char)EOF); + String doc; + std::getline(is, doc, static_cast<char> EOF); return parse(doc.data(), doc.data() + doc.size(), root, collectComments); } -bool Reader::parse(const char* beginDoc, - const char* endDoc, - Value& root, +bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments) { if (!features_.allowComments_) { collectComments = false; @@ -348,8 +353,8 @@ bool Reader::parse(const char* beginDoc, end_ = endDoc; collectComments_ = collectComments; current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; commentsBefore_.clear(); errors_.clear(); while (!nodes_.empty()) @@ -379,9 +384,11 @@ bool Reader::parse(const char* beginDoc, bool Reader::readValue() { // readValue() may call itself only if it calls readObject() or ReadArray(). - // These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue(). - // parse() executes one nodes_.push(), so > instead of >=. - if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + // These methods execute nodes_.push() just before and nodes_.pop)() just + // after calling readValue(). parse() executes one nodes_.push(), so > instead + // of >=. + if (nodes_.size() > stackLimit_g) + throwRuntimeError("Exceeded stackLimit in readValue()."); Token token; skipCommentTokens(token); @@ -407,30 +414,24 @@ bool Reader::readValue() { case tokenString: successful = decodeString(token); break; - case tokenTrue: - { + case tokenTrue: { Value v(true); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { + } break; + case tokenFalse: { Value v(false); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { + } break; + case tokenNull: { Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; + } break; case tokenArraySeparator: case tokenObjectEnd: case tokenArrayEnd: @@ -536,7 +537,7 @@ bool Reader::readToken(Token& token) { if (!ok) token.type_ = tokenError; token.end_ = current_; - return true; + return ok; } void Reader::skipSpaces() { @@ -549,7 +550,7 @@ void Reader::skipSpaces() { } } -bool Reader::match(Location pattern, int patternLength) { +bool Reader::match(const Char* pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; @@ -583,16 +584,16 @@ bool Reader::readComment() { return true; } -JSONCPP_STRING Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { - JSONCPP_STRING normalized; +String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { + String normalized; normalized.reserve(static_cast<size_t>(end - begin)); Reader::Location current = begin; while (current != end) { char c = *current++; if (c == '\r') { if (current != end && *current == '\n') - // convert dos EOL - ++current; + // convert dos EOL + ++current; // convert Mac EOL normalized += '\n'; } else { @@ -602,12 +603,12 @@ JSONCPP_STRING Reader::normalizeEOL(Reader::Location begin, Reader::Location end return normalized; } -void -Reader::addComment(Location begin, Location end, CommentPlacement placement) { +void Reader::addComment(Location begin, Location end, + CommentPlacement placement) { assert(collectComments_); - const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + const String& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); + assert(lastValue_ != nullptr); lastValue_->setComment(normalized, placement); } else { commentsBefore_ += normalized; @@ -640,7 +641,7 @@ bool Reader::readCppStyleComment() { } void Reader::readNumber() { - const char *p = current_; + Location p = current_; char c = '0'; // stopgap for already consumed character // integral part while (c >= '0' && c <= '9') @@ -673,12 +674,12 @@ bool Reader::readString() { return c == '"'; } -bool Reader::readObject(Token& tokenStart) { +bool Reader::readObject(Token& token) { Token tokenName; - JSONCPP_STRING name; + String name; Value init(objectValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); + currentValue().setOffsetStart(token.start_ - begin_); while (readToken(tokenName)) { bool initialTokenOk = true; while (tokenName.type_ == tokenComment && initialTokenOk) @@ -695,15 +696,15 @@ bool Reader::readObject(Token& tokenStart) { Value numberName; if (!decodeNumber(tokenName, numberName)) return recoverFromError(tokenObjectEnd); - name = JSONCPP_STRING(numberName.asCString()); + name = numberName.asString(); } else { break; } Token colon; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover( - "Missing ':' after object member name", colon, tokenObjectEnd); + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); } Value& value = currentValue()[name]; nodes_.push(&value); @@ -716,8 +717,8 @@ bool Reader::readObject(Token& tokenStart) { if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); } bool finalizeTokenOk = true; while (comma.type_ == tokenComment && finalizeTokenOk) @@ -725,14 +726,14 @@ bool Reader::readObject(Token& tokenStart) { if (comma.type_ == tokenObjectEnd) return true; } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); } -bool Reader::readArray(Token& tokenStart) { +bool Reader::readArray(Token& token) { Value init(arrayValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); + currentValue().setOffsetStart(token.start_ - begin_); skipSpaces(); if (current_ != end_ && *current_ == ']') // empty array { @@ -749,19 +750,19 @@ bool Reader::readArray(Token& tokenStart) { if (!ok) // error already set return recoverFromError(tokenArrayEnd); - Token token; + Token currentToken; // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); } - if (token.type_ == tokenArrayEnd) + if (currentToken.type_ == tokenArrayEnd) break; } return true; @@ -785,7 +786,8 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { bool isNegative = *current == '-'; if (isNegative) ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of them. + // TODO: Help the compiler do the div and mod at compile time or get rid of + // them. Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 : Value::maxLargestUInt; @@ -795,7 +797,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); - Value::UInt digit(static_cast<Value::UInt>(c - '0')); + auto digit(static_cast<Value::UInt>(c - '0')); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If // a) we've only just touched the limit, b) this is the last digit, and @@ -831,18 +833,17 @@ bool Reader::decodeDouble(Token& token) { bool Reader::decodeDouble(Token& token, Value& decoded) { double value = 0; - JSONCPP_STRING buffer(token.start_, token.end_); - JSONCPP_ISTRINGSTREAM is(buffer); + String buffer(token.start_, token.end_); + IStringStream is(buffer); if (!(is >> value)) - return addError("'" + JSONCPP_STRING(token.start_, token.end_) + - "' is not a number.", - token); + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); decoded = value; return true; } bool Reader::decodeString(Token& token) { - JSONCPP_STRING decoded_string; + String decoded_string; if (!decodeString(token, decoded_string)) return false; Value decoded(decoded_string); @@ -852,7 +853,7 @@ bool Reader::decodeString(Token& token) { return true; } -bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { +bool Reader::decodeString(Token& token, String& decoded) { decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2)); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' @@ -860,7 +861,7 @@ bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { Char c = *current++; if (c == '"') break; - else if (c == '\\') { + if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); Char escape = *current++; @@ -905,10 +906,8 @@ bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { return true; } -bool Reader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { +bool Reader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false; @@ -917,10 +916,9 @@ bool Reader::decodeUnicodeCodePoint(Token& token, if (end - current < 6) return addError( "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; + token, current); if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } else @@ -928,20 +926,17 @@ bool Reader::decodeUnicodeCodePoint(Token& token, } else return addError("expecting another \\u token to begin the second half of " "a unicode surrogate pair", - token, - current); + token, current); } return true; } -bool Reader::decodeUnicodeEscapeSequence(Token& token, - Location& current, +bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& ret_unicode) { if (end - current < 4) return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, + "Bad unicode escape sequence in string: four digits expected.", token, current); int unicode = 0; for (int index = 0; index < 4; ++index) { @@ -956,15 +951,13 @@ bool Reader::decodeUnicodeEscapeSequence(Token& token, else return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); + token, current); } ret_unicode = static_cast<unsigned int>(unicode); return true; } -bool -Reader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { +bool Reader::addError(const String& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -986,8 +979,7 @@ bool Reader::recoverFromError(TokenType skipUntilToken) { return false; } -bool Reader::addErrorAndRecover(const JSONCPP_STRING& message, - Token& token, +bool Reader::addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); @@ -1001,8 +993,7 @@ Reader::Char Reader::getNextChar() { return *current_++; } -void Reader::getLocationLineAndColumn(Location location, - int& line, +void Reader::getLocationLineAndColumn(Location location, int& line, int& column) const { Location current = begin_; Location lastLineStart = current; @@ -1024,25 +1015,22 @@ void Reader::getLocationLineAndColumn(Location location, ++line; } -JSONCPP_STRING Reader::getLocationLineAndColumn(Location location) const { +String Reader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; - snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); return buffer; } // Deprecated. Preserved for backward compatibility -JSONCPP_STRING Reader::getFormatedErrorMessages() const { +String Reader::getFormatedErrorMessages() const { return getFormattedErrorMessages(); } -JSONCPP_STRING Reader::getFormattedErrorMessages() const { - JSONCPP_STRING formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; +String Reader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; formattedMessage += " " + error.message_ + "\n"; @@ -1055,10 +1043,7 @@ JSONCPP_STRING Reader::getFormattedErrorMessages() const { std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { std::vector<Reader::StructuredError> allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; + for (const auto& error : errors_) { Reader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; @@ -1068,28 +1053,27 @@ std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { return allErrors; } -bool Reader::pushError(const Value& value, const JSONCPP_STRING& message) { +bool Reader::pushError(const Value& value, const String& message) { ptrdiff_t const length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length) + if (value.getOffsetStart() > length || value.getOffsetLimit() > length) return false; Token token; token.type_ = tokenError; token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); + token.end_ = begin_ + value.getOffsetLimit(); ErrorInfo info; info.token_ = token; info.message_ = message; - info.extra_ = 0; + info.extra_ = nullptr; errors_.push_back(info); return true; } -bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { +bool Reader::pushError(const Value& value, const String& message, + const Value& extra) { ptrdiff_t const length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length - || extra.getOffsetLimit() > length) + if (value.getOffsetStart() > length || value.getOffsetLimit() > length || + extra.getOffsetLimit() > length) return false; Token token; token.type_ = tokenError; @@ -1103,15 +1087,15 @@ bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const return true; } -bool Reader::good() const { - return !errors_.size(); -} +bool Reader::good() const { return errors_.empty(); } -// exact copy of Features +// Originally copied from the Features class (now deprecated), used internally +// for features implementation. class OurFeatures { public: static OurFeatures all(); bool allowComments_; + bool allowTrailingCommas_; bool strictRoot_; bool allowDroppedNullPlaceholders_; bool allowNumericKeys_; @@ -1119,42 +1103,36 @@ public: bool failIfExtra_; bool rejectDupKeys_; bool allowSpecialFloats_; - int stackLimit_; -}; // OurFeatures + bool skipBom_; + size_t stackLimit_; +}; // OurFeatures -// exact copy of Implementation of class Features -// //////////////////////////////// - -OurFeatures OurFeatures::all() { return OurFeatures(); } +OurFeatures OurFeatures::all() { return {}; } // Implementation of class Reader // //////////////////////////////// -// exact copy of Reader, renamed to OurReader +// Originally copied from the Reader class (now deprecated), used internally +// for implementing JSON reading. class OurReader { public: - typedef char Char; - typedef const Char* Location; + using Char = char; + using Location = const Char*; struct StructuredError { ptrdiff_t offset_start; ptrdiff_t offset_limit; - JSONCPP_STRING message; + String message; }; - OurReader(OurFeatures const& features); - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, + explicit OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); - JSONCPP_STRING getFormattedErrorMessages() const; + String getFormattedErrorMessages() const; std::vector<StructuredError> getStructuredErrors() const; - bool pushError(const Value& value, const JSONCPP_STRING& message); - bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); - bool good() const; private: - OurReader(OurReader const&); // no impl - void operator=(OurReader const&); // no impl + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl enum TokenType { tokenEndOfStream = 0, @@ -1186,17 +1164,18 @@ private: class ErrorInfo { public: Token token_; - JSONCPP_STRING message_; + String message_; Location extra_; }; - typedef std::deque<ErrorInfo> Errors; + using Errors = std::deque<ErrorInfo>; bool readToken(Token& token); void skipSpaces(); - bool match(Location pattern, int patternLength); + void skipBom(bool skipBom); + bool match(const Char* pattern, int patternLength); bool readComment(); - bool readCStyleComment(); + bool readCStyleComment(bool* containsNewLineResult); bool readCppStyleComment(); bool readString(); bool readStringSingleQuote(); @@ -1207,68 +1186,57 @@ private: bool decodeNumber(Token& token); bool decodeNumber(Token& token, Value& decoded); bool decodeString(Token& token); - bool decodeString(Token& token, JSONCPP_STRING& decoded); + bool decodeString(Token& token, String& decoded); bool decodeDouble(Token& token); bool decodeDouble(Token& token, Value& decoded); - bool decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, + bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode); - bool decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0); + bool decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const JSONCPP_STRING& message, - Token& token, + bool addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken); void skipUntilSpace(); Value& currentValue(); Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - JSONCPP_STRING getLocationLineAndColumn(Location location) const; + void getLocationLineAndColumn(Location location, int& line, + int& column) const; + String getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); - static JSONCPP_STRING normalizeEOL(Location begin, Location end); + static String normalizeEOL(Location begin, Location end); static bool containsNewLine(Location begin, Location end); - typedef std::stack<Value*> Nodes; - Nodes nodes_; - Errors errors_; - JSONCPP_STRING document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value* lastValue_; - JSONCPP_STRING commentsBefore_; + using Nodes = std::stack<Value*>; + + Nodes nodes_{}; + Errors errors_{}; + String document_{}; + Location begin_ = nullptr; + Location end_ = nullptr; + Location current_ = nullptr; + Location lastValueEnd_ = nullptr; + Value* lastValue_ = nullptr; + bool lastValueHasAComment_ = false; + String commentsBefore_{}; OurFeatures const features_; - bool collectComments_; -}; // OurReader + bool collectComments_ = false; +}; // OurReader // complete copy of Read impl, for OurReader -bool OurReader::containsNewLine(OurReader::Location begin, OurReader::Location end) { - for (; begin < end; ++begin) - if (*begin == '\n' || *begin == '\r') - return true; - return false; +bool OurReader::containsNewLine(OurReader::Location begin, + OurReader::Location end) { + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); } -OurReader::OurReader(OurFeatures const& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), - features_(features), collectComments_() { -} +OurReader::OurReader(OurFeatures const& features) : features_(features) {} -bool OurReader::parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments) { +bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments) { if (!features_.allowComments_) { collectComments = false; } @@ -1277,22 +1245,23 @@ bool OurReader::parse(const char* beginDoc, end_ = endDoc; collectComments_ = collectComments; current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; commentsBefore_.clear(); errors_.clear(); while (!nodes_.empty()) nodes_.pop(); nodes_.push(&root); + // skip byte order mark if it exists at the beginning of the UTF-8 text. + skipBom(features_.skipBom_); bool successful = readValue(); + nodes_.pop(); Token token; skipCommentTokens(token); - if (features_.failIfExtra_) { - if ((features_.strictRoot_ || token.type_ != tokenError) && token.type_ != tokenEndOfStream) { - addError("Extra non-whitespace after JSON value.", token); - return false; - } + if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) { + addError("Extra non-whitespace after JSON value.", token); + return false; } if (collectComments_ && !commentsBefore_.empty()) root.setComment(commentsBefore_, commentAfter); @@ -1314,7 +1283,8 @@ bool OurReader::parse(const char* beginDoc, bool OurReader::readValue() { // To preserve the old behaviour we cast size_t to int. - if (static_cast<int>(nodes_.size()) > features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + if (nodes_.size() > features_.stackLimit_) + throwRuntimeError("Exceeded stackLimit in readValue()."); Token token; skipCommentTokens(token); bool successful = true; @@ -1339,54 +1309,42 @@ bool OurReader::readValue() { case tokenString: successful = decodeString(token); break; - case tokenTrue: - { + case tokenTrue: { Value v(true); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { + } break; + case tokenFalse: { Value v(false); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { + } break; + case tokenNull: { Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNaN: - { + } break; + case tokenNaN: { Value v(std::numeric_limits<double>::quiet_NaN()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenPosInf: - { + } break; + case tokenPosInf: { Value v(std::numeric_limits<double>::infinity()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNegInf: - { + } break; + case tokenNegInf: { Value v(-std::numeric_limits<double>::infinity()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; + } break; case tokenArraySeparator: case tokenObjectEnd: case tokenArrayEnd: @@ -1408,6 +1366,7 @@ bool OurReader::readValue() { if (collectComments_) { lastValueEnd_ = current_; + lastValueHasAComment_ = false; lastValue_ = ¤tValue(); } @@ -1448,10 +1407,13 @@ bool OurReader::readToken(Token& token) { break; case '\'': if (features_.allowSingleQuotes_) { - token.type_ = tokenString; - ok = readStringSingleQuote(); + token.type_ = tokenString; + ok = readStringSingleQuote(); + } else { + // If we don't allow single quotes, this is a failure case. + ok = false; + } break; - } // else fall through case '/': token.type_ = tokenComment; ok = readComment(); @@ -1477,6 +1439,14 @@ bool OurReader::readToken(Token& token) { ok = features_.allowSpecialFloats_ && match("nfinity", 7); } break; + case '+': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenPosInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; case 't': token.type_ = tokenTrue; ok = match("rue", 3); @@ -1521,7 +1491,7 @@ bool OurReader::readToken(Token& token) { if (!ok) token.type_ = tokenError; token.end_ = current_; - return true; + return ok; } void OurReader::skipSpaces() { @@ -1534,7 +1504,17 @@ void OurReader::skipSpaces() { } } -bool OurReader::match(Location pattern, int patternLength) { +void OurReader::skipBom(bool skipBom) { + // The default behavior is to skip BOM. + if (skipBom) { + if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) { + begin_ += 3; + current_ = begin_; + } + } +} + +bool OurReader::match(const Char* pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; @@ -1546,21 +1526,32 @@ bool OurReader::match(Location pattern, int patternLength) { } bool OurReader::readComment() { - Location commentBegin = current_ - 1; - Char c = getNextChar(); + const Location commentBegin = current_ - 1; + const Char c = getNextChar(); bool successful = false; - if (c == '*') - successful = readCStyleComment(); - else if (c == '/') + bool cStyleWithEmbeddedNewline = false; + + const bool isCStyleComment = (c == '*'); + const bool isCppStyleComment = (c == '/'); + if (isCStyleComment) { + successful = readCStyleComment(&cStyleWithEmbeddedNewline); + } else if (isCppStyleComment) { successful = readCppStyleComment(); + } + if (!successful) return false; if (collectComments_) { CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) - placement = commentAfterOnSameLine; + + if (!lastValueHasAComment_) { + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (isCppStyleComment || !cStyleWithEmbeddedNewline) { + placement = commentAfterOnSameLine; + lastValueHasAComment_ = true; + } + } } addComment(commentBegin, current_, placement); @@ -1568,16 +1559,17 @@ bool OurReader::readComment() { return true; } -JSONCPP_STRING OurReader::normalizeEOL(OurReader::Location begin, OurReader::Location end) { - JSONCPP_STRING normalized; +String OurReader::normalizeEOL(OurReader::Location begin, + OurReader::Location end) { + String normalized; normalized.reserve(static_cast<size_t>(end - begin)); OurReader::Location current = begin; while (current != end) { char c = *current++; if (c == '\r') { if (current != end && *current == '\n') - // convert dos EOL - ++current; + // convert dos EOL + ++current; // convert Mac EOL normalized += '\n'; } else { @@ -1587,24 +1579,29 @@ JSONCPP_STRING OurReader::normalizeEOL(OurReader::Location begin, OurReader::Loc return normalized; } -void -OurReader::addComment(Location begin, Location end, CommentPlacement placement) { +void OurReader::addComment(Location begin, Location end, + CommentPlacement placement) { assert(collectComments_); - const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + const String& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); + assert(lastValue_ != nullptr); lastValue_->setComment(normalized, placement); } else { commentsBefore_ += normalized; } } -bool OurReader::readCStyleComment() { +bool OurReader::readCStyleComment(bool* containsNewLineResult) { + *containsNewLineResult = false; + while ((current_ + 1) < end_) { Char c = getNextChar(); if (c == '*' && *current_ == '/') break; + if (c == '\n') + *containsNewLineResult = true; } + return getNextChar() == '/'; } @@ -1625,7 +1622,7 @@ bool OurReader::readCppStyleComment() { } bool OurReader::readNumber(bool checkInf) { - const char *p = current_; + Location p = current_; if (checkInf && p != end_ && *p == 'I') { current_ = ++p; return false; @@ -1662,7 +1659,6 @@ bool OurReader::readString() { return c == '"'; } - bool OurReader::readStringSingleQuote() { Char c = 0; while (current_ != end_) { @@ -1675,19 +1671,21 @@ bool OurReader::readStringSingleQuote() { return c == '\''; } -bool OurReader::readObject(Token& tokenStart) { +bool OurReader::readObject(Token& token) { Token tokenName; - JSONCPP_STRING name; + String name; Value init(objectValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); + currentValue().setOffsetStart(token.start_ - begin_); while (readToken(tokenName)) { bool initialTokenOk = true; while (tokenName.type_ == tokenComment && initialTokenOk) initialTokenOk = readToken(tokenName); if (!initialTokenOk) break; - if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + if (tokenName.type_ == tokenObjectEnd && + (name.empty() || + features_.allowTrailingCommas_)) // empty object or trailing comma return true; name.clear(); if (tokenName.type_ == tokenString) { @@ -1701,17 +1699,17 @@ bool OurReader::readObject(Token& tokenStart) { } else { break; } + if (name.length() >= (1U << 30)) + throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + String msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover(msg, tokenName, tokenObjectEnd); + } Token colon; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover( - "Missing ':' after object member name", colon, tokenObjectEnd); - } - if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); - if (features_.rejectDupKeys_ && currentValue().isMember(name)) { - JSONCPP_STRING msg = "Duplicate key: '" + name + "'"; - return addErrorAndRecover( - msg, tokenName, tokenObjectEnd); + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); } Value& value = currentValue()[name]; nodes_.push(&value); @@ -1724,8 +1722,8 @@ bool OurReader::readObject(Token& tokenStart) { if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); } bool finalizeTokenOk = true; while (comma.type_ == tokenComment && finalizeTokenOk) @@ -1733,23 +1731,27 @@ bool OurReader::readObject(Token& tokenStart) { if (comma.type_ == tokenObjectEnd) return true; } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); } -bool OurReader::readArray(Token& tokenStart) { +bool OurReader::readArray(Token& token) { Value init(arrayValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); - skipSpaces(); - if (current_ != end_ && *current_ == ']') // empty array - { - Token endArray; - readToken(endArray); - return true; - } + currentValue().setOffsetStart(token.start_ - begin_); int index = 0; for (;;) { + skipSpaces(); + if (current_ != end_ && *current_ == ']' && + (index == 0 || + (features_.allowTrailingCommas_ && + !features_.allowDroppedNullPlaceholders_))) // empty array or trailing + // comma + { + Token endArray; + readToken(endArray); + return true; + } Value& value = currentValue()[index++]; nodes_.push(&value); bool ok = readValue(); @@ -1757,19 +1759,19 @@ bool OurReader::readArray(Token& tokenStart) { if (!ok) // error already set return recoverFromError(tokenArrayEnd); - Token token; + Token currentToken; // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); } - if (token.type_ == tokenArrayEnd) + if (currentToken.type_ == tokenArrayEnd) break; } return true; @@ -1790,38 +1792,78 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { // larger than the maximum supported value of an integer then // we decode the number as a double. Location current = token.start_; - bool isNegative = *current == '-'; - if (isNegative) + const bool isNegative = *current == '-'; + if (isNegative) { ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of them. - Value::LargestUInt maxIntegerValue = - isNegative ? Value::LargestUInt(-Value::minLargestInt) - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; + } + + // We assume we can represent the largest and smallest integer types as + // unsigned integers with separate sign. This is only true if they can fit + // into an unsigned integer. + static_assert(Value::maxLargestInt <= Value::maxLargestUInt, + "Int must be smaller than UInt"); + + // We need to convert minLargestInt into a positive number. The easiest way + // to do this conversion is to assume our "threshold" value of minLargestInt + // divided by 10 can fit in maxLargestInt when absolute valued. This should + // be a safe assumption. + static_assert(Value::minLargestInt <= -Value::maxLargestInt, + "The absolute value of minLargestInt must be greater than or " + "equal to maxLargestInt"); + static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt, + "The absolute value of minLargestInt must be only 1 magnitude " + "larger than maxLargest Int"); + + static constexpr Value::LargestUInt positive_threshold = + Value::maxLargestUInt / 10; + static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10; + + // For the negative values, we have to be more careful. Since typically + // -Value::minLargestInt will cause an overflow, we first divide by 10 and + // then take the inverse. This assumes that minLargestInt is only a single + // power of 10 different in magnitude, which we check above. For the last + // digit, we take the modulus before negating for the same reason. + static constexpr auto negative_threshold = + Value::LargestUInt(-(Value::minLargestInt / 10)); + static constexpr auto negative_last_digit = + Value::UInt(-(Value::minLargestInt % 10)); + + const Value::LargestUInt threshold = + isNegative ? negative_threshold : positive_threshold; + const Value::UInt max_last_digit = + isNegative ? negative_last_digit : positive_last_digit; + Value::LargestUInt value = 0; while (current < token.end_) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); - Value::UInt digit(static_cast<Value::UInt>(c - '0')); + + const auto digit(static_cast<Value::UInt>(c - '0')); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If - // a) we've only just touched the limit, b) this is the last digit, and + // a) we've only just touched the limit, meaing value == threshold, + // b) this is the last digit, or // c) it's small enough to fit in that rounding delta, we're okay. // Otherwise treat this number as a double to avoid overflow. if (value > threshold || current != token.end_ || - digit > maxIntegerValue % 10) { + digit > max_last_digit) { return decodeDouble(token, decoded); } } value = value * 10 + digit; } - if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) + + if (isNegative) { + // We use the same magnitude assumption here, just in case. + const auto last_digit = static_cast<Value::UInt>(value % 10); + decoded = -Value::LargestInt(value / 10) * 10 - last_digit; + } else if (value <= Value::LargestUInt(Value::maxLargestInt)) { decoded = Value::LargestInt(value); - else + } else { decoded = value; + } + return true; } @@ -1837,44 +1879,18 @@ bool OurReader::decodeDouble(Token& token) { bool OurReader::decodeDouble(Token& token, Value& decoded) { double value = 0; - const int bufferSize = 32; - int count; - ptrdiff_t const length = token.end_ - token.start_; - - // Sanity check to avoid buffer overflow exploits. - if (length < 0) { - return addError("Unable to parse token length", token); - } - size_t const ulength = static_cast<size_t>(length); - - // Avoid using a string constant for the format control string given to - // sscanf, as this can cause hard to debug crashes on OS X. See here for more - // info: - // - // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html - char format[] = "%lf"; - - if (length <= bufferSize) { - Char buffer[bufferSize + 1]; - memcpy(buffer, token.start_, ulength); - buffer[length] = 0; - fixNumericLocaleInput(buffer, buffer + length); - count = sscanf(buffer, format, &value); - } else { - JSONCPP_STRING buffer(token.start_, token.end_); - count = sscanf(buffer.c_str(), format, &value); + const String buffer(token.start_, token.end_); + IStringStream is(buffer); + if (!(is >> value)) { + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); } - - if (count != 1) - return addError("'" + JSONCPP_STRING(token.start_, token.end_) + - "' is not a number.", - token); decoded = value; return true; } bool OurReader::decodeString(Token& token) { - JSONCPP_STRING decoded_string; + String decoded_string; if (!decodeString(token, decoded_string)) return false; Value decoded(decoded_string); @@ -1884,7 +1900,7 @@ bool OurReader::decodeString(Token& token) { return true; } -bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { +bool OurReader::decodeString(Token& token, String& decoded) { decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2)); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' @@ -1892,7 +1908,7 @@ bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { Char c = *current++; if (c == '"') break; - else if (c == '\\') { + if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); Char escape = *current++; @@ -1937,10 +1953,8 @@ bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { return true; } -bool OurReader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { +bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false; @@ -1949,10 +1963,9 @@ bool OurReader::decodeUnicodeCodePoint(Token& token, if (end - current < 6) return addError( "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; + token, current); if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } else @@ -1960,20 +1973,17 @@ bool OurReader::decodeUnicodeCodePoint(Token& token, } else return addError("expecting another \\u token to begin the second half of " "a unicode surrogate pair", - token, - current); + token, current); } return true; } -bool OurReader::decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& ret_unicode) { +bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, + unsigned int& ret_unicode) { if (end - current < 4) return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, + "Bad unicode escape sequence in string: four digits expected.", token, current); int unicode = 0; for (int index = 0; index < 4; ++index) { @@ -1988,15 +1998,13 @@ bool OurReader::decodeUnicodeEscapeSequence(Token& token, else return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); + token, current); } ret_unicode = static_cast<unsigned int>(unicode); return true; } -bool -OurReader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { +bool OurReader::addError(const String& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -2018,9 +2026,8 @@ bool OurReader::recoverFromError(TokenType skipUntilToken) { return false; } -bool OurReader::addErrorAndRecover(const JSONCPP_STRING& message, - Token& token, - TokenType skipUntilToken) { +bool OurReader::addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); } @@ -2033,9 +2040,8 @@ OurReader::Char OurReader::getNextChar() { return *current_++; } -void OurReader::getLocationLineAndColumn(Location location, - int& line, - int& column) const { +void OurReader::getLocationLineAndColumn(Location location, int& line, + int& column) const { Location current = begin_; Location lastLineStart = current; line = 0; @@ -2056,20 +2062,17 @@ void OurReader::getLocationLineAndColumn(Location location, ++line; } -JSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const { +String OurReader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; - snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); return buffer; } -JSONCPP_STRING OurReader::getFormattedErrorMessages() const { - JSONCPP_STRING formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; +String OurReader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; formattedMessage += " " + error.message_ + "\n"; @@ -2082,10 +2085,7 @@ JSONCPP_STRING OurReader::getFormattedErrorMessages() const { std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const { std::vector<OurReader::StructuredError> allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; + for (const auto& error : errors_) { OurReader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; @@ -2095,59 +2095,15 @@ std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const { return allErrors; } -bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message) { - ptrdiff_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = 0; - errors_.push_back(info); - return true; -} - -bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { - ptrdiff_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length - || extra.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = begin_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = begin_ + extra.getOffsetStart(); - errors_.push_back(info); - return true; -} - -bool OurReader::good() const { - return !errors_.size(); -} - - class OurCharReader : public CharReader { bool const collectComments_; OurReader reader_; + public: - OurCharReader( - bool collectComments, - OurFeatures const& features) - : collectComments_(collectComments) - , reader_(features) - {} - bool parse( - char const* beginDoc, char const* endDoc, - Value* root, JSONCPP_STRING* errs) JSONCPP_OVERRIDE { + OurCharReader(bool collectComments, OurFeatures const& features) + : collectComments_(collectComments), reader_(features) {} + bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) override { bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); if (errs) { *errs = reader_.getFormattedErrorMessages(); @@ -2156,67 +2112,64 @@ public: } }; -CharReaderBuilder::CharReaderBuilder() -{ - setDefaults(&settings_); -} -CharReaderBuilder::~CharReaderBuilder() -{} -CharReader* CharReaderBuilder::newCharReader() const -{ +CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } +CharReaderBuilder::~CharReaderBuilder() = default; +CharReader* CharReaderBuilder::newCharReader() const { bool collectComments = settings_["collectComments"].asBool(); OurFeatures features = OurFeatures::all(); features.allowComments_ = settings_["allowComments"].asBool(); + features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool(); features.strictRoot_ = settings_["strictRoot"].asBool(); - features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowDroppedNullPlaceholders_ = + settings_["allowDroppedNullPlaceholders"].asBool(); features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); - features.stackLimit_ = settings_["stackLimit"].asInt(); + + // Stack limit is always a size_t, so we get this as an unsigned int + // regardless of it we have 64-bit integer support enabled. + features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt()); features.failIfExtra_ = settings_["failIfExtra"].asBool(); features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + features.skipBom_ = settings_["skipBom"].asBool(); return new OurCharReader(collectComments, features); } -static void getValidReaderKeys(std::set<JSONCPP_STRING>* valid_keys) -{ - valid_keys->clear(); - valid_keys->insert("collectComments"); - valid_keys->insert("allowComments"); - valid_keys->insert("strictRoot"); - valid_keys->insert("allowDroppedNullPlaceholders"); - valid_keys->insert("allowNumericKeys"); - valid_keys->insert("allowSingleQuotes"); - valid_keys->insert("stackLimit"); - valid_keys->insert("failIfExtra"); - valid_keys->insert("rejectDupKeys"); - valid_keys->insert("allowSpecialFloats"); -} -bool CharReaderBuilder::validate(Json::Value* invalid) const -{ - Json::Value my_invalid; - if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set<JSONCPP_STRING> valid_keys; - getValidReaderKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - JSONCPP_STRING const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } + +bool CharReaderBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set<String>{ + "collectComments", + "allowComments", + "allowTrailingCommas", + "strictRoot", + "allowDroppedNullPlaceholders", + "allowNumericKeys", + "allowSingleQuotes", + "stackLimit", + "failIfExtra", + "rejectDupKeys", + "allowSpecialFloats", + "skipBom", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[std::move(key)] = *si; + else + return false; } - return 0u == inv.size(); + return invalid ? invalid->empty() : true; } -Value& CharReaderBuilder::operator[](JSONCPP_STRING key) -{ + +Value& CharReaderBuilder::operator[](const String& key) { return settings_[key]; } // static -void CharReaderBuilder::strictMode(Json::Value* settings) -{ -//! [CharReaderBuilderStrictMode] +void CharReaderBuilder::strictMode(Json::Value* settings) { + //! [CharReaderBuilderStrictMode] (*settings)["allowComments"] = false; + (*settings)["allowTrailingCommas"] = false; (*settings)["strictRoot"] = true; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; @@ -2225,14 +2178,15 @@ void CharReaderBuilder::strictMode(Json::Value* settings) (*settings)["failIfExtra"] = true; (*settings)["rejectDupKeys"] = true; (*settings)["allowSpecialFloats"] = false; -//! [CharReaderBuilderStrictMode] + (*settings)["skipBom"] = true; + //! [CharReaderBuilderStrictMode] } // static -void CharReaderBuilder::setDefaults(Json::Value* settings) -{ -//! [CharReaderBuilderDefaults] +void CharReaderBuilder::setDefaults(Json::Value* settings) { + //! [CharReaderBuilderDefaults] (*settings)["collectComments"] = true; (*settings)["allowComments"] = true; + (*settings)["allowTrailingCommas"] = true; (*settings)["strictRoot"] = false; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; @@ -2241,19 +2195,18 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) (*settings)["failIfExtra"] = false; (*settings)["rejectDupKeys"] = false; (*settings)["allowSpecialFloats"] = false; -//! [CharReaderBuilderDefaults] + (*settings)["skipBom"] = true; + //! [CharReaderBuilderDefaults] } ////////////////////////////////// // global functions -bool parseFromStream( - CharReader::Factory const& fact, JSONCPP_ISTREAM& sin, - Value* root, JSONCPP_STRING* errs) -{ - JSONCPP_OSTRINGSTREAM ssin; +bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root, + String* errs) { + OStringStream ssin; ssin << sin.rdbuf(); - JSONCPP_STRING doc = ssin.str(); + String doc = ssin.str(); char const* begin = doc.data(); char const* end = begin + doc.size(); // Note that we do not actually need a null-terminator. @@ -2261,9 +2214,9 @@ bool parseFromStream( return reader->parse(begin, end, root, errs); } -JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root) { +IStream& operator>>(IStream& sin, Value& root) { CharReaderBuilder b; - JSONCPP_STRING errs; + String errs; bool ok = parseFromStream(b, sin, &root, &errs); if (!ok) { throwRuntimeError(errs); @@ -2303,31 +2256,21 @@ namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIteratorBase::ValueIteratorBase() - : current_(), isNull_(true) { -} +ValueIteratorBase::ValueIteratorBase() : current_() {} ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator& current) : current_(current), isNull_(false) {} -Value& ValueIteratorBase::deref() const { - return current_->second; -} +Value& ValueIteratorBase::deref() { return current_->second; } +const Value& ValueIteratorBase::deref() const { return current_->second; } -void ValueIteratorBase::increment() { - ++current_; -} +void ValueIteratorBase::increment() { ++current_; } -void ValueIteratorBase::decrement() { - --current_; -} +void ValueIteratorBase::decrement() { --current_; } ValueIteratorBase::difference_type ValueIteratorBase::computeDistance(const SelfType& other) const { -#ifdef JSON_USE_CPPTL_SMALLMAP - return other.current_ - current_; -#else // Iterator for null value are initialized using the default // constructor, which initialize current_ to the default // std::map::iterator. As begin() and end() are two instance @@ -2348,7 +2291,6 @@ ValueIteratorBase::computeDistance(const SelfType& other) const { ++myDistance; } return myDistance; -#endif } bool ValueIteratorBase::isEqual(const SelfType& other) const { @@ -2380,12 +2322,13 @@ UInt ValueIteratorBase::index() const { return Value::UInt(-1); } -JSONCPP_STRING ValueIteratorBase::name() const { +String ValueIteratorBase::name() const { char const* keey; char const* end; keey = memberName(&end); - if (!keey) return JSONCPP_STRING(); - return JSONCPP_STRING(keey, end); + if (!keey) + return String(); + return String(keey, end); } char const* ValueIteratorBase::memberName() const { @@ -2396,8 +2339,8 @@ char const* ValueIteratorBase::memberName() const { char const* ValueIteratorBase::memberName(char const** end) const { const char* cname = (*current_).first.data(); if (!cname) { - *end = NULL; - return NULL; + *end = nullptr; + return nullptr; } *end = cname + (*current_).first.length(); return cname; @@ -2411,7 +2354,7 @@ char const* ValueIteratorBase::memberName(char const** end) const { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueConstIterator::ValueConstIterator() {} +ValueConstIterator::ValueConstIterator() = default; ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator& current) @@ -2434,7 +2377,7 @@ operator=(const ValueIteratorBase& other) { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIterator::ValueIterator() {} +ValueIterator::ValueIterator() = default; ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) : ValueIteratorBase(current) {} @@ -2444,8 +2387,7 @@ ValueIterator::ValueIterator(const ValueConstIterator& other) throwRuntimeError("ConstIterator to Iterator should never be allowed."); } -ValueIterator::ValueIterator(const ValueIterator& other) - : ValueIteratorBase(other) {} +ValueIterator::ValueIterator(const ValueIterator& other) = default; ValueIterator& ValueIterator::operator=(const SelfType& other) { copy(other); @@ -2477,20 +2419,54 @@ ValueIterator& ValueIterator::operator=(const SelfType& other) { #include <json/value.h> #include <json/writer.h> #endif // if !defined(JSON_IS_AMALGAMATION) -#include <math.h> +#include <algorithm> +#include <cassert> +#include <cmath> +#include <cstddef> +#include <cstring> +#include <iostream> #include <sstream> #include <utility> -#include <cstring> -#include <cassert> -#ifdef JSON_USE_CPPTL -#include <cpptl/conststring.h> + +// Provide implementation equivalent of std::snprintf for older _MSC compilers +#if defined(_MSC_VER) && _MSC_VER < 1900 +#include <stdarg.h> +static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size, + const char* format, va_list ap) { + int count = -1; + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + return count; +} + +int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...) { + va_list ap; + va_start(ap, format); + const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + return count; +} +#endif + +// Disable warning C4702 : unreachable code +#if defined(_MSC_VER) +#pragma warning(disable : 4702) #endif -#include <cstddef> // size_t -#include <algorithm> // min() #define JSON_ASSERT_UNREACHABLE assert(false) namespace Json { +template <typename T> +static std::unique_ptr<T> cloneUnique(const std::unique_ptr<T>& p) { + std::unique_ptr<T> r; + if (p) { + r = std::unique_ptr<T>(new T(*p)); + } + return r; +} // This is a walkaround to avoid the static initialization of Value::null. // kNull must be word-aligned to avoid crashing on ARM. We use an alignment of @@ -2500,50 +2476,34 @@ namespace Json { #else #define ALIGNAS(byte_alignment) #endif -//static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; -//const unsigned char& kNullRef = kNull[0]; -//const Value& Value::null = reinterpret_cast<const Value&>(kNullRef); -//const Value& Value::nullRef = null; // static -Value const& Value::nullSingleton() -{ - static Value const nullStatic; - return nullStatic; +Value const& Value::nullSingleton() { + static Value const nullStatic; + return nullStatic; } -// for backwards compatibility, we'll leave these global references around, but DO NOT -// use them in JSONCPP library code any more! +#if JSON_USE_NULLREF +// for backwards compatibility, we'll leave these global references around, but +// DO NOT use them in JSONCPP library code any more! +// static Value const& Value::null = Value::nullSingleton(); -Value const& Value::nullRef = Value::nullSingleton(); -const Int Value::minInt = Int(~(UInt(-1) / 2)); -const Int Value::maxInt = Int(UInt(-1) / 2); -const UInt Value::maxUInt = UInt(-1); -#if defined(JSON_HAS_INT64) -const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); -const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); -const UInt64 Value::maxUInt64 = UInt64(-1); -// The constant is hard-coded because some compiler have trouble -// converting Value::maxUInt64 to a double correctly (AIX/xlC). -// Assumes that UInt64 is a 64 bits integer. -static const double maxUInt64AsDouble = 18446744073709551615.0; -#endif // defined(JSON_HAS_INT64) -const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); -const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); -const LargestUInt Value::maxLargestUInt = LargestUInt(-1); +// static +Value const& Value::nullRef = Value::nullSingleton(); +#endif #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) template <typename T, typename U> static inline bool InRange(double d, T min, U max) { // The casts can lose precision, but we are looking only for // an approximate range. Might fail on edge cases though. ~cdunn - //return d >= static_cast<double>(min) && d <= static_cast<double>(max); - return d >= min && d <= max; + return d >= static_cast<double>(min) && d <= static_cast<double>(max); } #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) static inline double integerToDouble(Json::UInt64 value) { - return static_cast<double>(Int64(value / 2)) * 2.0 + static_cast<double>(Int64(value & 1)); + return static_cast<double>(Int64(value / 2)) * 2.0 + + static_cast<double>(Int64(value & 1)); } template <typename T> static inline double integerToDouble(T value) { @@ -2563,19 +2523,16 @@ static inline bool InRange(double d, T min, U max) { * computed using strlen(value). * @return Pointer on the duplicate instance of string. */ -static inline char* duplicateStringValue(const char* value, - size_t length) -{ +static inline char* duplicateStringValue(const char* value, size_t length) { // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. if (length >= static_cast<size_t>(Value::maxInt)) length = Value::maxInt - 1; - char* newString = static_cast<char*>(malloc(length + 1)); - if (newString == NULL) { - throwRuntimeError( - "in Json::Value::duplicateStringValue(): " - "Failed to allocate string value buffer"); + auto newString = static_cast<char*>(malloc(length + 1)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); } memcpy(newString, value, length); newString[length] = 0; @@ -2584,31 +2541,28 @@ static inline char* duplicateStringValue(const char* value, /* Record the length as a prefix. */ -static inline char* duplicateAndPrefixStringValue( - const char* value, - unsigned int length) -{ +static inline char* duplicateAndPrefixStringValue(const char* value, + unsigned int length) { // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. - JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) - sizeof(unsigned) - 1U, + JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) - + sizeof(unsigned) - 1U, "in Json::Value::duplicateAndPrefixStringValue(): " "length too big for prefixing"); - unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U; - char* newString = static_cast<char*>(malloc(actualLength)); - if (newString == 0) { - throwRuntimeError( - "in Json::Value::duplicateAndPrefixStringValue(): " - "Failed to allocate string value buffer"); + size_t actualLength = sizeof(length) + length + 1; + auto newString = static_cast<char*>(malloc(actualLength)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); } *reinterpret_cast<unsigned*>(newString) = length; memcpy(newString + sizeof(unsigned), value, length); - newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + newString[actualLength - 1U] = + 0; // to avoid buffer over-run accidents by users later return newString; } -inline static void decodePrefixedString( - bool isPrefixed, char const* prefixed, - unsigned* length, char const** value) -{ +inline static void decodePrefixedString(bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) { if (!isPrefixed) { *length = static_cast<unsigned>(strlen(prefixed)); *value = prefixed; @@ -2617,7 +2571,8 @@ inline static void decodePrefixedString( *value = prefixed + sizeof(unsigned); } } -/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). +/** Free the string duplicated by + * duplicateStringValue()/duplicateAndPrefixStringValue(). */ #if JSONCPP_USING_SECURE_MEMORY static inline void releasePrefixedStringValue(char* value) { @@ -2630,17 +2585,13 @@ static inline void releasePrefixedStringValue(char* value) { } static inline void releaseStringValue(char* value, unsigned length) { // length==0 => we allocated the strings memory - size_t size = (length==0) ? strlen(value) : length; + size_t size = (length == 0) ? strlen(value) : length; memset(value, 0, size); free(value); } -#else // !JSONCPP_USING_SECURE_MEMORY -static inline void releasePrefixedStringValue(char* value) { - free(value); -} -static inline void releaseStringValue(char* value, unsigned) { - free(value); -} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { free(value); } +static inline void releaseStringValue(char* value, unsigned) { free(value); } #endif // JSONCPP_USING_SECURE_MEMORY } // namespace Json @@ -2659,58 +2610,28 @@ static inline void releaseStringValue(char* value, unsigned) { namespace Json { -Exception::Exception(JSONCPP_STRING const& msg) - : msg_(msg) -{} -Exception::~Exception() JSONCPP_NOEXCEPT -{} -char const* Exception::what() const JSONCPP_NOEXCEPT -{ - return msg_.c_str(); -} -RuntimeError::RuntimeError(JSONCPP_STRING const& msg) - : Exception(msg) -{} -LogicError::LogicError(JSONCPP_STRING const& msg) - : Exception(msg) -{} -JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg) -{ +#if JSON_USE_EXCEPTION +Exception::Exception(String msg) : msg_(std::move(msg)) {} +Exception::~Exception() noexcept = default; +char const* Exception::what() const noexcept { return msg_.c_str(); } +RuntimeError::RuntimeError(String const& msg) : Exception(msg) {} +LogicError::LogicError(String const& msg) : Exception(msg) {} +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { throw RuntimeError(msg); } -JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg) -{ +JSONCPP_NORETURN void throwLogicError(String const& msg) { throw LogicError(msg); } - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CommentInfo -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -Value::CommentInfo::CommentInfo() : comment_(0) -{} - -Value::CommentInfo::~CommentInfo() { - if (comment_) - releaseStringValue(comment_, 0u); +#else // !JSON_USE_EXCEPTION +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { + std::cerr << msg << std::endl; + abort(); } - -void Value::CommentInfo::setComment(const char* text, size_t len) { - if (comment_) { - releaseStringValue(comment_, 0u); - comment_ = 0; - } - JSON_ASSERT(text != 0); - JSON_ASSERT_MESSAGE( - text[0] == '\0' || text[0] == '/', - "in Json::Value::setComment(): Comments must start with /"); - // It seems that /**/ style comments are acceptable as well. - comment_ = duplicateStringValue(text, len); +JSONCPP_NORETURN void throwLogicError(String const& msg) { + std::cerr << msg << std::endl; + abort(); } +#endif // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -2723,36 +2644,44 @@ void Value::CommentInfo::setComment(const char* text, size_t len) { // Notes: policy_ indicates if the string was allocated when // a string is stored. -Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} +Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {} -Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) +Value::CZString::CZString(char const* str, unsigned length, + DuplicationPolicy allocate) : cstr_(str) { // allocate != duplicate storage_.policy_ = allocate & 0x3; - storage_.length_ = ulength & 0x3FFFFFFF; + storage_.length_ = length & 0x3FFFFFFF; } Value::CZString::CZString(const CZString& other) { - cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0 - ? duplicateStringValue(other.cstr_, other.storage_.length_) - : other.cstr_); - storage_.policy_ = static_cast<unsigned>(other.cstr_ - ? (static_cast<DuplicationPolicy>(other.storage_.policy_) == noDuplication - ? noDuplication : duplicate) - : static_cast<DuplicationPolicy>(other.storage_.policy_)) & 3U; + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = + static_cast<unsigned>( + other.cstr_ + ? (static_cast<DuplicationPolicy>(other.storage_.policy_) == + noDuplication + ? noDuplication + : duplicate) + : static_cast<DuplicationPolicy>(other.storage_.policy_)) & + 3U; storage_.length_ = other.storage_.length_; } -#if JSON_HAS_RVALUE_REFERENCES Value::CZString::CZString(CZString&& other) - : cstr_(other.cstr_), index_(other.index_) { + : cstr_(other.cstr_), index_(other.index_) { other.cstr_ = nullptr; } -#endif Value::CZString::~CZString() { if (cstr_ && storage_.policy_ == duplicate) { - releaseStringValue(const_cast<char*>(cstr_), storage_.length_ + 1u); //+1 for null terminating character for sake of completeness but not actually necessary + releaseStringValue(const_cast<char*>(cstr_), + storage_.length_ + 1U); // +1 for null terminating + // character for sake of + // completeness but not actually + // necessary } } @@ -2767,36 +2696,39 @@ Value::CZString& Value::CZString::operator=(const CZString& other) { return *this; } -#if JSON_HAS_RVALUE_REFERENCES Value::CZString& Value::CZString::operator=(CZString&& other) { cstr_ = other.cstr_; index_ = other.index_; other.cstr_ = nullptr; return *this; } -#endif bool Value::CZString::operator<(const CZString& other) const { - if (!cstr_) return index_ < other.index_; - //return strcmp(cstr_, other.cstr_) < 0; + if (!cstr_) + return index_ < other.index_; + // return strcmp(cstr_, other.cstr_) < 0; // Assume both are strings. unsigned this_len = this->storage_.length_; unsigned other_len = other.storage_.length_; unsigned min_len = std::min<unsigned>(this_len, other_len); JSON_ASSERT(this->cstr_ && other.cstr_); int comp = memcmp(this->cstr_, other.cstr_, min_len); - if (comp < 0) return true; - if (comp > 0) return false; + if (comp < 0) + return true; + if (comp > 0) + return false; return (this_len < other_len); } bool Value::CZString::operator==(const CZString& other) const { - if (!cstr_) return index_ == other.index_; - //return strcmp(cstr_, other.cstr_) == 0; + if (!cstr_) + return index_ == other.index_; + // return strcmp(cstr_, other.cstr_) == 0; // Assume both are strings. unsigned this_len = this->storage_.length_; unsigned other_len = other.storage_.length_; - if (this_len != other_len) return false; + if (this_len != other_len) + return false; JSON_ASSERT(this->cstr_ && other.cstr_); int comp = memcmp(this->cstr_, other.cstr_, this_len); return comp == 0; @@ -2804,10 +2736,12 @@ bool Value::CZString::operator==(const CZString& other) const { ArrayIndex Value::CZString::index() const { return index_; } -//const char* Value::CZString::c_str() const { return cstr_; } +// const char* Value::CZString::c_str() const { return cstr_; } const char* Value::CZString::data() const { return cstr_; } unsigned Value::CZString::length() const { return storage_.length_; } -bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } +bool Value::CZString::isStaticString() const { + return storage_.policy_ == noDuplication; +} // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -2821,10 +2755,10 @@ bool Value::CZString::isStaticString() const { return storage_.policy_ == noDupl * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ -Value::Value(ValueType vtype) { +Value::Value(ValueType type) { static char const emptyString[] = ""; - initBasic(vtype); - switch (vtype) { + initBasic(type); + switch (type) { case nullValue: break; case intValue: @@ -2877,20 +2811,22 @@ Value::Value(double value) { Value::Value(const char* value) { initBasic(stringValue, true); - JSON_ASSERT_MESSAGE(value != NULL, "Null Value Passed to Value Constructor"); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value))); + JSON_ASSERT_MESSAGE(value != nullptr, + "Null Value Passed to Value Constructor"); + value_.string_ = duplicateAndPrefixStringValue( + value, static_cast<unsigned>(strlen(value))); } -Value::Value(const char* beginValue, const char* endValue) { +Value::Value(const char* begin, const char* end) { initBasic(stringValue, true); value_.string_ = - duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue)); + duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin)); } -Value::Value(const JSONCPP_STRING& value) { +Value::Value(const String& value) { initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length())); + value_.string_ = duplicateAndPrefixStringValue( + value.data(), static_cast<unsigned>(value.length())); } Value::Value(const StaticString& value) { @@ -2898,114 +2834,44 @@ Value::Value(const StaticString& value) { value_.string_ = const_cast<char*>(value.c_str()); } -#ifdef JSON_USE_CPPTL -Value::Value(const CppTL::ConstString& value) { - initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length())); -} -#endif - Value::Value(bool value) { initBasic(booleanValue); value_.bool_ = value; } -Value::Value(Value const& other) - : type_(other.type_), allocated_(false) - , - comments_(0), start_(other.start_), limit_(other.limit_) -{ - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - value_ = other.value_; - break; - case stringValue: - if (other.value_.string_ && other.allocated_) { - unsigned len; - char const* str; - decodePrefixedString(other.allocated_, other.value_.string_, - &len, &str); - value_.string_ = duplicateAndPrefixStringValue(str, len); - allocated_ = true; - } else { - value_.string_ = other.value_.string_; - allocated_ = false; - } - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(*other.value_.map_); - break; - default: - JSON_ASSERT_UNREACHABLE; - } - if (other.comments_) { - comments_ = new CommentInfo[numberOfCommentPlacement]; - for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { - const CommentInfo& otherComment = other.comments_[comment]; - if (otherComment.comment_) - comments_[comment].setComment( - otherComment.comment_, strlen(otherComment.comment_)); - } - } +Value::Value(const Value& other) { + dupPayload(other); + dupMeta(other); } -#if JSON_HAS_RVALUE_REFERENCES -// Move constructor Value::Value(Value&& other) { initBasic(nullValue); swap(other); } -#endif Value::~Value() { - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue: - if (allocated_) - releasePrefixedStringValue(value_.string_); - break; - case arrayValue: - case objectValue: - delete value_.map_; - break; - default: - JSON_ASSERT_UNREACHABLE; - } - - delete[] comments_; - + releasePayload(); value_.uint_ = 0; } -Value& Value::operator=(Value other) { - swap(other); +Value& Value::operator=(const Value& other) { + Value(other).swap(*this); + return *this; +} + +Value& Value::operator=(Value&& other) { + other.swap(*this); return *this; } void Value::swapPayload(Value& other) { - ValueType temp = type_; - type_ = other.type_; - other.type_ = temp; + std::swap(bits_, other.bits_); std::swap(value_, other.value_); - int temp2 = allocated_; - allocated_ = other.allocated_; - other.allocated_ = temp2 & 0x1; } void Value::copyPayload(const Value& other) { - type_ = other.type_; - value_ = other.value_; - allocated_ = other.allocated_; + releasePayload(); + dupPayload(other); } void Value::swap(Value& other) { @@ -3017,12 +2883,12 @@ void Value::swap(Value& other) { void Value::copy(const Value& other) { copyPayload(other); - comments_ = other.comments_; - start_ = other.start_; - limit_ = other.limit_; + dupMeta(other); } -ValueType Value::type() const { return type_; } +ValueType Value::type() const { + return static_cast<ValueType>(bits_.value_type_); +} int Value::compare(const Value& other) const { if (*this < other) @@ -3033,10 +2899,10 @@ int Value::compare(const Value& other) const { } bool Value::operator<(const Value& other) const { - int typeDelta = type_ - other.type_; + int typeDelta = type() - other.type(); if (typeDelta) - return typeDelta < 0 ? true : false; - switch (type_) { + return typeDelta < 0; + switch (type()) { case nullValue: return false; case intValue: @@ -3047,30 +2913,33 @@ bool Value::operator<(const Value& other) const { return value_.real_ < other.value_.real_; case booleanValue: return value_.bool_ < other.value_.bool_; - case stringValue: - { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { - if (other.value_.string_) return true; - else return false; + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { + return other.value_.string_ != nullptr; } unsigned this_len; unsigned other_len; char const* this_str; char const* other_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); unsigned min_len = std::min<unsigned>(this_len, other_len); JSON_ASSERT(this_str && other_str); int comp = memcmp(this_str, other_str, min_len); - if (comp < 0) return true; - if (comp > 0) return false; + if (comp < 0) + return true; + if (comp > 0) + return false; return (this_len < other_len); } case arrayValue: case objectValue: { - int delta = int(value_.map_->size() - other.value_.map_->size()); - if (delta) - return delta < 0; + auto thisSize = value_.map_->size(); + auto otherSize = other.value_.map_->size(); + if (thisSize != otherSize) + return thisSize < otherSize; return (*value_.map_) < (*other.value_.map_); } default: @@ -3086,14 +2955,9 @@ bool Value::operator>=(const Value& other) const { return !(*this < other); } bool Value::operator>(const Value& other) const { return other < *this; } bool Value::operator==(const Value& other) const { - // if ( type_ != other.type_ ) - // GCC 2.95.3 says: - // attempt to take address of bit-field structure member `Json::Value::type_' - // Beats me, but a temp solves the problem. - int temp = other.type_; - if (type_ != temp) + if (type() != other.type()) return false; - switch (type_) { + switch (type()) { case nullValue: return true; case intValue: @@ -3104,18 +2968,20 @@ bool Value::operator==(const Value& other) const { return value_.real_ == other.value_.real_; case booleanValue: return value_.bool_ == other.value_.bool_; - case stringValue: - { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { return (value_.string_ == other.value_.string_); } unsigned this_len; unsigned other_len; char const* this_str; char const* other_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); - if (this_len != other_len) return false; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + if (this_len != other_len) + return false; JSON_ASSERT(this_str && other_str); int comp = memcmp(this_str, other_str, this_len); return comp == 0; @@ -3133,47 +2999,55 @@ bool Value::operator==(const Value& other) const { bool Value::operator!=(const Value& other) const { return !(*this == other); } const char* Value::asCString() const { - JSON_ASSERT_MESSAGE(type_ == stringValue, + JSON_ASSERT_MESSAGE(type() == stringValue, "in Json::Value::asCString(): requires stringValue"); - if (value_.string_ == 0) return 0; + if (value_.string_ == nullptr) + return nullptr; unsigned this_len; char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); return this_str; } #if JSONCPP_USING_SECURE_MEMORY unsigned Value::getCStringLength() const { - JSON_ASSERT_MESSAGE(type_ == stringValue, - "in Json::Value::asCString(): requires stringValue"); - if (value_.string_ == 0) return 0; + JSON_ASSERT_MESSAGE(type() == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) + return 0; unsigned this_len; char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); return this_len; } #endif -bool Value::getString(char const** str, char const** cend) const { - if (type_ != stringValue) return false; - if (value_.string_ == 0) return false; +bool Value::getString(char const** begin, char const** end) const { + if (type() != stringValue) + return false; + if (value_.string_ == nullptr) + return false; unsigned length; - decodePrefixedString(this->allocated_, this->value_.string_, &length, str); - *cend = *str + length; + decodePrefixedString(this->isAllocated(), this->value_.string_, &length, + begin); + *end = *begin + length; return true; } -JSONCPP_STRING Value::asString() const { - switch (type_) { +String Value::asString() const { + switch (type()) { case nullValue: return ""; - case stringValue: - { - if (value_.string_ == 0) return ""; + case stringValue: { + if (value_.string_ == nullptr) + return ""; unsigned this_len; char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - return JSONCPP_STRING(this_str, this_len); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return String(this_str, this_len); } case booleanValue: return value_.bool_ ? "true" : "false"; @@ -3188,18 +3062,8 @@ JSONCPP_STRING Value::asString() const { } } -#ifdef JSON_USE_CPPTL -CppTL::ConstString Value::asConstString() const { - unsigned len; - char const* str; - decodePrefixedString(allocated_, value_.string_, - &len, &str); - return CppTL::ConstString(str, len); -} -#endif - Value::Int Value::asInt() const { - switch (type_) { + switch (type()) { case intValue: JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); return Int(value_.int_); @@ -3221,7 +3085,7 @@ Value::Int Value::asInt() const { } Value::UInt Value::asUInt() const { - switch (type_) { + switch (type()) { case intValue: JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); return UInt(value_.int_); @@ -3245,7 +3109,7 @@ Value::UInt Value::asUInt() const { #if defined(JSON_HAS_INT64) Value::Int64 Value::asInt64() const { - switch (type_) { + switch (type()) { case intValue: return Int64(value_.int_); case uintValue: @@ -3266,7 +3130,7 @@ Value::Int64 Value::asInt64() const { } Value::UInt64 Value::asUInt64() const { - switch (type_) { + switch (type()) { case intValue: JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); return UInt64(value_.int_); @@ -3304,7 +3168,7 @@ LargestUInt Value::asLargestUInt() const { } double Value::asDouble() const { - switch (type_) { + switch (type()) { case intValue: return static_cast<double>(value_.int_); case uintValue: @@ -3326,7 +3190,7 @@ double Value::asDouble() const { } float Value::asFloat() const { - switch (type_) { + switch (type()) { case intValue: return static_cast<float>(value_.int_); case uintValue: @@ -3341,7 +3205,7 @@ float Value::asFloat() const { case nullValue: return 0.0; case booleanValue: - return value_.bool_ ? 1.0f : 0.0f; + return value_.bool_ ? 1.0F : 0.0F; default: break; } @@ -3349,18 +3213,20 @@ float Value::asFloat() const { } bool Value::asBool() const { - switch (type_) { + switch (type()) { case booleanValue: return value_.bool_; case nullValue: return false; case intValue: - return value_.int_ ? true : false; + return value_.int_ != 0; case uintValue: - return value_.uint_ ? true : false; - case realValue: - // This is kind of strange. Not recommended. - return (value_.real_ != 0.0) ? true : false; + return value_.uint_ != 0; + case realValue: { + // According to JavaScript language zero or NaN is regarded as false + const auto value_classification = std::fpclassify(value_.real_); + return value_classification != FP_ZERO && value_classification != FP_NAN; + } default: break; } @@ -3371,30 +3237,30 @@ bool Value::isConvertibleTo(ValueType other) const { switch (other) { case nullValue: return (isNumeric() && asDouble() == 0.0) || - (type_ == booleanValue && value_.bool_ == false) || - (type_ == stringValue && asString().empty()) || - (type_ == arrayValue && value_.map_->size() == 0) || - (type_ == objectValue && value_.map_->size() == 0) || - type_ == nullValue; + (type() == booleanValue && !value_.bool_) || + (type() == stringValue && asString().empty()) || + (type() == arrayValue && value_.map_->empty()) || + (type() == objectValue && value_.map_->empty()) || + type() == nullValue; case intValue: return isInt() || - (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || - type_ == booleanValue || type_ == nullValue; + (type() == realValue && InRange(value_.real_, minInt, maxInt)) || + type() == booleanValue || type() == nullValue; case uintValue: return isUInt() || - (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || - type_ == booleanValue || type_ == nullValue; + (type() == realValue && InRange(value_.real_, 0, maxUInt)) || + type() == booleanValue || type() == nullValue; case realValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; + return isNumeric() || type() == booleanValue || type() == nullValue; case booleanValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; + return isNumeric() || type() == booleanValue || type() == nullValue; case stringValue: - return isNumeric() || type_ == booleanValue || type_ == stringValue || - type_ == nullValue; + return isNumeric() || type() == booleanValue || type() == stringValue || + type() == nullValue; case arrayValue: - return type_ == arrayValue || type_ == nullValue; + return type() == arrayValue || type() == nullValue; case objectValue: - return type_ == objectValue || type_ == nullValue; + return type() == objectValue || type() == nullValue; } JSON_ASSERT_UNREACHABLE; return false; @@ -3402,7 +3268,7 @@ bool Value::isConvertibleTo(ValueType other) const { /// Number of values in array or object ArrayIndex Value::size() const { - switch (type_) { + switch (type()) { case nullValue: case intValue: case uintValue: @@ -3426,20 +3292,19 @@ ArrayIndex Value::size() const { bool Value::empty() const { if (isNull() || isArray() || isObject()) - return size() == 0u; - else - return false; + return size() == 0U; + return false; } -Value::operator bool() const { return ! isNull(); } +Value::operator bool() const { return !isNull(); } void Value::clear() { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || - type_ == objectValue, + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue || + type() == objectValue, "in Json::Value::clear(): requires complex value"); start_ = 0; limit_ = 0; - switch (type_) { + switch (type()) { case arrayValue: case objectValue: value_.map_->clear(); @@ -3450,15 +3315,15 @@ void Value::clear() { } void Value::resize(ArrayIndex newSize) { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, "in Json::Value::resize(): requires arrayValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(arrayValue); ArrayIndex oldSize = size(); if (newSize == 0) clear(); else if (newSize > oldSize) - (*this)[newSize - 1]; + this->operator[](newSize - 1); else { for (ArrayIndex index = newSize; index < oldSize; ++index) { value_.map_->erase(index); @@ -3469,12 +3334,12 @@ void Value::resize(ArrayIndex newSize) { Value& Value::operator[](ArrayIndex index) { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, + type() == nullValue || type() == arrayValue, "in Json::Value::operator[](ArrayIndex): requires arrayValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(arrayValue); CZString key(index); - ObjectValues::iterator it = value_.map_->lower_bound(key); + auto it = value_.map_->lower_bound(key); if (it != value_.map_->end() && (*it).first == key) return (*it).second; @@ -3492,9 +3357,9 @@ Value& Value::operator[](int index) { const Value& Value::operator[](ArrayIndex index) const { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, + type() == nullValue || type() == arrayValue, "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); - if (type_ == nullValue) + if (type() == nullValue) return nullSingleton(); CZString key(index); ObjectValues::const_iterator it = value_.map_->find(key); @@ -3510,26 +3375,85 @@ const Value& Value::operator[](int index) const { return (*this)[ArrayIndex(index)]; } -void Value::initBasic(ValueType vtype, bool allocated) { - type_ = vtype; - allocated_ = allocated; - comments_ = 0; +void Value::initBasic(ValueType type, bool allocated) { + setType(type); + setIsAllocated(allocated); + comments_ = Comments{}; start_ = 0; limit_ = 0; } +void Value::dupPayload(const Value& other) { + setType(other.type()); + setIsAllocated(false); + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.isAllocated()) { + unsigned len; + char const* str; + decodePrefixedString(other.isAllocated(), other.value_.string_, &len, + &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + setIsAllocated(true); + } else { + value_.string_ = other.value_.string_; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::releasePayload() { + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (isAllocated()) + releasePrefixedStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::dupMeta(const Value& other) { + comments_ = other.comments_; + start_ = other.start_; + limit_ = other.limit_; +} + // Access an object value by name, create a null member if it does not exist. // @pre Type of '*this' is object or null. // @param key is null-terminated. Value& Value::resolveReference(const char* key) { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, + type() == nullValue || type() == objectValue, "in Json::Value::resolveReference(): requires objectValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(objectValue); - CZString actualKey( - key, static_cast<unsigned>(strlen(key)), CZString::noDuplication); // NOTE! - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + CZString actualKey(key, static_cast<unsigned>(strlen(key)), + CZString::noDuplication); // NOTE! + auto it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; @@ -3540,16 +3464,15 @@ Value& Value::resolveReference(const char* key) { } // @param key is not null-terminated. -Value& Value::resolveReference(char const* key, char const* cend) -{ +Value& Value::resolveReference(char const* key, char const* end) { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, + type() == nullValue || type() == objectValue, "in Json::Value::resolveReference(key, end): requires objectValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(objectValue); - CZString actualKey( - key, static_cast<unsigned>(cend-key), CZString::duplicateOnCopy); - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + CZString actualKey(key, static_cast<unsigned>(end - key), + CZString::duplicateOnCopy); + auto it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; @@ -3566,27 +3489,35 @@ Value Value::get(ArrayIndex index, const Value& defaultValue) const { bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } -Value const* Value::find(char const* key, char const* cend) const -{ - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, - "in Json::Value::find(key, end, found): requires objectValue or nullValue"); - if (type_ == nullValue) return NULL; - CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication); +Value const* Value::find(char const* begin, char const* end) const { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::find(begin, end): requires " + "objectValue or nullValue"); + if (type() == nullValue) + return nullptr; + CZString actualKey(begin, static_cast<unsigned>(end - begin), + CZString::noDuplication); ObjectValues::const_iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) return NULL; + if (it == value_.map_->end()) + return nullptr; return &(*it).second; } -const Value& Value::operator[](const char* key) const -{ +Value* Value::demand(char const* begin, char const* end) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::demand(begin, end): requires " + "objectValue or nullValue"); + return &resolveReference(begin, end); +} +const Value& Value::operator[](const char* key) const { Value const* found = find(key, key + strlen(key)); - if (!found) return nullSingleton(); + if (!found) + return nullSingleton(); return *found; } -Value const& Value::operator[](JSONCPP_STRING const& key) const -{ +Value const& Value::operator[](const String& key) const { Value const* found = find(key.data(), key.data() + key.length()); - if (!found) return nullSingleton(); + if (!found) + return nullSingleton(); return *found; } @@ -3594,7 +3525,7 @@ Value& Value::operator[](const char* key) { return resolveReference(key, key + strlen(key)); } -Value& Value::operator[](const JSONCPP_STRING& key) { +Value& Value::operator[](const String& key) { return resolveReference(key.data(), key.data() + key.length()); } @@ -3602,178 +3533,140 @@ Value& Value::operator[](const StaticString& key) { return resolveReference(key.c_str()); } -#ifdef JSON_USE_CPPTL -Value& Value::operator[](const CppTL::ConstString& key) { - return resolveReference(key.c_str(), key.end_c_str()); -} -Value const& Value::operator[](CppTL::ConstString const& key) const -{ - Value const* found = find(key.c_str(), key.end_c_str()); - if (!found) return nullSingleton(); - return *found; +Value& Value::append(const Value& value) { return append(Value(value)); } + +Value& Value::append(Value&& value) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::append: requires arrayValue"); + if (type() == nullValue) { + *this = Value(arrayValue); + } + return this->value_.map_->emplace(size(), std::move(value)).first->second; } -#endif -Value& Value::append(const Value& value) { return (*this)[size()] = value; } +bool Value::insert(ArrayIndex index, const Value& newValue) { + return insert(index, Value(newValue)); +} -#if JSON_HAS_RVALUE_REFERENCES - Value& Value::append(Value&& value) { return (*this)[size()] = std::move(value); } -#endif +bool Value::insert(ArrayIndex index, Value&& newValue) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::insert: requires arrayValue"); + ArrayIndex length = size(); + if (index > length) { + return false; + } + for (ArrayIndex i = length; i > index; i--) { + (*this)[i] = std::move((*this)[i - 1]); + } + (*this)[index] = std::move(newValue); + return true; +} -Value Value::get(char const* key, char const* cend, Value const& defaultValue) const -{ - Value const* found = find(key, cend); +Value Value::get(char const* begin, char const* end, + Value const& defaultValue) const { + Value const* found = find(begin, end); return !found ? defaultValue : *found; } -Value Value::get(char const* key, Value const& defaultValue) const -{ +Value Value::get(char const* key, Value const& defaultValue) const { return get(key, key + strlen(key), defaultValue); } -Value Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const -{ +Value Value::get(String const& key, Value const& defaultValue) const { return get(key.data(), key.data() + key.length(), defaultValue); } - -bool Value::removeMember(const char* key, const char* cend, Value* removed) -{ - if (type_ != objectValue) { +bool Value::removeMember(const char* begin, const char* end, Value* removed) { + if (type() != objectValue) { return false; } - CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication); - ObjectValues::iterator it = value_.map_->find(actualKey); + CZString actualKey(begin, static_cast<unsigned>(end - begin), + CZString::noDuplication); + auto it = value_.map_->find(actualKey); if (it == value_.map_->end()) return false; - *removed = it->second; + if (removed) + *removed = std::move(it->second); value_.map_->erase(it); return true; } -bool Value::removeMember(const char* key, Value* removed) -{ +bool Value::removeMember(const char* key, Value* removed) { return removeMember(key, key + strlen(key), removed); } -bool Value::removeMember(JSONCPP_STRING const& key, Value* removed) -{ +bool Value::removeMember(String const& key, Value* removed) { return removeMember(key.data(), key.data() + key.length(), removed); } -void Value::removeMember(const char* key) -{ - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, +void Value::removeMember(const char* key) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, "in Json::Value::removeMember(): requires objectValue"); - if (type_ == nullValue) + if (type() == nullValue) return; CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication); value_.map_->erase(actualKey); } -void Value::removeMember(const JSONCPP_STRING& key) -{ - removeMember(key.c_str()); -} +void Value::removeMember(const String& key) { removeMember(key.c_str()); } bool Value::removeIndex(ArrayIndex index, Value* removed) { - if (type_ != arrayValue) { + if (type() != arrayValue) { return false; } CZString key(index); - ObjectValues::iterator it = value_.map_->find(key); + auto it = value_.map_->find(key); if (it == value_.map_->end()) { return false; } - *removed = it->second; + if (removed) + *removed = it->second; ArrayIndex oldSize = size(); // shift left all items left, into the place of the "removed" - for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + for (ArrayIndex i = index; i < (oldSize - 1); ++i) { CZString keey(i); (*value_.map_)[keey] = (*this)[i + 1]; } // erase the last one ("leftover") CZString keyLast(oldSize - 1); - ObjectValues::iterator itLast = value_.map_->find(keyLast); + auto itLast = value_.map_->find(keyLast); value_.map_->erase(itLast); return true; } -#ifdef JSON_USE_CPPTL -Value Value::get(const CppTL::ConstString& key, - const Value& defaultValue) const { - return get(key.c_str(), key.end_c_str(), defaultValue); +bool Value::isMember(char const* begin, char const* end) const { + Value const* value = find(begin, end); + return nullptr != value; } -#endif - -bool Value::isMember(char const* key, char const* cend) const -{ - Value const* value = find(key, cend); - return NULL != value; -} -bool Value::isMember(char const* key) const -{ +bool Value::isMember(char const* key) const { return isMember(key, key + strlen(key)); } -bool Value::isMember(JSONCPP_STRING const& key) const -{ +bool Value::isMember(String const& key) const { return isMember(key.data(), key.data() + key.length()); } -#ifdef JSON_USE_CPPTL -bool Value::isMember(const CppTL::ConstString& key) const { - return isMember(key.c_str(), key.end_c_str()); -} -#endif - Value::Members Value::getMemberNames() const { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, + type() == nullValue || type() == objectValue, "in Json::Value::getMemberNames(), value must be objectValue"); - if (type_ == nullValue) + if (type() == nullValue) return Value::Members(); Members members; members.reserve(value_.map_->size()); ObjectValues::const_iterator it = value_.map_->begin(); ObjectValues::const_iterator itEnd = value_.map_->end(); for (; it != itEnd; ++it) { - members.push_back(JSONCPP_STRING((*it).first.data(), - (*it).first.length())); + members.push_back(String((*it).first.data(), (*it).first.length())); } return members; } -// -//# ifdef JSON_USE_CPPTL -// EnumMemberNames -// Value::enumMemberNames() const -//{ -// if ( type_ == objectValue ) -// { -// return CppTL::Enum::any( CppTL::Enum::transform( -// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ), -// MemberNamesTransform() ) ); -// } -// return EnumMemberNames(); -//} -// -// -// EnumValues -// Value::enumValues() const -//{ -// if ( type_ == objectValue || type_ == arrayValue ) -// return CppTL::Enum::anyValues( *(value_.map_), -// CppTL::Type<const Value &>() ); -// return EnumValues(); -//} -// -//# endif static bool IsIntegral(double d) { double integral_part; return modf(d, &integral_part) == 0.0; } -bool Value::isNull() const { return type_ == nullValue; } +bool Value::isNull() const { return type() == nullValue; } -bool Value::isBool() const { return type_ == booleanValue; } +bool Value::isBool() const { return type() == booleanValue; } bool Value::isInt() const { - switch (type_) { + switch (type()) { case intValue: #if defined(JSON_HAS_INT64) return value_.int_ >= minInt && value_.int_ <= maxInt; @@ -3792,7 +3685,7 @@ bool Value::isInt() const { } bool Value::isUInt() const { - switch (type_) { + switch (type()) { case intValue: #if defined(JSON_HAS_INT64) return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); @@ -3816,7 +3709,7 @@ bool Value::isUInt() const { bool Value::isInt64() const { #if defined(JSON_HAS_INT64) - switch (type_) { + switch (type()) { case intValue: return true; case uintValue: @@ -3836,7 +3729,7 @@ bool Value::isInt64() const { bool Value::isUInt64() const { #if defined(JSON_HAS_INT64) - switch (type_) { + switch (type()) { case intValue: return value_.int_ >= 0; case uintValue: @@ -3855,61 +3748,92 @@ bool Value::isUInt64() const { } bool Value::isIntegral() const { - switch (type_) { - case intValue: - case uintValue: - return true; - case realValue: + switch (type()) { + case intValue: + case uintValue: + return true; + case realValue: #if defined(JSON_HAS_INT64) - // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a - // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= double(minInt64) && value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); #else - return value_.real_ >= minInt && value_.real_ <= maxUInt && IsIntegral(value_.real_); + return value_.real_ >= minInt && value_.real_ <= maxUInt && + IsIntegral(value_.real_); #endif // JSON_HAS_INT64 - default: - break; + default: + break; } return false; } -bool Value::isDouble() const { return type_ == intValue || type_ == uintValue || type_ == realValue; } +bool Value::isDouble() const { + return type() == intValue || type() == uintValue || type() == realValue; +} bool Value::isNumeric() const { return isDouble(); } -bool Value::isString() const { return type_ == stringValue; } +bool Value::isString() const { return type() == stringValue; } -bool Value::isArray() const { return type_ == arrayValue; } +bool Value::isArray() const { return type() == arrayValue; } -bool Value::isObject() const { return type_ == objectValue; } +bool Value::isObject() const { return type() == objectValue; } -void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { - if (!comments_) - comments_ = new CommentInfo[numberOfCommentPlacement]; - if ((len > 0) && (comment[len-1] == '\n')) { - // Always discard trailing newline, to aid indentation. - len -= 1; - } - comments_[placement].setComment(comment, len); +Value::Comments::Comments(const Comments& that) + : ptr_{cloneUnique(that.ptr_)} {} + +Value::Comments::Comments(Comments&& that) : ptr_{std::move(that.ptr_)} {} + +Value::Comments& Value::Comments::operator=(const Comments& that) { + ptr_ = cloneUnique(that.ptr_); + return *this; +} + +Value::Comments& Value::Comments::operator=(Comments&& that) { + ptr_ = std::move(that.ptr_); + return *this; +} + +bool Value::Comments::has(CommentPlacement slot) const { + return ptr_ && !(*ptr_)[slot].empty(); } -void Value::setComment(const char* comment, CommentPlacement placement) { - setComment(comment, strlen(comment), placement); +String Value::Comments::get(CommentPlacement slot) const { + if (!ptr_) + return {}; + return (*ptr_)[slot]; } -void Value::setComment(const JSONCPP_STRING& comment, CommentPlacement placement) { - setComment(comment.c_str(), comment.length(), placement); +void Value::Comments::set(CommentPlacement slot, String comment) { + if (!ptr_) { + ptr_ = std::unique_ptr<Array>(new Array()); + } + // check comments array boundry. + if (slot < CommentPlacement::numberOfCommentPlacement) { + (*ptr_)[slot] = std::move(comment); + } +} + +void Value::setComment(String comment, CommentPlacement placement) { + if (!comment.empty() && (comment.back() == '\n')) { + // Always discard trailing newline, to aid indentation. + comment.pop_back(); + } + JSON_ASSERT(!comment.empty()); + JSON_ASSERT_MESSAGE( + comment[0] == '\0' || comment[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + comments_.set(placement, std::move(comment)); } bool Value::hasComment(CommentPlacement placement) const { - return comments_ != 0 && comments_[placement].comment_ != 0; + return comments_.has(placement); } -JSONCPP_STRING Value::getComment(CommentPlacement placement) const { - if (hasComment(placement)) - return comments_[placement].comment_; - return ""; +String Value::getComment(CommentPlacement placement) const { + return comments_.get(placement); } void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } @@ -3920,18 +3844,18 @@ ptrdiff_t Value::getOffsetStart() const { return start_; } ptrdiff_t Value::getOffsetLimit() const { return limit_; } -JSONCPP_STRING Value::toStyledString() const { +String Value::toStyledString() const { StreamWriterBuilder builder; - JSONCPP_STRING out = this->hasComment(commentBefore) ? "\n" : ""; + String out = this->hasComment(commentBefore) ? "\n" : ""; out += Json::writeString(builder, *this); - out += "\n"; + out += '\n'; return out; } Value::const_iterator Value::begin() const { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3940,11 +3864,11 @@ Value::const_iterator Value::begin() const { default: break; } - return const_iterator(); + return {}; } Value::const_iterator Value::end() const { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3953,11 +3877,11 @@ Value::const_iterator Value::end() const { default: break; } - return const_iterator(); + return {}; } Value::iterator Value::begin() { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3970,7 +3894,7 @@ Value::iterator Value::begin() { } Value::iterator Value::end() { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3985,25 +3909,20 @@ Value::iterator Value::end() { // class PathArgument // ////////////////////////////////////////////////////////////////// -PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} +PathArgument::PathArgument() = default; PathArgument::PathArgument(ArrayIndex index) - : key_(), index_(index), kind_(kindIndex) {} + : index_(index), kind_(kindIndex) {} -PathArgument::PathArgument(const char* key) - : key_(key), index_(), kind_(kindKey) {} +PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {} -PathArgument::PathArgument(const JSONCPP_STRING& key) - : key_(key.c_str()), index_(), kind_(kindKey) {} +PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {} // class Path // ////////////////////////////////////////////////////////////////// -Path::Path(const JSONCPP_STRING& path, - const PathArgument& a1, - const PathArgument& a2, - const PathArgument& a3, - const PathArgument& a4, +Path::Path(const String& path, const PathArgument& a1, const PathArgument& a2, + const PathArgument& a3, const PathArgument& a4, const PathArgument& a5) { InArgs in; in.reserve(5); @@ -4015,10 +3934,10 @@ Path::Path(const JSONCPP_STRING& path, makePath(path, in); } -void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) { +void Path::makePath(const String& path, const InArgs& in) { const char* current = path.c_str(); const char* end = current + path.length(); - InArgs::const_iterator itInArg = in.begin(); + auto itInArg = in.begin(); while (current != end) { if (*current == '[') { ++current; @@ -4041,13 +3960,12 @@ void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) { const char* beginName = current; while (current != end && !strchr("[.", *current)) ++current; - args_.push_back(JSONCPP_STRING(beginName, current)); + args_.push_back(String(beginName, current)); } } } -void Path::addPathInArg(const JSONCPP_STRING& /*path*/, - const InArgs& in, +void Path::addPathInArg(const String& /*path*/, const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind) { if (itInArg == in.end()) { @@ -4059,30 +3977,29 @@ void Path::addPathInArg(const JSONCPP_STRING& /*path*/, } } -void Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/) { +void Path::invalidPath(const String& /*path*/, int /*location*/) { // Error: invalid path. } const Value& Path::resolve(const Value& root) const { const Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; + for (const auto& arg : args_) { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) { - // Error: unable to resolve path (array value expected at position... - return Value::null; + // Error: unable to resolve path (array value expected at position... ) + return Value::nullSingleton(); } node = &((*node)[arg.index_]); } else if (arg.kind_ == PathArgument::kindKey) { if (!node->isObject()) { // Error: unable to resolve path (object value expected at position...) - return Value::null; + return Value::nullSingleton(); } node = &((*node)[arg.key_]); if (node == &Value::nullSingleton()) { // Error: unable to resolve path (object has no member named '' at // position...) - return Value::null; + return Value::nullSingleton(); } } } @@ -4091,8 +4008,7 @@ const Value& Path::resolve(const Value& root) const { Value Path::resolve(const Value& root, const Value& defaultValue) const { const Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; + for (const auto& arg : args_) { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) return defaultValue; @@ -4110,8 +4026,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const { Value& Path::make(Value& root) const { Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; + for (const auto& arg : args_) { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray()) { // Error: node is not an array at position ... @@ -4148,71 +4063,81 @@ Value& Path::make(Value& root) const { // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) -#include <json/writer.h> #include "json_tool.h" +#include <json/writer.h> #endif // if !defined(JSON_IS_AMALGAMATION) +#include <algorithm> +#include <cassert> +#include <cctype> +#include <cstring> #include <iomanip> #include <memory> +#include <set> #include <sstream> #include <utility> -#include <set> -#include <cassert> -#include <cstring> + +#if __cplusplus >= 201103L +#include <cmath> #include <cstdio> -#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#if !defined(isnan) +#define isnan std::isnan +#endif + +#if !defined(isfinite) +#define isfinite std::isfinite +#endif + +#else +#include <cmath> +#include <cstdio> + +#if defined(_MSC_VER) +#if !defined(isnan) +#include <float.h> +#define isnan _isnan +#endif + +#if !defined(isfinite) #include <float.h> #define isfinite _finite -#elif defined(__sun) && defined(__SVR4) //Solaris +#endif + +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES + +#endif //_MSC_VER + +#if defined(__sun) && defined(__SVR4) // Solaris #if !defined(isfinite) #include <ieeefp.h> #define isfinite finite #endif -#elif defined(_AIX) -#if !defined(isfinite) -#include <math.h> -#define isfinite finite #endif -#elif defined(__hpux) + +#if defined(__hpux) #if !defined(isfinite) #if defined(__ia64) && !defined(finite) -#define isfinite(x) ((sizeof(x) == sizeof(float) ? \ - _Isfinitef(x) : _IsFinite(x))) -#else -#include <math.h> -#define isfinite finite -#endif +#define isfinite(x) \ + ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x))) #endif -#else -#include <cmath> -#if !(defined(__QNXNTO__)) // QNX already defines isfinite -#define isfinite std::isfinite #endif #endif -#if defined(_MSC_VER) -#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above -#define snprintf sprintf_s -#elif _MSC_VER >= 1900 // VC++ 14.0 and above -#define snprintf std::snprintf -#else -#define snprintf _snprintf +#if !defined(isnan) +// IEEE standard states that NaN values will not compare to themselves +#define isnan(x) (x != x) #endif -#elif defined(__ANDROID__) || defined(__QNXNTO__) -#define snprintf snprintf -#elif __cplusplus >= 201103L -#if !defined(__MINGW32__) && !defined(__CYGWIN__) -#define snprintf std::snprintf + +#if !defined(__APPLE__) +#if !defined(isfinite) +#define isfinite finite #endif #endif - -#if defined(__BORLANDC__) -#include <float.h> -#define isfinite _finite -#define snprintf _snprintf #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +#if defined(_MSC_VER) // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif @@ -4220,12 +4145,12 @@ Value& Path::make(Value& root) const { namespace Json { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr<StreamWriter> StreamWriterPtr; +using StreamWriterPtr = std::unique_ptr<StreamWriter>; #else -typedef std::auto_ptr<StreamWriter> StreamWriterPtr; +using StreamWriterPtr = std::auto_ptr<StreamWriter>; #endif -JSONCPP_STRING valueToString(LargestInt value) { +String valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); if (value == Value::minLargestInt) { @@ -4241,7 +4166,7 @@ JSONCPP_STRING valueToString(LargestInt value) { return current; } -JSONCPP_STRING valueToString(LargestUInt value) { +String valueToString(LargestUInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); uintToString(value, current); @@ -4251,67 +4176,70 @@ JSONCPP_STRING valueToString(LargestUInt value) { #if defined(JSON_HAS_INT64) -JSONCPP_STRING valueToString(Int value) { - return valueToString(LargestInt(value)); -} +String valueToString(Int value) { return valueToString(LargestInt(value)); } -JSONCPP_STRING valueToString(UInt value) { - return valueToString(LargestUInt(value)); -} +String valueToString(UInt value) { return valueToString(LargestUInt(value)); } #endif // # if defined(JSON_HAS_INT64) namespace { -JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) { - // Allocate a buffer that is more than large enough to store the 16 digits of - // precision requested below. - char buffer[36]; - int len = -1; - - char formatString[15]; - snprintf(formatString, sizeof(formatString), "%%.%ug", precision); - +String valueToString(double value, bool useSpecialFloats, + unsigned int precision, PrecisionType precisionType) { // Print into the buffer. We need not request the alternative representation // that always has a decimal point because JSON doesn't distinguish the // concepts of reals and integers. - if (isfinite(value)) { - len = snprintf(buffer, sizeof(buffer), formatString, value); - fixNumericLocale(buffer, buffer + len); - - // try to ensure we preserve the fact that this was given to us as a double on input - if (!strchr(buffer, '.') && !strchr(buffer, 'e')) { - strcat(buffer, ".0"); + if (!isfinite(value)) { + static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"}, + {"null", "-1e+9999", "1e+9999"}}; + return reps[useSpecialFloats ? 0 : 1] + [isnan(value) ? 0 : (value < 0) ? 1 : 2]; + } + + String buffer(size_t(36), '\0'); + while (true) { + int len = jsoncpp_snprintf( + &*buffer.begin(), buffer.size(), + (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f", + precision, value); + assert(len >= 0); + auto wouldPrint = static_cast<size_t>(len); + if (wouldPrint >= buffer.size()) { + buffer.resize(wouldPrint + 1); + continue; } + buffer.resize(wouldPrint); + break; + } - } else { - // IEEE standard states that NaN values will not compare to themselves - if (value != value) { - len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null"); - } else if (value < 0) { - len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999"); - } else { - len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999"); - } + buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end()); + + // strip the zero padding from the right + if (precisionType == PrecisionType::decimalPlaces) { + buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end()); + } + + // try to ensure we preserve the fact that this was given to us as a double on + // input + if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) { + buffer += ".0"; } - assert(len >= 0); return buffer; } -} +} // namespace -JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); } +String valueToString(double value, unsigned int precision, + PrecisionType precisionType) { + return valueToString(value, false, precision, precisionType); +} -JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; } +String valueToString(bool value) { return value ? "true" : "false"; } -static bool isAnyCharRequiredQuoting(char const* s, size_t n) { +static bool doesAnyCharRequireEscaping(char const* s, size_t n) { assert(s || !n); - char const* const end = s + n; - for (char const* cur = s; cur < end; ++cur) { - if (*cur == '\\' || *cur == '\"' || *cur < ' ' - || static_cast<unsigned char>(*cur) < 0x80) - return true; - } - return false; + return std::any_of(s, s + n, [](unsigned char c) { + return c == '\\' || c == '"' || c < 0x20 || c > 0x7F; + }); } static unsigned int utf8ToCodepoint(const char*& s, const char* e) { @@ -4326,8 +4254,8 @@ static unsigned int utf8ToCodepoint(const char*& s, const char* e) { if (e - s < 2) return REPLACEMENT_CHARACTER; - unsigned int calculated = ((firstByte & 0x1F) << 6) - | (static_cast<unsigned int>(s[1]) & 0x3F); + unsigned int calculated = + ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F); s += 1; // oversized encoded characters are invalid return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated; @@ -4337,9 +4265,9 @@ static unsigned int utf8ToCodepoint(const char*& s, const char* e) { if (e - s < 3) return REPLACEMENT_CHARACTER; - unsigned int calculated = ((firstByte & 0x0F) << 12) - | ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) - | (static_cast<unsigned int>(s[2]) & 0x3F); + unsigned int calculated = ((firstByte & 0x0F) << 12) | + ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) | + (static_cast<unsigned int>(s[2]) & 0x3F); s += 2; // surrogates aren't valid codepoints itself // shouldn't be UTF-8 encoded @@ -4353,10 +4281,10 @@ static unsigned int utf8ToCodepoint(const char*& s, const char* e) { if (e - s < 4) return REPLACEMENT_CHARACTER; - unsigned int calculated = ((firstByte & 0x07) << 24) - | ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) - | ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) - | (static_cast<unsigned int>(s[3]) & 0x3F); + unsigned int calculated = ((firstByte & 0x07) << 18) | + ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) | + ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) | + (static_cast<unsigned int>(s[3]) & 0x3F); s += 3; // oversized encoded characters are invalid return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated; @@ -4365,28 +4293,27 @@ static unsigned int utf8ToCodepoint(const char*& s, const char* e) { return REPLACEMENT_CHARACTER; } -static const char hex2[] = - "000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f" - "505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; - -static JSONCPP_STRING toHex16Bit(unsigned int x) { +static const char hex2[] = "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + +static String toHex16Bit(unsigned int x) { const unsigned int hi = (x >> 8) & 0xff; const unsigned int lo = x & 0xff; - JSONCPP_STRING result(4, ' '); + String result(4, ' '); result[0] = hex2[2 * hi]; result[1] = hex2[2 * hi + 1]; result[2] = hex2[2 * lo]; @@ -4394,18 +4321,26 @@ static JSONCPP_STRING toHex16Bit(unsigned int x) { return result; } -static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) { - if (value == NULL) +static void appendRaw(String& result, unsigned ch) { + result += static_cast<char>(ch); +} + +static void appendHex(String& result, unsigned ch) { + result.append("\\u").append(toHex16Bit(ch)); +} + +static String valueToQuotedStringN(const char* value, unsigned length, + bool emitUTF8 = false) { + if (value == nullptr) return ""; - if (!isAnyCharRequiredQuoting(value, length)) - return JSONCPP_STRING("\"") + value + "\""; + if (!doesAnyCharRequireEscaping(value, length)) + return String("\"") + value + "\""; // We have to walk value and escape any special characters. - // Appending to JSONCPP_STRING is not efficient, but this should be rare. + // Appending to String is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) - JSONCPP_STRING::size_type maxsize = - length * 2 + 3; // allescaped+quotes+NULL - JSONCPP_STRING result; + String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL + String result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; char const* end = value + length; @@ -4441,45 +4376,50 @@ static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) { // Should add a flag to allow this compatibility mode and prevent this // sequence from occurring. default: { - unsigned int cp = utf8ToCodepoint(c, end); - // don't escape non-control characters - // (short escape sequence are applied above) - if (cp < 0x80 && cp >= 0x20) - result += static_cast<char>(cp); - else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane - result += "\\u"; - result += toHex16Bit(cp); + if (emitUTF8) { + unsigned codepoint = static_cast<unsigned char>(*c); + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else { + appendRaw(result, codepoint); } - else { // codepoint is not in Basic Multilingual Plane - // convert to surrogate pair first - cp -= 0x10000; - result += "\\u"; - result += toHex16Bit((cp >> 10) + 0xD800); - result += "\\u"; - result += toHex16Bit((cp & 0x3FF) + 0xDC00); + } else { + unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c` + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else if (codepoint < 0x80) { + appendRaw(result, codepoint); + } else if (codepoint < 0x10000) { + // Basic Multilingual Plane + appendHex(result, codepoint); + } else { + // Extended Unicode. Encode 20 bits as a surrogate pair. + codepoint -= 0x10000; + appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff)); + appendHex(result, 0xdc00 + (codepoint & 0x3ff)); } } - break; + } break; } } result += "\""; return result; } -JSONCPP_STRING valueToQuotedString(const char* value) { +String valueToQuotedString(const char* value) { return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value))); } // Class Writer // ////////////////////////////////////////////////////////////////// -Writer::~Writer() {} +Writer::~Writer() = default; // Class FastWriter // ////////////////////////////////////////////////////////////////// FastWriter::FastWriter() - : yamlCompatibilityEnabled_(false), dropNullPlaceholders_(false), - omitEndingLineFeed_(false) {} + + = default; void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; } @@ -4487,11 +4427,11 @@ void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } -JSONCPP_STRING FastWriter::write(const Value& root) { +String FastWriter::write(const Value& root) { document_.clear(); writeValue(root); if (!omitEndingLineFeed_) - document_ += "\n"; + document_ += '\n'; return document_; } @@ -4510,13 +4450,13 @@ void FastWriter::writeValue(const Value& value) { case realValue: document_ += valueToString(value.asDouble()); break; - case stringValue: - { + case stringValue: { // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str)); + if (ok) + document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str)); break; } case booleanValue: @@ -4535,12 +4475,12 @@ void FastWriter::writeValue(const Value& value) { case objectValue: { Value::Members members(value.getMemberNames()); document_ += '{'; - for (Value::Members::iterator it = members.begin(); it != members.end(); - ++it) { - const JSONCPP_STRING& name = *it; + for (auto it = members.begin(); it != members.end(); ++it) { + const String& name = *it; if (it != members.begin()) document_ += ','; - document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())); + document_ += valueToQuotedStringN(name.data(), + static_cast<unsigned>(name.length())); document_ += yamlCompatibilityEnabled_ ? ": " : ":"; writeValue(value[name]); } @@ -4552,17 +4492,16 @@ void FastWriter::writeValue(const Value& value) { // Class StyledWriter // ////////////////////////////////////////////////////////////////// -StyledWriter::StyledWriter() - : rightMargin_(74), indentSize_(3), addChildValues_() {} +StyledWriter::StyledWriter() = default; -JSONCPP_STRING StyledWriter::write(const Value& root) { +String StyledWriter::write(const Value& root) { document_.clear(); addChildValues_ = false; indentString_.clear(); writeCommentBeforeValue(root); writeValue(root); writeCommentAfterValueOnSameLine(root); - document_ += "\n"; + document_ += '\n'; return document_; } @@ -4580,14 +4519,15 @@ void StyledWriter::writeValue(const Value& value) { case realValue: pushValue(valueToString(value.asDouble())); break; - case stringValue: - { + case stringValue: { // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); - else pushValue(""); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str))); + else + pushValue(""); break; } case booleanValue: @@ -4603,9 +4543,9 @@ void StyledWriter::writeValue(const Value& value) { else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + auto it = members.begin(); for (;;) { - const JSONCPP_STRING& name = *it; + const String& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); @@ -4675,7 +4615,7 @@ bool StyledWriter::isMultilineArray(const Value& value) { for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); + !childValue.empty()); } if (!isMultiLine) // check if line length > max line length { @@ -4695,7 +4635,7 @@ bool StyledWriter::isMultilineArray(const Value& value) { return isMultiLine; } -void StyledWriter::pushValue(const JSONCPP_STRING& value) { +void StyledWriter::pushValue(const String& value) { if (addChildValues_) childValues_.push_back(value); else @@ -4713,12 +4653,12 @@ void StyledWriter::writeIndent() { document_ += indentString_; } -void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) { +void StyledWriter::writeWithIndent(const String& value) { writeIndent(); document_ += value; } -void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); } +void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); } void StyledWriter::unindent() { assert(indentString_.size() >= indentSize_); @@ -4729,20 +4669,19 @@ void StyledWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; - document_ += "\n"; + document_ += '\n'; writeIndent(); - const JSONCPP_STRING& comment = root.getComment(commentBefore); - JSONCPP_STRING::const_iterator iter = comment.begin(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); while (iter != comment.end()) { document_ += *iter; - if (*iter == '\n' && - ((iter+1) != comment.end() && *(iter + 1) == '/')) + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) writeIndent(); ++iter; } // Comments are stripped of trailing newlines, so add one here - document_ += "\n"; + document_ += '\n'; } void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { @@ -4750,9 +4689,9 @@ void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { document_ += " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { - document_ += "\n"; + document_ += '\n'; document_ += root.getComment(commentAfter); - document_ += "\n"; + document_ += '\n'; } } @@ -4765,22 +4704,23 @@ bool StyledWriter::hasCommentForValue(const Value& value) { // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// -StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation) - : document_(NULL), rightMargin_(74), indentation_(indentation), - addChildValues_() {} +StyledStreamWriter::StyledStreamWriter(String indentation) + : document_(nullptr), indentation_(std::move(indentation)), + addChildValues_(), indented_(false) {} -void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) { +void StyledStreamWriter::write(OStream& out, const Value& root) { document_ = &out; addChildValues_ = false; indentString_.clear(); indented_ = true; writeCommentBeforeValue(root); - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *document_ << "\n"; - document_ = NULL; // Forget the stream, for safety. + document_ = nullptr; // Forget the stream, for safety. } void StyledStreamWriter::writeValue(const Value& value) { @@ -4797,14 +4737,15 @@ void StyledStreamWriter::writeValue(const Value& value) { case realValue: pushValue(valueToString(value.asDouble())); break; - case stringValue: - { + case stringValue: { // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); - else pushValue(""); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str))); + else + pushValue(""); break; } case booleanValue: @@ -4820,9 +4761,9 @@ void StyledStreamWriter::writeValue(const Value& value) { else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + auto it = members.begin(); for (;;) { - const JSONCPP_STRING& name = *it; + const String& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); @@ -4859,7 +4800,8 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { if (hasChildValue) writeWithIndent(childValues_[index]); else { - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; @@ -4894,7 +4836,7 @@ bool StyledStreamWriter::isMultilineArray(const Value& value) { for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); + !childValue.empty()); } if (!isMultiLine) // check if line length > max line length { @@ -4914,7 +4856,7 @@ bool StyledStreamWriter::isMultilineArray(const Value& value) { return isMultiLine; } -void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) { +void StyledStreamWriter::pushValue(const String& value) { if (addChildValues_) childValues_.push_back(value); else @@ -4929,8 +4871,9 @@ void StyledStreamWriter::writeIndent() { *document_ << '\n' << indentString_; } -void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) { - if (!indented_) writeIndent(); +void StyledStreamWriter::writeWithIndent(const String& value) { + if (!indented_) + writeIndent(); *document_ << value; indented_ = false; } @@ -4946,13 +4889,13 @@ void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; - if (!indented_) writeIndent(); - const JSONCPP_STRING& comment = root.getComment(commentBefore); - JSONCPP_STRING::const_iterator iter = comment.begin(); + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); while (iter != comment.end()) { *document_ << *iter; - if (*iter == '\n' && - ((iter+1) != comment.end() && *(iter + 1) == '/')) + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would include newline *document_ << indentString_; ++iter; @@ -4984,84 +4927,73 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { struct CommentStyle { /// Decide whether to write comments. enum Enum { - None, ///< Drop all comments. - Most, ///< Recover odd behavior of previous versions (not implemented yet). - All ///< Keep all comments. + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. }; }; -struct BuiltStyledStreamWriter : public StreamWriter -{ - BuiltStyledStreamWriter( - JSONCPP_STRING const& indentation, - CommentStyle::Enum cs, - JSONCPP_STRING const& colonSymbol, - JSONCPP_STRING const& nullSymbol, - JSONCPP_STRING const& endingLineFeedSymbol, - bool useSpecialFloats, - unsigned int precision); - int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE; +struct BuiltStyledStreamWriter : public StreamWriter { + BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs, + String colonSymbol, String nullSymbol, + String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, + PrecisionType precisionType); + int write(Value const& root, OStream* sout) override; + private: void writeValue(Value const& value); void writeArrayValue(Value const& value); bool isMultilineArray(Value const& value); - void pushValue(JSONCPP_STRING const& value); + void pushValue(String const& value); void writeIndent(); - void writeWithIndent(JSONCPP_STRING const& value); + void writeWithIndent(String const& value); void indent(); void unindent(); void writeCommentBeforeValue(Value const& root); void writeCommentAfterValueOnSameLine(Value const& root); static bool hasCommentForValue(const Value& value); - typedef std::vector<JSONCPP_STRING> ChildValues; + using ChildValues = std::vector<String>; ChildValues childValues_; - JSONCPP_STRING indentString_; + String indentString_; unsigned int rightMargin_; - JSONCPP_STRING indentation_; + String indentation_; CommentStyle::Enum cs_; - JSONCPP_STRING colonSymbol_; - JSONCPP_STRING nullSymbol_; - JSONCPP_STRING endingLineFeedSymbol_; + String colonSymbol_; + String nullSymbol_; + String endingLineFeedSymbol_; bool addChildValues_ : 1; bool indented_ : 1; bool useSpecialFloats_ : 1; + bool emitUTF8_ : 1; unsigned int precision_; + PrecisionType precisionType_; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( - JSONCPP_STRING const& indentation, - CommentStyle::Enum cs, - JSONCPP_STRING const& colonSymbol, - JSONCPP_STRING const& nullSymbol, - JSONCPP_STRING const& endingLineFeedSymbol, - bool useSpecialFloats, - unsigned int precision) - : rightMargin_(74) - , indentation_(indentation) - , cs_(cs) - , colonSymbol_(colonSymbol) - , nullSymbol_(nullSymbol) - , endingLineFeedSymbol_(endingLineFeedSymbol) - , addChildValues_(false) - , indented_(false) - , useSpecialFloats_(useSpecialFloats) - , precision_(precision) -{ -} -int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout) -{ + String indentation, CommentStyle::Enum cs, String colonSymbol, + String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, PrecisionType precisionType) + : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs), + colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)), + endingLineFeedSymbol_(std::move(endingLineFeedSymbol)), + addChildValues_(false), indented_(false), + useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8), + precision_(precision), precisionType_(precisionType) {} +int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) { sout_ = sout; addChildValues_ = false; indented_ = true; indentString_.clear(); writeCommentBeforeValue(root); - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *sout_ << endingLineFeedSymbol_; - sout_ = NULL; + sout_ = nullptr; return 0; } void BuiltStyledStreamWriter::writeValue(Value const& value) { @@ -5076,16 +5008,19 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { pushValue(valueToString(value.asLargestUInt())); break; case realValue: - pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, + precisionType_)); break; - case stringValue: - { + case stringValue: { // Is NULL is possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); - else pushValue(""); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str), + emitUTF8_)); + else + pushValue(""); break; } case booleanValue: @@ -5101,12 +5036,13 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + auto it = members.begin(); for (;;) { - JSONCPP_STRING const& name = *it; + String const& name = *it; Value const& childValue = value[name]; writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()))); + writeWithIndent(valueToQuotedStringN( + name.data(), static_cast<unsigned>(name.length()), emitUTF8_)); *sout_ << colonSymbol_; writeValue(childValue); if (++it == members.end()) { @@ -5140,7 +5076,8 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { if (hasChildValue) writeWithIndent(childValues_[index]); else { - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; @@ -5158,13 +5095,15 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { { assert(childValues_.size() == size); *sout_ << "["; - if (!indentation_.empty()) *sout_ << " "; + if (!indentation_.empty()) + *sout_ << " "; for (unsigned index = 0; index < size; ++index) { if (index > 0) *sout_ << ((!indentation_.empty()) ? ", " : ","); *sout_ << childValues_[index]; } - if (!indentation_.empty()) *sout_ << " "; + if (!indentation_.empty()) + *sout_ << " "; *sout_ << "]"; } } @@ -5177,7 +5116,7 @@ bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { Value const& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); + !childValue.empty()); } if (!isMultiLine) // check if line length > max line length { @@ -5197,7 +5136,7 @@ bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { return isMultiLine; } -void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) { +void BuiltStyledStreamWriter::pushValue(String const& value) { if (addChildValues_) childValues_.push_back(value); else @@ -5216,8 +5155,9 @@ void BuiltStyledStreamWriter::writeIndent() { } } -void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) { - if (!indented_) writeIndent(); +void BuiltStyledStreamWriter::writeWithIndent(String const& value) { + if (!indented_) + writeIndent(); *sout_ << value; indented_ = false; } @@ -5230,17 +5170,18 @@ void BuiltStyledStreamWriter::unindent() { } void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { - if (cs_ == CommentStyle::None) return; + if (cs_ == CommentStyle::None) + return; if (!root.hasComment(commentBefore)) return; - if (!indented_) writeIndent(); - const JSONCPP_STRING& comment = root.getComment(commentBefore); - JSONCPP_STRING::const_iterator iter = comment.begin(); + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); while (iter != comment.end()) { *sout_ << *iter; - if (*iter == '\n' && - ((iter+1) != comment.end() && *(iter + 1) == '/')) + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would write extra newline *sout_ << indentString_; ++iter; @@ -5248,8 +5189,10 @@ void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { indented_ = false; } -void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { - if (cs_ == CommentStyle::None) return; +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine( + Value const& root) { + if (cs_ == CommentStyle::None) + return; if (root.hasComment(commentAfterOnSameLine)) *sout_ << " " + root.getComment(commentAfterOnSameLine); @@ -5269,28 +5212,19 @@ bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { /////////////// // StreamWriter -StreamWriter::StreamWriter() - : sout_(NULL) -{ -} -StreamWriter::~StreamWriter() -{ -} -StreamWriter::Factory::~Factory() -{} -StreamWriterBuilder::StreamWriterBuilder() -{ - setDefaults(&settings_); -} -StreamWriterBuilder::~StreamWriterBuilder() -{} -StreamWriter* StreamWriterBuilder::newStreamWriter() const -{ - JSONCPP_STRING indentation = settings_["indentation"].asString(); - JSONCPP_STRING cs_str = settings_["commentStyle"].asString(); - bool eyc = settings_["enableYAMLCompatibility"].asBool(); - bool dnp = settings_["dropNullPlaceholders"].asBool(); - bool usf = settings_["useSpecialFloats"].asBool(); +StreamWriter::StreamWriter() : sout_(nullptr) {} +StreamWriter::~StreamWriter() = default; +StreamWriter::Factory::~Factory() = default; +StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } +StreamWriterBuilder::~StreamWriterBuilder() = default; +StreamWriter* StreamWriterBuilder::newStreamWriter() const { + const String indentation = settings_["indentation"].asString(); + const String cs_str = settings_["commentStyle"].asString(); + const String pt_str = settings_["precisionType"].asString(); + const bool eyc = settings_["enableYAMLCompatibility"].asBool(); + const bool dnp = settings_["dropNullPlaceholders"].asBool(); + const bool usf = settings_["useSpecialFloats"].asBool(); + const bool emitUTF8 = settings_["emitUTF8"].asBool(); unsigned int pre = settings_["precision"].asUInt(); CommentStyle::Enum cs = CommentStyle::All; if (cs_str == "All") { @@ -5300,74 +5234,80 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const } else { throwRuntimeError("commentStyle must be 'All' or 'None'"); } - JSONCPP_STRING colonSymbol = " : "; + PrecisionType precisionType(significantDigits); + if (pt_str == "significant") { + precisionType = PrecisionType::significantDigits; + } else if (pt_str == "decimal") { + precisionType = PrecisionType::decimalPlaces; + } else { + throwRuntimeError("precisionType must be 'significant' or 'decimal'"); + } + String colonSymbol = " : "; if (eyc) { colonSymbol = ": "; } else if (indentation.empty()) { colonSymbol = ":"; } - JSONCPP_STRING nullSymbol = "null"; + String nullSymbol = "null"; if (dnp) { nullSymbol.clear(); } - if (pre > 17) pre = 17; - JSONCPP_STRING endingLineFeedSymbol; - return new BuiltStyledStreamWriter( - indentation, cs, - colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); -} -static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys) -{ - valid_keys->clear(); - valid_keys->insert("indentation"); - valid_keys->insert("commentStyle"); - valid_keys->insert("enableYAMLCompatibility"); - valid_keys->insert("dropNullPlaceholders"); - valid_keys->insert("useSpecialFloats"); - valid_keys->insert("precision"); -} -bool StreamWriterBuilder::validate(Json::Value* invalid) const -{ - Json::Value my_invalid; - if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set<JSONCPP_STRING> valid_keys; - getValidWriterKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - JSONCPP_STRING const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } + if (pre > 17) + pre = 17; + String endingLineFeedSymbol; + return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol, + endingLineFeedSymbol, usf, emitUTF8, pre, + precisionType); +} + +bool StreamWriterBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set<String>{ + "indentation", + "commentStyle", + "enableYAMLCompatibility", + "dropNullPlaceholders", + "useSpecialFloats", + "emitUTF8", + "precision", + "precisionType", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[std::move(key)] = *si; + else + return false; } - return 0u == inv.size(); + return invalid ? invalid->empty() : true; } -Value& StreamWriterBuilder::operator[](JSONCPP_STRING key) -{ + +Value& StreamWriterBuilder::operator[](const String& key) { return settings_[key]; } // static -void StreamWriterBuilder::setDefaults(Json::Value* settings) -{ +void StreamWriterBuilder::setDefaults(Json::Value* settings) { //! [StreamWriterBuilderDefaults] (*settings)["commentStyle"] = "All"; (*settings)["indentation"] = "\t"; (*settings)["enableYAMLCompatibility"] = false; (*settings)["dropNullPlaceholders"] = false; (*settings)["useSpecialFloats"] = false; + (*settings)["emitUTF8"] = false; (*settings)["precision"] = 17; + (*settings)["precisionType"] = "significant"; //! [StreamWriterBuilderDefaults] } -JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) { - JSONCPP_OSTRINGSTREAM sout; - StreamWriterPtr const writer(builder.newStreamWriter()); +String writeString(StreamWriter::Factory const& factory, Value const& root) { + OStringStream sout; + StreamWriterPtr const writer(factory.newStreamWriter()); writer->write(root, &sout); return sout.str(); } -JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) { +OStream& operator<<(OStream& sout, Value const& root) { StreamWriterBuilder builder; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); |