From 7f1d3349f792a56be2a94e603c48c20778b232fb Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 11:00:25 +0000 Subject: [PATCH 01/41] Add eclipse ignore settings --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index ef226a887..5357054f8 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,11 @@ *.tlog/ *.pdb +# eclipse project files +.project +.cproject +/.settings/ + # CMake-generated files: CMakeFiles/ CTestTestFile.cmake From fbe5f2f24797fa7fd27aa8880ad4ef704be51f25 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 11:25:00 +0000 Subject: [PATCH 02/41] Copy all cpp files to inl files to convert into templates All cpp files have been copied to inl files in the include folder The inl file for value type has been moved into the include folder, later Exception clases must be moved back into value --- include/json/reader.inl | 2032 +++++++++++++++++ .../json_tool.h => include/json/tool.h | 0 include/json/value.inl | 1541 +++++++++++++ .../json/valueiterator.inl | 0 include/json/writer.inl | 1214 ++++++++++ src/lib_json/json_writer.cpp | 2 +- 6 files changed, 4788 insertions(+), 1 deletion(-) create mode 100644 include/json/reader.inl rename src/lib_json/json_tool.h => include/json/tool.h (100%) create mode 100644 include/json/value.inl rename src/lib_json/json_valueiterator.inl => include/json/valueiterator.inl (100%) create mode 100644 include/json/writer.inl diff --git a/include/json/reader.inl b/include/json/reader.inl new file mode 100644 index 000000000..2eae15de9 --- /dev/null +++ b/include/json/reader.inl @@ -0,0 +1,2032 @@ +// Copyright 2007-2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#define snprintf std::snprintf +#endif + +#if defined(__QNXNTO__) +#define sscanf std::sscanf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +static int const stackLimit_g = 1000; +static int stackDepth_g = 0; // see readValue() + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr CharReaderPtr; +#else +typedef std::auto_ptr CharReaderPtr; +#endif + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_(true), strictRoot_(false), + allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} + +Features Features::all() { return Features(); } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +static bool containsNewLine(Reader::Location begin, Reader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(Features::all()), + collectComments_() {} + +Reader::Reader(const Features& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool +Reader::parse(const std::string& document, Value& root, bool collectComments) { + document_ = document; + 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 begin(sin); + // std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse(doc, root, collectComments); +} + +bool Reader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + stackDepth_g = 0; // Yes, this is bad coding, but options are limited. + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + // This is a non-reentrant way to support a stackLimit. Terrible! + // But this deprecated class has a security problem: Bad input can + // cause a seg-fault. This seems like a fair, binary-compatible way + // to prevent the problem. + if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_g; + + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // Else, fall through... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + --stackDepth_g; + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { + std::string normalized; + normalized.reserve(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 Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void +Reader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const std::string& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool Reader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void Reader::readNumber() { + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : 0; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } +} + +bool Reader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& tokenStart) { + Token tokenName; + std::string name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.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 + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool Reader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // 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) + ++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::maxLargestInt) + 1 + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(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 + // 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) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative && value == maxIntegerValue) + decoded = Value::minLargestInt; + else if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + std::string buffer(token.start_, token.end_); + std::istringstream is(buffer); + if (!(is >> value)) + return addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + std::string decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, std::string& decoded) { + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + return true; +} + +bool +Reader::addError(const std::string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + int errorCount = int(errors_.size()); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::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); + return buffer; +} + +// Deprecated. Preserved for backward compatibility +std::string Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +std::string Reader::getFormattedErrorMessages() const { + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector Reader::getStructuredErrors() const { + std::vector allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const std::string& message) { + size_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 Reader::pushError(const Value& value, const std::string& message, const Value& extra) { + size_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 Reader::good() const { + return !errors_.size(); +} + +// exact copy of Features +class OurFeatures { +public: + static OurFeatures all(); + bool allowComments_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + bool allowSpecialFloats_; + int stackLimit_; +}; // OurFeatures + +// exact copy of Implementation of class Features +// //////////////////////////////// + +OurFeatures OurFeatures::all() { return OurFeatures(); } + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + size_t offset_start; + size_t offset_limit; + std::string message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + std::string getFormattedErrorMessages() const; + std::vector getStructuredErrors() const; + bool pushError(const Value& value, const std::string& message); + bool pushError(const Value& value, const std::string& message, const Value& extra); + bool good() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenNaN, + tokenPosInf, + tokenNegInf, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + bool readNumber(bool checkInf); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + 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 std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + int stackDepth_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +// complete copy of Read impl, for OurReader + +OurReader::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), + stackDepth_(0), + features_(features), collectComments_() { +} + +bool OurReader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + stackDepth_ = 0; + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_) { + if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_; + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNaN: + { + Value v(std::numeric_limits::quiet_NaN()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenPosInf: + { + Value v(std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNegInf: + { + Value v(-std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + --stackDepth_; + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + break; + } // else continue + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token.type_ = tokenNumber; + readNumber(false); + break; + case '-': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenNegInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case 'N': + if (features_.allowSpecialFloats_) { + token.type_ = tokenNaN; + ok = match("aN", 2); + } else { + ok = false; + } + break; + case 'I': + if (features_.allowSpecialFloats_) { + token.type_ = tokenPosInf; + ok = match("nfinity", 7); + } else { + ok = false; + } + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool OurReader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool OurReader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +void +OurReader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const std::string& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +bool OurReader::readNumber(bool checkInf) { + const char *p = current_; + if (checkInf && p != end_ && *p == 'I') { + current_ = ++p; + return false; + } + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : 0; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } + return true; +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& tokenStart) { + Token tokenName; + std::string name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.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 + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + 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)) { + std::string msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover( + msg, tokenName, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool OurReader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // 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) + ++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; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(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 + // 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) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError("Unable to parse token length", token); + } + + // 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_, length); + buffer[length] = 0; + count = sscanf(buffer, format, &value); + } else { + std::string buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool OurReader::decodeString(Token& token) { + std::string decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeString(Token& token, std::string& decoded) { + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool OurReader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool OurReader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + return true; +} + +bool +OurReader::addError(const std::string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool OurReader::recoverFromError(TokenType skipUntilToken) { + int errorCount = int(errors_.size()); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool OurReader::addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& OurReader::currentValue() { return *(nodes_.top()); } + +OurReader::Char OurReader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void OurReader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::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); + return buffer; +} + +std::string OurReader::getFormattedErrorMessages() const { + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector OurReader::getStructuredErrors() const { + std::vector allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + OurReader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool OurReader::pushError(const Value& value, const std::string& message) { + size_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 std::string& message, const Value& extra) { + size_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, std::string* errs) override { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() +{ + setDefaults(&settings_); +} +CharReaderBuilder::~CharReaderBuilder() +{} +CharReader* CharReaderBuilder::newCharReader() const +{ + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + features.stackLimit_ = settings_["stackLimit"].asInt(); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + return new OurCharReader(collectComments, features); +} +static void getValidReaderKeys(std::set* 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 valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& CharReaderBuilder::operator[](std::string key) +{ + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) +{ +//! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) +{ +//! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream( + CharReader::Factory const& fact, std::istream& sin, + Value* root, std::string* errs) +{ + std::ostringstream ssin; + ssin << sin.rdbuf(); + std::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. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +std::istream& operator>>(std::istream& sin, Value& root) { + CharReaderBuilder b; + std::string errs; + bool ok = parseFromStream(b, sin, &root, &errs); + if (!ok) { + fprintf(stderr, + "Error from reader: %s", + errs.c_str()); + + throwRuntimeError(errs); + } + return sin; +} + +} // namespace Json diff --git a/src/lib_json/json_tool.h b/include/json/tool.h similarity index 100% rename from src/lib_json/json_tool.h rename to include/json/tool.h diff --git a/include/json/value.inl b/include/json/value.inl new file mode 100644 index 000000000..91b5f23f9 --- /dev/null +++ b/include/json/value.inl @@ -0,0 +1,1541 @@ +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +#include +#endif +#include // size_t +#include // min() + +#define JSON_ASSERT_UNREACHABLE assert(false) + +namespace Json { + +// 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 +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#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(kNullRef); +const Value& Value::nullRef = null; + +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); + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template +static inline bool InRange(double d, T min, U max) { + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast(Int64(value / 2)) * 2.0 + Int64(value & 1); +} + +template static inline double integerToDouble(T value) { + return static_cast(value); +} + +template +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +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 >= (size_t)Value::maxInt) + length = Value::maxInt - 1; + + char* newString = static_cast(malloc(length + 1)); + if (newString == NULL) { + throwRuntimeError( + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/* Record the length as a prefix. + */ +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 <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; + char* newString = static_cast(malloc(actualLength)); + if (newString == 0) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + 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) +{ + if (!isPrefixed) { + *length = static_cast(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). + */ +static inline void releaseStringValue(char* value) { free(value); } + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +Exception::Exception(std::string const& msg) + : msg_(msg) +{} +Exception::~Exception() throw() +{} +char const* Exception::what() const throw() +{ + return msg_.c_str(); +} +RuntimeError::RuntimeError(std::string const& msg) + : Exception(msg) +{} +LogicError::LogicError(std::string const& msg) + : Exception(msg) +{} +void throwRuntimeError(std::string const& msg) +{ + throw RuntimeError(msg); +} +void throwLogicError(std::string const& msg) +{ + throw LogicError(msg); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() : comment_(0) {} + +Value::CommentInfo::~CommentInfo() { + if (comment_) + releaseStringValue(comment_); +} + +void Value::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { + releaseStringValue(comment_); + 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); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +// 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(char const* str, unsigned ulength, DuplicationPolicy allocate) + : cstr_(str) { + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = ulength & 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_ = (other.cstr_ + ? (static_cast(other.storage_.policy_) == noDuplication + ? noDuplication : duplicate) + : static_cast(other.storage_.policy_)); + storage_.length_ = other.storage_.length_; +} + +#if JSON_HAS_RVALUE_REFERENCES +Value::CZString::CZString(CZString&& other) + : cstr_(other.cstr_), index_(other.index_) { + other.cstr_ = nullptr; +} +#endif + +Value::CZString::~CZString() { + if (cstr_ && storage_.policy_ == duplicate) + releaseStringValue(const_cast(cstr_)); +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(CZString other) { + swap(other); + return *this; +} + +bool Value::CZString::operator<(const CZString& other) const { + 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(this_len, other_len); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + 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; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) return false; + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +//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; } + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType vtype) { + initBasic(vtype); + switch (vtype) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); +} + +Value::Value(const char* beginValue, const char* endValue) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); +} + +Value::Value(const std::string& value) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast(value.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value::Value(const CppTL::ConstString& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(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_)); + } + } +} + +#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_) + releaseStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } + + if (comments_) + delete[] comments_; +} + +Value& Value::operator=(Value other) { + swap(other); + return *this; +} + +void Value::swapPayload(Value& other) { + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap(value_, other.value_); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2 & 0x1; +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +ValueType Value::type() const { return type_; } + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type_ - other.type_; + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type_) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + 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; + } + 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); + unsigned min_len = std::min(this_len, other_len); + int comp = memcmp(this_str, other_str, min_len); + 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; + return (*value_.map_) < (*other.value_.map_); + } + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +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) + return false; + switch (type_) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + 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; + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + 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); + return this_str; +} + +bool Value::getString(char const** str, char const** cend) const { + if (type_ != stringValue) return false; + if (value_.string_ == 0) return false; + unsigned length; + decodePrefixedString(this->allocated_, this->value_.string_, &length, str); + *cend = *str + length; + return true; +} + +std::string Value::asString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + { + if (value_.string_ == 0) return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return std::string(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +#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_) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type_) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type_) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + // This is kind of strange. Not recommended. + return (value_.real_ != 0.0) ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type_ == booleanValue && value_.bool_ == false) || + (type_ == stringValue && asString() == "") || + (type_ == arrayValue && value_.map_->size() == 0) || + (type_ == objectValue && value_.map_->size() == 0) || + type_ == nullValue; + case intValue: + return isInt() || + (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; + case realValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case booleanValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case stringValue: + return isNumeric() || type_ == booleanValue || type_ == stringValue || + type_ == nullValue; + case arrayValue: + return type_ == arrayValue || type_ == nullValue; + case objectValue: + return type_ == objectValue || type_ == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +bool Value::operator!() const { return isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || + type_ == objectValue, + "in Json::Value::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type_) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + (*this)[newSize - 1]; + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + assert(size() == newSize); + } +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + CZString key(index); + ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, nullRef); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type_ == nullValue) + return nullRef; + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullRef; + return (*it).second; +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +void Value::initBasic(ValueType vtype, bool allocated) { + type_ = vtype; + allocated_ = allocated; + comments_ = 0; + start_ = 0; + limit_ = 0; +} + +// 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, + "in Json::Value::resolveReference(): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullRef); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* cend) +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(cend-key), CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullRef); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &nullRef ? defaultValue : *value; +} + +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(cend-key), CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) return NULL; + return &(*it).second; +} +const Value& Value::operator[](const char* key) const +{ + Value const* found = find(key, key + strlen(key)); + if (!found) return nullRef; + return *found; +} +Value const& Value::operator[](std::string const& key) const +{ + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) return nullRef; + return *found; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +Value& Value::operator[](const std::string& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +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 nullRef; + return *found; +} +#endif + +Value& Value::append(const Value& value) { return (*this)[size()] = value; } + +Value Value::get(char const* key, char const* cend, Value const& defaultValue) const +{ + Value const* found = find(key, cend); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const +{ + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(std::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) { + return false; + } + CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return false; + *removed = it->second; + value_.map_->erase(it); + return true; +} +bool Value::removeMember(const char* key, Value* removed) +{ + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(std::string const& key, Value* removed) +{ + return removeMember(key.data(), key.data() + key.length(), removed); +} +Value Value::removeMember(const char* key) +{ + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return nullRef; + + Value removed; // null + removeMember(key, key + strlen(key), &removed); + return removed; // still null if removeMember() did nothing +} +Value Value::removeMember(const std::string& key) +{ + return removeMember(key.c_str()); +} + +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type_ != arrayValue) { + return false; + } + CZString key(index); + ObjectValues::iterator it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + *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){ + 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); + 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); +} +#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 +{ + return isMember(key, key + strlen(key)); +} +bool Value::isMember(std::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, + "in Json::Value::getMemberNames(), value must be objectValue"); + 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(std::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() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// 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::isBool() const { return type_ == booleanValue; } + +bool Value::isInt() const { + switch (type_) { + case intValue: + return value_.int_ >= minInt && value_.int_ <= maxInt; + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type_) { + case intValue: + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); + case uintValue: + return value_.uint_ <= maxUInt; + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // 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_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { +#if defined(JSON_HAS_INT64) + return isInt64() || isUInt64(); +#else + return isInt() || isUInt(); +#endif +} + +bool Value::isDouble() const { return type_ == realValue || isIntegral(); } + +bool Value::isNumeric() const { return isIntegral() || isDouble(); } + +bool Value::isString() const { return type_ == stringValue; } + +bool Value::isArray() const { return type_ == arrayValue; } + +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); +} + +void Value::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); +} + +void Value::setComment(const std::string& comment, CommentPlacement placement) { + setComment(comment.c_str(), comment.length(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string Value::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +void Value::setOffsetStart(size_t start) { start_ = start; } + +void Value::setOffsetLimit(size_t limit) { limit_ = limit; } + +size_t Value::getOffsetStart() const { return start_; } + +size_t Value::getOffsetLimit() const { return limit_; } + +std::string Value::toStyledString() const { + StyledWriter writer; + return writer.write(*this); +} + +Value::const_iterator Value::begin() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} + +PathArgument::PathArgument(ArrayIndex index) + : key_(), index_(index), kind_(kindIndex) {} + +PathArgument::PathArgument(const char* key) + : key_(key), index_(), kind_(kindKey) {} + +PathArgument::PathArgument(const std::string& key) + : key_(key.c_str()), index_(), kind_(kindKey) {} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const std::string& path, + const PathArgument& a1, + const PathArgument& a2, + const PathArgument& a3, + const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const std::string& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *current++ != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(std::string(beginName, current)); + } + } +} + +void Path::addPathInArg(const std::string& /*path*/, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg); + } +} + +void Path::invalidPath(const std::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; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if (node == &Value::nullRef) { + // Error: unable to resolve path (object has no member named '' at + // position...) + } + } + } + return *node; +} + +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; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::nullRef) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json diff --git a/src/lib_json/json_valueiterator.inl b/include/json/valueiterator.inl similarity index 100% rename from src/lib_json/json_valueiterator.inl rename to include/json/valueiterator.inl diff --git a/include/json/writer.inl b/include/json/writer.inl new file mode 100644 index 000000000..0b2d7d5be --- /dev/null +++ b/include/json/writer.inl @@ -0,0 +1,1214 @@ +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#include +#define isfinite _finite +#elif defined(__sun) && defined(__SVR4) //Solaris +#if !defined(isfinite) +#include +#define isfinite finite +#endif +#elif defined(_AIX) +#if !defined(isfinite) +#include +#define isfinite finite +#endif +#elif defined(__hpux) +#if !defined(isfinite) +#if defined(__ia64) && !defined(finite) +#define isfinite(x) ((sizeof(x) == sizeof(float) ? \ + _Isfinitef(x) : _IsFinite(x))) +#else +#include +#define isfinite finite +#endif +#endif +#else +#include +#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 +#endif +#elif defined(__ANDROID__) || defined(__QNXNTO__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#define snprintf std::snprintf +#endif + +#if defined(__BORLANDC__) +#include +#define isfinite _finite +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr StreamWriterPtr; +#else +typedef std::auto_ptr StreamWriterPtr; +#endif + +static bool containsControlCharacter(const char* str) { + while (*str) { + if (isControlCharacter(*(str++))) + return true; + } + return false; +} + +static bool containsControlCharacter0(const char* str, unsigned len) { + char const* end = str + len; + while (end != str) { + if (isControlCharacter(*str) || 0==*str) + return true; + ++str; + } + return false; +} + +std::string valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + if (value == Value::minLargestInt) { + uintToString(LargestUInt(Value::maxLargestInt) + 1, current); + *--current = '-'; + } else if (value < 0) { + uintToString(LargestUInt(-value), current); + *--current = '-'; + } else { + uintToString(LargestUInt(value), current); + } + assert(current >= buffer); + return current; +} + +std::string valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +std::string valueToString(Int value) { + return valueToString(LargestInt(value)); +} + +std::string valueToString(UInt value) { + return valueToString(LargestUInt(value)); +} + +#endif // # if defined(JSON_HAS_INT64) + +std::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[32]; + int len = -1; + + char formatString[6]; + sprintf(formatString, "%%.%dg", precision); + + // Print into the buffer. We need not request the alternative representation + // that always has a decimal point because JSON doesn't distingish the + // concepts of reals and integers. + if (isfinite(value)) { + len = snprintf(buffer, sizeof(buffer), formatString, value); + } 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"); + } + // For those, we do not need to call fixNumLoc, but it is fast. + } + assert(len >= 0); + fixNumericLocale(buffer, buffer + len); + return buffer; +} + +std::string valueToString(double value) { return valueToString(value, false, 17); } + +std::string valueToString(bool value) { return value ? "true" : "false"; } + +std::string valueToQuotedString(const char* value) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && + !containsControlCharacter(value)) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = + strlen(value) * 2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c = value; *c != 0; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp +static char const* strnpbrk(char const* s, char const* accept, size_t n) { + assert((s || !n) && accept); + + char const* const end = s + n; + for (char const* cur = s; cur < end; ++cur) { + int const c = *cur; + for (char const* a = accept; *a; ++a) { + if (*a == c) { + return cur; + } + } + } + return NULL; +} +static std::string valueToQuotedStringN(const char* value, unsigned length) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && + !containsControlCharacter0(value, length)) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = + length * 2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something.) + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() {} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), + omitEndingLineFeed_(false) {} + +void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } + +void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } + +void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } + +std::string FastWriter::write(const Value& root) { + document_ = ""; + writeValue(root); + if (!omitEndingLineFeed_) + document_ += "\n"; + return document_; +} + +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); + break; + } + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += '['; + int size = value.size(); + for (int index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += '{'; + for (Value::Members::iterator it = members.begin(); it != members.end(); + ++it) { + const std::string& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); + document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_(74), indentSize_(3), addChildValues_() {} + +std::string StyledWriter::write(const Value& root) { + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const std::string& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultineArray(const Value& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const std::string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const std::string& value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } + +void StyledWriter::unindent() { + assert(int(indentString_.size()) >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += "\n"; + writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + document_ += *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += root.getComment(commentAfter); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(std::string indentation) + : document_(NULL), rightMargin_(74), indentation_(indentation), + addChildValues_() {} + +void StyledStreamWriter::write(std::ostream& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + indented_ = true; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const std::string& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultineArray(const Value& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const std::string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const std::string& value) { + if (!indented_) writeIndent(); + *document_ << value; + indented_ = false; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +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. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter +{ + BuiltStyledStreamWriter( + std::string const& indentation, + CommentStyle::Enum cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::string const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision); + int write(Value const& root, std::ostream* sout) override; +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultineArray(Value const& value); + void pushValue(std::string const& value); + void writeIndent(); + void writeWithIndent(std::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 ChildValues; + + ChildValues childValues_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + CommentStyle::Enum cs_; + std::string colonSymbol_; + std::string nullSymbol_; + std::string endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; + bool useSpecialFloats_ : 1; + unsigned int precision_; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + std::string const& indentation, + CommentStyle::Enum cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::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, std::ostream* sout) +{ + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_ = ""; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = NULL; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); + break; + case stringValue: + { + // Is NULL is possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + std::string const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ", "; + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(std::string const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { + if (!indented_) writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() + : sout_(NULL) +{ +} +StreamWriter::~StreamWriter() +{ +} +StreamWriter::Factory::~Factory() +{} +StreamWriterBuilder::StreamWriterBuilder() +{ + setDefaults(&settings_); +} +StreamWriterBuilder::~StreamWriterBuilder() +{} +StreamWriter* StreamWriterBuilder::newStreamWriter() const +{ + std::string indentation = settings_["indentation"].asString(); + std::string cs_str = settings_["commentStyle"].asString(); + bool eyc = settings_["enableYAMLCompatibility"].asBool(); + bool dnp = settings_["dropNullPlaceholders"].asBool(); + bool usf = settings_["useSpecialFloats"].asBool(); + unsigned int pre = settings_["precision"].asUInt(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + std::string colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + std::string nullSymbol = "null"; + if (dnp) { + nullSymbol = ""; + } + if (pre > 17) pre = 17; + std::string endingLineFeedSymbol = ""; + return new BuiltStyledStreamWriter( + indentation, cs, + colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); +} +static void getValidWriterKeys(std::set* 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 valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& StreamWriterBuilder::operator[](std::string key) +{ + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) +{ + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + (*settings)["useSpecialFloats"] = false; + (*settings)["precision"] = 17; + //! [StreamWriterBuilderDefaults] +} + +std::string writeString(StreamWriter::Factory const& builder, Value const& root) { + std::ostringstream sout; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} + +std::ostream& operator<<(std::ostream& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; +} + +} // namespace Json diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 0b2d7d5be..a5d0bba2b 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -5,7 +5,7 @@ #if !defined(JSON_IS_AMALGAMATION) #include -#include "json_tool.h" +#include #endif // if !defined(JSON_IS_AMALGAMATION) #include #include From 1f2e9d1d5ffa572dfb4ed86d32abd1ab0445aa23 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 11:46:39 +0000 Subject: [PATCH 03/41] Remove from the cpp file anything that has to be templated The cpp files contain a lot of code that has to be moved into the inl files that will be used for the templates --- src/lib_json/json_reader.cpp | 1955 +--------------------------------- src/lib_json/json_value.cpp | 1492 -------------------------- src/lib_json/json_writer.cpp | 1098 ------------------- 3 files changed, 1 insertion(+), 4544 deletions(-) diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 2eae15de9..5fafbdbf7 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -7,7 +7,7 @@ #include #include #include -#include "json_tool.h" +#include #endif // if !defined(JSON_IS_AMALGAMATION) #include #include @@ -71,1962 +71,9 @@ Features Features::strictMode() { return features; } -// Implementation of class Reader -// //////////////////////////////// - -static bool containsNewLine(Reader::Location begin, Reader::Location end) { - for (; begin < end; ++begin) - if (*begin == '\n' || *begin == '\r') - return true; - return false; -} - -// Class Reader -// ////////////////////////////////////////////////////////////////// - -Reader::Reader() - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(Features::all()), - collectComments_() {} - -Reader::Reader(const Features& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(features), collectComments_() { -} - -bool -Reader::parse(const std::string& document, Value& root, bool collectComments) { - document_ = document; - 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 begin(sin); - // std::istream_iterator end; - // Those would allow streamed input from a file, if parse() were a - // template function. - - // Since std::string is reference-counted, this at least does not - // create an extra copy. - std::string doc; - std::getline(sin, doc, (char)EOF); - return parse(doc, root, collectComments); -} - -bool Reader::parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments) { - if (!features_.allowComments_) { - collectComments = false; - } - - begin_ = beginDoc; - end_ = endDoc; - collectComments_ = collectComments; - current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - commentsBefore_ = ""; - errors_.clear(); - while (!nodes_.empty()) - nodes_.pop(); - nodes_.push(&root); - - stackDepth_g = 0; // Yes, this is bad coding, but options are limited. - bool successful = readValue(); - Token token; - skipCommentTokens(token); - if (collectComments_ && !commentsBefore_.empty()) - root.setComment(commentsBefore_, commentAfter); - if (features_.strictRoot_) { - if (!root.isArray() && !root.isObject()) { - // Set error location to start of doc, ideally should be first token found - // in doc - token.type_ = tokenError; - token.start_ = beginDoc; - token.end_ = endDoc; - addError( - "A valid JSON document must be either an array or an object value.", - token); - return false; - } - } - return successful; -} - -bool Reader::readValue() { - // This is a non-reentrant way to support a stackLimit. Terrible! - // But this deprecated class has a security problem: Bad input can - // cause a seg-fault. This seems like a fair, binary-compatible way - // to prevent the problem. - if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); - ++stackDepth_g; - - Token token; - skipCommentTokens(token); - bool successful = true; - - if (collectComments_ && !commentsBefore_.empty()) { - currentValue().setComment(commentsBefore_, commentBefore); - commentsBefore_ = ""; - } - - switch (token.type_) { - case tokenObjectBegin: - successful = readObject(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenArrayBegin: - successful = readArray(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenNumber: - successful = decodeNumber(token); - break; - case tokenString: - successful = decodeString(token); - break; - case tokenTrue: - { - Value v(true); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { - Value v(false); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenArraySeparator: - case tokenObjectEnd: - case tokenArrayEnd: - if (features_.allowDroppedNullPlaceholders_) { - // "Un-read" the current token and mark the current value as a null - // token. - current_--; - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(current_ - begin_ - 1); - currentValue().setOffsetLimit(current_ - begin_); - break; - } // Else, fall through... - default: - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return addError("Syntax error: value, object or array expected.", token); - } - - if (collectComments_) { - lastValueEnd_ = current_; - lastValue_ = ¤tValue(); - } - - --stackDepth_g; - return successful; -} - -void Reader::skipCommentTokens(Token& token) { - if (features_.allowComments_) { - do { - readToken(token); - } while (token.type_ == tokenComment); - } else { - readToken(token); - } -} - -bool Reader::readToken(Token& token) { - skipSpaces(); - token.start_ = current_; - Char c = getNextChar(); - bool ok = true; - switch (c) { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': - token.type_ = tokenString; - ok = readString(); - break; - case '/': - token.type_ = tokenComment; - ok = readComment(); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - token.type_ = tokenNumber; - readNumber(); - break; - case 't': - token.type_ = tokenTrue; - ok = match("rue", 3); - break; - case 'f': - token.type_ = tokenFalse; - ok = match("alse", 4); - break; - case 'n': - token.type_ = tokenNull; - ok = match("ull", 3); - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: - ok = false; - break; - } - if (!ok) - token.type_ = tokenError; - token.end_ = current_; - return true; -} - -void Reader::skipSpaces() { - while (current_ != end_) { - Char c = *current_; - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') - ++current_; - else - break; - } -} - -bool Reader::match(Location pattern, int patternLength) { - if (end_ - current_ < patternLength) - return false; - int index = patternLength; - while (index--) - if (current_[index] != pattern[index]) - return false; - current_ += patternLength; - return true; -} - -bool Reader::readComment() { - Location commentBegin = current_ - 1; - Char c = getNextChar(); - bool successful = false; - if (c == '*') - successful = readCStyleComment(); - else if (c == '/') - successful = readCppStyleComment(); - if (!successful) - return false; - - if (collectComments_) { - CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) - placement = commentAfterOnSameLine; - } - - addComment(commentBegin, current_, placement); - } - return true; -} - -static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { - std::string normalized; - normalized.reserve(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 Mac EOL - normalized += '\n'; - } else { - normalized += c; - } - } - return normalized; -} - -void -Reader::addComment(Location begin, Location end, CommentPlacement placement) { - assert(collectComments_); - const std::string& normalized = normalizeEOL(begin, end); - if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); - lastValue_->setComment(normalized, placement); - } else { - commentsBefore_ += normalized; - } -} - -bool Reader::readCStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '*' && *current_ == '/') - break; - } - return getNextChar() == '/'; -} - -bool Reader::readCppStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '\n') - break; - if (c == '\r') { - // Consume DOS EOL. It will be normalized in addComment. - if (current_ != end_ && *current_ == '\n') - getNextChar(); - // Break on Moc OS 9 EOL. - break; - } - } - return true; -} - -void Reader::readNumber() { - const char *p = current_; - char c = '0'; // stopgap for already consumed character - // integral part - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - // fractional part - if (c == '.') { - c = (current_ = p) < end_ ? *p++ : 0; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - } - // exponential part - if (c == 'e' || c == 'E') { - c = (current_ = p) < end_ ? *p++ : 0; - if (c == '+' || c == '-') - c = (current_ = p) < end_ ? *p++ : 0; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - } -} - -bool Reader::readString() { - Char c = 0; - while (current_ != end_) { - c = getNextChar(); - if (c == '\\') - getNextChar(); - else if (c == '"') - break; - } - return c == '"'; -} - -bool Reader::readObject(Token& tokenStart) { - Token tokenName; - std::string name; - Value init(objectValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.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 - return true; - name = ""; - if (tokenName.type_ == tokenString) { - if (!decodeString(tokenName, name)) - return recoverFromError(tokenObjectEnd); - } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { - Value numberName; - if (!decodeNumber(tokenName, numberName)) - return recoverFromError(tokenObjectEnd); - name = numberName.asString(); - } else { - break; - } - - Token colon; - if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover( - "Missing ':' after object member name", colon, tokenObjectEnd); - } - Value& value = currentValue()[name]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenObjectEnd); - - Token comma; - if (!readToken(comma) || - (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); - } - bool finalizeTokenOk = true; - while (comma.type_ == tokenComment && finalizeTokenOk) - finalizeTokenOk = readToken(comma); - if (comma.type_ == tokenObjectEnd) - return true; - } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); -} - -bool Reader::readArray(Token& tokenStart) { - Value init(arrayValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); - skipSpaces(); - if (*current_ == ']') // empty array - { - Token endArray; - readToken(endArray); - return true; - } - int index = 0; - for (;;) { - Value& value = currentValue()[index++]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenArrayEnd); - - Token token; - // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); - } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); - if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); - } - if (token.type_ == tokenArrayEnd) - break; - } - return true; -} - -bool Reader::decodeNumber(Token& token) { - Value decoded; - if (!decodeNumber(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool Reader::decodeNumber(Token& token, Value& decoded) { - // Attempts to parse the number as an integer. If the number is - // 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) - ++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::maxLargestInt) + 1 - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; - Value::LargestUInt value = 0; - while (current < token.end_) { - Char c = *current++; - if (c < '0' || c > '9') - return decodeDouble(token, decoded); - Value::UInt digit(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 - // 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) { - return decodeDouble(token, decoded); - } - } - value = value * 10 + digit; - } - if (isNegative && value == maxIntegerValue) - decoded = Value::minLargestInt; - else if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) - decoded = Value::LargestInt(value); - else - decoded = value; - return true; -} - -bool Reader::decodeDouble(Token& token) { - Value decoded; - if (!decodeDouble(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool Reader::decodeDouble(Token& token, Value& decoded) { - double value = 0; - std::string buffer(token.start_, token.end_); - std::istringstream is(buffer); - if (!(is >> value)) - return addError("'" + std::string(token.start_, token.end_) + - "' is not a number.", - token); - decoded = value; - return true; -} - -bool Reader::decodeString(Token& token) { - std::string decoded_string; - if (!decodeString(token, decoded_string)) - return false; - Value decoded(decoded_string); - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool Reader::decodeString(Token& token, std::string& decoded) { - decoded.reserve(token.end_ - token.start_ - 2); - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - while (current != end) { - Char c = *current++; - if (c == '"') - break; - else if (c == '\\') { - if (current == end) - return addError("Empty escape sequence in string", token, current); - Char escape = *current++; - switch (escape) { - case '"': - decoded += '"'; - break; - case '/': - decoded += '/'; - break; - case '\\': - decoded += '\\'; - break; - case 'b': - decoded += '\b'; - break; - case 'f': - decoded += '\f'; - break; - case 'n': - decoded += '\n'; - break; - case 'r': - decoded += '\r'; - break; - case 't': - decoded += '\t'; - break; - case 'u': { - unsigned int unicode; - if (!decodeUnicodeCodePoint(token, current, end, unicode)) - return false; - decoded += codePointToUTF8(unicode); - } break; - default: - return addError("Bad escape sequence in string", token, current); - } - } else { - decoded += c; - } - } - return true; -} - -bool Reader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - - if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) - return false; - if (unicode >= 0xD800 && unicode <= 0xDBFF) { - // surrogate pairs - if (end - current < 6) - return addError( - "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; - if (*(current++) == '\\' && *(current++) == 'u') { - if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } else - return false; - } else - return addError("expecting another \\u token to begin the second half of " - "a unicode surrogate pair", - token, - current); - } - return true; -} - -bool Reader::decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - if (end - current < 4) - return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, - current); - unicode = 0; - for (int index = 0; index < 4; ++index) { - Char c = *current++; - unicode *= 16; - if (c >= '0' && c <= '9') - unicode += c - '0'; - else if (c >= 'a' && c <= 'f') - unicode += c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - unicode += c - 'A' + 10; - else - return addError( - "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); - } - return true; -} - -bool -Reader::addError(const std::string& message, Token& token, Location extra) { - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back(info); - return false; -} - -bool Reader::recoverFromError(TokenType skipUntilToken) { - int errorCount = int(errors_.size()); - Token skip; - for (;;) { - if (!readToken(skip)) - errors_.resize(errorCount); // discard errors caused by recovery - if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) - break; - } - errors_.resize(errorCount); - return false; -} - -bool Reader::addErrorAndRecover(const std::string& message, - Token& token, - TokenType skipUntilToken) { - addError(message, token); - return recoverFromError(skipUntilToken); -} - -Value& Reader::currentValue() { return *(nodes_.top()); } - -Reader::Char Reader::getNextChar() { - if (current_ == end_) - return 0; - return *current_++; -} - -void Reader::getLocationLineAndColumn(Location location, - int& line, - int& column) const { - Location current = begin_; - Location lastLineStart = current; - line = 0; - while (current < location && current != end_) { - Char c = *current++; - if (c == '\r') { - if (*current == '\n') - ++current; - lastLineStart = current; - ++line; - } else if (c == '\n') { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int(location - lastLineStart) + 1; - ++line; -} - -std::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); - return buffer; -} - -// Deprecated. Preserved for backward compatibility -std::string Reader::getFormatedErrorMessages() const { - return getFormattedErrorMessages(); -} - -std::string Reader::getFormattedErrorMessages() const { - std::string formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; - formattedMessage += - "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if (error.extra_) - formattedMessage += - "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; - } - return formattedMessage; -} - -std::vector Reader::getStructuredErrors() const { - std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; - Reader::StructuredError structured; - structured.offset_start = error.token_.start_ - begin_; - structured.offset_limit = error.token_.end_ - begin_; - structured.message = error.message_; - allErrors.push_back(structured); - } - return allErrors; -} - -bool Reader::pushError(const Value& value, const std::string& message) { - size_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 Reader::pushError(const Value& value, const std::string& message, const Value& extra) { - size_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 Reader::good() const { - return !errors_.size(); -} - -// exact copy of Features -class OurFeatures { -public: - static OurFeatures all(); - bool allowComments_; - bool strictRoot_; - bool allowDroppedNullPlaceholders_; - bool allowNumericKeys_; - bool allowSingleQuotes_; - bool failIfExtra_; - bool rejectDupKeys_; - bool allowSpecialFloats_; - int stackLimit_; -}; // OurFeatures - // exact copy of Implementation of class Features // //////////////////////////////// OurFeatures OurFeatures::all() { return OurFeatures(); } -// Implementation of class Reader -// //////////////////////////////// - -// exact copy of Reader, renamed to OurReader -class OurReader { -public: - typedef char Char; - typedef const Char* Location; - struct StructuredError { - size_t offset_start; - size_t offset_limit; - std::string message; - }; - - OurReader(OurFeatures const& features); - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments = true); - std::string getFormattedErrorMessages() const; - std::vector getStructuredErrors() const; - bool pushError(const Value& value, const std::string& message); - bool pushError(const Value& value, const std::string& message, const Value& extra); - bool good() const; - -private: - OurReader(OurReader const&); // no impl - void operator=(OurReader const&); // no impl - - enum TokenType { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - tokenNaN, - tokenPosInf, - tokenNegInf, - tokenArraySeparator, - tokenMemberSeparator, - tokenComment, - tokenError - }; - - class Token { - public: - TokenType type_; - Location start_; - Location end_; - }; - - class ErrorInfo { - public: - Token token_; - std::string message_; - Location extra_; - }; - - typedef std::deque Errors; - - bool readToken(Token& token); - void skipSpaces(); - bool match(Location pattern, int patternLength); - bool readComment(); - bool readCStyleComment(); - bool readCppStyleComment(); - bool readString(); - bool readStringSingleQuote(); - bool readNumber(bool checkInf); - bool readValue(); - bool readObject(Token& token); - bool readArray(Token& token); - bool decodeNumber(Token& token); - bool decodeNumber(Token& token, Value& decoded); - bool decodeString(Token& token); - bool decodeString(Token& token, std::string& decoded); - bool decodeDouble(Token& token); - bool decodeDouble(Token& token, Value& decoded); - 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 std::string& message, Token& token, Location extra = 0); - bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const std::string& message, - Token& token, - TokenType skipUntilToken); - void skipUntilSpace(); - Value& currentValue(); - Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - std::string getLocationLineAndColumn(Location location) const; - void addComment(Location begin, Location end, CommentPlacement placement); - void skipCommentTokens(Token& token); - - typedef std::stack Nodes; - Nodes nodes_; - Errors errors_; - std::string document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value* lastValue_; - std::string commentsBefore_; - int stackDepth_; - - OurFeatures const features_; - bool collectComments_; -}; // OurReader - -// complete copy of Read impl, for OurReader - -OurReader::OurReader(OurFeatures const& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), - stackDepth_(0), - features_(features), collectComments_() { -} - -bool OurReader::parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments) { - if (!features_.allowComments_) { - collectComments = false; - } - - begin_ = beginDoc; - end_ = endDoc; - collectComments_ = collectComments; - current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - commentsBefore_ = ""; - errors_.clear(); - while (!nodes_.empty()) - nodes_.pop(); - nodes_.push(&root); - - stackDepth_ = 0; - bool successful = readValue(); - Token token; - skipCommentTokens(token); - if (features_.failIfExtra_) { - if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { - addError("Extra non-whitespace after JSON value.", token); - return false; - } - } - if (collectComments_ && !commentsBefore_.empty()) - root.setComment(commentsBefore_, commentAfter); - if (features_.strictRoot_) { - if (!root.isArray() && !root.isObject()) { - // Set error location to start of doc, ideally should be first token found - // in doc - token.type_ = tokenError; - token.start_ = beginDoc; - token.end_ = endDoc; - addError( - "A valid JSON document must be either an array or an object value.", - token); - return false; - } - } - return successful; -} - -bool OurReader::readValue() { - if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); - ++stackDepth_; - Token token; - skipCommentTokens(token); - bool successful = true; - - if (collectComments_ && !commentsBefore_.empty()) { - currentValue().setComment(commentsBefore_, commentBefore); - commentsBefore_ = ""; - } - - switch (token.type_) { - case tokenObjectBegin: - successful = readObject(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenArrayBegin: - successful = readArray(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenNumber: - successful = decodeNumber(token); - break; - case tokenString: - successful = decodeString(token); - break; - case tokenTrue: - { - Value v(true); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { - Value v(false); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNaN: - { - Value v(std::numeric_limits::quiet_NaN()); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenPosInf: - { - Value v(std::numeric_limits::infinity()); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNegInf: - { - Value v(-std::numeric_limits::infinity()); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenArraySeparator: - case tokenObjectEnd: - case tokenArrayEnd: - if (features_.allowDroppedNullPlaceholders_) { - // "Un-read" the current token and mark the current value as a null - // token. - current_--; - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(current_ - begin_ - 1); - currentValue().setOffsetLimit(current_ - begin_); - break; - } // else, fall through ... - default: - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return addError("Syntax error: value, object or array expected.", token); - } - - if (collectComments_) { - lastValueEnd_ = current_; - lastValue_ = ¤tValue(); - } - - --stackDepth_; - return successful; -} - -void OurReader::skipCommentTokens(Token& token) { - if (features_.allowComments_) { - do { - readToken(token); - } while (token.type_ == tokenComment); - } else { - readToken(token); - } -} - -bool OurReader::readToken(Token& token) { - skipSpaces(); - token.start_ = current_; - Char c = getNextChar(); - bool ok = true; - switch (c) { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': - token.type_ = tokenString; - ok = readString(); - break; - case '\'': - if (features_.allowSingleQuotes_) { - token.type_ = tokenString; - ok = readStringSingleQuote(); - break; - } // else continue - case '/': - token.type_ = tokenComment; - ok = readComment(); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - token.type_ = tokenNumber; - readNumber(false); - break; - case '-': - if (readNumber(true)) { - token.type_ = tokenNumber; - } else { - token.type_ = tokenNegInf; - ok = features_.allowSpecialFloats_ && match("nfinity", 7); - } - break; - case 't': - token.type_ = tokenTrue; - ok = match("rue", 3); - break; - case 'f': - token.type_ = tokenFalse; - ok = match("alse", 4); - break; - case 'n': - token.type_ = tokenNull; - ok = match("ull", 3); - break; - case 'N': - if (features_.allowSpecialFloats_) { - token.type_ = tokenNaN; - ok = match("aN", 2); - } else { - ok = false; - } - break; - case 'I': - if (features_.allowSpecialFloats_) { - token.type_ = tokenPosInf; - ok = match("nfinity", 7); - } else { - ok = false; - } - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: - ok = false; - break; - } - if (!ok) - token.type_ = tokenError; - token.end_ = current_; - return true; -} - -void OurReader::skipSpaces() { - while (current_ != end_) { - Char c = *current_; - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') - ++current_; - else - break; - } -} - -bool OurReader::match(Location pattern, int patternLength) { - if (end_ - current_ < patternLength) - return false; - int index = patternLength; - while (index--) - if (current_[index] != pattern[index]) - return false; - current_ += patternLength; - return true; -} - -bool OurReader::readComment() { - Location commentBegin = current_ - 1; - Char c = getNextChar(); - bool successful = false; - if (c == '*') - successful = readCStyleComment(); - else if (c == '/') - successful = readCppStyleComment(); - if (!successful) - return false; - - if (collectComments_) { - CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) - placement = commentAfterOnSameLine; - } - - addComment(commentBegin, current_, placement); - } - return true; -} - -void -OurReader::addComment(Location begin, Location end, CommentPlacement placement) { - assert(collectComments_); - const std::string& normalized = normalizeEOL(begin, end); - if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); - lastValue_->setComment(normalized, placement); - } else { - commentsBefore_ += normalized; - } -} - -bool OurReader::readCStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '*' && *current_ == '/') - break; - } - return getNextChar() == '/'; -} - -bool OurReader::readCppStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '\n') - break; - if (c == '\r') { - // Consume DOS EOL. It will be normalized in addComment. - if (current_ != end_ && *current_ == '\n') - getNextChar(); - // Break on Moc OS 9 EOL. - break; - } - } - return true; -} - -bool OurReader::readNumber(bool checkInf) { - const char *p = current_; - if (checkInf && p != end_ && *p == 'I') { - current_ = ++p; - return false; - } - char c = '0'; // stopgap for already consumed character - // integral part - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - // fractional part - if (c == '.') { - c = (current_ = p) < end_ ? *p++ : 0; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - } - // exponential part - if (c == 'e' || c == 'E') { - c = (current_ = p) < end_ ? *p++ : 0; - if (c == '+' || c == '-') - c = (current_ = p) < end_ ? *p++ : 0; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - } - return true; -} -bool OurReader::readString() { - Char c = 0; - while (current_ != end_) { - c = getNextChar(); - if (c == '\\') - getNextChar(); - else if (c == '"') - break; - } - return c == '"'; -} - - -bool OurReader::readStringSingleQuote() { - Char c = 0; - while (current_ != end_) { - c = getNextChar(); - if (c == '\\') - getNextChar(); - else if (c == '\'') - break; - } - return c == '\''; -} - -bool OurReader::readObject(Token& tokenStart) { - Token tokenName; - std::string name; - Value init(objectValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.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 - return true; - name = ""; - if (tokenName.type_ == tokenString) { - if (!decodeString(tokenName, name)) - return recoverFromError(tokenObjectEnd); - } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { - Value numberName; - if (!decodeNumber(tokenName, numberName)) - return recoverFromError(tokenObjectEnd); - name = numberName.asString(); - } else { - break; - } - - 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)) { - std::string msg = "Duplicate key: '" + name + "'"; - return addErrorAndRecover( - msg, tokenName, tokenObjectEnd); - } - Value& value = currentValue()[name]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenObjectEnd); - - Token comma; - if (!readToken(comma) || - (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); - } - bool finalizeTokenOk = true; - while (comma.type_ == tokenComment && finalizeTokenOk) - finalizeTokenOk = readToken(comma); - if (comma.type_ == tokenObjectEnd) - return true; - } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); -} - -bool OurReader::readArray(Token& tokenStart) { - Value init(arrayValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); - skipSpaces(); - if (*current_ == ']') // empty array - { - Token endArray; - readToken(endArray); - return true; - } - int index = 0; - for (;;) { - Value& value = currentValue()[index++]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenArrayEnd); - - Token token; - // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); - } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); - if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); - } - if (token.type_ == tokenArrayEnd) - break; - } - return true; -} - -bool OurReader::decodeNumber(Token& token) { - Value decoded; - if (!decodeNumber(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool OurReader::decodeNumber(Token& token, Value& decoded) { - // Attempts to parse the number as an integer. If the number is - // 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) - ++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; - Value::LargestUInt value = 0; - while (current < token.end_) { - Char c = *current++; - if (c < '0' || c > '9') - return decodeDouble(token, decoded); - Value::UInt digit(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 - // 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) { - return decodeDouble(token, decoded); - } - } - value = value * 10 + digit; - } - if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) - decoded = Value::LargestInt(value); - else - decoded = value; - return true; -} - -bool OurReader::decodeDouble(Token& token) { - Value decoded; - if (!decodeDouble(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool OurReader::decodeDouble(Token& token, Value& decoded) { - double value = 0; - const int bufferSize = 32; - int count; - int length = int(token.end_ - token.start_); - - // Sanity check to avoid buffer overflow exploits. - if (length < 0) { - return addError("Unable to parse token length", token); - } - - // 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_, length); - buffer[length] = 0; - count = sscanf(buffer, format, &value); - } else { - std::string buffer(token.start_, token.end_); - count = sscanf(buffer.c_str(), format, &value); - } - - if (count != 1) - return addError("'" + std::string(token.start_, token.end_) + - "' is not a number.", - token); - decoded = value; - return true; -} - -bool OurReader::decodeString(Token& token) { - std::string decoded_string; - if (!decodeString(token, decoded_string)) - return false; - Value decoded(decoded_string); - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool OurReader::decodeString(Token& token, std::string& decoded) { - decoded.reserve(token.end_ - token.start_ - 2); - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - while (current != end) { - Char c = *current++; - if (c == '"') - break; - else if (c == '\\') { - if (current == end) - return addError("Empty escape sequence in string", token, current); - Char escape = *current++; - switch (escape) { - case '"': - decoded += '"'; - break; - case '/': - decoded += '/'; - break; - case '\\': - decoded += '\\'; - break; - case 'b': - decoded += '\b'; - break; - case 'f': - decoded += '\f'; - break; - case 'n': - decoded += '\n'; - break; - case 'r': - decoded += '\r'; - break; - case 't': - decoded += '\t'; - break; - case 'u': { - unsigned int unicode; - if (!decodeUnicodeCodePoint(token, current, end, unicode)) - return false; - decoded += codePointToUTF8(unicode); - } break; - default: - return addError("Bad escape sequence in string", token, current); - } - } else { - decoded += c; - } - } - return true; -} - -bool OurReader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - - if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) - return false; - if (unicode >= 0xD800 && unicode <= 0xDBFF) { - // surrogate pairs - if (end - current < 6) - return addError( - "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; - if (*(current++) == '\\' && *(current++) == 'u') { - if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } else - return false; - } else - return addError("expecting another \\u token to begin the second half of " - "a unicode surrogate pair", - token, - current); - } - return true; -} - -bool OurReader::decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - if (end - current < 4) - return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, - current); - unicode = 0; - for (int index = 0; index < 4; ++index) { - Char c = *current++; - unicode *= 16; - if (c >= '0' && c <= '9') - unicode += c - '0'; - else if (c >= 'a' && c <= 'f') - unicode += c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - unicode += c - 'A' + 10; - else - return addError( - "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); - } - return true; -} - -bool -OurReader::addError(const std::string& message, Token& token, Location extra) { - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back(info); - return false; -} - -bool OurReader::recoverFromError(TokenType skipUntilToken) { - int errorCount = int(errors_.size()); - Token skip; - for (;;) { - if (!readToken(skip)) - errors_.resize(errorCount); // discard errors caused by recovery - if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) - break; - } - errors_.resize(errorCount); - return false; -} - -bool OurReader::addErrorAndRecover(const std::string& message, - Token& token, - TokenType skipUntilToken) { - addError(message, token); - return recoverFromError(skipUntilToken); -} - -Value& OurReader::currentValue() { return *(nodes_.top()); } - -OurReader::Char OurReader::getNextChar() { - if (current_ == end_) - return 0; - return *current_++; -} - -void OurReader::getLocationLineAndColumn(Location location, - int& line, - int& column) const { - Location current = begin_; - Location lastLineStart = current; - line = 0; - while (current < location && current != end_) { - Char c = *current++; - if (c == '\r') { - if (*current == '\n') - ++current; - lastLineStart = current; - ++line; - } else if (c == '\n') { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int(location - lastLineStart) + 1; - ++line; -} - -std::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); - return buffer; -} - -std::string OurReader::getFormattedErrorMessages() const { - std::string formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; - formattedMessage += - "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if (error.extra_) - formattedMessage += - "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; - } - return formattedMessage; -} - -std::vector OurReader::getStructuredErrors() const { - std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; - OurReader::StructuredError structured; - structured.offset_start = error.token_.start_ - begin_; - structured.offset_limit = error.token_.end_ - begin_; - structured.message = error.message_; - allErrors.push_back(structured); - } - return allErrors; -} - -bool OurReader::pushError(const Value& value, const std::string& message) { - size_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 std::string& message, const Value& extra) { - size_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, std::string* errs) override { - bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); - if (errs) { - *errs = reader_.getFormattedErrorMessages(); - } - return ok; - } -}; - -CharReaderBuilder::CharReaderBuilder() -{ - setDefaults(&settings_); -} -CharReaderBuilder::~CharReaderBuilder() -{} -CharReader* CharReaderBuilder::newCharReader() const -{ - bool collectComments = settings_["collectComments"].asBool(); - OurFeatures features = OurFeatures::all(); - features.allowComments_ = settings_["allowComments"].asBool(); - features.strictRoot_ = settings_["strictRoot"].asBool(); - features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); - features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); - features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); - features.stackLimit_ = settings_["stackLimit"].asInt(); - features.failIfExtra_ = settings_["failIfExtra"].asBool(); - features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); - features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); - return new OurCharReader(collectComments, features); -} -static void getValidReaderKeys(std::set* 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 valid_keys; - getValidReaderKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - std::string const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } - } - return 0u == inv.size(); -} -Value& CharReaderBuilder::operator[](std::string key) -{ - return settings_[key]; -} -// static -void CharReaderBuilder::strictMode(Json::Value* settings) -{ -//! [CharReaderBuilderStrictMode] - (*settings)["allowComments"] = false; - (*settings)["strictRoot"] = true; - (*settings)["allowDroppedNullPlaceholders"] = false; - (*settings)["allowNumericKeys"] = false; - (*settings)["allowSingleQuotes"] = false; - (*settings)["stackLimit"] = 1000; - (*settings)["failIfExtra"] = true; - (*settings)["rejectDupKeys"] = true; - (*settings)["allowSpecialFloats"] = false; -//! [CharReaderBuilderStrictMode] -} -// static -void CharReaderBuilder::setDefaults(Json::Value* settings) -{ -//! [CharReaderBuilderDefaults] - (*settings)["collectComments"] = true; - (*settings)["allowComments"] = true; - (*settings)["strictRoot"] = false; - (*settings)["allowDroppedNullPlaceholders"] = false; - (*settings)["allowNumericKeys"] = false; - (*settings)["allowSingleQuotes"] = false; - (*settings)["stackLimit"] = 1000; - (*settings)["failIfExtra"] = false; - (*settings)["rejectDupKeys"] = false; - (*settings)["allowSpecialFloats"] = false; -//! [CharReaderBuilderDefaults] -} - -////////////////////////////////// -// global functions - -bool parseFromStream( - CharReader::Factory const& fact, std::istream& sin, - Value* root, std::string* errs) -{ - std::ostringstream ssin; - ssin << sin.rdbuf(); - std::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. - CharReaderPtr const reader(fact.newCharReader()); - return reader->parse(begin, end, root, errs); -} - -std::istream& operator>>(std::istream& sin, Value& root) { - CharReaderBuilder b; - std::string errs; - bool ok = parseFromStream(b, sin, &root, &errs); - if (!ok) { - fprintf(stderr, - "Error from reader: %s", - errs.c_str()); - - throwRuntimeError(errs); - } - return sin; -} - } // namespace Json diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 91b5f23f9..6cfe428f6 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -23,135 +23,6 @@ namespace Json { -// 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 -// 8 (instead of 4) as a bit of future-proofing. -#if defined(__ARMEL__) -#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) -#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(kNullRef); -const Value& Value::nullRef = null; - -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); - -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) -template -static inline bool InRange(double d, T min, U max) { - return d >= min && d <= max; -} -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) -static inline double integerToDouble(Json::UInt64 value) { - return static_cast(Int64(value / 2)) * 2.0 + Int64(value & 1); -} - -template static inline double integerToDouble(T value) { - return static_cast(value); -} - -template -static inline bool InRange(double d, T min, U max) { - return d >= integerToDouble(min) && d <= integerToDouble(max); -} -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - -/** Duplicates the specified string value. - * @param value Pointer to the string to duplicate. Must be zero-terminated if - * length is "unknown". - * @param length Length of the value. if equals to unknown, then it will be - * computed using strlen(value). - * @return Pointer on the duplicate instance of string. - */ -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 >= (size_t)Value::maxInt) - length = Value::maxInt - 1; - - char* newString = static_cast(malloc(length + 1)); - if (newString == NULL) { - throwRuntimeError( - "in Json::Value::duplicateStringValue(): " - "Failed to allocate string value buffer"); - } - memcpy(newString, value, length); - newString[length] = 0; - return newString; -} - -/* Record the length as a prefix. - */ -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 <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U, - "in Json::Value::duplicateAndPrefixStringValue(): " - "length too big for prefixing"); - unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; - char* newString = static_cast(malloc(actualLength)); - if (newString == 0) { - throwRuntimeError( - "in Json::Value::duplicateAndPrefixStringValue(): " - "Failed to allocate string value buffer"); - } - *reinterpret_cast(newString) = length; - memcpy(newString + sizeof(unsigned), value, length); - 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) -{ - if (!isPrefixed) { - *length = static_cast(strlen(prefixed)); - *value = prefixed; - } else { - *length = *reinterpret_cast(prefixed); - *value = prefixed + sizeof(unsigned); - } -} -/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). - */ -static inline void releaseStringValue(char* value) { free(value); } - -} // namespace Json - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ValueInternals... -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -#if !defined(JSON_IS_AMALGAMATION) - -#include "json_valueiterator.inl" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - Exception::Exception(std::string const& msg) : msg_(msg) {} @@ -175,1367 +46,4 @@ void throwLogicError(std::string const& msg) { throw LogicError(msg); } - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CommentInfo -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -Value::CommentInfo::CommentInfo() : comment_(0) {} - -Value::CommentInfo::~CommentInfo() { - if (comment_) - releaseStringValue(comment_); -} - -void Value::CommentInfo::setComment(const char* text, size_t len) { - if (comment_) { - releaseStringValue(comment_); - 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); -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CZString -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -// 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(char const* str, unsigned ulength, DuplicationPolicy allocate) - : cstr_(str) { - // allocate != duplicate - storage_.policy_ = allocate & 0x3; - storage_.length_ = ulength & 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_ = (other.cstr_ - ? (static_cast(other.storage_.policy_) == noDuplication - ? noDuplication : duplicate) - : static_cast(other.storage_.policy_)); - storage_.length_ = other.storage_.length_; -} - -#if JSON_HAS_RVALUE_REFERENCES -Value::CZString::CZString(CZString&& other) - : cstr_(other.cstr_), index_(other.index_) { - other.cstr_ = nullptr; -} -#endif - -Value::CZString::~CZString() { - if (cstr_ && storage_.policy_ == duplicate) - releaseStringValue(const_cast(cstr_)); -} - -void Value::CZString::swap(CZString& other) { - std::swap(cstr_, other.cstr_); - std::swap(index_, other.index_); -} - -Value::CZString& Value::CZString::operator=(CZString other) { - swap(other); - return *this; -} - -bool Value::CZString::operator<(const CZString& other) const { - 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(this_len, other_len); - int comp = memcmp(this->cstr_, other.cstr_, min_len); - 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; - // Assume both are strings. - unsigned this_len = this->storage_.length_; - unsigned other_len = other.storage_.length_; - if (this_len != other_len) return false; - int comp = memcmp(this->cstr_, other.cstr_, this_len); - return comp == 0; -} - -ArrayIndex Value::CZString::index() const { return index_; } - -//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; } - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::Value -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -/*! \internal Default constructor initialization must be equivalent to: - * memset( this, 0, sizeof(Value) ) - * This optimization is used in ValueInternalMap fast allocator. - */ -Value::Value(ValueType vtype) { - initBasic(vtype); - switch (vtype) { - case nullValue: - break; - case intValue: - case uintValue: - value_.int_ = 0; - break; - case realValue: - value_.real_ = 0.0; - break; - case stringValue: - value_.string_ = 0; - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(); - break; - case booleanValue: - value_.bool_ = false; - break; - default: - JSON_ASSERT_UNREACHABLE; - } -} - -Value::Value(Int value) { - initBasic(intValue); - value_.int_ = value; -} - -Value::Value(UInt value) { - initBasic(uintValue); - value_.uint_ = value; -} -#if defined(JSON_HAS_INT64) -Value::Value(Int64 value) { - initBasic(intValue); - value_.int_ = value; -} -Value::Value(UInt64 value) { - initBasic(uintValue); - value_.uint_ = value; -} -#endif // defined(JSON_HAS_INT64) - -Value::Value(double value) { - initBasic(realValue); - value_.real_ = value; -} - -Value::Value(const char* value) { - initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); -} - -Value::Value(const char* beginValue, const char* endValue) { - initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); -} - -Value::Value(const std::string& value) { - initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); -} - -Value::Value(const StaticString& value) { - initBasic(stringValue); - value_.string_ = const_cast(value.c_str()); -} - -#ifdef JSON_USE_CPPTL -Value::Value(const CppTL::ConstString& value) { - initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast(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_)); - } - } -} - -#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_) - releaseStringValue(value_.string_); - break; - case arrayValue: - case objectValue: - delete value_.map_; - break; - default: - JSON_ASSERT_UNREACHABLE; - } - - if (comments_) - delete[] comments_; -} - -Value& Value::operator=(Value other) { - swap(other); - return *this; -} - -void Value::swapPayload(Value& other) { - ValueType temp = type_; - type_ = other.type_; - other.type_ = temp; - std::swap(value_, other.value_); - int temp2 = allocated_; - allocated_ = other.allocated_; - other.allocated_ = temp2 & 0x1; -} - -void Value::swap(Value& other) { - swapPayload(other); - std::swap(comments_, other.comments_); - std::swap(start_, other.start_); - std::swap(limit_, other.limit_); -} - -ValueType Value::type() const { return type_; } - -int Value::compare(const Value& other) const { - if (*this < other) - return -1; - if (*this > other) - return 1; - return 0; -} - -bool Value::operator<(const Value& other) const { - int typeDelta = type_ - other.type_; - if (typeDelta) - return typeDelta < 0 ? true : false; - switch (type_) { - case nullValue: - return false; - case intValue: - return value_.int_ < other.value_.int_; - case uintValue: - return value_.uint_ < other.value_.uint_; - case realValue: - 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; - } - 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); - unsigned min_len = std::min(this_len, other_len); - int comp = memcmp(this_str, other_str, min_len); - 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; - return (*value_.map_) < (*other.value_.map_); - } - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable -} - -bool Value::operator<=(const Value& other) const { return !(other < *this); } - -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) - return false; - switch (type_) { - case nullValue: - return true; - case intValue: - return value_.int_ == other.value_.int_; - case uintValue: - return value_.uint_ == other.value_.uint_; - case realValue: - return value_.real_ == other.value_.real_; - case booleanValue: - return value_.bool_ == other.value_.bool_; - case stringValue: - { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { - 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; - int comp = memcmp(this_str, other_str, this_len); - return comp == 0; - } - case arrayValue: - case objectValue: - return value_.map_->size() == other.value_.map_->size() && - (*value_.map_) == (*other.value_.map_); - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable -} - -bool Value::operator!=(const Value& other) const { return !(*this == other); } - -const char* Value::asCString() const { - 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); - return this_str; -} - -bool Value::getString(char const** str, char const** cend) const { - if (type_ != stringValue) return false; - if (value_.string_ == 0) return false; - unsigned length; - decodePrefixedString(this->allocated_, this->value_.string_, &length, str); - *cend = *str + length; - return true; -} - -std::string Value::asString() const { - switch (type_) { - case nullValue: - return ""; - case stringValue: - { - if (value_.string_ == 0) return ""; - unsigned this_len; - char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - return std::string(this_str, this_len); - } - case booleanValue: - return value_.bool_ ? "true" : "false"; - case intValue: - return valueToString(value_.int_); - case uintValue: - return valueToString(value_.uint_); - case realValue: - return valueToString(value_.real_); - default: - JSON_FAIL_MESSAGE("Type is not convertible to string"); - } -} - -#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_) { - case intValue: - JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); - return Int(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); - return Int(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), - "double out of Int range"); - return Int(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to Int."); -} - -Value::UInt Value::asUInt() const { - switch (type_) { - case intValue: - JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); - return UInt(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); - return UInt(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), - "double out of UInt range"); - return UInt(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to UInt."); -} - -#if defined(JSON_HAS_INT64) - -Value::Int64 Value::asInt64() const { - switch (type_) { - case intValue: - return Int64(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); - return Int64(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), - "double out of Int64 range"); - return Int64(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to Int64."); -} - -Value::UInt64 Value::asUInt64() const { - switch (type_) { - case intValue: - JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); - return UInt64(value_.int_); - case uintValue: - return UInt64(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), - "double out of UInt64 range"); - return UInt64(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); -} -#endif // if defined(JSON_HAS_INT64) - -LargestInt Value::asLargestInt() const { -#if defined(JSON_NO_INT64) - return asInt(); -#else - return asInt64(); -#endif -} - -LargestUInt Value::asLargestUInt() const { -#if defined(JSON_NO_INT64) - return asUInt(); -#else - return asUInt64(); -#endif -} - -double Value::asDouble() const { - switch (type_) { - case intValue: - return static_cast(value_.int_); - case uintValue: -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return static_cast(value_.uint_); -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return integerToDouble(value_.uint_); -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - case realValue: - return value_.real_; - case nullValue: - return 0.0; - case booleanValue: - return value_.bool_ ? 1.0 : 0.0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to double."); -} - -float Value::asFloat() const { - switch (type_) { - case intValue: - return static_cast(value_.int_); - case uintValue: -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return static_cast(value_.uint_); -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return integerToDouble(value_.uint_); -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - case realValue: - return static_cast(value_.real_); - case nullValue: - return 0.0; - case booleanValue: - return value_.bool_ ? 1.0f : 0.0f; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to float."); -} - -bool Value::asBool() const { - switch (type_) { - case booleanValue: - return value_.bool_; - case nullValue: - return false; - case intValue: - return value_.int_ ? true : false; - case uintValue: - return value_.uint_ ? true : false; - case realValue: - // This is kind of strange. Not recommended. - return (value_.real_ != 0.0) ? true : false; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to bool."); -} - -bool Value::isConvertibleTo(ValueType other) const { - switch (other) { - case nullValue: - return (isNumeric() && asDouble() == 0.0) || - (type_ == booleanValue && value_.bool_ == false) || - (type_ == stringValue && asString() == "") || - (type_ == arrayValue && value_.map_->size() == 0) || - (type_ == objectValue && value_.map_->size() == 0) || - type_ == nullValue; - case intValue: - return isInt() || - (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; - case realValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; - case booleanValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; - case stringValue: - return isNumeric() || type_ == booleanValue || type_ == stringValue || - type_ == nullValue; - case arrayValue: - return type_ == arrayValue || type_ == nullValue; - case objectValue: - return type_ == objectValue || type_ == nullValue; - } - JSON_ASSERT_UNREACHABLE; - return false; -} - -/// Number of values in array or object -ArrayIndex Value::size() const { - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - case stringValue: - return 0; - case arrayValue: // size of the array is highest index + 1 - if (!value_.map_->empty()) { - ObjectValues::const_iterator itLast = value_.map_->end(); - --itLast; - return (*itLast).first.index() + 1; - } - return 0; - case objectValue: - return ArrayIndex(value_.map_->size()); - } - JSON_ASSERT_UNREACHABLE; - return 0; // unreachable; -} - -bool Value::empty() const { - if (isNull() || isArray() || isObject()) - return size() == 0u; - else - return false; -} - -bool Value::operator!() const { return isNull(); } - -void Value::clear() { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || - type_ == objectValue, - "in Json::Value::clear(): requires complex value"); - start_ = 0; - limit_ = 0; - switch (type_) { - case arrayValue: - case objectValue: - value_.map_->clear(); - break; - default: - break; - } -} - -void Value::resize(ArrayIndex newSize) { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, - "in Json::Value::resize(): requires arrayValue"); - if (type_ == nullValue) - *this = Value(arrayValue); - ArrayIndex oldSize = size(); - if (newSize == 0) - clear(); - else if (newSize > oldSize) - (*this)[newSize - 1]; - else { - for (ArrayIndex index = newSize; index < oldSize; ++index) { - value_.map_->erase(index); - } - assert(size() == newSize); - } -} - -Value& Value::operator[](ArrayIndex index) { - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, - "in Json::Value::operator[](ArrayIndex): requires arrayValue"); - if (type_ == nullValue) - *this = Value(arrayValue); - CZString key(index); - ObjectValues::iterator it = value_.map_->lower_bound(key); - if (it != value_.map_->end() && (*it).first == key) - return (*it).second; - - ObjectValues::value_type defaultValue(key, nullRef); - it = value_.map_->insert(it, defaultValue); - return (*it).second; -} - -Value& Value::operator[](int index) { - JSON_ASSERT_MESSAGE( - index >= 0, - "in Json::Value::operator[](int index): index cannot be negative"); - return (*this)[ArrayIndex(index)]; -} - -const Value& Value::operator[](ArrayIndex index) const { - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, - "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); - if (type_ == nullValue) - return nullRef; - CZString key(index); - ObjectValues::const_iterator it = value_.map_->find(key); - if (it == value_.map_->end()) - return nullRef; - return (*it).second; -} - -const Value& Value::operator[](int index) const { - JSON_ASSERT_MESSAGE( - index >= 0, - "in Json::Value::operator[](int index) const: index cannot be negative"); - return (*this)[ArrayIndex(index)]; -} - -void Value::initBasic(ValueType vtype, bool allocated) { - type_ = vtype; - allocated_ = allocated; - comments_ = 0; - start_ = 0; - limit_ = 0; -} - -// 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, - "in Json::Value::resolveReference(): requires objectValue"); - if (type_ == nullValue) - *this = Value(objectValue); - CZString actualKey( - key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); - if (it != value_.map_->end() && (*it).first == actualKey) - return (*it).second; - - ObjectValues::value_type defaultValue(actualKey, nullRef); - it = value_.map_->insert(it, defaultValue); - Value& value = (*it).second; - return value; -} - -// @param key is not null-terminated. -Value& Value::resolveReference(char const* key, char const* cend) -{ - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, - "in Json::Value::resolveReference(key, end): requires objectValue"); - if (type_ == nullValue) - *this = Value(objectValue); - CZString actualKey( - key, static_cast(cend-key), CZString::duplicateOnCopy); - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); - if (it != value_.map_->end() && (*it).first == actualKey) - return (*it).second; - - ObjectValues::value_type defaultValue(actualKey, nullRef); - it = value_.map_->insert(it, defaultValue); - Value& value = (*it).second; - return value; -} - -Value Value::get(ArrayIndex index, const Value& defaultValue) const { - const Value* value = &((*this)[index]); - return value == &nullRef ? defaultValue : *value; -} - -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(cend-key), CZString::noDuplication); - ObjectValues::const_iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) return NULL; - return &(*it).second; -} -const Value& Value::operator[](const char* key) const -{ - Value const* found = find(key, key + strlen(key)); - if (!found) return nullRef; - return *found; -} -Value const& Value::operator[](std::string const& key) const -{ - Value const* found = find(key.data(), key.data() + key.length()); - if (!found) return nullRef; - return *found; -} - -Value& Value::operator[](const char* key) { - return resolveReference(key, key + strlen(key)); -} - -Value& Value::operator[](const std::string& key) { - return resolveReference(key.data(), key.data() + key.length()); -} - -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 nullRef; - return *found; -} -#endif - -Value& Value::append(const Value& value) { return (*this)[size()] = value; } - -Value Value::get(char const* key, char const* cend, Value const& defaultValue) const -{ - Value const* found = find(key, cend); - return !found ? defaultValue : *found; -} -Value Value::get(char const* key, Value const& defaultValue) const -{ - return get(key, key + strlen(key), defaultValue); -} -Value Value::get(std::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) { - return false; - } - CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); - ObjectValues::iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) - return false; - *removed = it->second; - value_.map_->erase(it); - return true; -} -bool Value::removeMember(const char* key, Value* removed) -{ - return removeMember(key, key + strlen(key), removed); -} -bool Value::removeMember(std::string const& key, Value* removed) -{ - return removeMember(key.data(), key.data() + key.length(), removed); -} -Value Value::removeMember(const char* key) -{ - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, - "in Json::Value::removeMember(): requires objectValue"); - if (type_ == nullValue) - return nullRef; - - Value removed; // null - removeMember(key, key + strlen(key), &removed); - return removed; // still null if removeMember() did nothing -} -Value Value::removeMember(const std::string& key) -{ - return removeMember(key.c_str()); -} - -bool Value::removeIndex(ArrayIndex index, Value* removed) { - if (type_ != arrayValue) { - return false; - } - CZString key(index); - ObjectValues::iterator it = value_.map_->find(key); - if (it == value_.map_->end()) { - return false; - } - *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){ - 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); - 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); -} -#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 -{ - return isMember(key, key + strlen(key)); -} -bool Value::isMember(std::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, - "in Json::Value::getMemberNames(), value must be objectValue"); - 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(std::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() ), -// MemberNamesTransform() ) ); -// } -// return EnumMemberNames(); -//} -// -// -// EnumValues -// Value::enumValues() const -//{ -// if ( type_ == objectValue || type_ == arrayValue ) -// return CppTL::Enum::anyValues( *(value_.map_), -// CppTL::Type() ); -// 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::isBool() const { return type_ == booleanValue; } - -bool Value::isInt() const { - switch (type_) { - case intValue: - return value_.int_ >= minInt && value_.int_ <= maxInt; - case uintValue: - return value_.uint_ <= UInt(maxInt); - case realValue: - return value_.real_ >= minInt && value_.real_ <= maxInt && - IsIntegral(value_.real_); - default: - break; - } - return false; -} - -bool Value::isUInt() const { - switch (type_) { - case intValue: - return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); - case uintValue: - return value_.uint_ <= maxUInt; - case realValue: - return value_.real_ >= 0 && value_.real_ <= maxUInt && - IsIntegral(value_.real_); - default: - break; - } - return false; -} - -bool Value::isInt64() const { -#if defined(JSON_HAS_INT64) - switch (type_) { - case intValue: - return true; - case uintValue: - return value_.uint_ <= UInt64(maxInt64); - case realValue: - // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a - // double, so double(maxInt64) will be rounded up to 2^63. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= double(minInt64) && - value_.real_ < double(maxInt64) && IsIntegral(value_.real_); - default: - break; - } -#endif // JSON_HAS_INT64 - return false; -} - -bool Value::isUInt64() const { -#if defined(JSON_HAS_INT64) - switch (type_) { - case intValue: - return value_.int_ >= 0; - case uintValue: - return true; - case realValue: - // 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_ >= 0 && value_.real_ < maxUInt64AsDouble && - IsIntegral(value_.real_); - default: - break; - } -#endif // JSON_HAS_INT64 - return false; -} - -bool Value::isIntegral() const { -#if defined(JSON_HAS_INT64) - return isInt64() || isUInt64(); -#else - return isInt() || isUInt(); -#endif -} - -bool Value::isDouble() const { return type_ == realValue || isIntegral(); } - -bool Value::isNumeric() const { return isIntegral() || isDouble(); } - -bool Value::isString() const { return type_ == stringValue; } - -bool Value::isArray() const { return type_ == arrayValue; } - -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); -} - -void Value::setComment(const char* comment, CommentPlacement placement) { - setComment(comment, strlen(comment), placement); -} - -void Value::setComment(const std::string& comment, CommentPlacement placement) { - setComment(comment.c_str(), comment.length(), placement); -} - -bool Value::hasComment(CommentPlacement placement) const { - return comments_ != 0 && comments_[placement].comment_ != 0; -} - -std::string Value::getComment(CommentPlacement placement) const { - if (hasComment(placement)) - return comments_[placement].comment_; - return ""; -} - -void Value::setOffsetStart(size_t start) { start_ = start; } - -void Value::setOffsetLimit(size_t limit) { limit_ = limit; } - -size_t Value::getOffsetStart() const { return start_; } - -size_t Value::getOffsetLimit() const { return limit_; } - -std::string Value::toStyledString() const { - StyledWriter writer; - return writer.write(*this); -} - -Value::const_iterator Value::begin() const { - switch (type_) { - case arrayValue: - case objectValue: - if (value_.map_) - return const_iterator(value_.map_->begin()); - break; - default: - break; - } - return const_iterator(); -} - -Value::const_iterator Value::end() const { - switch (type_) { - case arrayValue: - case objectValue: - if (value_.map_) - return const_iterator(value_.map_->end()); - break; - default: - break; - } - return const_iterator(); -} - -Value::iterator Value::begin() { - switch (type_) { - case arrayValue: - case objectValue: - if (value_.map_) - return iterator(value_.map_->begin()); - break; - default: - break; - } - return iterator(); -} - -Value::iterator Value::end() { - switch (type_) { - case arrayValue: - case objectValue: - if (value_.map_) - return iterator(value_.map_->end()); - break; - default: - break; - } - return iterator(); -} - -// class PathArgument -// ////////////////////////////////////////////////////////////////// - -PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} - -PathArgument::PathArgument(ArrayIndex index) - : key_(), index_(index), kind_(kindIndex) {} - -PathArgument::PathArgument(const char* key) - : key_(key), index_(), kind_(kindKey) {} - -PathArgument::PathArgument(const std::string& key) - : key_(key.c_str()), index_(), kind_(kindKey) {} - -// class Path -// ////////////////////////////////////////////////////////////////// - -Path::Path(const std::string& path, - const PathArgument& a1, - const PathArgument& a2, - const PathArgument& a3, - const PathArgument& a4, - const PathArgument& a5) { - InArgs in; - in.push_back(&a1); - in.push_back(&a2); - in.push_back(&a3); - in.push_back(&a4); - in.push_back(&a5); - makePath(path, in); -} - -void Path::makePath(const std::string& path, const InArgs& in) { - const char* current = path.c_str(); - const char* end = current + path.length(); - InArgs::const_iterator itInArg = in.begin(); - while (current != end) { - if (*current == '[') { - ++current; - if (*current == '%') - addPathInArg(path, in, itInArg, PathArgument::kindIndex); - else { - ArrayIndex index = 0; - for (; current != end && *current >= '0' && *current <= '9'; ++current) - index = index * 10 + ArrayIndex(*current - '0'); - args_.push_back(index); - } - if (current == end || *current++ != ']') - invalidPath(path, int(current - path.c_str())); - } else if (*current == '%') { - addPathInArg(path, in, itInArg, PathArgument::kindKey); - ++current; - } else if (*current == '.') { - ++current; - } else { - const char* beginName = current; - while (current != end && !strchr("[.", *current)) - ++current; - args_.push_back(std::string(beginName, current)); - } - } -} - -void Path::addPathInArg(const std::string& /*path*/, - const InArgs& in, - InArgs::const_iterator& itInArg, - PathArgument::Kind kind) { - if (itInArg == in.end()) { - // Error: missing argument %d - } else if ((*itInArg)->kind_ != kind) { - // Error: bad argument type - } else { - args_.push_back(**itInArg); - } -} - -void Path::invalidPath(const std::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; - if (arg.kind_ == PathArgument::kindIndex) { - if (!node->isArray() || !node->isValidIndex(arg.index_)) { - // Error: unable to resolve path (array value expected at position... - } - node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { - if (!node->isObject()) { - // Error: unable to resolve path (object value expected at position...) - } - node = &((*node)[arg.key_]); - if (node == &Value::nullRef) { - // Error: unable to resolve path (object has no member named '' at - // position...) - } - } - } - return *node; -} - -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; - if (arg.kind_ == PathArgument::kindIndex) { - if (!node->isArray() || !node->isValidIndex(arg.index_)) - return defaultValue; - node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { - if (!node->isObject()) - return defaultValue; - node = &((*node)[arg.key_]); - if (node == &Value::nullRef) - return defaultValue; - } - } - return *node; -} - -Value& Path::make(Value& root) const { - Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; - if (arg.kind_ == PathArgument::kindIndex) { - if (!node->isArray()) { - // Error: node is not an array at position ... - } - node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { - if (!node->isObject()) { - // Error: node is not an object at position... - } - node = &((*node)[arg.key_]); - } - } - return *node; -} - } // namespace Json diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index a5d0bba2b..479533371 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -97,138 +97,6 @@ static bool containsControlCharacter0(const char* str, unsigned len) { return false; } -std::string valueToString(LargestInt value) { - UIntToStringBuffer buffer; - char* current = buffer + sizeof(buffer); - if (value == Value::minLargestInt) { - uintToString(LargestUInt(Value::maxLargestInt) + 1, current); - *--current = '-'; - } else if (value < 0) { - uintToString(LargestUInt(-value), current); - *--current = '-'; - } else { - uintToString(LargestUInt(value), current); - } - assert(current >= buffer); - return current; -} - -std::string valueToString(LargestUInt value) { - UIntToStringBuffer buffer; - char* current = buffer + sizeof(buffer); - uintToString(value, current); - assert(current >= buffer); - return current; -} - -#if defined(JSON_HAS_INT64) - -std::string valueToString(Int value) { - return valueToString(LargestInt(value)); -} - -std::string valueToString(UInt value) { - return valueToString(LargestUInt(value)); -} - -#endif // # if defined(JSON_HAS_INT64) - -std::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[32]; - int len = -1; - - char formatString[6]; - sprintf(formatString, "%%.%dg", precision); - - // Print into the buffer. We need not request the alternative representation - // that always has a decimal point because JSON doesn't distingish the - // concepts of reals and integers. - if (isfinite(value)) { - len = snprintf(buffer, sizeof(buffer), formatString, value); - } 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"); - } - // For those, we do not need to call fixNumLoc, but it is fast. - } - assert(len >= 0); - fixNumericLocale(buffer, buffer + len); - return buffer; -} - -std::string valueToString(double value) { return valueToString(value, false, 17); } - -std::string valueToString(bool value) { return value ? "true" : "false"; } - -std::string valueToQuotedString(const char* value) { - if (value == NULL) - return ""; - // Not sure how to handle unicode... - if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && - !containsControlCharacter(value)) - return std::string("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = - strlen(value) * 2 + 3; // allescaped+quotes+NULL - std::string result; - result.reserve(maxsize); // to avoid lots of mallocs - result += "\""; - for (const char* c = value; *c != 0; ++c) { - switch (*c) { - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - // case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something. - // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); - result += oss.str(); - } else { - result += *c; - } - break; - } - } - result += "\""; - return result; -} - // https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp static char const* strnpbrk(char const* s, char const* accept, size_t n) { assert((s || !n) && accept); @@ -244,971 +112,5 @@ static char const* strnpbrk(char const* s, char const* accept, size_t n) { } return NULL; } -static std::string valueToQuotedStringN(const char* value, unsigned length) { - if (value == NULL) - return ""; - // Not sure how to handle unicode... - if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && - !containsControlCharacter0(value, length)) - return std::string("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = - length * 2 + 3; // allescaped+quotes+NULL - std::string result; - result.reserve(maxsize); // to avoid lots of mallocs - result += "\""; - char const* end = value + length; - for (const char* c = value; c != end; ++c) { - switch (*c) { - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - // case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something.) - // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); - result += oss.str(); - } else { - result += *c; - } - break; - } - } - result += "\""; - return result; -} - -// Class Writer -// ////////////////////////////////////////////////////////////////// -Writer::~Writer() {} - -// Class FastWriter -// ////////////////////////////////////////////////////////////////// - -FastWriter::FastWriter() - : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), - omitEndingLineFeed_(false) {} - -void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } - -void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } - -void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } - -std::string FastWriter::write(const Value& root) { - document_ = ""; - writeValue(root); - if (!omitEndingLineFeed_) - document_ += "\n"; - return document_; -} - -void FastWriter::writeValue(const Value& value) { - switch (value.type()) { - case nullValue: - if (!dropNullPlaceholders_) - document_ += "null"; - break; - case intValue: - document_ += valueToString(value.asLargestInt()); - break; - case uintValue: - document_ += valueToString(value.asLargestUInt()); - break; - case realValue: - document_ += valueToString(value.asDouble()); - break; - case stringValue: - { - // Is NULL possible for value.string_? - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); - break; - } - case booleanValue: - document_ += valueToString(value.asBool()); - break; - case arrayValue: { - document_ += '['; - int size = value.size(); - for (int index = 0; index < size; ++index) { - if (index > 0) - document_ += ','; - writeValue(value[index]); - } - document_ += ']'; - } break; - case objectValue: { - Value::Members members(value.getMemberNames()); - document_ += '{'; - for (Value::Members::iterator it = members.begin(); it != members.end(); - ++it) { - const std::string& name = *it; - if (it != members.begin()) - document_ += ','; - document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); - document_ += yamlCompatiblityEnabled_ ? ": " : ":"; - writeValue(value[name]); - } - document_ += '}'; - } break; - } -} - -// Class StyledWriter -// ////////////////////////////////////////////////////////////////// - -StyledWriter::StyledWriter() - : rightMargin_(74), indentSize_(3), addChildValues_() {} - -std::string StyledWriter::write(const Value& root) { - document_ = ""; - addChildValues_ = false; - indentString_ = ""; - writeCommentBeforeValue(root); - writeValue(root); - writeCommentAfterValueOnSameLine(root); - document_ += "\n"; - return document_; -} - -void StyledWriter::writeValue(const Value& value) { - switch (value.type()) { - case nullValue: - pushValue("null"); - break; - case intValue: - pushValue(valueToString(value.asLargestInt())); - break; - case uintValue: - pushValue(valueToString(value.asLargestUInt())); - break; - case realValue: - pushValue(valueToString(value.asDouble())); - break; - case stringValue: - { - // Is NULL possible for value.string_? - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); - break; - } - case booleanValue: - pushValue(valueToString(value.asBool())); - break; - case arrayValue: - writeArrayValue(value); - break; - case objectValue: { - Value::Members members(value.getMemberNames()); - if (members.empty()) - pushValue("{}"); - else { - writeWithIndent("{"); - indent(); - Value::Members::iterator it = members.begin(); - for (;;) { - const std::string& name = *it; - const Value& childValue = value[name]; - writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); - document_ += " : "; - writeValue(childValue); - if (++it == members.end()) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - document_ += ','; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("}"); - } - } break; - } -} - -void StyledWriter::writeArrayValue(const Value& value) { - unsigned size = value.size(); - if (size == 0) - pushValue("[]"); - else { - bool isArrayMultiLine = isMultineArray(value); - if (isArrayMultiLine) { - writeWithIndent("["); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index = 0; - for (;;) { - const Value& childValue = value[index]; - writeCommentBeforeValue(childValue); - if (hasChildValue) - writeWithIndent(childValues_[index]); - else { - writeIndent(); - writeValue(childValue); - } - if (++index == size) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - document_ += ','; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("]"); - } else // output on a single line - { - assert(childValues_.size() == size); - document_ += "[ "; - for (unsigned index = 0; index < size; ++index) { - if (index > 0) - document_ += ", "; - document_ += childValues_[index]; - } - document_ += " ]"; - } - } -} - -bool StyledWriter::isMultineArray(const Value& value) { - int size = value.size(); - bool isMultiLine = size * 3 >= rightMargin_; - childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { - const Value& childValue = value[index]; - isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); - } - if (!isMultiLine) // check if line length > max line length - { - childValues_.reserve(size); - addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (int index = 0; index < size; ++index) { - if (hasCommentForValue(value[index])) { - isMultiLine = true; - } - writeValue(value[index]); - lineLength += int(childValues_[index].length()); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - -void StyledWriter::pushValue(const std::string& value) { - if (addChildValues_) - childValues_.push_back(value); - else - document_ += value; -} - -void StyledWriter::writeIndent() { - if (!document_.empty()) { - char last = document_[document_.length() - 1]; - if (last == ' ') // already indented - return; - if (last != '\n') // Comments may add new-line - document_ += '\n'; - } - document_ += indentString_; -} - -void StyledWriter::writeWithIndent(const std::string& value) { - writeIndent(); - document_ += value; -} - -void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } - -void StyledWriter::unindent() { - assert(int(indentString_.size()) >= indentSize_); - indentString_.resize(indentString_.size() - indentSize_); -} - -void StyledWriter::writeCommentBeforeValue(const Value& root) { - if (!root.hasComment(commentBefore)) - return; - - document_ += "\n"; - writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); - while (iter != comment.end()) { - document_ += *iter; - if (*iter == '\n' && - (iter != comment.end() && *(iter + 1) == '/')) - writeIndent(); - ++iter; - } - - // Comments are stripped of trailing newlines, so add one here - document_ += "\n"; -} - -void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { - if (root.hasComment(commentAfterOnSameLine)) - document_ += " " + root.getComment(commentAfterOnSameLine); - - if (root.hasComment(commentAfter)) { - document_ += "\n"; - document_ += root.getComment(commentAfter); - document_ += "\n"; - } -} - -bool StyledWriter::hasCommentForValue(const Value& value) { - return value.hasComment(commentBefore) || - value.hasComment(commentAfterOnSameLine) || - value.hasComment(commentAfter); -} - -// Class StyledStreamWriter -// ////////////////////////////////////////////////////////////////// - -StyledStreamWriter::StyledStreamWriter(std::string indentation) - : document_(NULL), rightMargin_(74), indentation_(indentation), - addChildValues_() {} - -void StyledStreamWriter::write(std::ostream& out, const Value& root) { - document_ = &out; - addChildValues_ = false; - indentString_ = ""; - indented_ = true; - writeCommentBeforeValue(root); - if (!indented_) writeIndent(); - indented_ = true; - writeValue(root); - writeCommentAfterValueOnSameLine(root); - *document_ << "\n"; - document_ = NULL; // Forget the stream, for safety. -} - -void StyledStreamWriter::writeValue(const Value& value) { - switch (value.type()) { - case nullValue: - pushValue("null"); - break; - case intValue: - pushValue(valueToString(value.asLargestInt())); - break; - case uintValue: - pushValue(valueToString(value.asLargestUInt())); - break; - case realValue: - pushValue(valueToString(value.asDouble())); - break; - case stringValue: - { - // Is NULL possible for value.string_? - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); - break; - } - case booleanValue: - pushValue(valueToString(value.asBool())); - break; - case arrayValue: - writeArrayValue(value); - break; - case objectValue: { - Value::Members members(value.getMemberNames()); - if (members.empty()) - pushValue("{}"); - else { - writeWithIndent("{"); - indent(); - Value::Members::iterator it = members.begin(); - for (;;) { - const std::string& name = *it; - const Value& childValue = value[name]; - writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); - *document_ << " : "; - writeValue(childValue); - if (++it == members.end()) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("}"); - } - } break; - } -} - -void StyledStreamWriter::writeArrayValue(const Value& value) { - unsigned size = value.size(); - if (size == 0) - pushValue("[]"); - else { - bool isArrayMultiLine = isMultineArray(value); - if (isArrayMultiLine) { - writeWithIndent("["); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index = 0; - for (;;) { - const Value& childValue = value[index]; - writeCommentBeforeValue(childValue); - if (hasChildValue) - writeWithIndent(childValues_[index]); - else { - if (!indented_) writeIndent(); - indented_ = true; - writeValue(childValue); - indented_ = false; - } - if (++index == size) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("]"); - } else // output on a single line - { - assert(childValues_.size() == size); - *document_ << "[ "; - for (unsigned index = 0; index < size; ++index) { - if (index > 0) - *document_ << ", "; - *document_ << childValues_[index]; - } - *document_ << " ]"; - } - } -} - -bool StyledStreamWriter::isMultineArray(const Value& value) { - int size = value.size(); - bool isMultiLine = size * 3 >= rightMargin_; - childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { - const Value& childValue = value[index]; - isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); - } - if (!isMultiLine) // check if line length > max line length - { - childValues_.reserve(size); - addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (int index = 0; index < size; ++index) { - if (hasCommentForValue(value[index])) { - isMultiLine = true; - } - writeValue(value[index]); - lineLength += int(childValues_[index].length()); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - -void StyledStreamWriter::pushValue(const std::string& value) { - if (addChildValues_) - childValues_.push_back(value); - else - *document_ << value; -} - -void StyledStreamWriter::writeIndent() { - // blep intended this to look at the so-far-written string - // to determine whether we are already indented, but - // with a stream we cannot do that. So we rely on some saved state. - // The caller checks indented_. - *document_ << '\n' << indentString_; -} - -void StyledStreamWriter::writeWithIndent(const std::string& value) { - if (!indented_) writeIndent(); - *document_ << value; - indented_ = false; -} - -void StyledStreamWriter::indent() { indentString_ += indentation_; } - -void StyledStreamWriter::unindent() { - assert(indentString_.size() >= indentation_.size()); - indentString_.resize(indentString_.size() - indentation_.size()); -} - -void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { - if (!root.hasComment(commentBefore)) - return; - - if (!indented_) writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); - while (iter != comment.end()) { - *document_ << *iter; - if (*iter == '\n' && - (iter != comment.end() && *(iter + 1) == '/')) - // writeIndent(); // would include newline - *document_ << indentString_; - ++iter; - } - indented_ = false; -} - -void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { - if (root.hasComment(commentAfterOnSameLine)) - *document_ << ' ' << root.getComment(commentAfterOnSameLine); - - if (root.hasComment(commentAfter)) { - writeIndent(); - *document_ << root.getComment(commentAfter); - } - indented_ = false; -} - -bool StyledStreamWriter::hasCommentForValue(const Value& value) { - return value.hasComment(commentBefore) || - value.hasComment(commentAfterOnSameLine) || - value.hasComment(commentAfter); -} - -////////////////////////// -// BuiltStyledStreamWriter - -/// Scoped enums are not available until C++11. -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. - }; -}; - -struct BuiltStyledStreamWriter : public StreamWriter -{ - BuiltStyledStreamWriter( - std::string const& indentation, - CommentStyle::Enum cs, - std::string const& colonSymbol, - std::string const& nullSymbol, - std::string const& endingLineFeedSymbol, - bool useSpecialFloats, - unsigned int precision); - int write(Value const& root, std::ostream* sout) override; -private: - void writeValue(Value const& value); - void writeArrayValue(Value const& value); - bool isMultineArray(Value const& value); - void pushValue(std::string const& value); - void writeIndent(); - void writeWithIndent(std::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 ChildValues; - - ChildValues childValues_; - std::string indentString_; - int rightMargin_; - std::string indentation_; - CommentStyle::Enum cs_; - std::string colonSymbol_; - std::string nullSymbol_; - std::string endingLineFeedSymbol_; - bool addChildValues_ : 1; - bool indented_ : 1; - bool useSpecialFloats_ : 1; - unsigned int precision_; -}; -BuiltStyledStreamWriter::BuiltStyledStreamWriter( - std::string const& indentation, - CommentStyle::Enum cs, - std::string const& colonSymbol, - std::string const& nullSymbol, - std::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, std::ostream* sout) -{ - sout_ = sout; - addChildValues_ = false; - indented_ = true; - indentString_ = ""; - writeCommentBeforeValue(root); - if (!indented_) writeIndent(); - indented_ = true; - writeValue(root); - writeCommentAfterValueOnSameLine(root); - *sout_ << endingLineFeedSymbol_; - sout_ = NULL; - return 0; -} -void BuiltStyledStreamWriter::writeValue(Value const& value) { - switch (value.type()) { - case nullValue: - pushValue(nullSymbol_); - break; - case intValue: - pushValue(valueToString(value.asLargestInt())); - break; - case uintValue: - pushValue(valueToString(value.asLargestUInt())); - break; - case realValue: - pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); - break; - case stringValue: - { - // Is NULL is possible for value.string_? - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); - break; - } - case booleanValue: - pushValue(valueToString(value.asBool())); - break; - case arrayValue: - writeArrayValue(value); - break; - case objectValue: { - Value::Members members(value.getMemberNames()); - if (members.empty()) - pushValue("{}"); - else { - writeWithIndent("{"); - indent(); - Value::Members::iterator it = members.begin(); - for (;;) { - std::string const& name = *it; - Value const& childValue = value[name]; - writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); - *sout_ << colonSymbol_; - writeValue(childValue); - if (++it == members.end()) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *sout_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("}"); - } - } break; - } -} - -void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { - unsigned size = value.size(); - if (size == 0) - pushValue("[]"); - else { - bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); - if (isMultiLine) { - writeWithIndent("["); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index = 0; - for (;;) { - Value const& childValue = value[index]; - writeCommentBeforeValue(childValue); - if (hasChildValue) - writeWithIndent(childValues_[index]); - else { - if (!indented_) writeIndent(); - indented_ = true; - writeValue(childValue); - indented_ = false; - } - if (++index == size) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *sout_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("]"); - } else // output on a single line - { - assert(childValues_.size() == size); - *sout_ << "["; - if (!indentation_.empty()) *sout_ << " "; - for (unsigned index = 0; index < size; ++index) { - if (index > 0) - *sout_ << ", "; - *sout_ << childValues_[index]; - } - if (!indentation_.empty()) *sout_ << " "; - *sout_ << "]"; - } - } -} - -bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { - int size = value.size(); - bool isMultiLine = size * 3 >= rightMargin_; - childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { - Value const& childValue = value[index]; - isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); - } - if (!isMultiLine) // check if line length > max line length - { - childValues_.reserve(size); - addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (int index = 0; index < size; ++index) { - if (hasCommentForValue(value[index])) { - isMultiLine = true; - } - writeValue(value[index]); - lineLength += int(childValues_[index].length()); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - -void BuiltStyledStreamWriter::pushValue(std::string const& value) { - if (addChildValues_) - childValues_.push_back(value); - else - *sout_ << value; -} - -void BuiltStyledStreamWriter::writeIndent() { - // blep intended this to look at the so-far-written string - // to determine whether we are already indented, but - // with a stream we cannot do that. So we rely on some saved state. - // The caller checks indented_. - - if (!indentation_.empty()) { - // In this case, drop newlines too. - *sout_ << '\n' << indentString_; - } -} - -void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { - if (!indented_) writeIndent(); - *sout_ << value; - indented_ = false; -} - -void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } - -void BuiltStyledStreamWriter::unindent() { - assert(indentString_.size() >= indentation_.size()); - indentString_.resize(indentString_.size() - indentation_.size()); -} - -void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { - if (cs_ == CommentStyle::None) return; - if (!root.hasComment(commentBefore)) - return; - - if (!indented_) writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); - while (iter != comment.end()) { - *sout_ << *iter; - if (*iter == '\n' && - (iter != comment.end() && *(iter + 1) == '/')) - // writeIndent(); // would write extra newline - *sout_ << indentString_; - ++iter; - } - indented_ = false; -} - -void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { - if (cs_ == CommentStyle::None) return; - if (root.hasComment(commentAfterOnSameLine)) - *sout_ << " " + root.getComment(commentAfterOnSameLine); - - if (root.hasComment(commentAfter)) { - writeIndent(); - *sout_ << root.getComment(commentAfter); - } -} - -// static -bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { - return value.hasComment(commentBefore) || - value.hasComment(commentAfterOnSameLine) || - value.hasComment(commentAfter); -} - -/////////////// -// StreamWriter - -StreamWriter::StreamWriter() - : sout_(NULL) -{ -} -StreamWriter::~StreamWriter() -{ -} -StreamWriter::Factory::~Factory() -{} -StreamWriterBuilder::StreamWriterBuilder() -{ - setDefaults(&settings_); -} -StreamWriterBuilder::~StreamWriterBuilder() -{} -StreamWriter* StreamWriterBuilder::newStreamWriter() const -{ - std::string indentation = settings_["indentation"].asString(); - std::string cs_str = settings_["commentStyle"].asString(); - bool eyc = settings_["enableYAMLCompatibility"].asBool(); - bool dnp = settings_["dropNullPlaceholders"].asBool(); - bool usf = settings_["useSpecialFloats"].asBool(); - unsigned int pre = settings_["precision"].asUInt(); - CommentStyle::Enum cs = CommentStyle::All; - if (cs_str == "All") { - cs = CommentStyle::All; - } else if (cs_str == "None") { - cs = CommentStyle::None; - } else { - throwRuntimeError("commentStyle must be 'All' or 'None'"); - } - std::string colonSymbol = " : "; - if (eyc) { - colonSymbol = ": "; - } else if (indentation.empty()) { - colonSymbol = ":"; - } - std::string nullSymbol = "null"; - if (dnp) { - nullSymbol = ""; - } - if (pre > 17) pre = 17; - std::string endingLineFeedSymbol = ""; - return new BuiltStyledStreamWriter( - indentation, cs, - colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); -} -static void getValidWriterKeys(std::set* 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 valid_keys; - getValidWriterKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - std::string const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } - } - return 0u == inv.size(); -} -Value& StreamWriterBuilder::operator[](std::string key) -{ - return settings_[key]; -} -// static -void StreamWriterBuilder::setDefaults(Json::Value* settings) -{ - //! [StreamWriterBuilderDefaults] - (*settings)["commentStyle"] = "All"; - (*settings)["indentation"] = "\t"; - (*settings)["enableYAMLCompatibility"] = false; - (*settings)["dropNullPlaceholders"] = false; - (*settings)["useSpecialFloats"] = false; - (*settings)["precision"] = 17; - //! [StreamWriterBuilderDefaults] -} - -std::string writeString(StreamWriter::Factory const& builder, Value const& root) { - std::ostringstream sout; - StreamWriterPtr const writer(builder.newStreamWriter()); - writer->write(root, &sout); - return sout.str(); -} - -std::ostream& operator<<(std::ostream& sout, Value const& root) { - StreamWriterBuilder builder; - StreamWriterPtr const writer(builder.newStreamWriter()); - writer->write(root, &sout); - return sout; -} } // namespace Json From 614a5c3d1751b1f1a1f3ca2a674ec469309a9d17 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 11:56:10 +0000 Subject: [PATCH 04/41] Remove from the inl files the code that remains in cpp For the classes that aren't templated the code must remain in the cpp file, this has been removed so that if you put the inl and cpp files back together you'd end up with the original --- include/json/reader.inl | 23 ----------------------- include/json/value.inl | 24 ------------------------ include/json/writer.inl | 33 --------------------------------- 3 files changed, 80 deletions(-) diff --git a/include/json/reader.inl b/include/json/reader.inl index 2eae15de9..0c2f5c424 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -53,24 +53,6 @@ typedef std::unique_ptr CharReaderPtr; typedef std::auto_ptr CharReaderPtr; #endif -// Implementation of class Features -// //////////////////////////////// - -Features::Features() - : allowComments_(true), strictRoot_(false), - allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} - -Features Features::all() { return Features(); } - -Features Features::strictMode() { - Features features; - features.allowComments_ = false; - features.strictRoot_ = true; - features.allowDroppedNullPlaceholders_ = false; - features.allowNumericKeys_ = false; - return features; -} - // Implementation of class Reader // //////////////////////////////// @@ -904,11 +886,6 @@ public: int stackLimit_; }; // OurFeatures -// exact copy of Implementation of class Features -// //////////////////////////////// - -OurFeatures OurFeatures::all() { return OurFeatures(); } - // Implementation of class Reader // //////////////////////////////// diff --git a/include/json/value.inl b/include/json/value.inl index 91b5f23f9..434a03e6c 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -152,30 +152,6 @@ static inline void releaseStringValue(char* value) { free(value); } namespace Json { -Exception::Exception(std::string const& msg) - : msg_(msg) -{} -Exception::~Exception() throw() -{} -char const* Exception::what() const throw() -{ - return msg_.c_str(); -} -RuntimeError::RuntimeError(std::string const& msg) - : Exception(msg) -{} -LogicError::LogicError(std::string const& msg) - : Exception(msg) -{} -void throwRuntimeError(std::string const& msg) -{ - throw RuntimeError(msg); -} -void throwLogicError(std::string const& msg) -{ - throw LogicError(msg); -} - // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// diff --git a/include/json/writer.inl b/include/json/writer.inl index 0b2d7d5be..ff6acd987 100644 --- a/include/json/writer.inl +++ b/include/json/writer.inl @@ -79,24 +79,6 @@ typedef std::unique_ptr StreamWriterPtr; typedef std::auto_ptr StreamWriterPtr; #endif -static bool containsControlCharacter(const char* str) { - while (*str) { - if (isControlCharacter(*(str++))) - return true; - } - return false; -} - -static bool containsControlCharacter0(const char* str, unsigned len) { - char const* end = str + len; - while (end != str) { - if (isControlCharacter(*str) || 0==*str) - return true; - ++str; - } - return false; -} - std::string valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); @@ -229,21 +211,6 @@ std::string valueToQuotedString(const char* value) { return result; } -// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp -static char const* strnpbrk(char const* s, char const* accept, size_t n) { - assert((s || !n) && accept); - - char const* const end = s + n; - for (char const* cur = s; cur < end; ++cur) { - int const c = *cur; - for (char const* a = accept; *a; ++a) { - if (*a == c) { - return cur; - } - } - } - return NULL; -} static std::string valueToQuotedStringN(const char* value, unsigned length) { if (value == NULL) return ""; From 8e7fb04f4b4abf8cb86206f71db5104cba406710 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 12:26:34 +0000 Subject: [PATCH 05/41] Definition of templates Onto all the classes and methods in the header files the template definition has been made to define them all as templated --- include/json/reader.h | 5 +++++ include/json/value.h | 9 ++++++++- include/json/writer.h | 15 +++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/json/reader.h b/include/json/reader.h index 0a1574fad..6391dfb27 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -30,6 +30,7 @@ namespace Json { * * \deprecated Use CharReader and CharReaderBuilder. */ +template class JSON_API Reader { public: typedef char Char; @@ -244,6 +245,7 @@ class JSON_API Reader { /** Interface for reading JSON from a char array. */ +template class JSON_API CharReader { public: virtual ~CharReader() {} @@ -290,6 +292,7 @@ class JSON_API CharReader { bool ok = parseFromStream(builder, std::cin, &value, &errs); \endcode */ +template class JSON_API CharReaderBuilder : public CharReader::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class @@ -364,6 +367,7 @@ class JSON_API CharReaderBuilder : public CharReader::Factory { * Someday we might have a real StreamReader, but for now this * is convenient. */ +template bool JSON_API parseFromStream( CharReader::Factory const&, std::istream&, @@ -393,6 +397,7 @@ bool JSON_API parseFromStream( \throw std::exception on parse error. \see Json::operator<<() */ +template JSON_API std::istream& operator>>(std::istream&, Value&); } // namespace Json diff --git a/include/json/value.h b/include/json/value.h index 1cfda0774..214a35db7 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -159,6 +159,8 @@ class JSON_API StaticString { * but the Value API does *not* check bounds. That is the responsibility * of the caller. */ +template class _Alloc = std::allocator, + class _String = std::basic_string, std::allocator>> class JSON_API Value { friend class ValueIteratorBase; public: @@ -605,6 +607,7 @@ Json::Value obj_value(Json::objectValue); // {} /** \brief Experimental and untested: represents an element of the "path" to * access a node. */ +template class JSON_API PathArgument { public: friend class Path; @@ -636,6 +639,7 @@ class JSON_API PathArgument { * - ".%" => member name is provided as parameter * - ".[%]" => index is provied as parameter */ +template class JSON_API Path { public: Path(const std::string& path, @@ -668,6 +672,7 @@ class JSON_API Path { /** \brief base class for Value iterators. * */ +template class JSON_API ValueIteratorBase { public: typedef std::bidirectional_iterator_tag iterator_category; @@ -733,6 +738,7 @@ class JSON_API ValueIteratorBase { /** \brief const iterator for object and array value. * */ +template class JSON_API ValueConstIterator : public ValueIteratorBase { friend class Value; @@ -783,6 +789,7 @@ class JSON_API ValueConstIterator : public ValueIteratorBase { /** \brief Iterator for object and array value. */ +template class JSON_API ValueIterator : public ValueIteratorBase { friend class Value; @@ -837,7 +844,7 @@ class JSON_API ValueIterator : public ValueIteratorBase { namespace std { /// Specialize std::swap() for Json::Value. -template<> +template inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } } diff --git a/include/json/writer.h b/include/json/writer.h index f94aa1fe7..8c8582c68 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -37,6 +37,7 @@ class Value; } \endcode */ +template class JSON_API StreamWriter { protected: std::ostream* sout_; // not owned; will not delete @@ -66,6 +67,7 @@ class JSON_API StreamWriter { /** \brief Write into stringstream, then return string, for convenience. * A StreamWriter will be created from the factory, used, and then deleted. */ +template std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); @@ -84,6 +86,7 @@ std::string JSON_API writeString(StreamWriter::Factory const& factory, Value con std::cout << std::endl; // add lf and flush \endcode */ +template class JSON_API StreamWriterBuilder : public StreamWriter::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class @@ -138,6 +141,7 @@ class JSON_API StreamWriterBuilder : public StreamWriter::Factory { /** \brief Abstract class for writers. * \deprecated Use StreamWriter. (And really, this is an implementation detail.) */ +template class JSON_API Writer { public: virtual ~Writer(); @@ -154,6 +158,7 @@ class JSON_API Writer { * \sa Reader, Value * \deprecated Use StreamWriterBuilder. */ +template class JSON_API FastWriter : public Writer { public: @@ -207,6 +212,7 @@ class JSON_API FastWriter : public Writer { * \sa Reader, Value, Value::setComment() * \deprecated Use StreamWriterBuilder. */ +template class JSON_API StyledWriter : public Writer { public: StyledWriter(); @@ -269,6 +275,7 @@ class JSON_API StyledWriter : public Writer { * \sa Reader, Value, Value::setComment() * \deprecated Use StreamWriterBuilder. */ +template class JSON_API StyledStreamWriter { public: StyledStreamWriter(std::string indentation = "\t"); @@ -309,17 +316,25 @@ class JSON_API StyledStreamWriter { }; #if defined(JSON_HAS_INT64) +template std::string JSON_API valueToString(Int value); +template std::string JSON_API valueToString(UInt value); #endif // if defined(JSON_HAS_INT64) +template std::string JSON_API valueToString(LargestInt value); +template std::string JSON_API valueToString(LargestUInt value); +template std::string JSON_API valueToString(double value); +template std::string JSON_API valueToString(bool value); +template std::string JSON_API valueToQuotedString(const char* value); /// \brief Output using the StyledStreamWriter. /// \see Json::operator>>() +template JSON_API std::ostream& operator<<(std::ostream&, const Value& root); } // namespace Json From 96ce8959ff4970092ccbab056151f811e598ec2b Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 12:38:40 +0000 Subject: [PATCH 06/41] Forward declarations also need the template declaration --- include/json/forwards.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/json/forwards.h b/include/json/forwards.h index ccfe09abf..cd9165f54 100644 --- a/include/json/forwards.h +++ b/include/json/forwards.h @@ -13,10 +13,13 @@ namespace Json { // writer.h +template class FastWriter; +template class StyledWriter; // reader.h +template class Reader; // features.h @@ -25,11 +28,18 @@ class Features; // value.h typedef unsigned int ArrayIndex; class StaticString; +template class Path; +template class PathArgument; +template class _Alloc = std::allocator, + class _String = std::basic_string, std::allocator>> class Value; +template class ValueIteratorBase; +template class ValueIterator; +template class ValueConstIterator; } // namespace Json From 75b3de3a3001cc4c37d410b8304ce22088f25e35 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 12:46:14 +0000 Subject: [PATCH 07/41] Fix compilation scripts for new paths --- include/CMakeLists.txt | 1 + include/json/json.h | 6 +++--- src/lib_json/CMakeLists.txt | 2 -- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 7dde10d6f..591bf7bc9 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,2 +1,3 @@ FILE(GLOB INCLUDE_FILES "json/*.h") +FILE(GLOB INCLUDE_FILES "json/*.inl") INSTALL(FILES ${INCLUDE_FILES} DESTINATION ${INCLUDE_INSTALL_DIR}/json) diff --git a/include/json/json.h b/include/json/json.h index 8f10ac2bf..eac0125cb 100644 --- a/include/json/json.h +++ b/include/json/json.h @@ -7,9 +7,9 @@ #define JSON_JSON_H_INCLUDED #include "autolink.h" -#include "value.h" -#include "reader.h" -#include "writer.h" +#include "value.inl" +#include "reader.inl" +#include "writer.inl" #include "features.h" #endif // JSON_JSON_H_INCLUDED diff --git a/src/lib_json/CMakeLists.txt b/src/lib_json/CMakeLists.txt index 99ddc7f8b..4b0de3708 100644 --- a/src/lib_json/CMakeLists.txt +++ b/src/lib_json/CMakeLists.txt @@ -25,9 +25,7 @@ SET( PUBLIC_HEADERS SOURCE_GROUP( "Public API" FILES ${PUBLIC_HEADERS} ) SET(jsoncpp_sources - json_tool.h json_reader.cpp - json_valueiterator.inl json_value.cpp json_writer.cpp version.h.in) From 5804843f4f3b0d641611b871deec7f3ce6c429e5 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 13:25:35 +0000 Subject: [PATCH 08/41] Add template in front of every method in the template implementation --- include/json/reader.inl | 93 +++++++++++++++++++++- include/json/value.inl | 140 ++++++++++++++++++++++++++++++++- include/json/valueiterator.inl | 22 ++++++ include/json/writer.inl | 68 ++++++++++++++++ 4 files changed, 317 insertions(+), 6 deletions(-) diff --git a/include/json/reader.inl b/include/json/reader.inl index 0c2f5c424..988277ed2 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -4,9 +4,9 @@ // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include +#include "assertions.h" +#include "reader.h" +#include "value.h" #include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include @@ -56,6 +56,7 @@ typedef std::auto_ptr CharReaderPtr; // Implementation of class Reader // //////////////////////////////// +template static bool containsNewLine(Reader::Location begin, Reader::Location end) { for (; begin < end; ++begin) if (*begin == '\n' || *begin == '\r') @@ -66,16 +67,19 @@ static bool containsNewLine(Reader::Location begin, Reader::Location end) { // Class Reader // ////////////////////////////////////////////////////////////////// +template Reader::Reader() : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), lastValue_(), commentsBefore_(), features_(Features::all()), collectComments_() {} +template Reader::Reader(const Features& features) : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), lastValue_(), commentsBefore_(), features_(features), collectComments_() { } +template bool Reader::parse(const std::string& document, Value& root, bool collectComments) { document_ = document; @@ -84,6 +88,7 @@ Reader::parse(const std::string& document, Value& root, bool collectComments) { return parse(begin, end, root, collectComments); } +template bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { // std::istream_iterator begin(sin); // std::istream_iterator end; @@ -97,6 +102,7 @@ bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { return parse(doc, root, collectComments); } +template bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root, @@ -139,6 +145,7 @@ bool Reader::parse(const char* beginDoc, return successful; } +template bool Reader::readValue() { // This is a non-reentrant way to support a stackLimit. Terrible! // But this deprecated class has a security problem: Bad input can @@ -223,6 +230,7 @@ bool Reader::readValue() { return successful; } +template void Reader::skipCommentTokens(Token& token) { if (features_.allowComments_) { do { @@ -233,6 +241,7 @@ void Reader::skipCommentTokens(Token& token) { } } +template bool Reader::readToken(Token& token) { skipSpaces(); token.start_ = current_; @@ -314,6 +323,7 @@ void Reader::skipSpaces() { } } +template bool Reader::match(Location pattern, int patternLength) { if (end_ - current_ < patternLength) return false; @@ -325,6 +335,7 @@ bool Reader::match(Location pattern, int patternLength) { return true; } +template bool Reader::readComment() { Location commentBegin = current_ - 1; Char c = getNextChar(); @@ -348,6 +359,7 @@ bool Reader::readComment() { return true; } +template static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { std::string normalized; normalized.reserve(end - begin); @@ -367,6 +379,7 @@ static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { return normalized; } +template void Reader::addComment(Location begin, Location end, CommentPlacement placement) { assert(collectComments_); @@ -379,6 +392,7 @@ Reader::addComment(Location begin, Location end, CommentPlacement placement) { } } +template bool Reader::readCStyleComment() { while (current_ != end_) { Char c = getNextChar(); @@ -388,6 +402,7 @@ bool Reader::readCStyleComment() { return getNextChar() == '/'; } +template bool Reader::readCppStyleComment() { while (current_ != end_) { Char c = getNextChar(); @@ -404,6 +419,7 @@ bool Reader::readCppStyleComment() { return true; } +template void Reader::readNumber() { const char *p = current_; char c = '0'; // stopgap for already consumed character @@ -426,6 +442,7 @@ void Reader::readNumber() { } } +template bool Reader::readString() { Char c = 0; while (current_ != end_) { @@ -438,6 +455,7 @@ bool Reader::readString() { return c == '"'; } +template bool Reader::readObject(Token& tokenStart) { Token tokenName; std::string name; @@ -494,6 +512,7 @@ bool Reader::readObject(Token& tokenStart) { "Missing '}' or object member name", tokenName, tokenObjectEnd); } +template bool Reader::readArray(Token& tokenStart) { Value init(arrayValue); currentValue().swapPayload(init); @@ -532,6 +551,7 @@ bool Reader::readArray(Token& tokenStart) { return true; } +template bool Reader::decodeNumber(Token& token) { Value decoded; if (!decodeNumber(token, decoded)) @@ -542,6 +562,7 @@ bool Reader::decodeNumber(Token& token) { return true; } +template bool Reader::decodeNumber(Token& token, Value& decoded) { // Attempts to parse the number as an integer. If the number is // larger than the maximum supported value of an integer then @@ -584,6 +605,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { return true; } +template bool Reader::decodeDouble(Token& token) { Value decoded; if (!decodeDouble(token, decoded)) @@ -594,6 +616,7 @@ bool Reader::decodeDouble(Token& token) { return true; } +template bool Reader::decodeDouble(Token& token, Value& decoded) { double value = 0; std::string buffer(token.start_, token.end_); @@ -606,6 +629,7 @@ bool Reader::decodeDouble(Token& token, Value& decoded) { return true; } +template bool Reader::decodeString(Token& token) { std::string decoded_string; if (!decodeString(token, decoded_string)) @@ -617,6 +641,7 @@ bool Reader::decodeString(Token& token) { return true; } +template bool Reader::decodeString(Token& token, std::string& decoded) { decoded.reserve(token.end_ - token.start_ - 2); Location current = token.start_ + 1; // skip '"' @@ -670,6 +695,7 @@ bool Reader::decodeString(Token& token, std::string& decoded) { return true; } +template bool Reader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, @@ -699,6 +725,7 @@ bool Reader::decodeUnicodeCodePoint(Token& token, return true; } +template bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, @@ -727,6 +754,7 @@ bool Reader::decodeUnicodeEscapeSequence(Token& token, return true; } +template bool Reader::addError(const std::string& message, Token& token, Location extra) { ErrorInfo info; @@ -737,6 +765,7 @@ Reader::addError(const std::string& message, Token& token, Location extra) { return false; } +template bool Reader::recoverFromError(TokenType skipUntilToken) { int errorCount = int(errors_.size()); Token skip; @@ -750,6 +779,7 @@ bool Reader::recoverFromError(TokenType skipUntilToken) { return false; } +template bool Reader::addErrorAndRecover(const std::string& message, Token& token, TokenType skipUntilToken) { @@ -757,14 +787,17 @@ bool Reader::addErrorAndRecover(const std::string& message, return recoverFromError(skipUntilToken); } +template Value& Reader::currentValue() { return *(nodes_.top()); } +template Reader::Char Reader::getNextChar() { if (current_ == end_) return 0; return *current_++; } +template void Reader::getLocationLineAndColumn(Location location, int& line, int& column) const { @@ -788,6 +821,7 @@ void Reader::getLocationLineAndColumn(Location location, ++line; } +template std::string Reader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); @@ -797,10 +831,12 @@ std::string Reader::getLocationLineAndColumn(Location location) const { } // Deprecated. Preserved for backward compatibility +template std::string Reader::getFormatedErrorMessages() const { return getFormattedErrorMessages(); } +template std::string Reader::getFormattedErrorMessages() const { std::string formattedMessage; for (Errors::const_iterator itError = errors_.begin(); @@ -817,6 +853,7 @@ std::string Reader::getFormattedErrorMessages() const { return formattedMessage; } +template std::vector Reader::getStructuredErrors() const { std::vector allErrors; for (Errors::const_iterator itError = errors_.begin(); @@ -832,6 +869,7 @@ std::vector Reader::getStructuredErrors() const { return allErrors; } +template bool Reader::pushError(const Value& value, const std::string& message) { size_t length = end_ - begin_; if(value.getOffsetStart() > length @@ -849,6 +887,7 @@ bool Reader::pushError(const Value& value, const std::string& message) { return true; } +template bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) { size_t length = end_ - begin_; if(value.getOffsetStart() > length @@ -867,6 +906,7 @@ bool Reader::pushError(const Value& value, const std::string& message, const Val return true; } +template bool Reader::good() const { return !errors_.size(); } @@ -890,6 +930,7 @@ public: // //////////////////////////////// // exact copy of Reader, renamed to OurReader +template class OurReader { public: typedef char Char; @@ -1009,6 +1050,7 @@ private: // complete copy of Read impl, for OurReader +template OurReader::OurReader(OurFeatures const& features) : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), lastValue_(), commentsBefore_(), @@ -1016,6 +1058,7 @@ OurReader::OurReader(OurFeatures const& features) features_(features), collectComments_() { } +template bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root, @@ -1064,6 +1107,7 @@ bool OurReader::parse(const char* beginDoc, return successful; } +template bool OurReader::readValue() { if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); ++stackDepth_; @@ -1167,6 +1211,7 @@ bool OurReader::readValue() { return successful; } +template void OurReader::skipCommentTokens(Token& token) { if (features_.allowComments_) { do { @@ -1177,6 +1222,7 @@ void OurReader::skipCommentTokens(Token& token) { } } +template bool OurReader::readToken(Token& token) { skipSpaces(); token.start_ = current_; @@ -1277,6 +1323,7 @@ bool OurReader::readToken(Token& token) { return true; } +template void OurReader::skipSpaces() { while (current_ != end_) { Char c = *current_; @@ -1287,6 +1334,7 @@ void OurReader::skipSpaces() { } } +template bool OurReader::match(Location pattern, int patternLength) { if (end_ - current_ < patternLength) return false; @@ -1298,6 +1346,7 @@ bool OurReader::match(Location pattern, int patternLength) { return true; } +template bool OurReader::readComment() { Location commentBegin = current_ - 1; Char c = getNextChar(); @@ -1321,6 +1370,7 @@ bool OurReader::readComment() { return true; } +template void OurReader::addComment(Location begin, Location end, CommentPlacement placement) { assert(collectComments_); @@ -1333,6 +1383,7 @@ OurReader::addComment(Location begin, Location end, CommentPlacement placement) } } +template bool OurReader::readCStyleComment() { while (current_ != end_) { Char c = getNextChar(); @@ -1342,6 +1393,7 @@ bool OurReader::readCStyleComment() { return getNextChar() == '/'; } +template bool OurReader::readCppStyleComment() { while (current_ != end_) { Char c = getNextChar(); @@ -1358,6 +1410,7 @@ bool OurReader::readCppStyleComment() { return true; } +template bool OurReader::readNumber(bool checkInf) { const char *p = current_; if (checkInf && p != end_ && *p == 'I') { @@ -1384,6 +1437,7 @@ bool OurReader::readNumber(bool checkInf) { } return true; } +template bool OurReader::readString() { Char c = 0; while (current_ != end_) { @@ -1397,6 +1451,7 @@ bool OurReader::readString() { } +template bool OurReader::readStringSingleQuote() { Char c = 0; while (current_ != end_) { @@ -1409,6 +1464,7 @@ bool OurReader::readStringSingleQuote() { return c == '\''; } +template bool OurReader::readObject(Token& tokenStart) { Token tokenName; std::string name; @@ -1471,6 +1527,7 @@ bool OurReader::readObject(Token& tokenStart) { "Missing '}' or object member name", tokenName, tokenObjectEnd); } +template bool OurReader::readArray(Token& tokenStart) { Value init(arrayValue); currentValue().swapPayload(init); @@ -1509,6 +1566,7 @@ bool OurReader::readArray(Token& tokenStart) { return true; } +template bool OurReader::decodeNumber(Token& token) { Value decoded; if (!decodeNumber(token, decoded)) @@ -1519,6 +1577,7 @@ bool OurReader::decodeNumber(Token& token) { return true; } +template bool OurReader::decodeNumber(Token& token, Value& decoded) { // Attempts to parse the number as an integer. If the number is // larger than the maximum supported value of an integer then @@ -1559,6 +1618,7 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { return true; } +template bool OurReader::decodeDouble(Token& token) { Value decoded; if (!decodeDouble(token, decoded)) @@ -1569,6 +1629,7 @@ bool OurReader::decodeDouble(Token& token) { return true; } +template bool OurReader::decodeDouble(Token& token, Value& decoded) { double value = 0; const int bufferSize = 32; @@ -1605,6 +1666,7 @@ bool OurReader::decodeDouble(Token& token, Value& decoded) { return true; } +template bool OurReader::decodeString(Token& token) { std::string decoded_string; if (!decodeString(token, decoded_string)) @@ -1616,6 +1678,7 @@ bool OurReader::decodeString(Token& token) { return true; } +template bool OurReader::decodeString(Token& token, std::string& decoded) { decoded.reserve(token.end_ - token.start_ - 2); Location current = token.start_ + 1; // skip '"' @@ -1669,6 +1732,7 @@ bool OurReader::decodeString(Token& token, std::string& decoded) { return true; } +template bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, @@ -1698,6 +1762,7 @@ bool OurReader::decodeUnicodeCodePoint(Token& token, return true; } +template bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, @@ -1726,6 +1791,7 @@ bool OurReader::decodeUnicodeEscapeSequence(Token& token, return true; } +template bool OurReader::addError(const std::string& message, Token& token, Location extra) { ErrorInfo info; @@ -1736,6 +1802,7 @@ OurReader::addError(const std::string& message, Token& token, Location extra) { return false; } +template bool OurReader::recoverFromError(TokenType skipUntilToken) { int errorCount = int(errors_.size()); Token skip; @@ -1756,14 +1823,17 @@ bool OurReader::addErrorAndRecover(const std::string& message, return recoverFromError(skipUntilToken); } +template Value& OurReader::currentValue() { return *(nodes_.top()); } +template OurReader::Char OurReader::getNextChar() { if (current_ == end_) return 0; return *current_++; } +template void OurReader::getLocationLineAndColumn(Location location, int& line, int& column) const { @@ -1787,6 +1857,7 @@ void OurReader::getLocationLineAndColumn(Location location, ++line; } +template std::string OurReader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); @@ -1795,6 +1866,7 @@ std::string OurReader::getLocationLineAndColumn(Location location) const { return buffer; } +template std::string OurReader::getFormattedErrorMessages() const { std::string formattedMessage; for (Errors::const_iterator itError = errors_.begin(); @@ -1811,6 +1883,7 @@ std::string OurReader::getFormattedErrorMessages() const { return formattedMessage; } +template std::vector OurReader::getStructuredErrors() const { std::vector allErrors; for (Errors::const_iterator itError = errors_.begin(); @@ -1826,6 +1899,7 @@ std::vector OurReader::getStructuredErrors() const { return allErrors; } +template bool OurReader::pushError(const Value& value, const std::string& message) { size_t length = end_ - begin_; if(value.getOffsetStart() > length @@ -1843,6 +1917,7 @@ bool OurReader::pushError(const Value& value, const std::string& message) { return true; } +template bool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) { size_t length = end_ - begin_; if(value.getOffsetStart() > length @@ -1861,11 +1936,13 @@ bool OurReader::pushError(const Value& value, const std::string& message, const return true; } +template bool OurReader::good() const { return !errors_.size(); } +template class OurCharReader : public CharReader { bool const collectComments_; OurReader reader_; @@ -1887,12 +1964,15 @@ public: } }; +template CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } +template CharReaderBuilder::~CharReaderBuilder() {} +template CharReader* CharReaderBuilder::newCharReader() const { bool collectComments = settings_["collectComments"].asBool(); @@ -1908,6 +1988,7 @@ CharReader* CharReaderBuilder::newCharReader() const features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); return new OurCharReader(collectComments, features); } +template static void getValidReaderKeys(std::set* valid_keys) { valid_keys->clear(); @@ -1922,6 +2003,7 @@ static void getValidReaderKeys(std::set* valid_keys) valid_keys->insert("rejectDupKeys"); valid_keys->insert("allowSpecialFloats"); } +template bool CharReaderBuilder::validate(Json::Value* invalid) const { Json::Value my_invalid; @@ -1939,11 +2021,13 @@ bool CharReaderBuilder::validate(Json::Value* invalid) const } return 0u == inv.size(); } +template Value& CharReaderBuilder::operator[](std::string key) { return settings_[key]; } // static +template void CharReaderBuilder::strictMode(Json::Value* settings) { //! [CharReaderBuilderStrictMode] @@ -1959,6 +2043,7 @@ void CharReaderBuilder::strictMode(Json::Value* settings) //! [CharReaderBuilderStrictMode] } // static +template void CharReaderBuilder::setDefaults(Json::Value* settings) { //! [CharReaderBuilderDefaults] @@ -1978,6 +2063,7 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) ////////////////////////////////// // global functions +template bool parseFromStream( CharReader::Factory const& fact, std::istream& sin, Value* root, std::string* errs) @@ -1992,6 +2078,7 @@ bool parseFromStream( return reader->parse(begin, end, root, errs); } +template std::istream& operator>>(std::istream& sin, Value& root) { CharReaderBuilder b; std::string errs; diff --git a/include/json/value.inl b/include/json/value.inl index 434a03e6c..7f28f96ce 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -4,9 +4,9 @@ // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include +#include "assertions.h" +#include "value.h" +#include "writer.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include @@ -79,6 +79,7 @@ static inline bool InRange(double d, T min, U max) { * computed using strlen(value). * @return Pointer on the duplicate instance of string. */ +template static inline char* duplicateStringValue(const char* value, size_t length) { // Avoid an integer overflow in the call to malloc below by limiting length @@ -99,6 +100,7 @@ static inline char* duplicateStringValue(const char* value, /* Record the length as a prefix. */ +template static inline char* duplicateAndPrefixStringValue( const char* value, unsigned int length) @@ -120,6 +122,7 @@ static inline char* duplicateAndPrefixStringValue( newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later return newString; } +template inline static void decodePrefixedString( bool isPrefixed, char const* prefixed, unsigned* length, char const** value) @@ -134,6 +137,7 @@ inline static void decodePrefixedString( } /** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). */ +template static inline void releaseStringValue(char* value) { free(value); } } // namespace Json @@ -160,13 +164,16 @@ namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// +template class _Alloc, class _String> Value::CommentInfo::CommentInfo() : comment_(0) {} +template class _Alloc, class _String> Value::CommentInfo::~CommentInfo() { if (comment_) releaseStringValue(comment_); } +template class _Alloc, class _String> void Value::CommentInfo::setComment(const char* text, size_t len) { if (comment_) { releaseStringValue(comment_); @@ -191,8 +198,10 @@ void Value::CommentInfo::setComment(const char* text, size_t len) { // Notes: policy_ indicates if the string was allocated when // a string is stored. +template class _Alloc, class _String> Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} +template class _Alloc, class _String> Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) : cstr_(str) { // allocate != duplicate @@ -212,27 +221,32 @@ Value::CZString::CZString(const CZString& other) } #if JSON_HAS_RVALUE_REFERENCES +template class _Alloc, class _String> Value::CZString::CZString(CZString&& other) : cstr_(other.cstr_), index_(other.index_) { other.cstr_ = nullptr; } #endif +template class _Alloc, class _String> Value::CZString::~CZString() { if (cstr_ && storage_.policy_ == duplicate) releaseStringValue(const_cast(cstr_)); } +template class _Alloc, class _String> void Value::CZString::swap(CZString& other) { std::swap(cstr_, other.cstr_); std::swap(index_, other.index_); } +template class _Alloc, class _String> Value::CZString& Value::CZString::operator=(CZString other) { swap(other); return *this; } +template class _Alloc, class _String> bool Value::CZString::operator<(const CZString& other) const { if (!cstr_) return index_ < other.index_; //return strcmp(cstr_, other.cstr_) < 0; @@ -246,6 +260,7 @@ bool Value::CZString::operator<(const CZString& other) const { return (this_len < other_len); } +template class _Alloc, class _String> bool Value::CZString::operator==(const CZString& other) const { if (!cstr_) return index_ == other.index_; //return strcmp(cstr_, other.cstr_) == 0; @@ -257,11 +272,15 @@ bool Value::CZString::operator==(const CZString& other) const { return comp == 0; } +template class _Alloc, class _String> ArrayIndex Value::CZString::index() const { return index_; } //const char* Value::CZString::c_str() const { return cstr_; } +template class _Alloc, class _String> const char* Value::CZString::data() const { return cstr_; } +template class _Alloc, class _String> unsigned Value::CZString::length() const { return storage_.length_; } +template class _Alloc, class _String> bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } // ////////////////////////////////////////////////////////////////// @@ -276,6 +295,7 @@ bool Value::CZString::isStaticString() const { return storage_.policy_ == noDupl * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ +template class _Alloc, class _String> Value::Value(ValueType vtype) { initBasic(vtype); switch (vtype) { @@ -303,65 +323,77 @@ Value::Value(ValueType vtype) { } } +template class _Alloc, class _String> Value::Value(Int value) { initBasic(intValue); value_.int_ = value; } +template class _Alloc, class _String> Value::Value(UInt value) { initBasic(uintValue); value_.uint_ = value; } #if defined(JSON_HAS_INT64) +template class _Alloc, class _String> Value::Value(Int64 value) { initBasic(intValue); value_.int_ = value; } +template class _Alloc, class _String> Value::Value(UInt64 value) { initBasic(uintValue); value_.uint_ = value; } #endif // defined(JSON_HAS_INT64) +template class _Alloc, class _String> Value::Value(double value) { initBasic(realValue); value_.real_ = value; } +template class _Alloc, class _String> Value::Value(const char* value) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); } +template class _Alloc, class _String> Value::Value(const char* beginValue, const char* endValue) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); } +template class _Alloc, class _String> Value::Value(const std::string& value) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); } +template class _Alloc, class _String> Value::Value(const StaticString& value) { initBasic(stringValue); value_.string_ = const_cast(value.c_str()); } #ifdef JSON_USE_CPPTL +template class _Alloc, class _String> Value::Value(const CppTL::ConstString& value) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); } #endif +template class _Alloc, class _String> Value::Value(bool value) { initBasic(booleanValue); value_.bool_ = value; } +template class _Alloc, class _String> Value::Value(Value const& other) : type_(other.type_), allocated_(false) , @@ -408,12 +440,14 @@ Value::Value(Value const& other) #if JSON_HAS_RVALUE_REFERENCES // Move constructor +template class _Alloc, class _String> Value::Value(Value&& other) { initBasic(nullValue); swap(other); } #endif +template class _Alloc, class _String> Value::~Value() { switch (type_) { case nullValue: @@ -438,11 +472,13 @@ Value::~Value() { delete[] comments_; } +template class _Alloc, class _String> Value& Value::operator=(Value other) { swap(other); return *this; } +template class _Alloc, class _String> void Value::swapPayload(Value& other) { ValueType temp = type_; type_ = other.type_; @@ -453,6 +489,7 @@ void Value::swapPayload(Value& other) { other.allocated_ = temp2 & 0x1; } +template class _Alloc, class _String> void Value::swap(Value& other) { swapPayload(other); std::swap(comments_, other.comments_); @@ -460,8 +497,10 @@ void Value::swap(Value& other) { std::swap(limit_, other.limit_); } +template class _Alloc, class _String> ValueType Value::type() const { return type_; } +template class _Alloc, class _String> int Value::compare(const Value& other) const { if (*this < other) return -1; @@ -470,6 +509,7 @@ int Value::compare(const Value& other) const { return 0; } +template class _Alloc, class _String> bool Value::operator<(const Value& other) const { int typeDelta = type_ - other.type_; if (typeDelta) @@ -516,12 +556,16 @@ bool Value::operator<(const Value& other) const { return false; // unreachable } +template class _Alloc, class _String> bool Value::operator<=(const Value& other) const { return !(other < *this); } +template class _Alloc, class _String> bool Value::operator>=(const Value& other) const { return !(*this < other); } +template class _Alloc, class _String> bool Value::operator>(const Value& other) const { return other < *this; } +template class _Alloc, class _String> bool Value::operator==(const Value& other) const { // if ( type_ != other.type_ ) // GCC 2.95.3 says: @@ -566,8 +610,10 @@ bool Value::operator==(const Value& other) const { return false; // unreachable } +template class _Alloc, class _String> bool Value::operator!=(const Value& other) const { return !(*this == other); } +template class _Alloc, class _String> const char* Value::asCString() const { JSON_ASSERT_MESSAGE(type_ == stringValue, "in Json::Value::asCString(): requires stringValue"); @@ -578,6 +624,7 @@ const char* Value::asCString() const { return this_str; } +template class _Alloc, class _String> bool Value::getString(char const** str, char const** cend) const { if (type_ != stringValue) return false; if (value_.string_ == 0) return false; @@ -587,6 +634,7 @@ bool Value::getString(char const** str, char const** cend) const { return true; } +template class _Alloc, class _String> std::string Value::asString() const { switch (type_) { case nullValue: @@ -613,6 +661,7 @@ std::string Value::asString() const { } #ifdef JSON_USE_CPPTL +template class _Alloc, class _String> CppTL::ConstString Value::asConstString() const { unsigned len; char const* str; @@ -622,6 +671,7 @@ CppTL::ConstString Value::asConstString() const { } #endif +template class _Alloc, class _String> Value::Int Value::asInt() const { switch (type_) { case intValue: @@ -644,6 +694,7 @@ Value::Int Value::asInt() const { JSON_FAIL_MESSAGE("Value is not convertible to Int."); } +template class _Alloc, class _String> Value::UInt Value::asUInt() const { switch (type_) { case intValue: @@ -668,6 +719,7 @@ Value::UInt Value::asUInt() const { #if defined(JSON_HAS_INT64) +template class _Alloc, class _String> Value::Int64 Value::asInt64() const { switch (type_) { case intValue: @@ -689,6 +741,7 @@ Value::Int64 Value::asInt64() const { JSON_FAIL_MESSAGE("Value is not convertible to Int64."); } +template class _Alloc, class _String> Value::UInt64 Value::asUInt64() const { switch (type_) { case intValue: @@ -711,6 +764,7 @@ Value::UInt64 Value::asUInt64() const { } #endif // if defined(JSON_HAS_INT64) +template class _Alloc, class _String> LargestInt Value::asLargestInt() const { #if defined(JSON_NO_INT64) return asInt(); @@ -719,6 +773,7 @@ LargestInt Value::asLargestInt() const { #endif } +template class _Alloc, class _String> LargestUInt Value::asLargestUInt() const { #if defined(JSON_NO_INT64) return asUInt(); @@ -727,6 +782,7 @@ LargestUInt Value::asLargestUInt() const { #endif } +template class _Alloc, class _String> double Value::asDouble() const { switch (type_) { case intValue: @@ -749,6 +805,7 @@ double Value::asDouble() const { JSON_FAIL_MESSAGE("Value is not convertible to double."); } +template class _Alloc, class _String> float Value::asFloat() const { switch (type_) { case intValue: @@ -771,6 +828,7 @@ float Value::asFloat() const { JSON_FAIL_MESSAGE("Value is not convertible to float."); } +template class _Alloc, class _String> bool Value::asBool() const { switch (type_) { case booleanValue: @@ -790,6 +848,7 @@ bool Value::asBool() const { JSON_FAIL_MESSAGE("Value is not convertible to bool."); } +template class _Alloc, class _String> bool Value::isConvertibleTo(ValueType other) const { switch (other) { case nullValue: @@ -824,6 +883,7 @@ bool Value::isConvertibleTo(ValueType other) const { } /// Number of values in array or object +template class _Alloc, class _String> ArrayIndex Value::size() const { switch (type_) { case nullValue: @@ -847,6 +907,7 @@ ArrayIndex Value::size() const { return 0; // unreachable; } +template class _Alloc, class _String> bool Value::empty() const { if (isNull() || isArray() || isObject()) return size() == 0u; @@ -854,8 +915,10 @@ bool Value::empty() const { return false; } +template class _Alloc, class _String> bool Value::operator!() const { return isNull(); } +template class _Alloc, class _String> void Value::clear() { JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || type_ == objectValue, @@ -872,6 +935,7 @@ void Value::clear() { } } +template class _Alloc, class _String> void Value::resize(ArrayIndex newSize) { JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, "in Json::Value::resize(): requires arrayValue"); @@ -890,6 +954,7 @@ void Value::resize(ArrayIndex newSize) { } } +template class _Alloc, class _String> Value& Value::operator[](ArrayIndex index) { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue, @@ -906,6 +971,7 @@ Value& Value::operator[](ArrayIndex index) { return (*it).second; } +template class _Alloc, class _String> Value& Value::operator[](int index) { JSON_ASSERT_MESSAGE( index >= 0, @@ -913,6 +979,7 @@ Value& Value::operator[](int index) { return (*this)[ArrayIndex(index)]; } +template class _Alloc, class _String> const Value& Value::operator[](ArrayIndex index) const { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue, @@ -926,6 +993,7 @@ const Value& Value::operator[](ArrayIndex index) const { return (*it).second; } +template class _Alloc, class _String> const Value& Value::operator[](int index) const { JSON_ASSERT_MESSAGE( index >= 0, @@ -933,6 +1001,7 @@ const Value& Value::operator[](int index) const { return (*this)[ArrayIndex(index)]; } +template class _Alloc, class _String> void Value::initBasic(ValueType vtype, bool allocated) { type_ = vtype; allocated_ = allocated; @@ -944,6 +1013,7 @@ void Value::initBasic(ValueType vtype, bool allocated) { // 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. +template class _Alloc, class _String> Value& Value::resolveReference(const char* key) { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, @@ -963,6 +1033,7 @@ Value& Value::resolveReference(const char* key) { } // @param key is not null-terminated. +template class _Alloc, class _String> Value& Value::resolveReference(char const* key, char const* cend) { JSON_ASSERT_MESSAGE( @@ -982,13 +1053,16 @@ Value& Value::resolveReference(char const* key, char const* cend) return value; } +template class _Alloc, class _String> Value Value::get(ArrayIndex index, const Value& defaultValue) const { const Value* value = &((*this)[index]); return value == &nullRef ? defaultValue : *value; } +template class _Alloc, class _String> bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } +template class _Alloc, class _String> Value const* Value::find(char const* key, char const* cend) const { JSON_ASSERT_MESSAGE( @@ -1000,12 +1074,14 @@ Value const* Value::find(char const* key, char const* cend) const if (it == value_.map_->end()) return NULL; return &(*it).second; } +template class _Alloc, class _String> const Value& Value::operator[](const char* key) const { Value const* found = find(key, key + strlen(key)); if (!found) return nullRef; return *found; } +template class _Alloc, class _String> Value const& Value::operator[](std::string const& key) const { Value const* found = find(key.data(), key.data() + key.length()); @@ -1013,22 +1089,27 @@ Value const& Value::operator[](std::string const& key) const return *found; } +template class _Alloc, class _String> Value& Value::operator[](const char* key) { return resolveReference(key, key + strlen(key)); } +template class _Alloc, class _String> Value& Value::operator[](const std::string& key) { return resolveReference(key.data(), key.data() + key.length()); } +template class _Alloc, class _String> Value& Value::operator[](const StaticString& key) { return resolveReference(key.c_str()); } #ifdef JSON_USE_CPPTL +template class _Alloc, class _String> Value& Value::operator[](const CppTL::ConstString& key) { return resolveReference(key.c_str(), key.end_c_str()); } +template class _Alloc, class _String> Value const& Value::operator[](CppTL::ConstString const& key) const { Value const* found = find(key.c_str(), key.end_c_str()); @@ -1037,23 +1118,28 @@ Value const& Value::operator[](CppTL::ConstString const& key) const } #endif +template class _Alloc, class _String> Value& Value::append(const Value& value) { return (*this)[size()] = value; } +template class _Alloc, class _String> Value Value::get(char const* key, char const* cend, Value const& defaultValue) const { Value const* found = find(key, cend); return !found ? defaultValue : *found; } +template class _Alloc, class _String> Value Value::get(char const* key, Value const& defaultValue) const { return get(key, key + strlen(key), defaultValue); } +template class _Alloc, class _String> Value Value::get(std::string const& key, Value const& defaultValue) const { return get(key.data(), key.data() + key.length(), defaultValue); } +template class _Alloc, class _String> bool Value::removeMember(const char* key, const char* cend, Value* removed) { if (type_ != objectValue) { @@ -1067,14 +1153,17 @@ bool Value::removeMember(const char* key, const char* cend, Value* removed) value_.map_->erase(it); return true; } +template class _Alloc, class _String> bool Value::removeMember(const char* key, Value* removed) { return removeMember(key, key + strlen(key), removed); } +template class _Alloc, class _String> bool Value::removeMember(std::string const& key, Value* removed) { return removeMember(key.data(), key.data() + key.length(), removed); } +template class _Alloc, class _String> Value Value::removeMember(const char* key) { JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, @@ -1086,11 +1175,13 @@ Value Value::removeMember(const char* key) removeMember(key, key + strlen(key), &removed); return removed; // still null if removeMember() did nothing } +template class _Alloc, class _String> Value Value::removeMember(const std::string& key) { return removeMember(key.c_str()); } +template class _Alloc, class _String> bool Value::removeIndex(ArrayIndex index, Value* removed) { if (type_ != arrayValue) { return false; @@ -1115,32 +1206,38 @@ bool Value::removeIndex(ArrayIndex index, Value* removed) { } #ifdef JSON_USE_CPPTL +template class _Alloc, class _String> Value Value::get(const CppTL::ConstString& key, const Value& defaultValue) const { return get(key.c_str(), key.end_c_str(), defaultValue); } #endif +template class _Alloc, class _String> bool Value::isMember(char const* key, char const* cend) const { Value const* value = find(key, cend); return NULL != value; } +template class _Alloc, class _String> bool Value::isMember(char const* key) const { return isMember(key, key + strlen(key)); } +template class _Alloc, class _String> bool Value::isMember(std::string const& key) const { return isMember(key.data(), key.data() + key.length()); } #ifdef JSON_USE_CPPTL +template class _Alloc, class _String> bool Value::isMember(const CppTL::ConstString& key) const { return isMember(key.c_str(), key.end_c_str()); } #endif +template class _Alloc, class _String> Value::Members Value::getMemberNames() const { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, @@ -1188,10 +1285,13 @@ static bool IsIntegral(double d) { return modf(d, &integral_part) == 0.0; } +template class _Alloc, class _String> bool Value::isNull() const { return type_ == nullValue; } +template class _Alloc, class _String> bool Value::isBool() const { return type_ == booleanValue; } +template class _Alloc, class _String> bool Value::isInt() const { switch (type_) { case intValue: @@ -1207,6 +1307,7 @@ bool Value::isInt() const { return false; } +template class _Alloc, class _String> bool Value::isUInt() const { switch (type_) { case intValue: @@ -1222,6 +1323,7 @@ bool Value::isUInt() const { return false; } +template class _Alloc, class _String> bool Value::isInt64() const { #if defined(JSON_HAS_INT64) switch (type_) { @@ -1242,6 +1344,7 @@ bool Value::isInt64() const { return false; } +template class _Alloc, class _String> bool Value::isUInt64() const { #if defined(JSON_HAS_INT64) switch (type_) { @@ -1262,6 +1365,7 @@ bool Value::isUInt64() const { return false; } +template class _Alloc, class _String> bool Value::isIntegral() const { #if defined(JSON_HAS_INT64) return isInt64() || isUInt64(); @@ -1270,16 +1374,22 @@ bool Value::isIntegral() const { #endif } +template class _Alloc, class _String> bool Value::isDouble() const { return type_ == realValue || isIntegral(); } +template class _Alloc, class _String> bool Value::isNumeric() const { return isIntegral() || isDouble(); } +template class _Alloc, class _String> bool Value::isString() const { return type_ == stringValue; } +template class _Alloc, class _String> bool Value::isArray() const { return type_ == arrayValue; } +template class _Alloc, class _String> bool Value::isObject() const { return type_ == objectValue; } +template class _Alloc, class _String> void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { if (!comments_) comments_ = new CommentInfo[numberOfCommentPlacement]; @@ -1290,37 +1400,47 @@ void Value::setComment(const char* comment, size_t len, CommentPlacement placeme comments_[placement].setComment(comment, len); } +template class _Alloc, class _String> void Value::setComment(const char* comment, CommentPlacement placement) { setComment(comment, strlen(comment), placement); } +template class _Alloc, class _String> void Value::setComment(const std::string& comment, CommentPlacement placement) { setComment(comment.c_str(), comment.length(), placement); } +template class _Alloc, class _String> bool Value::hasComment(CommentPlacement placement) const { return comments_ != 0 && comments_[placement].comment_ != 0; } +template class _Alloc, class _String> std::string Value::getComment(CommentPlacement placement) const { if (hasComment(placement)) return comments_[placement].comment_; return ""; } +template class _Alloc, class _String> void Value::setOffsetStart(size_t start) { start_ = start; } +template class _Alloc, class _String> void Value::setOffsetLimit(size_t limit) { limit_ = limit; } +template class _Alloc, class _String> size_t Value::getOffsetStart() const { return start_; } +template class _Alloc, class _String> size_t Value::getOffsetLimit() const { return limit_; } +template class _Alloc, class _String> std::string Value::toStyledString() const { StyledWriter writer; return writer.write(*this); } +template class _Alloc, class _String> Value::const_iterator Value::begin() const { switch (type_) { case arrayValue: @@ -1334,6 +1454,7 @@ Value::const_iterator Value::begin() const { return const_iterator(); } +template class _Alloc, class _String> Value::const_iterator Value::end() const { switch (type_) { case arrayValue: @@ -1347,6 +1468,7 @@ Value::const_iterator Value::end() const { return const_iterator(); } +template class _Alloc, class _String> Value::iterator Value::begin() { switch (type_) { case arrayValue: @@ -1360,6 +1482,7 @@ Value::iterator Value::begin() { return iterator(); } +template class _Alloc, class _String> Value::iterator Value::end() { switch (type_) { case arrayValue: @@ -1376,20 +1499,25 @@ Value::iterator Value::end() { // class PathArgument // ////////////////////////////////////////////////////////////////// +template PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} +template PathArgument::PathArgument(ArrayIndex index) : key_(), index_(index), kind_(kindIndex) {} +template PathArgument::PathArgument(const char* key) : key_(key), index_(), kind_(kindKey) {} +template PathArgument::PathArgument(const std::string& key) : key_(key.c_str()), index_(), kind_(kindKey) {} // class Path // ////////////////////////////////////////////////////////////////// +template Path::Path(const std::string& path, const PathArgument& a1, const PathArgument& a2, @@ -1405,6 +1533,7 @@ Path::Path(const std::string& path, makePath(path, in); } +template void Path::makePath(const std::string& path, const InArgs& in) { const char* current = path.c_str(); const char* end = current + path.length(); @@ -1436,6 +1565,7 @@ void Path::makePath(const std::string& path, const InArgs& in) { } } +template void Path::addPathInArg(const std::string& /*path*/, const InArgs& in, InArgs::const_iterator& itInArg, @@ -1449,10 +1579,12 @@ void Path::addPathInArg(const std::string& /*path*/, } } +template void Path::invalidPath(const std::string& /*path*/, int /*location*/) { // Error: invalid path. } +template const Value& Path::resolve(const Value& root) const { const Value* node = &root; for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { @@ -1476,6 +1608,7 @@ const Value& Path::resolve(const Value& root) const { return *node; } +template 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) { @@ -1495,6 +1628,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const { return *node; } +template Value& Path::make(Value& root) const { Value* node = &root; for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { diff --git a/include/json/valueiterator.inl b/include/json/valueiterator.inl index ec9c85127..e1493e153 100644 --- a/include/json/valueiterator.inl +++ b/include/json/valueiterator.inl @@ -15,26 +15,32 @@ namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// +template ValueIteratorBase::ValueIteratorBase() : current_(), isNull_(true) { } +template ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator& current) : current_(current), isNull_(false) {} +template Value& ValueIteratorBase::deref() const { return current_->second; } +template void ValueIteratorBase::increment() { ++current_; } +template void ValueIteratorBase::decrement() { --current_; } +template ValueIteratorBase::difference_type ValueIteratorBase::computeDistance(const SelfType& other) const { #ifdef JSON_USE_CPPTL_SMALLMAP @@ -63,6 +69,7 @@ ValueIteratorBase::computeDistance(const SelfType& other) const { #endif } +template bool ValueIteratorBase::isEqual(const SelfType& other) const { if (isNull_) { return other.isNull_; @@ -70,11 +77,13 @@ bool ValueIteratorBase::isEqual(const SelfType& other) const { return current_ == other.current_; } +template void ValueIteratorBase::copy(const SelfType& other) { current_ = other.current_; isNull_ = other.isNull_; } +template Value ValueIteratorBase::key() const { const Value::CZString czstring = (*current_).first; if (czstring.data()) { @@ -85,6 +94,7 @@ Value ValueIteratorBase::key() const { return Value(czstring.index()); } +template UInt ValueIteratorBase::index() const { const Value::CZString czstring = (*current_).first; if (!czstring.data()) @@ -92,6 +102,7 @@ UInt ValueIteratorBase::index() const { return Value::UInt(-1); } +template std::string ValueIteratorBase::name() const { char const* keey; char const* end; @@ -100,11 +111,13 @@ std::string ValueIteratorBase::name() const { return std::string(keey, end); } +template char const* ValueIteratorBase::memberName() const { const char* cname = (*current_).first.data(); return cname ? cname : ""; } +template char const* ValueIteratorBase::memberName(char const** end) const { const char* cname = (*current_).first.data(); if (!cname) { @@ -123,15 +136,19 @@ char const* ValueIteratorBase::memberName(char const** end) const { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// +template ValueConstIterator::ValueConstIterator() {} +template ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator& current) : ValueIteratorBase(current) {} +template ValueConstIterator::ValueConstIterator(ValueIterator const& other) : ValueIteratorBase(other) {} +template ValueConstIterator& ValueConstIterator:: operator=(const ValueIteratorBase& other) { copy(other); @@ -146,19 +163,24 @@ operator=(const ValueIteratorBase& other) { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// +template ValueIterator::ValueIterator() {} +template ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) : ValueIteratorBase(current) {} +template ValueIterator::ValueIterator(const ValueConstIterator& other) : ValueIteratorBase(other) { throwRuntimeError("ConstIterator to Iterator should never be allowed."); } +template ValueIterator::ValueIterator(const ValueIterator& other) : ValueIteratorBase(other) {} +template ValueIterator& ValueIterator::operator=(const SelfType& other) { copy(other); return *this; diff --git a/include/json/writer.inl b/include/json/writer.inl index ff6acd987..9633b89d9 100644 --- a/include/json/writer.inl +++ b/include/json/writer.inl @@ -79,6 +79,7 @@ typedef std::unique_ptr StreamWriterPtr; typedef std::auto_ptr StreamWriterPtr; #endif +template std::string valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); @@ -95,6 +96,7 @@ std::string valueToString(LargestInt value) { return current; } +template std::string valueToString(LargestUInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); @@ -105,16 +107,19 @@ std::string valueToString(LargestUInt value) { #if defined(JSON_HAS_INT64) +template std::string valueToString(Int value) { return valueToString(LargestInt(value)); } +template std::string valueToString(UInt value) { return valueToString(LargestUInt(value)); } #endif // # if defined(JSON_HAS_INT64) +template std::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. @@ -145,10 +150,13 @@ std::string valueToString(double value, bool useSpecialFloats, unsigned int prec return buffer; } +template std::string valueToString(double value) { return valueToString(value, false, 17); } +template std::string valueToString(bool value) { return value ? "true" : "false"; } +template std::string valueToQuotedString(const char* value) { if (value == NULL) return ""; @@ -211,6 +219,7 @@ std::string valueToQuotedString(const char* value) { return result; } +template static std::string valueToQuotedStringN(const char* value, unsigned length) { if (value == NULL) return ""; @@ -276,21 +285,27 @@ static std::string valueToQuotedStringN(const char* value, unsigned length) { // Class Writer // ////////////////////////////////////////////////////////////////// +template Writer::~Writer() {} // Class FastWriter // ////////////////////////////////////////////////////////////////// +template FastWriter::FastWriter() : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), omitEndingLineFeed_(false) {} +template void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } +template void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } +template void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } +template std::string FastWriter::write(const Value& root) { document_ = ""; writeValue(root); @@ -299,6 +314,7 @@ std::string FastWriter::write(const Value& root) { return document_; } +template void FastWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: @@ -356,9 +372,11 @@ void FastWriter::writeValue(const Value& value) { // Class StyledWriter // ////////////////////////////////////////////////////////////////// +template StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3), addChildValues_() {} +template std::string StyledWriter::write(const Value& root) { document_ = ""; addChildValues_ = false; @@ -370,6 +388,7 @@ std::string StyledWriter::write(const Value& root) { return document_; } +template void StyledWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: @@ -429,6 +448,7 @@ void StyledWriter::writeValue(const Value& value) { } } +template void StyledWriter::writeArrayValue(const Value& value) { unsigned size = value.size(); if (size == 0) @@ -472,6 +492,7 @@ void StyledWriter::writeArrayValue(const Value& value) { } } +template bool StyledWriter::isMultineArray(const Value& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; @@ -499,6 +520,7 @@ bool StyledWriter::isMultineArray(const Value& value) { return isMultiLine; } +template void StyledWriter::pushValue(const std::string& value) { if (addChildValues_) childValues_.push_back(value); @@ -506,6 +528,7 @@ void StyledWriter::pushValue(const std::string& value) { document_ += value; } +template void StyledWriter::writeIndent() { if (!document_.empty()) { char last = document_[document_.length() - 1]; @@ -517,18 +540,22 @@ void StyledWriter::writeIndent() { document_ += indentString_; } +template void StyledWriter::writeWithIndent(const std::string& value) { writeIndent(); document_ += value; } +template void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } +template void StyledWriter::unindent() { assert(int(indentString_.size()) >= indentSize_); indentString_.resize(indentString_.size() - indentSize_); } +template void StyledWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; @@ -549,6 +576,7 @@ void StyledWriter::writeCommentBeforeValue(const Value& root) { document_ += "\n"; } +template void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) document_ += " " + root.getComment(commentAfterOnSameLine); @@ -560,6 +588,7 @@ void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { } } +template bool StyledWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || @@ -569,10 +598,12 @@ bool StyledWriter::hasCommentForValue(const Value& value) { // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// +template StyledStreamWriter::StyledStreamWriter(std::string indentation) : document_(NULL), rightMargin_(74), indentation_(indentation), addChildValues_() {} +template void StyledStreamWriter::write(std::ostream& out, const Value& root) { document_ = &out; addChildValues_ = false; @@ -587,6 +618,7 @@ void StyledStreamWriter::write(std::ostream& out, const Value& root) { document_ = NULL; // Forget the stream, for safety. } +template void StyledStreamWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: @@ -646,6 +678,7 @@ void StyledStreamWriter::writeValue(const Value& value) { } } +template void StyledStreamWriter::writeArrayValue(const Value& value) { unsigned size = value.size(); if (size == 0) @@ -691,6 +724,7 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { } } +template bool StyledStreamWriter::isMultineArray(const Value& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; @@ -718,6 +752,7 @@ bool StyledStreamWriter::isMultineArray(const Value& value) { return isMultiLine; } +template void StyledStreamWriter::pushValue(const std::string& value) { if (addChildValues_) childValues_.push_back(value); @@ -725,6 +760,7 @@ void StyledStreamWriter::pushValue(const std::string& value) { *document_ << value; } +template void StyledStreamWriter::writeIndent() { // blep intended this to look at the so-far-written string // to determine whether we are already indented, but @@ -733,19 +769,23 @@ void StyledStreamWriter::writeIndent() { *document_ << '\n' << indentString_; } +template void StyledStreamWriter::writeWithIndent(const std::string& value) { if (!indented_) writeIndent(); *document_ << value; indented_ = false; } +template void StyledStreamWriter::indent() { indentString_ += indentation_; } +template void StyledStreamWriter::unindent() { assert(indentString_.size() >= indentation_.size()); indentString_.resize(indentString_.size() - indentation_.size()); } +template void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; @@ -764,6 +804,7 @@ void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { indented_ = false; } +template void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) *document_ << ' ' << root.getComment(commentAfterOnSameLine); @@ -775,6 +816,7 @@ void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { indented_ = false; } +template bool StyledStreamWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || @@ -794,6 +836,7 @@ struct CommentStyle { }; }; +template struct BuiltStyledStreamWriter : public StreamWriter { BuiltStyledStreamWriter( @@ -833,6 +876,7 @@ private: bool useSpecialFloats_ : 1; unsigned int precision_; }; +template BuiltStyledStreamWriter::BuiltStyledStreamWriter( std::string const& indentation, CommentStyle::Enum cs, @@ -853,6 +897,7 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter( , precision_(precision) { } +template int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) { sout_ = sout; @@ -868,6 +913,7 @@ int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) sout_ = NULL; return 0; } +template void BuiltStyledStreamWriter::writeValue(Value const& value) { switch (value.type()) { case nullValue: @@ -927,6 +973,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { } } +template void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { unsigned size = value.size(); if (size == 0) @@ -974,6 +1021,7 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { } } +template bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; @@ -1001,6 +1049,7 @@ bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { return isMultiLine; } +template void BuiltStyledStreamWriter::pushValue(std::string const& value) { if (addChildValues_) childValues_.push_back(value); @@ -1008,6 +1057,7 @@ void BuiltStyledStreamWriter::pushValue(std::string const& value) { *sout_ << value; } +template void BuiltStyledStreamWriter::writeIndent() { // blep intended this to look at the so-far-written string // to determine whether we are already indented, but @@ -1020,19 +1070,23 @@ void BuiltStyledStreamWriter::writeIndent() { } } +template void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { if (!indented_) writeIndent(); *sout_ << value; indented_ = false; } +template void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } +template void BuiltStyledStreamWriter::unindent() { assert(indentString_.size() >= indentation_.size()); indentString_.resize(indentString_.size() - indentation_.size()); } +template void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { if (cs_ == CommentStyle::None) return; if (!root.hasComment(commentBefore)) @@ -1052,6 +1106,7 @@ void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { indented_ = false; } +template void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { if (cs_ == CommentStyle::None) return; if (root.hasComment(commentAfterOnSameLine)) @@ -1064,6 +1119,7 @@ void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root } // static +template bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || @@ -1073,21 +1129,27 @@ bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { /////////////// // StreamWriter +template StreamWriter::StreamWriter() : sout_(NULL) { } +template StreamWriter::~StreamWriter() { } +template StreamWriter::Factory::~Factory() {} +template StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } +template StreamWriterBuilder::~StreamWriterBuilder() {} +template StreamWriter* StreamWriterBuilder::newStreamWriter() const { std::string indentation = settings_["indentation"].asString(); @@ -1120,6 +1182,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const indentation, cs, colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); } +template static void getValidWriterKeys(std::set* valid_keys) { valid_keys->clear(); @@ -1130,6 +1193,7 @@ static void getValidWriterKeys(std::set* valid_keys) valid_keys->insert("useSpecialFloats"); valid_keys->insert("precision"); } +template bool StreamWriterBuilder::validate(Json::Value* invalid) const { Json::Value my_invalid; @@ -1147,11 +1211,13 @@ bool StreamWriterBuilder::validate(Json::Value* invalid) const } return 0u == inv.size(); } +template Value& StreamWriterBuilder::operator[](std::string key) { return settings_[key]; } // static +template void StreamWriterBuilder::setDefaults(Json::Value* settings) { //! [StreamWriterBuilderDefaults] @@ -1164,6 +1230,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings) //! [StreamWriterBuilderDefaults] } +template std::string writeString(StreamWriter::Factory const& builder, Value const& root) { std::ostringstream sout; StreamWriterPtr const writer(builder.newStreamWriter()); @@ -1171,6 +1238,7 @@ std::string writeString(StreamWriter::Factory const& builder, Value const& root) return sout.str(); } +template std::ostream& operator<<(std::ostream& sout, Value const& root) { StreamWriterBuilder builder; StreamWriterPtr const writer(builder.newStreamWriter()); From 0092dd7b7a6514bed58c47b6b96fc26f1f8de3ff Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 14:09:43 +0000 Subject: [PATCH 09/41] typedef's for classes that have been made templated Using typedef's for classes that have been templated makes it easier for backward compatibility. To do this and retain the naming the classes that have been made templates the original classes that have been templated have been moved to the namespace "detail" --- include/json/forwards.h | 13 ++++++++----- include/json/reader.h | 7 +++++++ include/json/reader.inl | 4 +++- include/json/value.h | 13 ++++++++++++- include/json/value.inl | 4 ++++ include/json/valueiterator.inl | 2 ++ include/json/writer.h | 12 ++++++++++++ include/json/writer.inl | 2 ++ 8 files changed, 50 insertions(+), 7 deletions(-) diff --git a/include/json/forwards.h b/include/json/forwards.h index cd9165f54..97959a5bb 100644 --- a/include/json/forwards.h +++ b/include/json/forwards.h @@ -12,6 +12,13 @@ namespace Json { +// features.h +class Features; +class StaticString; +typedef unsigned int ArrayIndex; + +namespace detail { + // writer.h template class FastWriter; @@ -22,12 +29,7 @@ class StyledWriter; template class Reader; -// features.h -class Features; - // value.h -typedef unsigned int ArrayIndex; -class StaticString; template class Path; template @@ -42,6 +44,7 @@ class ValueIterator; template class ValueConstIterator; +} // namespace detail } // namespace Json #endif // JSON_FORWARDS_H_INCLUDED diff --git a/include/json/reader.h b/include/json/reader.h index 6391dfb27..615de7ec4 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -24,6 +24,7 @@ #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) namespace Json { +namespace detail { /** \brief Unserialize a JSON document into a *Value. @@ -400,6 +401,12 @@ bool JSON_API parseFromStream( template JSON_API std::istream& operator>>(std::istream&, Value&); +} // namespace detail + +typedef detail::CharReader> CharReader; // class Json::CharReader +typedef detail::CharReaderBuilder> CharReaderBuilder; // class Json::CharReaderBuilder +typedef detail::Reader> Reader; // class Json::Reader + } // namespace Json #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) diff --git a/include/json/reader.inl b/include/json/reader.inl index 988277ed2..fd1623225 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -46,7 +46,8 @@ static int const stackLimit_g = 1000; static int stackDepth_g = 0; // see readValue() namespace Json { - +namespace detail { + #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) typedef std::unique_ptr CharReaderPtr; #else @@ -2093,4 +2094,5 @@ std::istream& operator>>(std::istream& sin, Value& root) { return sin; } +} // namespace detail } // namespace Json diff --git a/include/json/value.h b/include/json/value.h index 214a35db7..f34ef074a 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -33,7 +33,7 @@ */ namespace Json { -/** Base class for all exceptions we throw. + /** Base class for all exceptions we throw. * * We use nothing but these internally. Of course, STL can throw others. */ @@ -125,6 +125,8 @@ class JSON_API StaticString { const char* c_str_; }; +namespace detail { + /** \brief Represents a JSON value. * * This class is a discriminated union wrapper that can represents a: @@ -839,6 +841,15 @@ class JSON_API ValueIterator : public ValueIteratorBase { pointer operator->() const { return &deref(); } }; +} // namespace detail + +typedef detail::Path> Path; // class Json::Path +typedef detail::PathArgument> PathArgument; // class Json::PathArgument +typedef detail::Value<> Value; // class Json::Value +typedef detail::ValueConstIterator> ValueConstIterator; // class Json::ValueConstIterator +typedef detail::ValueIterator> ValueIterator; // class Json::ValueIterator +typedef detail::ValueIteratorBase> ValueIteratorBase; // class Json::ValueIteratorBase + } // namespace Json diff --git a/include/json/value.inl b/include/json/value.inl index 7f28f96ce..200d0468c 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -22,6 +22,7 @@ #define JSON_ASSERT_UNREACHABLE assert(false) namespace Json { +namespace detail { // 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 @@ -140,6 +141,7 @@ inline static void decodePrefixedString( template static inline void releaseStringValue(char* value) { free(value); } +} // namespace detail } // namespace Json // ////////////////////////////////////////////////////////////////// @@ -155,6 +157,7 @@ static inline void releaseStringValue(char* value) { free(value); } #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { +namespace detail { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -1648,4 +1651,5 @@ Value& Path::make(Value& root) const { return *node; } +} // namespace detail } // namespace Json diff --git a/include/json/valueiterator.inl b/include/json/valueiterator.inl index e1493e153..98af70338 100644 --- a/include/json/valueiterator.inl +++ b/include/json/valueiterator.inl @@ -6,6 +6,7 @@ // included by json_value.cpp namespace Json { +namespace detail { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -186,4 +187,5 @@ ValueIterator& ValueIterator::operator=(const SelfType& other) { return *this; } +} // namespace detail } // namespace Json diff --git a/include/json/writer.h b/include/json/writer.h index 8c8582c68..fc7c19cd8 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -21,7 +21,10 @@ #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) namespace Json { +namespace detail { +template class _Alloc = std::allocator, + class _String = std::basic_string, std::allocator>> class Value; /** @@ -337,6 +340,15 @@ std::string JSON_API valueToQuotedString(const char* value); template JSON_API std::ostream& operator<<(std::ostream&, const Value& root); +} // namespace detail + +typedef detail::FastWriter> FastWriter; // class Json::FastWriter +typedef detail::StreamWriter> StreamWriter; // class Json::StreamWriter +typedef detail::StreamWriterBuilder> StreamWriterBuilder; // class Json::StreamWriterBuilder +typedef detail::StyledStreamWriter> StyledStreamWriter; // class Json::StyledStreamWriter +typedef detail::StyledWriter> StyledWriter; // class Json::StyledWriter +typedef detail::Writer> Writer; // class Json::Writer + } // namespace Json #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) diff --git a/include/json/writer.inl b/include/json/writer.inl index 9633b89d9..26b842069 100644 --- a/include/json/writer.inl +++ b/include/json/writer.inl @@ -72,6 +72,7 @@ #endif namespace Json { +namespace detail { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) typedef std::unique_ptr StreamWriterPtr; @@ -1246,4 +1247,5 @@ std::ostream& operator<<(std::ostream& sout, Value const& root) { return sout; } +} // namespace detail } // namespace Json From 027ee3d5369d635cb34d60aeb767957d80893c19 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 21:12:35 +0000 Subject: [PATCH 10/41] Fix up code to execute Now with the templates in place we need to have the code so that it compiles, this is going through and adding typename in appropriate places and the links to the template names throughout the class names to get it to compile. --- include/json/forwards.h | 3 +- include/json/reader.h | 42 +-- include/json/reader.inl | 347 ++++++++--------- include/json/value.h | 97 ++--- include/json/value.inl | 670 +++++++++++++++++---------------- include/json/valueiterator.inl | 76 ++-- include/json/writer.h | 62 ++- include/json/writer.inl | 297 ++++++++------- src/jsontestrunner/main.cpp | 6 +- src/lib_json/json_reader.cpp | 7 +- src/lib_json/json_writer.cpp | 9 +- src/test_lib_json/jsontest.cpp | 4 +- src/test_lib_json/main.cpp | 32 +- 13 files changed, 840 insertions(+), 812 deletions(-) diff --git a/include/json/forwards.h b/include/json/forwards.h index 97959a5bb..77ecef426 100644 --- a/include/json/forwards.h +++ b/include/json/forwards.h @@ -34,8 +34,7 @@ template class Path; template class PathArgument; -template class _Alloc = std::allocator, - class _String = std::basic_string, std::allocator>> +template class Value; template class ValueIteratorBase; diff --git a/include/json/reader.h b/include/json/reader.h index 615de7ec4..2435f061d 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -74,7 +74,7 @@ class JSON_API Reader { * error occurred. */ bool - parse(const std::string& document, Value& root, bool collectComments = true); + parse(const std::string& document, _Value& root, bool collectComments = true); /** \brief Read a Value from a JSON document. @@ -96,12 +96,12 @@ class JSON_API Reader { */ bool parse(const char* beginDoc, const char* endDoc, - Value& root, + _Value& root, bool collectComments = true); /// \brief Parse from input stream. /// \see Json::operator>>(std::istream&, Json::Value&). - bool parse(std::istream& is, Value& root, bool collectComments = true); + bool parse(std::istream& is, _Value& root, bool collectComments = true); /** \brief Returns a user friendly string that list errors in the parsed * document. @@ -140,7 +140,7 @@ class JSON_API Reader { * \return \c true if the error was successfully added, \c false if the * Value offset exceeds the document size. */ - bool pushError(const Value& value, const std::string& message); + bool pushError(const _Value& value, const std::string& message); /** \brief Add a semantic error message with extra context. * \param value JSON Value location associated with the error @@ -149,7 +149,7 @@ class JSON_API Reader { * \return \c true if the error was successfully added, \c false if either * Value offset exceeds the document size. */ - bool pushError(const Value& value, const std::string& message, const Value& extra); + bool pushError(const _Value& value, const std::string& message, const _Value& extra); /** \brief Return whether there are any errors. * \return \c true if there are no errors to report \c false if @@ -203,11 +203,11 @@ class JSON_API Reader { bool readObject(Token& token); bool readArray(Token& token); bool decodeNumber(Token& token); - bool decodeNumber(Token& token, Value& decoded); + bool decodeNumber(Token& token, _Value& decoded); bool decodeString(Token& token); bool decodeString(Token& token, std::string& decoded); bool decodeDouble(Token& token); - bool decodeDouble(Token& token, Value& decoded); + bool decodeDouble(Token& token, _Value& decoded); bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, @@ -222,7 +222,7 @@ class JSON_API Reader { Token& token, TokenType skipUntilToken); void skipUntilSpace(); - Value& currentValue(); + _Value& currentValue(); Char getNextChar(); void getLocationLineAndColumn(Location location, int& line, int& column) const; @@ -230,7 +230,7 @@ class JSON_API Reader { void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); - typedef std::stack Nodes; + typedef std::stack<_Value*> Nodes; Nodes nodes_; Errors errors_; std::string document_; @@ -238,7 +238,7 @@ class JSON_API Reader { Location end_; Location current_; Location lastValueEnd_; - Value* lastValue_; + _Value* lastValue_; std::string commentsBefore_; Features features_; bool collectComments_; @@ -269,7 +269,7 @@ class JSON_API CharReader { */ virtual bool parse( char const* beginDoc, char const* endDoc, - Value* root, std::string* errs) = 0; + _Value* root, std::string* errs) = 0; class JSON_API Factory { public: @@ -294,7 +294,7 @@ class JSON_API CharReader { \endcode */ template -class JSON_API CharReaderBuilder : public CharReader::Factory { +class JSON_API CharReaderBuilder : public CharReader<_Value>::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. @@ -334,34 +334,34 @@ class JSON_API CharReaderBuilder : public CharReader::Factory { JSON Value. \sa setDefaults() */ - Json::Value settings_; + _Value settings_; CharReaderBuilder(); ~CharReaderBuilder() override; - CharReader* newCharReader() const override; + CharReader<_Value>* newCharReader() const override; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. */ - bool validate(Json::Value* invalid) const; + bool validate(_Value* invalid) const; /** A simple way to update a specific setting. */ - Value& operator[](std::string key); + _Value& operator[](std::string key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) * \remark Defaults: * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults */ - static void setDefaults(Json::Value* settings); + static void setDefaults(_Value* settings); /** Same as old Features::strictMode(). * \pre 'settings' != NULL (but Json::null is fine) * \remark Defaults: * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode */ - static void strictMode(Json::Value* settings); + static void strictMode(_Value* settings); }; /** Consume entire stream and use its begin/end. @@ -370,9 +370,9 @@ class JSON_API CharReaderBuilder : public CharReader::Factory { */ template bool JSON_API parseFromStream( - CharReader::Factory const&, + typename CharReader<_Value>::Factory const&, std::istream&, - Value* root, std::string* errs); + _Value* root, std::string* errs); /** \brief Read from 'sin' into 'root'. @@ -399,7 +399,7 @@ bool JSON_API parseFromStream( \see Json::operator<<() */ template -JSON_API std::istream& operator>>(std::istream&, Value&); +JSON_API std::istream& operator>>(std::istream&, _Value&); } // namespace detail diff --git a/include/json/reader.inl b/include/json/reader.inl index fd1623225..d86309301 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -7,7 +7,7 @@ #include "assertions.h" #include "reader.h" #include "value.h" -#include "json_tool.h" +#include "tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include @@ -47,18 +47,12 @@ static int stackDepth_g = 0; // see readValue() namespace Json { namespace detail { - -#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr CharReaderPtr; -#else -typedef std::auto_ptr CharReaderPtr; -#endif // Implementation of class Reader // //////////////////////////////// template -static bool containsNewLine(Reader::Location begin, Reader::Location end) { +static bool containsNewLine(typename Reader<_Value>::Location begin, typename Reader<_Value>::Location end) { for (; begin < end; ++begin) if (*begin == '\n' || *begin == '\r') return true; @@ -69,20 +63,20 @@ static bool containsNewLine(Reader::Location begin, Reader::Location end) { // ////////////////////////////////////////////////////////////////// template -Reader::Reader() +Reader<_Value>::Reader() : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), lastValue_(), commentsBefore_(), features_(Features::all()), collectComments_() {} template -Reader::Reader(const Features& features) +Reader<_Value>::Reader(const Features& features) : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), lastValue_(), commentsBefore_(), features_(features), collectComments_() { } template bool -Reader::parse(const std::string& document, Value& root, bool collectComments) { +Reader<_Value>::parse(const std::string& document, _Value& root, bool collectComments) { document_ = document; const char* begin = document_.c_str(); const char* end = begin + document_.length(); @@ -90,7 +84,7 @@ Reader::parse(const std::string& document, Value& root, bool collectComments) { } template -bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { +bool Reader<_Value>::parse(std::istream& sin, _Value& root, bool collectComments) { // std::istream_iterator begin(sin); // std::istream_iterator end; // Those would allow streamed input from a file, if parse() were a @@ -104,9 +98,9 @@ bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { } template -bool Reader::parse(const char* beginDoc, +bool Reader<_Value>::parse(const char* beginDoc, const char* endDoc, - Value& root, + _Value& root, bool collectComments) { if (!features_.allowComments_) { collectComments = false; @@ -147,7 +141,7 @@ bool Reader::parse(const char* beginDoc, } template -bool Reader::readValue() { +bool Reader<_Value>::readValue() { // This is a non-reentrant way to support a stackLimit. Terrible! // But this deprecated class has a security problem: Bad input can // cause a seg-fault. This seems like a fair, binary-compatible way @@ -181,7 +175,7 @@ bool Reader::readValue() { break; case tokenTrue: { - Value v(true); + _Value v(true); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -189,7 +183,7 @@ bool Reader::readValue() { break; case tokenFalse: { - Value v(false); + _Value v(false); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -197,7 +191,7 @@ bool Reader::readValue() { break; case tokenNull: { - Value v; + _Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -210,7 +204,7 @@ bool Reader::readValue() { // "Un-read" the current token and mark the current value as a null // token. current_--; - Value v; + _Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(current_ - begin_ - 1); currentValue().setOffsetLimit(current_ - begin_); @@ -232,7 +226,7 @@ bool Reader::readValue() { } template -void Reader::skipCommentTokens(Token& token) { +void Reader<_Value>::skipCommentTokens(Token& token) { if (features_.allowComments_) { do { readToken(token); @@ -243,7 +237,7 @@ void Reader::skipCommentTokens(Token& token) { } template -bool Reader::readToken(Token& token) { +bool Reader<_Value>::readToken(Token& token) { skipSpaces(); token.start_ = current_; Char c = getNextChar(); @@ -314,7 +308,8 @@ bool Reader::readToken(Token& token) { return true; } -void Reader::skipSpaces() { +template +void Reader<_Value>::skipSpaces() { while (current_ != end_) { Char c = *current_; if (c == ' ' || c == '\t' || c == '\r' || c == '\n') @@ -325,7 +320,7 @@ void Reader::skipSpaces() { } template -bool Reader::match(Location pattern, int patternLength) { +bool Reader<_Value>::match(Location pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; @@ -337,7 +332,7 @@ bool Reader::match(Location pattern, int patternLength) { } template -bool Reader::readComment() { +bool Reader<_Value>::readComment() { Location commentBegin = current_ - 1; Char c = getNextChar(); bool successful = false; @@ -350,8 +345,8 @@ bool Reader::readComment() { if (collectComments_) { CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) + if (lastValueEnd_ && !containsNewLine<_Value>(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine<_Value>(commentBegin, current_)) placement = commentAfterOnSameLine; } @@ -361,10 +356,10 @@ bool Reader::readComment() { } template -static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { +static std::string normalizeEOL(typename Reader<_Value>::Location begin, typename Reader<_Value>::Location end) { std::string normalized; normalized.reserve(end - begin); - Reader::Location current = begin; + typename Reader<_Value>::Location current = begin; while (current != end) { char c = *current++; if (c == '\r') { @@ -382,9 +377,9 @@ static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { template void -Reader::addComment(Location begin, Location end, CommentPlacement placement) { +Reader<_Value>::addComment(Location begin, Location end, CommentPlacement placement) { assert(collectComments_); - const std::string& normalized = normalizeEOL(begin, end); + const std::string& normalized = normalizeEOL<_Value>(begin, end); if (placement == commentAfterOnSameLine) { assert(lastValue_ != 0); lastValue_->setComment(normalized, placement); @@ -394,7 +389,7 @@ Reader::addComment(Location begin, Location end, CommentPlacement placement) { } template -bool Reader::readCStyleComment() { +bool Reader<_Value>::readCStyleComment() { while (current_ != end_) { Char c = getNextChar(); if (c == '*' && *current_ == '/') @@ -404,7 +399,7 @@ bool Reader::readCStyleComment() { } template -bool Reader::readCppStyleComment() { +bool Reader<_Value>::readCppStyleComment() { while (current_ != end_) { Char c = getNextChar(); if (c == '\n') @@ -421,7 +416,7 @@ bool Reader::readCppStyleComment() { } template -void Reader::readNumber() { +void Reader<_Value>::readNumber() { const char *p = current_; char c = '0'; // stopgap for already consumed character // integral part @@ -444,7 +439,7 @@ void Reader::readNumber() { } template -bool Reader::readString() { +bool Reader<_Value>::readString() { Char c = 0; while (current_ != end_) { c = getNextChar(); @@ -457,10 +452,10 @@ bool Reader::readString() { } template -bool Reader::readObject(Token& tokenStart) { +bool Reader<_Value>::readObject(Token& tokenStart) { Token tokenName; std::string name; - Value init(objectValue); + _Value init(objectValue); currentValue().swapPayload(init); currentValue().setOffsetStart(tokenStart.start_ - begin_); while (readToken(tokenName)) { @@ -476,7 +471,7 @@ bool Reader::readObject(Token& tokenStart) { if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { - Value numberName; + _Value numberName; if (!decodeNumber(tokenName, numberName)) return recoverFromError(tokenObjectEnd); name = numberName.asString(); @@ -489,7 +484,7 @@ bool Reader::readObject(Token& tokenStart) { return addErrorAndRecover( "Missing ':' after object member name", colon, tokenObjectEnd); } - Value& value = currentValue()[name]; + _Value& value = currentValue()[name]; nodes_.push(&value); bool ok = readValue(); nodes_.pop(); @@ -514,8 +509,8 @@ bool Reader::readObject(Token& tokenStart) { } template -bool Reader::readArray(Token& tokenStart) { - Value init(arrayValue); +bool Reader<_Value>::readArray(Token& tokenStart) { + _Value init(arrayValue); currentValue().swapPayload(init); currentValue().setOffsetStart(tokenStart.start_ - begin_); skipSpaces(); @@ -527,7 +522,7 @@ bool Reader::readArray(Token& tokenStart) { } int index = 0; for (;;) { - Value& value = currentValue()[index++]; + _Value& value = currentValue()[index++]; nodes_.push(&value); bool ok = readValue(); nodes_.pop(); @@ -553,8 +548,8 @@ bool Reader::readArray(Token& tokenStart) { } template -bool Reader::decodeNumber(Token& token) { - Value decoded; +bool Reader<_Value>::decodeNumber(Token& token) { + _Value decoded; if (!decodeNumber(token, decoded)) return false; currentValue().swapPayload(decoded); @@ -564,7 +559,7 @@ bool Reader::decodeNumber(Token& token) { } template -bool Reader::decodeNumber(Token& token, Value& decoded) { +bool Reader<_Value>::decodeNumber(Token& token, _Value& decoded) { // Attempts to parse the number as an integer. If the number is // larger than the maximum supported value of an integer then // we decode the number as a double. @@ -573,16 +568,16 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { 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::maxLargestInt) + 1 - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; - Value::LargestUInt value = 0; + typename _Value::LargestUInt maxIntegerValue = + isNegative ? typename _Value::LargestUInt(_Value::maxLargestInt) + 1 + : _Value::maxLargestUInt; + typename _Value::LargestUInt threshold = maxIntegerValue / 10; + typename _Value::LargestUInt value = 0; while (current < token.end_) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); - Value::UInt digit(c - '0'); + typename _Value::UInt digit(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 @@ -596,19 +591,19 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { value = value * 10 + digit; } if (isNegative && value == maxIntegerValue) - decoded = Value::minLargestInt; + decoded = _Value::minLargestInt; else if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) - decoded = Value::LargestInt(value); + decoded = -typename _Value::LargestInt(value); + else if (value <= typename _Value::LargestUInt(_Value::maxInt)) + decoded = typename _Value::LargestInt(value); else decoded = value; return true; } template -bool Reader::decodeDouble(Token& token) { - Value decoded; +bool Reader<_Value>::decodeDouble(Token& token) { + _Value decoded; if (!decodeDouble(token, decoded)) return false; currentValue().swapPayload(decoded); @@ -618,7 +613,7 @@ bool Reader::decodeDouble(Token& token) { } template -bool Reader::decodeDouble(Token& token, Value& decoded) { +bool Reader<_Value>::decodeDouble(Token& token, _Value& decoded) { double value = 0; std::string buffer(token.start_, token.end_); std::istringstream is(buffer); @@ -631,11 +626,11 @@ bool Reader::decodeDouble(Token& token, Value& decoded) { } template -bool Reader::decodeString(Token& token) { +bool Reader<_Value>::decodeString(Token& token) { std::string decoded_string; if (!decodeString(token, decoded_string)) return false; - Value decoded(decoded_string); + _Value decoded(decoded_string); currentValue().swapPayload(decoded); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -643,7 +638,7 @@ bool Reader::decodeString(Token& token) { } template -bool Reader::decodeString(Token& token, std::string& decoded) { +bool Reader<_Value>::decodeString(Token& token, std::string& decoded) { decoded.reserve(token.end_ - token.start_ - 2); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' @@ -697,7 +692,7 @@ bool Reader::decodeString(Token& token, std::string& decoded) { } template -bool Reader::decodeUnicodeCodePoint(Token& token, +bool Reader<_Value>::decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode) { @@ -727,7 +722,7 @@ bool Reader::decodeUnicodeCodePoint(Token& token, } template -bool Reader::decodeUnicodeEscapeSequence(Token& token, +bool Reader<_Value>::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode) { @@ -757,7 +752,7 @@ bool Reader::decodeUnicodeEscapeSequence(Token& token, template bool -Reader::addError(const std::string& message, Token& token, Location extra) { +Reader<_Value>::addError(const std::string& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -767,7 +762,7 @@ Reader::addError(const std::string& message, Token& token, Location extra) { } template -bool Reader::recoverFromError(TokenType skipUntilToken) { +bool Reader<_Value>::recoverFromError(TokenType skipUntilToken) { int errorCount = int(errors_.size()); Token skip; for (;;) { @@ -781,7 +776,7 @@ bool Reader::recoverFromError(TokenType skipUntilToken) { } template -bool Reader::addErrorAndRecover(const std::string& message, +bool Reader<_Value>::addErrorAndRecover(const std::string& message, Token& token, TokenType skipUntilToken) { addError(message, token); @@ -789,17 +784,17 @@ bool Reader::addErrorAndRecover(const std::string& message, } template -Value& Reader::currentValue() { return *(nodes_.top()); } +_Value& Reader<_Value>::currentValue() { return *(nodes_.top()); } template -Reader::Char Reader::getNextChar() { +typename Reader<_Value>::Char Reader<_Value>::getNextChar() { if (current_ == end_) return 0; return *current_++; } template -void Reader::getLocationLineAndColumn(Location location, +void Reader<_Value>::getLocationLineAndColumn(Location location, int& line, int& column) const { Location current = begin_; @@ -823,7 +818,7 @@ void Reader::getLocationLineAndColumn(Location location, } template -std::string Reader::getLocationLineAndColumn(Location location) const { +std::string Reader<_Value>::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; @@ -833,14 +828,14 @@ std::string Reader::getLocationLineAndColumn(Location location) const { // Deprecated. Preserved for backward compatibility template -std::string Reader::getFormatedErrorMessages() const { +std::string Reader<_Value>::getFormatedErrorMessages() const { return getFormattedErrorMessages(); } template -std::string Reader::getFormattedErrorMessages() const { +std::string Reader<_Value>::getFormattedErrorMessages() const { std::string formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); + for (typename Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) { const ErrorInfo& error = *itError; @@ -855,9 +850,9 @@ std::string Reader::getFormattedErrorMessages() const { } template -std::vector Reader::getStructuredErrors() const { +std::vector::StructuredError> Reader<_Value>::getStructuredErrors() const { std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); + for (typename Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) { const ErrorInfo& error = *itError; @@ -871,7 +866,7 @@ std::vector Reader::getStructuredErrors() const { } template -bool Reader::pushError(const Value& value, const std::string& message) { +bool Reader<_Value>::pushError(const _Value& value, const std::string& message) { size_t length = end_ - begin_; if(value.getOffsetStart() > length || value.getOffsetLimit() > length) @@ -889,7 +884,7 @@ bool Reader::pushError(const Value& value, const std::string& message) { } template -bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) { +bool Reader<_Value>::pushError(const _Value& value, const std::string& message, const _Value& extra) { size_t length = end_ - begin_; if(value.getOffsetStart() > length || value.getOffsetLimit() > length @@ -908,7 +903,7 @@ bool Reader::pushError(const Value& value, const std::string& message, const Val } template -bool Reader::good() const { +bool Reader<_Value>::good() const { return !errors_.size(); } @@ -942,15 +937,15 @@ public: std::string message; }; - OurReader(OurFeatures const& features); + OurReader(Json::detail::OurFeatures const& features); bool parse(const char* beginDoc, const char* endDoc, - Value& root, + _Value& root, bool collectComments = true); std::string getFormattedErrorMessages() const; std::vector getStructuredErrors() const; - bool pushError(const Value& value, const std::string& message); - bool pushError(const Value& value, const std::string& message, const Value& extra); + bool pushError(const _Value& value, const std::string& message); + bool pushError(const _Value& value, const std::string& message, const _Value& extra); bool good() const; private: @@ -1006,11 +1001,11 @@ private: bool readObject(Token& token); bool readArray(Token& token); bool decodeNumber(Token& token); - bool decodeNumber(Token& token, Value& decoded); + bool decodeNumber(Token& token, _Value& decoded); bool decodeString(Token& token); bool decodeString(Token& token, std::string& decoded); bool decodeDouble(Token& token); - bool decodeDouble(Token& token, Value& decoded); + bool decodeDouble(Token& token, _Value& decoded); bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, @@ -1025,7 +1020,7 @@ private: Token& token, TokenType skipUntilToken); void skipUntilSpace(); - Value& currentValue(); + _Value& currentValue(); Char getNextChar(); void getLocationLineAndColumn(Location location, int& line, int& column) const; @@ -1033,7 +1028,7 @@ private: void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); - typedef std::stack Nodes; + typedef std::stack<_Value*> Nodes; Nodes nodes_; Errors errors_; std::string document_; @@ -1041,7 +1036,7 @@ private: Location end_; Location current_; Location lastValueEnd_; - Value* lastValue_; + _Value* lastValue_; std::string commentsBefore_; int stackDepth_; @@ -1052,7 +1047,7 @@ private: // complete copy of Read impl, for OurReader template -OurReader::OurReader(OurFeatures const& features) +OurReader<_Value>::OurReader(OurFeatures const& features) : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), lastValue_(), commentsBefore_(), stackDepth_(0), @@ -1060,9 +1055,9 @@ OurReader::OurReader(OurFeatures const& features) } template -bool OurReader::parse(const char* beginDoc, +bool OurReader<_Value>::parse(const char* beginDoc, const char* endDoc, - Value& root, + _Value& root, bool collectComments) { if (!features_.allowComments_) { collectComments = false; @@ -1109,7 +1104,7 @@ bool OurReader::parse(const char* beginDoc, } template -bool OurReader::readValue() { +bool OurReader<_Value>::readValue() { if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); ++stackDepth_; Token token; @@ -1138,7 +1133,7 @@ bool OurReader::readValue() { break; case tokenTrue: { - Value v(true); + _Value v(true); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -1146,7 +1141,7 @@ bool OurReader::readValue() { break; case tokenFalse: { - Value v(false); + _Value v(false); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -1154,7 +1149,7 @@ bool OurReader::readValue() { break; case tokenNull: { - Value v; + _Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -1162,7 +1157,7 @@ bool OurReader::readValue() { break; case tokenNaN: { - Value v(std::numeric_limits::quiet_NaN()); + _Value v(std::numeric_limits::quiet_NaN()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -1170,7 +1165,7 @@ bool OurReader::readValue() { break; case tokenPosInf: { - Value v(std::numeric_limits::infinity()); + _Value v(std::numeric_limits::infinity()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -1178,7 +1173,7 @@ bool OurReader::readValue() { break; case tokenNegInf: { - Value v(-std::numeric_limits::infinity()); + _Value v(-std::numeric_limits::infinity()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -1191,7 +1186,7 @@ bool OurReader::readValue() { // "Un-read" the current token and mark the current value as a null // token. current_--; - Value v; + _Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(current_ - begin_ - 1); currentValue().setOffsetLimit(current_ - begin_); @@ -1213,7 +1208,7 @@ bool OurReader::readValue() { } template -void OurReader::skipCommentTokens(Token& token) { +void OurReader<_Value>::skipCommentTokens(Token& token) { if (features_.allowComments_) { do { readToken(token); @@ -1224,7 +1219,7 @@ void OurReader::skipCommentTokens(Token& token) { } template -bool OurReader::readToken(Token& token) { +bool OurReader<_Value>::readToken(Token& token) { skipSpaces(); token.start_ = current_; Char c = getNextChar(); @@ -1325,7 +1320,7 @@ bool OurReader::readToken(Token& token) { } template -void OurReader::skipSpaces() { +void OurReader<_Value>::skipSpaces() { while (current_ != end_) { Char c = *current_; if (c == ' ' || c == '\t' || c == '\r' || c == '\n') @@ -1336,7 +1331,7 @@ void OurReader::skipSpaces() { } template -bool OurReader::match(Location pattern, int patternLength) { +bool OurReader<_Value>::match(Location pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; @@ -1348,7 +1343,7 @@ bool OurReader::match(Location pattern, int patternLength) { } template -bool OurReader::readComment() { +bool OurReader<_Value>::readComment() { Location commentBegin = current_ - 1; Char c = getNextChar(); bool successful = false; @@ -1361,8 +1356,8 @@ bool OurReader::readComment() { if (collectComments_) { CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) + if (lastValueEnd_ && !containsNewLine<_Value>(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine<_Value>(commentBegin, current_)) placement = commentAfterOnSameLine; } @@ -1373,9 +1368,9 @@ bool OurReader::readComment() { template void -OurReader::addComment(Location begin, Location end, CommentPlacement placement) { +OurReader<_Value>::addComment(Location begin, Location end, CommentPlacement placement) { assert(collectComments_); - const std::string& normalized = normalizeEOL(begin, end); + const std::string& normalized = normalizeEOL<_Value>(begin, end); if (placement == commentAfterOnSameLine) { assert(lastValue_ != 0); lastValue_->setComment(normalized, placement); @@ -1385,7 +1380,7 @@ OurReader::addComment(Location begin, Location end, CommentPlacement placement) } template -bool OurReader::readCStyleComment() { +bool OurReader<_Value>::readCStyleComment() { while (current_ != end_) { Char c = getNextChar(); if (c == '*' && *current_ == '/') @@ -1395,7 +1390,7 @@ bool OurReader::readCStyleComment() { } template -bool OurReader::readCppStyleComment() { +bool OurReader<_Value>::readCppStyleComment() { while (current_ != end_) { Char c = getNextChar(); if (c == '\n') @@ -1412,7 +1407,7 @@ bool OurReader::readCppStyleComment() { } template -bool OurReader::readNumber(bool checkInf) { +bool OurReader<_Value>::readNumber(bool checkInf) { const char *p = current_; if (checkInf && p != end_ && *p == 'I') { current_ = ++p; @@ -1439,7 +1434,7 @@ bool OurReader::readNumber(bool checkInf) { return true; } template -bool OurReader::readString() { +bool OurReader<_Value>::readString() { Char c = 0; while (current_ != end_) { c = getNextChar(); @@ -1453,7 +1448,7 @@ bool OurReader::readString() { template -bool OurReader::readStringSingleQuote() { +bool OurReader<_Value>::readStringSingleQuote() { Char c = 0; while (current_ != end_) { c = getNextChar(); @@ -1466,10 +1461,10 @@ bool OurReader::readStringSingleQuote() { } template -bool OurReader::readObject(Token& tokenStart) { +bool OurReader<_Value>::readObject(Token& tokenStart) { Token tokenName; std::string name; - Value init(objectValue); + _Value init(objectValue); currentValue().swapPayload(init); currentValue().setOffsetStart(tokenStart.start_ - begin_); while (readToken(tokenName)) { @@ -1485,7 +1480,7 @@ bool OurReader::readObject(Token& tokenStart) { if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { - Value numberName; + _Value numberName; if (!decodeNumber(tokenName, numberName)) return recoverFromError(tokenObjectEnd); name = numberName.asString(); @@ -1504,7 +1499,7 @@ bool OurReader::readObject(Token& tokenStart) { return addErrorAndRecover( msg, tokenName, tokenObjectEnd); } - Value& value = currentValue()[name]; + _Value& value = currentValue()[name]; nodes_.push(&value); bool ok = readValue(); nodes_.pop(); @@ -1529,8 +1524,8 @@ bool OurReader::readObject(Token& tokenStart) { } template -bool OurReader::readArray(Token& tokenStart) { - Value init(arrayValue); +bool OurReader<_Value>::readArray(Token& tokenStart) { + _Value init(arrayValue); currentValue().swapPayload(init); currentValue().setOffsetStart(tokenStart.start_ - begin_); skipSpaces(); @@ -1542,7 +1537,7 @@ bool OurReader::readArray(Token& tokenStart) { } int index = 0; for (;;) { - Value& value = currentValue()[index++]; + _Value& value = currentValue()[index++]; nodes_.push(&value); bool ok = readValue(); nodes_.pop(); @@ -1568,8 +1563,8 @@ bool OurReader::readArray(Token& tokenStart) { } template -bool OurReader::decodeNumber(Token& token) { - Value decoded; +bool OurReader<_Value>::decodeNumber(Token& token) { + _Value decoded; if (!decodeNumber(token, decoded)) return false; currentValue().swapPayload(decoded); @@ -1579,7 +1574,7 @@ bool OurReader::decodeNumber(Token& token) { } template -bool OurReader::decodeNumber(Token& token, Value& decoded) { +bool OurReader<_Value>::decodeNumber(Token& token, _Value& decoded) { // Attempts to parse the number as an integer. If the number is // larger than the maximum supported value of an integer then // we decode the number as a double. @@ -1588,16 +1583,16 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { 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; - Value::LargestUInt value = 0; + typename _Value::LargestUInt maxIntegerValue = + isNegative ? typename _Value::LargestUInt(-_Value::minLargestInt) + : _Value::maxLargestUInt; + typename _Value::LargestUInt threshold = maxIntegerValue / 10; + typename _Value::LargestUInt value = 0; while (current < token.end_) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); - Value::UInt digit(c - '0'); + typename _Value::UInt digit(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 @@ -1611,17 +1606,17 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { value = value * 10 + digit; } if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) - decoded = Value::LargestInt(value); + decoded = -typename _Value::LargestInt(value); + else if (value <= typename _Value::LargestUInt(_Value::maxInt)) + decoded = typename _Value::LargestInt(value); else decoded = value; return true; } template -bool OurReader::decodeDouble(Token& token) { - Value decoded; +bool OurReader<_Value>::decodeDouble(Token& token) { + _Value decoded; if (!decodeDouble(token, decoded)) return false; currentValue().swapPayload(decoded); @@ -1631,7 +1626,7 @@ bool OurReader::decodeDouble(Token& token) { } template -bool OurReader::decodeDouble(Token& token, Value& decoded) { +bool OurReader<_Value>::decodeDouble(Token& token, _Value& decoded) { double value = 0; const int bufferSize = 32; int count; @@ -1668,11 +1663,11 @@ bool OurReader::decodeDouble(Token& token, Value& decoded) { } template -bool OurReader::decodeString(Token& token) { +bool OurReader<_Value>::decodeString(Token& token) { std::string decoded_string; if (!decodeString(token, decoded_string)) return false; - Value decoded(decoded_string); + _Value decoded(decoded_string); currentValue().swapPayload(decoded); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -1680,7 +1675,7 @@ bool OurReader::decodeString(Token& token) { } template -bool OurReader::decodeString(Token& token, std::string& decoded) { +bool OurReader<_Value>::decodeString(Token& token, std::string& decoded) { decoded.reserve(token.end_ - token.start_ - 2); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' @@ -1734,7 +1729,7 @@ bool OurReader::decodeString(Token& token, std::string& decoded) { } template -bool OurReader::decodeUnicodeCodePoint(Token& token, +bool OurReader<_Value>::decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode) { @@ -1764,7 +1759,7 @@ bool OurReader::decodeUnicodeCodePoint(Token& token, } template -bool OurReader::decodeUnicodeEscapeSequence(Token& token, +bool OurReader<_Value>::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode) { @@ -1794,7 +1789,7 @@ bool OurReader::decodeUnicodeEscapeSequence(Token& token, template bool -OurReader::addError(const std::string& message, Token& token, Location extra) { +OurReader<_Value>::addError(const std::string& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -1804,7 +1799,7 @@ OurReader::addError(const std::string& message, Token& token, Location extra) { } template -bool OurReader::recoverFromError(TokenType skipUntilToken) { +bool OurReader<_Value>::recoverFromError(TokenType skipUntilToken) { int errorCount = int(errors_.size()); Token skip; for (;;) { @@ -1817,7 +1812,8 @@ bool OurReader::recoverFromError(TokenType skipUntilToken) { return false; } -bool OurReader::addErrorAndRecover(const std::string& message, +template +bool OurReader<_Value>::addErrorAndRecover(const std::string& message, Token& token, TokenType skipUntilToken) { addError(message, token); @@ -1825,17 +1821,17 @@ bool OurReader::addErrorAndRecover(const std::string& message, } template -Value& OurReader::currentValue() { return *(nodes_.top()); } +_Value& OurReader<_Value>::currentValue() { return *(nodes_.top()); } template -OurReader::Char OurReader::getNextChar() { +typename OurReader<_Value>::Char OurReader<_Value>::getNextChar() { if (current_ == end_) return 0; return *current_++; } template -void OurReader::getLocationLineAndColumn(Location location, +void OurReader<_Value>::getLocationLineAndColumn(Location location, int& line, int& column) const { Location current = begin_; @@ -1859,7 +1855,7 @@ void OurReader::getLocationLineAndColumn(Location location, } template -std::string OurReader::getLocationLineAndColumn(Location location) const { +std::string OurReader<_Value>::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; @@ -1868,9 +1864,9 @@ std::string OurReader::getLocationLineAndColumn(Location location) const { } template -std::string OurReader::getFormattedErrorMessages() const { +std::string OurReader<_Value>::getFormattedErrorMessages() const { std::string formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); + for (typename Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) { const ErrorInfo& error = *itError; @@ -1885,9 +1881,9 @@ std::string OurReader::getFormattedErrorMessages() const { } template -std::vector OurReader::getStructuredErrors() const { - std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); +std::vector::StructuredError> OurReader<_Value>::getStructuredErrors() const { + std::vector::StructuredError> allErrors; + for (typename Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) { const ErrorInfo& error = *itError; @@ -1901,7 +1897,7 @@ std::vector OurReader::getStructuredErrors() const { } template -bool OurReader::pushError(const Value& value, const std::string& message) { +bool OurReader<_Value>::pushError(const _Value& value, const std::string& message) { size_t length = end_ - begin_; if(value.getOffsetStart() > length || value.getOffsetLimit() > length) @@ -1919,7 +1915,7 @@ bool OurReader::pushError(const Value& value, const std::string& message) { } template -bool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) { +bool OurReader<_Value>::pushError(const _Value& value, const std::string& message, const _Value& extra) { size_t length = end_ - begin_; if(value.getOffsetStart() > length || value.getOffsetLimit() > length @@ -1938,15 +1934,15 @@ bool OurReader::pushError(const Value& value, const std::string& message, const } template -bool OurReader::good() const { +bool OurReader<_Value>::good() const { return !errors_.size(); } template -class OurCharReader : public CharReader { +class OurCharReader : public CharReader<_Value> { bool const collectComments_; - OurReader reader_; + OurReader<_Value> reader_; public: OurCharReader( bool collectComments, @@ -1956,7 +1952,7 @@ public: {} bool parse( char const* beginDoc, char const* endDoc, - Value* root, std::string* errs) override { + _Value* root, std::string* errs) override { bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); if (errs) { *errs = reader_.getFormattedErrorMessages(); @@ -1966,15 +1962,15 @@ public: }; template -CharReaderBuilder::CharReaderBuilder() +CharReaderBuilder<_Value>::CharReaderBuilder() { setDefaults(&settings_); } template -CharReaderBuilder::~CharReaderBuilder() +CharReaderBuilder<_Value>::~CharReaderBuilder() {} template -CharReader* CharReaderBuilder::newCharReader() const +CharReader<_Value>* CharReaderBuilder<_Value>::newCharReader() const { bool collectComments = settings_["collectComments"].asBool(); OurFeatures features = OurFeatures::all(); @@ -1987,7 +1983,7 @@ CharReader* CharReaderBuilder::newCharReader() const features.failIfExtra_ = settings_["failIfExtra"].asBool(); features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); - return new OurCharReader(collectComments, features); + return new OurCharReader<_Value>(collectComments, features); } template static void getValidReaderKeys(std::set* valid_keys) @@ -2005,14 +2001,14 @@ static void getValidReaderKeys(std::set* valid_keys) valid_keys->insert("allowSpecialFloats"); } template -bool CharReaderBuilder::validate(Json::Value* invalid) const +bool CharReaderBuilder<_Value>::validate(_Value* invalid) const { - Json::Value my_invalid; + _Value my_invalid; if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; + _Value& inv = *invalid; std::set valid_keys; - getValidReaderKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); + getValidReaderKeys<_Value>(&valid_keys); + typename _Value::Members keys = settings_.getMemberNames(); size_t n = keys.size(); for (size_t i = 0; i < n; ++i) { std::string const& key = keys[i]; @@ -2023,13 +2019,13 @@ bool CharReaderBuilder::validate(Json::Value* invalid) const return 0u == inv.size(); } template -Value& CharReaderBuilder::operator[](std::string key) +_Value& CharReaderBuilder<_Value>::operator[](std::string key) { return settings_[key]; } // static template -void CharReaderBuilder::strictMode(Json::Value* settings) +void CharReaderBuilder<_Value>::strictMode(_Value* settings) { //! [CharReaderBuilderStrictMode] (*settings)["allowComments"] = false; @@ -2045,7 +2041,7 @@ void CharReaderBuilder::strictMode(Json::Value* settings) } // static template -void CharReaderBuilder::setDefaults(Json::Value* settings) +void CharReaderBuilder<_Value>::setDefaults(_Value* settings) { //! [CharReaderBuilderDefaults] (*settings)["collectComments"] = true; @@ -2066,8 +2062,8 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) template bool parseFromStream( - CharReader::Factory const& fact, std::istream& sin, - Value* root, std::string* errs) + typename CharReader<_Value>::Factory const& fact, std::istream& sin, + _Value* root, std::string* errs) { std::ostringstream ssin; ssin << sin.rdbuf(); @@ -2075,13 +2071,20 @@ bool parseFromStream( char const* begin = doc.data(); char const* end = begin + doc.size(); // Note that we do not actually need a null-terminator. + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr> CharReaderPtr; +#else +typedef std::auto_ptr> CharReaderPtr; +#endif + CharReaderPtr const reader(fact.newCharReader()); return reader->parse(begin, end, root, errs); } template -std::istream& operator>>(std::istream& sin, Value& root) { - CharReaderBuilder b; +std::istream& operator>>(std::istream& sin, _Value& root) { + CharReaderBuilder<_Value> b; std::string errs; bool ok = parseFromStream(b, sin, &root, &errs); if (!ok) { diff --git a/include/json/value.h b/include/json/value.h index f34ef074a..dd0fd4975 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -161,14 +161,15 @@ namespace detail { * but the Value API does *not* check bounds. That is the responsibility * of the caller. */ -template class _Alloc = std::allocator, +template, class _String = std::basic_string, std::allocator>> class JSON_API Value { + template friend class ValueIteratorBase; public: typedef std::vector Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; typedef Json::UInt UInt; typedef Json::Int Int; #if defined(JSON_HAS_INT64) @@ -293,7 +294,7 @@ Json::Value obj_value(Json::objectValue); // {} * Json::Value aValue(foo); * \endcode */ - Value(const StaticString& value); + Value(const Json::StaticString& value); Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. #ifdef JSON_USE_CPPTL Value(const CppTL::ConstString& value); @@ -612,6 +613,7 @@ Json::Value obj_value(Json::objectValue); // {} template class JSON_API PathArgument { public: + template friend class Path; PathArgument(); @@ -645,27 +647,27 @@ template class JSON_API Path { public: Path(const std::string& path, - const PathArgument& a1 = PathArgument(), - const PathArgument& a2 = PathArgument(), - const PathArgument& a3 = PathArgument(), - const PathArgument& a4 = PathArgument(), - const PathArgument& a5 = PathArgument()); - - const Value& resolve(const Value& root) const; - Value resolve(const Value& root, const Value& defaultValue) const; + const PathArgument<_Value>& a1 = PathArgument<_Value>(), + const PathArgument<_Value>& a2 = PathArgument<_Value>(), + const PathArgument<_Value>& a3 = PathArgument<_Value>(), + const PathArgument<_Value>& a4 = PathArgument<_Value>(), + const PathArgument<_Value>& a5 = PathArgument<_Value>()); + + const _Value& resolve(const _Value& root) const; + _Value resolve(const _Value& root, const _Value& defaultValue) const; /// Creates the "path" to access the specified node and returns a reference on /// the node. - Value& make(Value& root) const; + _Value& make(_Value& root) const; private: - typedef std::vector InArgs; - typedef std::vector Args; + typedef std::vector*> InArgs; + typedef std::vector> Args; void makePath(const std::string& path, const InArgs& in); void addPathInArg(const std::string& path, const InArgs& in, - InArgs::const_iterator& itInArg, - PathArgument::Kind kind); + typename InArgs::const_iterator& itInArg, + typename PathArgument<_Value>::Kind kind); void invalidPath(const std::string& path, int location); Args args_; @@ -680,7 +682,7 @@ class JSON_API ValueIteratorBase { typedef std::bidirectional_iterator_tag iterator_category; typedef unsigned int size_t; typedef int difference_type; - typedef ValueIteratorBase SelfType; + typedef ValueIteratorBase<_Value> SelfType; bool operator==(const SelfType& other) const { return isEqual(other); } @@ -692,7 +694,7 @@ class JSON_API ValueIteratorBase { /// Return either the index or the member name of the referenced value as a /// Value. - Value key() const; + _Value key() const; /// Return the index of the referenced Value, or -1 if it is not an arrayValue. UInt index() const; @@ -713,7 +715,7 @@ class JSON_API ValueIteratorBase { char const* memberName(char const** end) const; protected: - Value& deref() const; + _Value& deref() const; void increment(); @@ -726,7 +728,7 @@ class JSON_API ValueIteratorBase { void copy(const SelfType& other); private: - Value::ObjectValues::iterator current_; + typename _Value::ObjectValues::iterator current_; // Indicates that iterator is for a null value. bool isNull_; @@ -734,33 +736,34 @@ class JSON_API ValueIteratorBase { // For some reason, BORLAND needs these at the end, rather // than earlier. No idea why. ValueIteratorBase(); - explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); + explicit ValueIteratorBase(const typename _Value::ObjectValues::iterator& current); }; /** \brief const iterator for object and array value. * */ template -class JSON_API ValueConstIterator : public ValueIteratorBase { +class JSON_API ValueConstIterator : public ValueIteratorBase<_Value> { + template friend class Value; public: - typedef const Value value_type; + typedef const _Value value_type; //typedef unsigned int size_t; //typedef int difference_type; - typedef const Value& reference; - typedef const Value* pointer; + typedef const _Value& reference; + typedef const _Value* pointer; typedef ValueConstIterator SelfType; ValueConstIterator(); - ValueConstIterator(ValueIterator const& other); + ValueConstIterator(ValueIterator<_Value> const& other); private: /*! \internal Use by Value to create an iterator. */ - explicit ValueConstIterator(const Value::ObjectValues::iterator& current); + explicit ValueConstIterator(const typename _Value::ObjectValues::iterator& current); public: - SelfType& operator=(const ValueIteratorBase& other); + SelfType& operator=(const ValueIteratorBase<_Value>& other); SelfType operator++(int) { SelfType temp(*this); @@ -775,42 +778,43 @@ class JSON_API ValueConstIterator : public ValueIteratorBase { } SelfType& operator--() { - decrement(); + ValueIteratorBase<_Value>::decrement(); return *this; } SelfType& operator++() { - increment(); + ValueIteratorBase<_Value>::increment(); return *this; } - reference operator*() const { return deref(); } + reference operator*() const { return ValueIteratorBase<_Value>::deref(); } - pointer operator->() const { return &deref(); } + pointer operator->() const { return &ValueIteratorBase<_Value>::deref(); } }; /** \brief Iterator for object and array value. */ template -class JSON_API ValueIterator : public ValueIteratorBase { +class JSON_API ValueIterator : public ValueIteratorBase<_Value> { + template friend class Value; public: - typedef Value value_type; + typedef _Value value_type; typedef unsigned int size_t; typedef int difference_type; - typedef Value& reference; - typedef Value* pointer; - typedef ValueIterator SelfType; + typedef _Value& reference; + typedef _Value* pointer; + typedef ValueIterator<_Value> SelfType; ValueIterator(); - explicit ValueIterator(const ValueConstIterator& other); + explicit ValueIterator(const ValueConstIterator<_Value>& other); ValueIterator(const ValueIterator& other); private: /*! \internal Use by Value to create an iterator. */ - explicit ValueIterator(const Value::ObjectValues::iterator& current); + explicit ValueIterator(const typename _Value::ObjectValues::iterator& current); public: SelfType& operator=(const SelfType& other); @@ -827,18 +831,18 @@ class JSON_API ValueIterator : public ValueIteratorBase { } SelfType& operator--() { - decrement(); + ValueIteratorBase<_Value>::decrement(); return *this; } SelfType& operator++() { - increment(); + ValueIteratorBase<_Value>::increment(); return *this; } - reference operator*() const { return deref(); } + reference operator*() const { return ValueIteratorBase<_Value>::deref(); } - pointer operator->() const { return &deref(); } + pointer operator->() const { return &ValueIteratorBase<_Value>::deref(); } }; } // namespace detail @@ -855,8 +859,9 @@ typedef detail::ValueIteratorBase> ValueIteratorBase; // class namespace std { /// Specialize std::swap() for Json::Value. -template -inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } +template, + class _String = std::basic_string, std::allocator>> +inline void swap(Json::detail::Value<_Alloc, _String>& a, Json::detail::Value<_Alloc, _String>& b) { a.swap(b); } } diff --git a/include/json/value.inl b/include/json/value.inl index 200d0468c..9f1b343ea 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -32,26 +32,37 @@ namespace detail { #else #define ALIGNAS(byte_alignment) #endif -static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; +static const unsigned char ALIGNAS(8) kNull[2048] = { 0 }; //FIXME no sizeof(Value) exists const unsigned char& kNullRef = kNull[0]; -const Value& Value::null = reinterpret_cast(kNullRef); -const Value& Value::nullRef = null; +template +const Value& Value::null = reinterpret_cast&>(kNullRef); +template +const Value& Value::nullRef = null; -const Int Value::minInt = Int(~(UInt(-1) / 2)); -const Int Value::maxInt = Int(UInt(-1) / 2); -const UInt Value::maxUInt = UInt(-1); +template +const Int Value::minInt = Int(~(UInt(-1) / 2)); +template +const Int Value::maxInt = Int(UInt(-1) / 2); +template +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); +template +const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); +template +const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); +template +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); +template +const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +template +const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +template +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) template @@ -85,8 +96,8 @@ 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 >= (size_t)Value::maxInt) - length = Value::maxInt - 1; + if (length >= (size_t)_Value::maxInt) + length = _Value::maxInt - 1; char* newString = static_cast(malloc(length + 1)); if (newString == NULL) { @@ -108,7 +119,7 @@ static inline char* duplicateAndPrefixStringValue( { // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. - JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U, + JSON_ASSERT_MESSAGE(length <= (unsigned)_Value::maxInt - sizeof(unsigned) - 1U, "in Json::Value::duplicateAndPrefixStringValue(): " "length too big for prefixing"); unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; @@ -153,7 +164,7 @@ static inline void releaseStringValue(char* value) { free(value); } // ////////////////////////////////////////////////////////////////// #if !defined(JSON_IS_AMALGAMATION) -#include "json_valueiterator.inl" +#include "valueiterator.inl" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { @@ -167,19 +178,19 @@ namespace detail { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -template class _Alloc, class _String> -Value::CommentInfo::CommentInfo() : comment_(0) {} +template +Value<_Alloc, _String>::CommentInfo::CommentInfo() : comment_(0) {} -template class _Alloc, class _String> -Value::CommentInfo::~CommentInfo() { +template +Value<_Alloc, _String>::CommentInfo::~CommentInfo() { if (comment_) - releaseStringValue(comment_); + releaseStringValue>(comment_); } -template class _Alloc, class _String> -void Value::CommentInfo::setComment(const char* text, size_t len) { +template +void Value<_Alloc, _String>::CommentInfo::setComment(const char* text, size_t len) { if (comment_) { - releaseStringValue(comment_); + releaseStringValue>(comment_); comment_ = 0; } JSON_ASSERT(text != 0); @@ -187,7 +198,7 @@ void Value::CommentInfo::setComment(const char* text, size_t len) { 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); + comment_ = duplicateStringValue>(text, len); } // ////////////////////////////////////////////////////////////////// @@ -201,20 +212,21 @@ void Value::CommentInfo::setComment(const char* text, size_t len) { // Notes: policy_ indicates if the string was allocated when // a string is stored. -template class _Alloc, class _String> -Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} +template +Value<_Alloc, _String>::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} -template class _Alloc, class _String> -Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) +template +Value<_Alloc, _String>::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) : cstr_(str) { // allocate != duplicate storage_.policy_ = allocate & 0x3; storage_.length_ = ulength & 0x3FFFFFFF; } -Value::CZString::CZString(const CZString& other) +template +Value<_Alloc, _String>::CZString::CZString(const CZString& other) : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0 - ? duplicateStringValue(other.cstr_, other.storage_.length_) + ? duplicateStringValue>(other.cstr_, other.storage_.length_) : other.cstr_) { storage_.policy_ = (other.cstr_ ? (static_cast(other.storage_.policy_) == noDuplication @@ -224,33 +236,33 @@ Value::CZString::CZString(const CZString& other) } #if JSON_HAS_RVALUE_REFERENCES -template class _Alloc, class _String> -Value::CZString::CZString(CZString&& other) +template +Value<_Alloc, _String>::CZString::CZString(CZString&& other) : cstr_(other.cstr_), index_(other.index_) { other.cstr_ = nullptr; } #endif -template class _Alloc, class _String> -Value::CZString::~CZString() { +template +Value<_Alloc, _String>::CZString::~CZString() { if (cstr_ && storage_.policy_ == duplicate) - releaseStringValue(const_cast(cstr_)); + releaseStringValue>(const_cast(cstr_)); } -template class _Alloc, class _String> -void Value::CZString::swap(CZString& other) { +template +void Value<_Alloc, _String>::CZString::swap(CZString& other) { std::swap(cstr_, other.cstr_); std::swap(index_, other.index_); } -template class _Alloc, class _String> -Value::CZString& Value::CZString::operator=(CZString other) { +template +typename Value<_Alloc, _String>::CZString& Value<_Alloc, _String>::CZString::operator=(CZString other) { swap(other); return *this; } -template class _Alloc, class _String> -bool Value::CZString::operator<(const CZString& other) const { +template +bool Value<_Alloc, _String>::CZString::operator<(const CZString& other) const { if (!cstr_) return index_ < other.index_; //return strcmp(cstr_, other.cstr_) < 0; // Assume both are strings. @@ -263,8 +275,8 @@ bool Value::CZString::operator<(const CZString& other) const { return (this_len < other_len); } -template class _Alloc, class _String> -bool Value::CZString::operator==(const CZString& other) const { +template +bool Value<_Alloc, _String>::CZString::operator==(const CZString& other) const { if (!cstr_) return index_ == other.index_; //return strcmp(cstr_, other.cstr_) == 0; // Assume both are strings. @@ -275,16 +287,16 @@ bool Value::CZString::operator==(const CZString& other) const { return comp == 0; } -template class _Alloc, class _String> -ArrayIndex Value::CZString::index() const { return index_; } +template +ArrayIndex Value<_Alloc, _String>::CZString::index() const { return index_; } //const char* Value::CZString::c_str() const { return cstr_; } -template class _Alloc, class _String> -const char* Value::CZString::data() const { return cstr_; } -template class _Alloc, class _String> -unsigned Value::CZString::length() const { return storage_.length_; } -template class _Alloc, class _String> -bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } +template +const char* Value<_Alloc, _String>::CZString::data() const { return cstr_; } +template +unsigned Value<_Alloc, _String>::CZString::length() const { return storage_.length_; } +template +bool Value<_Alloc, _String>::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -298,8 +310,8 @@ bool Value::CZString::isStaticString() const { return storage_.policy_ == noDupl * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ -template class _Alloc, class _String> -Value::Value(ValueType vtype) { +template +Value<_Alloc, _String>::Value(ValueType vtype) { initBasic(vtype); switch (vtype) { case nullValue: @@ -326,78 +338,78 @@ Value::Value(ValueType vtype) { } } -template class _Alloc, class _String> -Value::Value(Int value) { +template +Value<_Alloc, _String>::Value(Int value) { initBasic(intValue); value_.int_ = value; } -template class _Alloc, class _String> -Value::Value(UInt value) { +template +Value<_Alloc, _String>::Value(UInt value) { initBasic(uintValue); value_.uint_ = value; } #if defined(JSON_HAS_INT64) -template class _Alloc, class _String> -Value::Value(Int64 value) { +template +Value<_Alloc, _String>::Value(Int64 value) { initBasic(intValue); value_.int_ = value; } -template class _Alloc, class _String> -Value::Value(UInt64 value) { +template +Value<_Alloc, _String>::Value(UInt64 value) { initBasic(uintValue); value_.uint_ = value; } #endif // defined(JSON_HAS_INT64) -template class _Alloc, class _String> -Value::Value(double value) { +template +Value<_Alloc, _String>::Value(double value) { initBasic(realValue); value_.real_ = value; } -template class _Alloc, class _String> -Value::Value(const char* value) { +template +Value<_Alloc, _String>::Value(const char* value) { initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); + value_.string_ = duplicateAndPrefixStringValue>(value, static_cast(strlen(value))); } -template class _Alloc, class _String> -Value::Value(const char* beginValue, const char* endValue) { +template +Value<_Alloc, _String>::Value(const char* beginValue, const char* endValue) { initBasic(stringValue, true); value_.string_ = - duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); + duplicateAndPrefixStringValue>(beginValue, static_cast(endValue - beginValue)); } -template class _Alloc, class _String> -Value::Value(const std::string& value) { +template +Value<_Alloc, _String>::Value(const std::string& value) { initBasic(stringValue, true); value_.string_ = - duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); + duplicateAndPrefixStringValue>(value.data(), static_cast(value.length())); } -template class _Alloc, class _String> -Value::Value(const StaticString& value) { +template +Value<_Alloc, _String>::Value(const StaticString& value) { initBasic(stringValue); value_.string_ = const_cast(value.c_str()); } #ifdef JSON_USE_CPPTL -template class _Alloc, class _String> +template Value::Value(const CppTL::ConstString& value) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); } #endif -template class _Alloc, class _String> -Value::Value(bool value) { +template +Value<_Alloc, _String>::Value(bool value) { initBasic(booleanValue); value_.bool_ = value; } -template class _Alloc, class _String> -Value::Value(Value const& other) +template +Value<_Alloc, _String>::Value(Value const& other) : type_(other.type_), allocated_(false) , comments_(0), start_(other.start_), limit_(other.limit_) @@ -414,9 +426,9 @@ Value::Value(Value const& other) if (other.value_.string_ && other.allocated_) { unsigned len; char const* str; - decodePrefixedString(other.allocated_, other.value_.string_, + decodePrefixedString>(other.allocated_, other.value_.string_, &len, &str); - value_.string_ = duplicateAndPrefixStringValue(str, len); + value_.string_ = duplicateAndPrefixStringValue>(str, len); allocated_ = true; } else { value_.string_ = other.value_.string_; @@ -443,15 +455,15 @@ Value::Value(Value const& other) #if JSON_HAS_RVALUE_REFERENCES // Move constructor -template class _Alloc, class _String> -Value::Value(Value&& other) { +template +Value<_Alloc, _String>::Value(Value&& other) { initBasic(nullValue); swap(other); } #endif -template class _Alloc, class _String> -Value::~Value() { +template +Value<_Alloc, _String>::~Value() { switch (type_) { case nullValue: case intValue: @@ -461,7 +473,7 @@ Value::~Value() { break; case stringValue: if (allocated_) - releaseStringValue(value_.string_); + releaseStringValue>(value_.string_); break; case arrayValue: case objectValue: @@ -475,14 +487,14 @@ Value::~Value() { delete[] comments_; } -template class _Alloc, class _String> -Value& Value::operator=(Value other) { +template +Value<_Alloc, _String>& Value<_Alloc, _String>::operator=(Value other) { swap(other); return *this; } -template class _Alloc, class _String> -void Value::swapPayload(Value& other) { +template +void Value<_Alloc, _String>::swapPayload(Value<_Alloc, _String>& other) { ValueType temp = type_; type_ = other.type_; other.type_ = temp; @@ -492,19 +504,19 @@ void Value::swapPayload(Value& other) { other.allocated_ = temp2 & 0x1; } -template class _Alloc, class _String> -void Value::swap(Value& other) { +template +void Value<_Alloc, _String>::swap(Value<_Alloc, _String>& other) { swapPayload(other); std::swap(comments_, other.comments_); std::swap(start_, other.start_); std::swap(limit_, other.limit_); } -template class _Alloc, class _String> -ValueType Value::type() const { return type_; } +template +ValueType Value<_Alloc, _String>::type() const { return type_; } -template class _Alloc, class _String> -int Value::compare(const Value& other) const { +template +int Value<_Alloc, _String>::compare(const Value<_Alloc, _String>& other) const { if (*this < other) return -1; if (*this > other) @@ -512,8 +524,8 @@ int Value::compare(const Value& other) const { return 0; } -template class _Alloc, class _String> -bool Value::operator<(const Value& other) const { +template +bool Value<_Alloc, _String>::operator<(const Value<_Alloc, _String>& other) const { int typeDelta = type_ - other.type_; if (typeDelta) return typeDelta < 0 ? true : false; @@ -538,8 +550,8 @@ bool Value::operator<(const Value& other) const { 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->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString>(other.allocated_, other.value_.string_, &other_len, &other_str); unsigned min_len = std::min(this_len, other_len); int comp = memcmp(this_str, other_str, min_len); if (comp < 0) return true; @@ -559,17 +571,17 @@ bool Value::operator<(const Value& other) const { return false; // unreachable } -template class _Alloc, class _String> -bool Value::operator<=(const Value& other) const { return !(other < *this); } +template +bool Value<_Alloc, _String>::operator<=(const Value<_Alloc, _String>& other) const { return !(other < *this); } -template class _Alloc, class _String> -bool Value::operator>=(const Value& other) const { return !(*this < other); } +template +bool Value<_Alloc, _String>::operator>=(const Value<_Alloc, _String>& other) const { return !(*this < other); } -template class _Alloc, class _String> -bool Value::operator>(const Value& other) const { return other < *this; } +template +bool Value<_Alloc, _String>::operator>(const Value<_Alloc, _String>& other) const { return other < *this; } -template class _Alloc, class _String> -bool Value::operator==(const Value& other) const { +template +bool Value<_Alloc, _String>::operator==(const Value<_Alloc, _String>& other) const { // if ( type_ != other.type_ ) // GCC 2.95.3 says: // attempt to take address of bit-field structure member `Json::Value::type_' @@ -597,8 +609,8 @@ bool Value::operator==(const Value& other) const { 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->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; int comp = memcmp(this_str, other_str, this_len); return comp == 0; @@ -613,32 +625,32 @@ bool Value::operator==(const Value& other) const { return false; // unreachable } -template class _Alloc, class _String> -bool Value::operator!=(const Value& other) const { return !(*this == other); } +template +bool Value<_Alloc, _String>::operator!=(const Value<_Alloc, _String>& other) const { return !(*this == other); } -template class _Alloc, class _String> -const char* Value::asCString() const { +template +const char* Value<_Alloc, _String>::asCString() const { 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->allocated_, this->value_.string_, &this_len, &this_str); return this_str; } -template class _Alloc, class _String> -bool Value::getString(char const** str, char const** cend) const { +template +bool Value<_Alloc, _String>::getString(char const** str, char const** cend) const { if (type_ != stringValue) return false; if (value_.string_ == 0) return false; unsigned length; - decodePrefixedString(this->allocated_, this->value_.string_, &length, str); + decodePrefixedString>(this->allocated_, this->value_.string_, &length, str); *cend = *str + length; return true; } -template class _Alloc, class _String> -std::string Value::asString() const { +template +std::string Value<_Alloc, _String>::asString() const { switch (type_) { case nullValue: return ""; @@ -647,25 +659,25 @@ std::string Value::asString() const { if (value_.string_ == 0) return ""; unsigned this_len; char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString>(this->allocated_, this->value_.string_, &this_len, &this_str); return std::string(this_str, this_len); } case booleanValue: return value_.bool_ ? "true" : "false"; case intValue: - return valueToString(value_.int_); + return valueToString>(value_.int_); case uintValue: - return valueToString(value_.uint_); + return valueToString>(value_.uint_); case realValue: - return valueToString(value_.real_); + return valueToString>(value_.real_); default: JSON_FAIL_MESSAGE("Type is not convertible to string"); } } #ifdef JSON_USE_CPPTL -template class _Alloc, class _String> -CppTL::ConstString Value::asConstString() const { +template +CppTL::ConstString Value<_Alloc, _String>::asConstString() const { unsigned len; char const* str; decodePrefixedString(allocated_, value_.string_, @@ -674,8 +686,8 @@ CppTL::ConstString Value::asConstString() const { } #endif -template class _Alloc, class _String> -Value::Int Value::asInt() const { +template +typename Value<_Alloc, _String>::Int Value<_Alloc, _String>::asInt() const { switch (type_) { case intValue: JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); @@ -697,8 +709,8 @@ Value::Int Value::asInt() const { JSON_FAIL_MESSAGE("Value is not convertible to Int."); } -template class _Alloc, class _String> -Value::UInt Value::asUInt() const { +template +typename Value<_Alloc, _String>::UInt Value<_Alloc, _String>::asUInt() const { switch (type_) { case intValue: JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); @@ -722,8 +734,8 @@ Value::UInt Value::asUInt() const { #if defined(JSON_HAS_INT64) -template class _Alloc, class _String> -Value::Int64 Value::asInt64() const { +template +typename Value<_Alloc, _String>::Int64 Value<_Alloc, _String>::asInt64() const { switch (type_) { case intValue: return Int64(value_.int_); @@ -744,8 +756,8 @@ Value::Int64 Value::asInt64() const { JSON_FAIL_MESSAGE("Value is not convertible to Int64."); } -template class _Alloc, class _String> -Value::UInt64 Value::asUInt64() const { +template +typename Value<_Alloc, _String>::UInt64 Value<_Alloc, _String>::asUInt64() const { switch (type_) { case intValue: JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); @@ -767,8 +779,8 @@ Value::UInt64 Value::asUInt64() const { } #endif // if defined(JSON_HAS_INT64) -template class _Alloc, class _String> -LargestInt Value::asLargestInt() const { +template +LargestInt Value<_Alloc, _String>::asLargestInt() const { #if defined(JSON_NO_INT64) return asInt(); #else @@ -776,8 +788,8 @@ LargestInt Value::asLargestInt() const { #endif } -template class _Alloc, class _String> -LargestUInt Value::asLargestUInt() const { +template +LargestUInt Value<_Alloc, _String>::asLargestUInt() const { #if defined(JSON_NO_INT64) return asUInt(); #else @@ -785,8 +797,8 @@ LargestUInt Value::asLargestUInt() const { #endif } -template class _Alloc, class _String> -double Value::asDouble() const { +template +double Value<_Alloc, _String>::asDouble() const { switch (type_) { case intValue: return static_cast(value_.int_); @@ -808,8 +820,8 @@ double Value::asDouble() const { JSON_FAIL_MESSAGE("Value is not convertible to double."); } -template class _Alloc, class _String> -float Value::asFloat() const { +template +float Value<_Alloc, _String>::asFloat() const { switch (type_) { case intValue: return static_cast(value_.int_); @@ -831,8 +843,8 @@ float Value::asFloat() const { JSON_FAIL_MESSAGE("Value is not convertible to float."); } -template class _Alloc, class _String> -bool Value::asBool() const { +template +bool Value<_Alloc, _String>::asBool() const { switch (type_) { case booleanValue: return value_.bool_; @@ -851,8 +863,8 @@ bool Value::asBool() const { JSON_FAIL_MESSAGE("Value is not convertible to bool."); } -template class _Alloc, class _String> -bool Value::isConvertibleTo(ValueType other) const { +template +bool Value<_Alloc, _String>::isConvertibleTo(ValueType other) const { switch (other) { case nullValue: return (isNumeric() && asDouble() == 0.0) || @@ -886,8 +898,8 @@ bool Value::isConvertibleTo(ValueType other) const { } /// Number of values in array or object -template class _Alloc, class _String> -ArrayIndex Value::size() const { +template +ArrayIndex Value<_Alloc, _String>::size() const { switch (type_) { case nullValue: case intValue: @@ -898,7 +910,7 @@ ArrayIndex Value::size() const { return 0; case arrayValue: // size of the array is highest index + 1 if (!value_.map_->empty()) { - ObjectValues::const_iterator itLast = value_.map_->end(); + typename ObjectValues::const_iterator itLast = value_.map_->end(); --itLast; return (*itLast).first.index() + 1; } @@ -910,19 +922,19 @@ ArrayIndex Value::size() const { return 0; // unreachable; } -template class _Alloc, class _String> -bool Value::empty() const { +template +bool Value<_Alloc, _String>::empty() const { if (isNull() || isArray() || isObject()) return size() == 0u; else return false; } -template class _Alloc, class _String> -bool Value::operator!() const { return isNull(); } +template +bool Value<_Alloc, _String>::operator!() const { return isNull(); } -template class _Alloc, class _String> -void Value::clear() { +template +void Value<_Alloc, _String>::clear() { JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || type_ == objectValue, "in Json::Value::clear(): requires complex value"); @@ -938,8 +950,8 @@ void Value::clear() { } } -template class _Alloc, class _String> -void Value::resize(ArrayIndex newSize) { +template +void Value<_Alloc, _String>::resize(ArrayIndex newSize) { JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, "in Json::Value::resize(): requires arrayValue"); if (type_ == nullValue) @@ -957,55 +969,55 @@ void Value::resize(ArrayIndex newSize) { } } -template class _Alloc, class _String> -Value& Value::operator[](ArrayIndex index) { +template +Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](ArrayIndex index) { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue, "in Json::Value::operator[](ArrayIndex): requires arrayValue"); if (type_ == nullValue) *this = Value(arrayValue); CZString key(index); - ObjectValues::iterator it = value_.map_->lower_bound(key); + typename ObjectValues::iterator it = value_.map_->lower_bound(key); if (it != value_.map_->end() && (*it).first == key) return (*it).second; - ObjectValues::value_type defaultValue(key, nullRef); + typename ObjectValues::value_type defaultValue(key, nullRef); it = value_.map_->insert(it, defaultValue); return (*it).second; } -template class _Alloc, class _String> -Value& Value::operator[](int index) { +template +Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](int index) { JSON_ASSERT_MESSAGE( index >= 0, "in Json::Value::operator[](int index): index cannot be negative"); return (*this)[ArrayIndex(index)]; } -template class _Alloc, class _String> -const Value& Value::operator[](ArrayIndex index) const { +template +const Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](ArrayIndex index) const { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue, "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); if (type_ == nullValue) return nullRef; CZString key(index); - ObjectValues::const_iterator it = value_.map_->find(key); + typename ObjectValues::const_iterator it = value_.map_->find(key); if (it == value_.map_->end()) return nullRef; return (*it).second; } -template class _Alloc, class _String> -const Value& Value::operator[](int index) const { +template +const Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](int index) const { JSON_ASSERT_MESSAGE( index >= 0, "in Json::Value::operator[](int index) const: index cannot be negative"); return (*this)[ArrayIndex(index)]; } -template class _Alloc, class _String> -void Value::initBasic(ValueType vtype, bool allocated) { +template +void Value<_Alloc, _String>::initBasic(ValueType vtype, bool allocated) { type_ = vtype; allocated_ = allocated; comments_ = 0; @@ -1016,8 +1028,8 @@ void Value::initBasic(ValueType vtype, bool allocated) { // 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. -template class _Alloc, class _String> -Value& Value::resolveReference(const char* key) { +template +Value<_Alloc, _String>& Value<_Alloc, _String>::resolveReference(const char* key) { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::resolveReference(): requires objectValue"); @@ -1025,19 +1037,19 @@ Value& Value::resolveReference(const char* key) { *this = Value(objectValue); CZString actualKey( key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + typename ObjectValues::iterator it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; - ObjectValues::value_type defaultValue(actualKey, nullRef); + typename ObjectValues::value_type defaultValue(actualKey, nullRef); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; return value; } // @param key is not null-terminated. -template class _Alloc, class _String> -Value& Value::resolveReference(char const* key, char const* cend) +template +Value<_Alloc, _String>& Value<_Alloc, _String>::resolveReference(char const* key, char const* cend) { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, @@ -1046,74 +1058,74 @@ Value& Value::resolveReference(char const* key, char const* cend) *this = Value(objectValue); CZString actualKey( key, static_cast(cend-key), CZString::duplicateOnCopy); - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + typename ObjectValues::iterator it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; - ObjectValues::value_type defaultValue(actualKey, nullRef); + typename ObjectValues::value_type defaultValue(actualKey, nullRef); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; return value; } -template class _Alloc, class _String> -Value Value::get(ArrayIndex index, const Value& defaultValue) const { +template +Value<_Alloc, _String> Value<_Alloc, _String>::get(ArrayIndex index, const Value& defaultValue) const { const Value* value = &((*this)[index]); return value == &nullRef ? defaultValue : *value; } -template class _Alloc, class _String> -bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } +template +bool Value<_Alloc, _String>::isValidIndex(ArrayIndex index) const { return index < size(); } -template class _Alloc, class _String> -Value const* Value::find(char const* key, char const* cend) const +template +Value<_Alloc, _String> const* Value<_Alloc, _String>::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(cend-key), CZString::noDuplication); - ObjectValues::const_iterator it = value_.map_->find(actualKey); + typename ObjectValues::const_iterator it = value_.map_->find(actualKey); if (it == value_.map_->end()) return NULL; return &(*it).second; } -template class _Alloc, class _String> -const Value& Value::operator[](const char* key) const +template +const Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const char* key) const { Value const* found = find(key, key + strlen(key)); if (!found) return nullRef; return *found; } -template class _Alloc, class _String> -Value const& Value::operator[](std::string const& key) const +template +Value<_Alloc, _String> const& Value<_Alloc, _String>::operator[](std::string const& key) const { Value const* found = find(key.data(), key.data() + key.length()); if (!found) return nullRef; return *found; } -template class _Alloc, class _String> -Value& Value::operator[](const char* key) { +template +Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const char* key) { return resolveReference(key, key + strlen(key)); } -template class _Alloc, class _String> -Value& Value::operator[](const std::string& key) { +template +Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const std::string& key) { return resolveReference(key.data(), key.data() + key.length()); } -template class _Alloc, class _String> -Value& Value::operator[](const StaticString& key) { +template +Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const StaticString& key) { return resolveReference(key.c_str()); } #ifdef JSON_USE_CPPTL -template class _Alloc, class _String> -Value& Value::operator[](const CppTL::ConstString& key) { +template +Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const CppTL::ConstString& key) { return resolveReference(key.c_str(), key.end_c_str()); } -template class _Alloc, class _String> -Value const& Value::operator[](CppTL::ConstString const& key) const +template +Value<_Alloc, _String> const& Value<_Alloc, _String>::operator[](CppTL::ConstString const& key) const { Value const* found = find(key.c_str(), key.end_c_str()); if (!found) return nullRef; @@ -1121,53 +1133,53 @@ Value const& Value::operator[](CppTL::ConstString const& key) const } #endif -template class _Alloc, class _String> -Value& Value::append(const Value& value) { return (*this)[size()] = value; } +template +Value<_Alloc, _String>& Value<_Alloc, _String>::append(const Value<_Alloc, _String>& value) { return (*this)[size()] = value; } -template class _Alloc, class _String> -Value Value::get(char const* key, char const* cend, Value const& defaultValue) const +template +Value<_Alloc, _String> Value<_Alloc, _String>::get(char const* key, char const* cend, Value<_Alloc, _String> const& defaultValue) const { Value const* found = find(key, cend); return !found ? defaultValue : *found; } -template class _Alloc, class _String> -Value Value::get(char const* key, Value const& defaultValue) const +template +Value<_Alloc, _String> Value<_Alloc, _String>::get(char const* key, Value<_Alloc, _String> const& defaultValue) const { return get(key, key + strlen(key), defaultValue); } -template class _Alloc, class _String> -Value Value::get(std::string const& key, Value const& defaultValue) const +template +Value<_Alloc, _String> Value<_Alloc, _String>::get(std::string const& key, Value<_Alloc, _String> const& defaultValue) const { return get(key.data(), key.data() + key.length(), defaultValue); } -template class _Alloc, class _String> -bool Value::removeMember(const char* key, const char* cend, Value* removed) +template +bool Value<_Alloc, _String>::removeMember(const char* key, const char* cend, Value<_Alloc, _String>* removed) { if (type_ != objectValue) { return false; } CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); - ObjectValues::iterator it = value_.map_->find(actualKey); + typename ObjectValues::iterator it = value_.map_->find(actualKey); if (it == value_.map_->end()) return false; *removed = it->second; value_.map_->erase(it); return true; } -template class _Alloc, class _String> -bool Value::removeMember(const char* key, Value* removed) +template +bool Value<_Alloc, _String>::removeMember(const char* key, Value<_Alloc, _String>* removed) { return removeMember(key, key + strlen(key), removed); } -template class _Alloc, class _String> -bool Value::removeMember(std::string const& key, Value* removed) +template +bool Value<_Alloc, _String>::removeMember(std::string const& key, Value<_Alloc, _String>* removed) { return removeMember(key.data(), key.data() + key.length(), removed); } -template class _Alloc, class _String> -Value Value::removeMember(const char* key) +template +Value<_Alloc, _String> Value<_Alloc, _String>::removeMember(const char* key) { JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, "in Json::Value::removeMember(): requires objectValue"); @@ -1178,19 +1190,19 @@ Value Value::removeMember(const char* key) removeMember(key, key + strlen(key), &removed); return removed; // still null if removeMember() did nothing } -template class _Alloc, class _String> -Value Value::removeMember(const std::string& key) +template +Value<_Alloc, _String> Value<_Alloc, _String>::removeMember(const std::string& key) { return removeMember(key.c_str()); } -template class _Alloc, class _String> -bool Value::removeIndex(ArrayIndex index, Value* removed) { +template +bool Value<_Alloc, _String>::removeIndex(ArrayIndex index, Value<_Alloc, _String>* removed) { if (type_ != arrayValue) { return false; } CZString key(index); - ObjectValues::iterator it = value_.map_->find(key); + typename ObjectValues::iterator it = value_.map_->find(key); if (it == value_.map_->end()) { return false; } @@ -1203,45 +1215,45 @@ bool Value::removeIndex(ArrayIndex index, Value* removed) { } // erase the last one ("leftover") CZString keyLast(oldSize - 1); - ObjectValues::iterator itLast = value_.map_->find(keyLast); + typename ObjectValues::iterator itLast = value_.map_->find(keyLast); value_.map_->erase(itLast); return true; } #ifdef JSON_USE_CPPTL -template class _Alloc, class _String> -Value Value::get(const CppTL::ConstString& key, - const Value& defaultValue) const { +template +Value<_Alloc, _String> Value<_Alloc, _String>::get(const CppTL::ConstString& key, + const Value<_Alloc, _String>& defaultValue) const { return get(key.c_str(), key.end_c_str(), defaultValue); } #endif -template class _Alloc, class _String> -bool Value::isMember(char const* key, char const* cend) const +template +bool Value<_Alloc, _String>::isMember(char const* key, char const* cend) const { Value const* value = find(key, cend); return NULL != value; } -template class _Alloc, class _String> -bool Value::isMember(char const* key) const +template +bool Value<_Alloc, _String>::isMember(char const* key) const { return isMember(key, key + strlen(key)); } -template class _Alloc, class _String> -bool Value::isMember(std::string const& key) const +template +bool Value<_Alloc, _String>::isMember(std::string const& key) const { return isMember(key.data(), key.data() + key.length()); } #ifdef JSON_USE_CPPTL -template class _Alloc, class _String> -bool Value::isMember(const CppTL::ConstString& key) const { +template +bool Value<_Alloc, _String>::isMember(const CppTL::ConstString& key) const { return isMember(key.c_str(), key.end_c_str()); } #endif -template class _Alloc, class _String> -Value::Members Value::getMemberNames() const { +template +typename Value<_Alloc, _String>::Members Value<_Alloc, _String>::getMemberNames() const { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::getMemberNames(), value must be objectValue"); @@ -1249,8 +1261,8 @@ Value::Members Value::getMemberNames() const { return Value::Members(); Members members; members.reserve(value_.map_->size()); - ObjectValues::const_iterator it = value_.map_->begin(); - ObjectValues::const_iterator itEnd = value_.map_->end(); + typename ObjectValues::const_iterator it = value_.map_->begin(); + typename ObjectValues::const_iterator itEnd = value_.map_->end(); for (; it != itEnd; ++it) { members.push_back(std::string((*it).first.data(), (*it).first.length())); @@ -1288,14 +1300,14 @@ static bool IsIntegral(double d) { return modf(d, &integral_part) == 0.0; } -template class _Alloc, class _String> -bool Value::isNull() const { return type_ == nullValue; } +template +bool Value<_Alloc, _String>::isNull() const { return type_ == nullValue; } -template class _Alloc, class _String> -bool Value::isBool() const { return type_ == booleanValue; } +template +bool Value<_Alloc, _String>::isBool() const { return type_ == booleanValue; } -template class _Alloc, class _String> -bool Value::isInt() const { +template +bool Value<_Alloc, _String>::isInt() const { switch (type_) { case intValue: return value_.int_ >= minInt && value_.int_ <= maxInt; @@ -1310,8 +1322,8 @@ bool Value::isInt() const { return false; } -template class _Alloc, class _String> -bool Value::isUInt() const { +template +bool Value<_Alloc, _String>::isUInt() const { switch (type_) { case intValue: return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); @@ -1326,8 +1338,8 @@ bool Value::isUInt() const { return false; } -template class _Alloc, class _String> -bool Value::isInt64() const { +template +bool Value<_Alloc, _String>::isInt64() const { #if defined(JSON_HAS_INT64) switch (type_) { case intValue: @@ -1347,8 +1359,8 @@ bool Value::isInt64() const { return false; } -template class _Alloc, class _String> -bool Value::isUInt64() const { +template +bool Value<_Alloc, _String>::isUInt64() const { #if defined(JSON_HAS_INT64) switch (type_) { case intValue: @@ -1368,8 +1380,8 @@ bool Value::isUInt64() const { return false; } -template class _Alloc, class _String> -bool Value::isIntegral() const { +template +bool Value<_Alloc, _String>::isIntegral() const { #if defined(JSON_HAS_INT64) return isInt64() || isUInt64(); #else @@ -1377,23 +1389,23 @@ bool Value::isIntegral() const { #endif } -template class _Alloc, class _String> -bool Value::isDouble() const { return type_ == realValue || isIntegral(); } +template +bool Value<_Alloc, _String>::isDouble() const { return type_ == realValue || isIntegral(); } -template class _Alloc, class _String> -bool Value::isNumeric() const { return isIntegral() || isDouble(); } +template +bool Value<_Alloc, _String>::isNumeric() const { return isIntegral() || isDouble(); } -template class _Alloc, class _String> -bool Value::isString() const { return type_ == stringValue; } +template +bool Value<_Alloc, _String>::isString() const { return type_ == stringValue; } -template class _Alloc, class _String> -bool Value::isArray() const { return type_ == arrayValue; } +template +bool Value<_Alloc, _String>::isArray() const { return type_ == arrayValue; } -template class _Alloc, class _String> -bool Value::isObject() const { return type_ == objectValue; } +template +bool Value<_Alloc, _String>::isObject() const { return type_ == objectValue; } -template class _Alloc, class _String> -void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { +template +void Value<_Alloc, _String>::setComment(const char* comment, size_t len, CommentPlacement placement) { if (!comments_) comments_ = new CommentInfo[numberOfCommentPlacement]; if ((len > 0) && (comment[len-1] == '\n')) { @@ -1403,48 +1415,48 @@ void Value::setComment(const char* comment, size_t len, CommentPlacement placeme comments_[placement].setComment(comment, len); } -template class _Alloc, class _String> -void Value::setComment(const char* comment, CommentPlacement placement) { +template +void Value<_Alloc, _String>::setComment(const char* comment, CommentPlacement placement) { setComment(comment, strlen(comment), placement); } -template class _Alloc, class _String> -void Value::setComment(const std::string& comment, CommentPlacement placement) { +template +void Value<_Alloc, _String>::setComment(const std::string& comment, CommentPlacement placement) { setComment(comment.c_str(), comment.length(), placement); } -template class _Alloc, class _String> -bool Value::hasComment(CommentPlacement placement) const { +template +bool Value<_Alloc, _String>::hasComment(CommentPlacement placement) const { return comments_ != 0 && comments_[placement].comment_ != 0; } -template class _Alloc, class _String> -std::string Value::getComment(CommentPlacement placement) const { +template +std::string Value<_Alloc, _String>::getComment(CommentPlacement placement) const { if (hasComment(placement)) return comments_[placement].comment_; return ""; } -template class _Alloc, class _String> -void Value::setOffsetStart(size_t start) { start_ = start; } +template +void Value<_Alloc, _String>::setOffsetStart(size_t start) { start_ = start; } -template class _Alloc, class _String> -void Value::setOffsetLimit(size_t limit) { limit_ = limit; } +template +void Value<_Alloc, _String>::setOffsetLimit(size_t limit) { limit_ = limit; } -template class _Alloc, class _String> -size_t Value::getOffsetStart() const { return start_; } +template +size_t Value<_Alloc, _String>::getOffsetStart() const { return start_; } -template class _Alloc, class _String> -size_t Value::getOffsetLimit() const { return limit_; } +template +size_t Value<_Alloc, _String>::getOffsetLimit() const { return limit_; } -template class _Alloc, class _String> -std::string Value::toStyledString() const { - StyledWriter writer; +template +std::string Value<_Alloc, _String>::toStyledString() const { + StyledWriter> writer; return writer.write(*this); } -template class _Alloc, class _String> -Value::const_iterator Value::begin() const { +template +typename Value<_Alloc, _String>::const_iterator Value<_Alloc, _String>::begin() const { switch (type_) { case arrayValue: case objectValue: @@ -1457,8 +1469,8 @@ Value::const_iterator Value::begin() const { return const_iterator(); } -template class _Alloc, class _String> -Value::const_iterator Value::end() const { +template +typename Value<_Alloc, _String>::const_iterator Value<_Alloc, _String>::end() const { switch (type_) { case arrayValue: case objectValue: @@ -1471,8 +1483,8 @@ Value::const_iterator Value::end() const { return const_iterator(); } -template class _Alloc, class _String> -Value::iterator Value::begin() { +template +typename Value<_Alloc, _String>::iterator Value<_Alloc, _String>::begin() { switch (type_) { case arrayValue: case objectValue: @@ -1485,8 +1497,8 @@ Value::iterator Value::begin() { return iterator(); } -template class _Alloc, class _String> -Value::iterator Value::end() { +template +typename Value<_Alloc, _String>::iterator Value<_Alloc, _String>::end() { switch (type_) { case arrayValue: case objectValue: @@ -1503,30 +1515,30 @@ Value::iterator Value::end() { // ////////////////////////////////////////////////////////////////// template -PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} +PathArgument<_Value>::PathArgument() : key_(), index_(), kind_(kindNone) {} template -PathArgument::PathArgument(ArrayIndex index) +PathArgument<_Value>::PathArgument(ArrayIndex index) : key_(), index_(index), kind_(kindIndex) {} template -PathArgument::PathArgument(const char* key) +PathArgument<_Value>::PathArgument(const char* key) : key_(key), index_(), kind_(kindKey) {} template -PathArgument::PathArgument(const std::string& key) +PathArgument<_Value>::PathArgument(const std::string& key) : key_(key.c_str()), index_(), kind_(kindKey) {} // class Path // ////////////////////////////////////////////////////////////////// template -Path::Path(const std::string& path, - const PathArgument& a1, - const PathArgument& a2, - const PathArgument& a3, - const PathArgument& a4, - const PathArgument& a5) { +Path<_Value>::Path(const std::string& path, + const PathArgument<_Value>& a1, + const PathArgument<_Value>& a2, + const PathArgument<_Value>& a3, + const PathArgument<_Value>& a4, + const PathArgument<_Value>& a5) { InArgs in; in.push_back(&a1); in.push_back(&a2); @@ -1537,15 +1549,15 @@ Path::Path(const std::string& path, } template -void Path::makePath(const std::string& path, const InArgs& in) { +void Path<_Value>::makePath(const std::string& path, const InArgs& in) { const char* current = path.c_str(); const char* end = current + path.length(); - InArgs::const_iterator itInArg = in.begin(); + typename InArgs::const_iterator itInArg = in.begin(); while (current != end) { if (*current == '[') { ++current; if (*current == '%') - addPathInArg(path, in, itInArg, PathArgument::kindIndex); + addPathInArg(path, in, itInArg, PathArgument<_Value>::kindIndex); else { ArrayIndex index = 0; for (; current != end && *current >= '0' && *current <= '9'; ++current) @@ -1555,7 +1567,7 @@ void Path::makePath(const std::string& path, const InArgs& in) { if (current == end || *current++ != ']') invalidPath(path, int(current - path.c_str())); } else if (*current == '%') { - addPathInArg(path, in, itInArg, PathArgument::kindKey); + addPathInArg(path, in, itInArg, PathArgument<_Value>::kindKey); ++current; } else if (*current == '.') { ++current; @@ -1569,10 +1581,10 @@ void Path::makePath(const std::string& path, const InArgs& in) { } template -void Path::addPathInArg(const std::string& /*path*/, +void Path<_Value>::addPathInArg(const std::string& /*path*/, const InArgs& in, - InArgs::const_iterator& itInArg, - PathArgument::Kind kind) { + typename InArgs::const_iterator& itInArg, + typename PathArgument<_Value>::Kind kind) { if (itInArg == in.end()) { // Error: missing argument %d } else if ((*itInArg)->kind_ != kind) { @@ -1583,26 +1595,26 @@ void Path::addPathInArg(const std::string& /*path*/, } template -void Path::invalidPath(const std::string& /*path*/, int /*location*/) { +void Path<_Value>::invalidPath(const std::string& /*path*/, int /*location*/) { // Error: invalid path. } template -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; - if (arg.kind_ == PathArgument::kindIndex) { +const _Value& Path<_Value>::resolve(const _Value& root) const { + const _Value* node = &root; + for (typename Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument<_Value>& arg = *it; + if (arg.kind_ == PathArgument<_Value>::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) { // Error: unable to resolve path (array value expected at position... } node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { + } else if (arg.kind_ == PathArgument<_Value>::kindKey) { if (!node->isObject()) { // Error: unable to resolve path (object value expected at position...) } node = &((*node)[arg.key_]); - if (node == &Value::nullRef) { + if (node == &_Value::nullRef) { // Error: unable to resolve path (object has no member named '' at // position...) } @@ -1612,19 +1624,19 @@ const Value& Path::resolve(const Value& root) const { } template -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; - if (arg.kind_ == PathArgument::kindIndex) { +_Value Path<_Value>::resolve(const _Value& root, const _Value& defaultValue) const { + const _Value* node = &root; + for (typename Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument<_Value>& arg = *it; + if (arg.kind_ == PathArgument<_Value>::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) return defaultValue; node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { + } else if (arg.kind_ == PathArgument<_Value>::kindKey) { if (!node->isObject()) return defaultValue; node = &((*node)[arg.key_]); - if (node == &Value::nullRef) + if (node == &_Value::nullRef) return defaultValue; } } @@ -1632,16 +1644,16 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const { } template -Value& Path::make(Value& root) const { - Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; - if (arg.kind_ == PathArgument::kindIndex) { +_Value& Path<_Value>::make(_Value& root) const { + _Value* node = &root; + for (typename Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument<_Value>& arg = *it; + if (arg.kind_ == PathArgument<_Value>::kindIndex) { if (!node->isArray()) { // Error: node is not an array at position ... } node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { + } else if (arg.kind_ == PathArgument<_Value>::kindKey) { if (!node->isObject()) { // Error: node is not an object at position... } diff --git a/include/json/valueiterator.inl b/include/json/valueiterator.inl index 98af70338..adf513008 100644 --- a/include/json/valueiterator.inl +++ b/include/json/valueiterator.inl @@ -17,33 +17,33 @@ namespace detail { // ////////////////////////////////////////////////////////////////// template -ValueIteratorBase::ValueIteratorBase() +ValueIteratorBase<_Value>::ValueIteratorBase() : current_(), isNull_(true) { } template -ValueIteratorBase::ValueIteratorBase( - const Value::ObjectValues::iterator& current) +ValueIteratorBase<_Value>::ValueIteratorBase( + const typename _Value::ObjectValues::iterator& current) : current_(current), isNull_(false) {} template -Value& ValueIteratorBase::deref() const { +_Value& ValueIteratorBase<_Value>::deref() const { return current_->second; } template -void ValueIteratorBase::increment() { +void ValueIteratorBase<_Value>::increment() { ++current_; } template -void ValueIteratorBase::decrement() { +void ValueIteratorBase<_Value>::decrement() { --current_; } template -ValueIteratorBase::difference_type -ValueIteratorBase::computeDistance(const SelfType& other) const { +typename ValueIteratorBase<_Value>::difference_type +ValueIteratorBase<_Value>::computeDistance(const SelfType& other) const { #ifdef JSON_USE_CPPTL_SMALLMAP return other.current_ - current_; #else @@ -62,7 +62,7 @@ ValueIteratorBase::computeDistance(const SelfType& other) const { // Using a portable hand-made version for non random iterator instead: // return difference_type( std::distance( current_, other.current_ ) ); difference_type myDistance = 0; - for (Value::ObjectValues::iterator it = current_; it != other.current_; + for (typename _Value::ObjectValues::iterator it = current_; it != other.current_; ++it) { ++myDistance; } @@ -71,7 +71,7 @@ ValueIteratorBase::computeDistance(const SelfType& other) const { } template -bool ValueIteratorBase::isEqual(const SelfType& other) const { +bool ValueIteratorBase<_Value>::isEqual(const SelfType& other) const { if (isNull_) { return other.isNull_; } @@ -79,32 +79,32 @@ bool ValueIteratorBase::isEqual(const SelfType& other) const { } template -void ValueIteratorBase::copy(const SelfType& other) { +void ValueIteratorBase<_Value>::copy(const SelfType& other) { current_ = other.current_; isNull_ = other.isNull_; } template -Value ValueIteratorBase::key() const { - const Value::CZString czstring = (*current_).first; +_Value ValueIteratorBase<_Value>::key() const { + const typename _Value::CZString czstring = (*current_).first; if (czstring.data()) { if (czstring.isStaticString()) - return Value(StaticString(czstring.data())); - return Value(czstring.data(), czstring.data() + czstring.length()); + return _Value(StaticString(czstring.data())); + return _Value(czstring.data(), czstring.data() + czstring.length()); } - return Value(czstring.index()); + return _Value(czstring.index()); } template -UInt ValueIteratorBase::index() const { - const Value::CZString czstring = (*current_).first; +UInt ValueIteratorBase<_Value>::index() const { + const typename _Value::CZString czstring = (*current_).first; if (!czstring.data()) return czstring.index(); - return Value::UInt(-1); + return typename _Value::UInt(-1); } template -std::string ValueIteratorBase::name() const { +std::string ValueIteratorBase<_Value>::name() const { char const* keey; char const* end; keey = memberName(&end); @@ -113,13 +113,13 @@ std::string ValueIteratorBase::name() const { } template -char const* ValueIteratorBase::memberName() const { +char const* ValueIteratorBase<_Value>::memberName() const { const char* cname = (*current_).first.data(); return cname ? cname : ""; } template -char const* ValueIteratorBase::memberName(char const** end) const { +char const* ValueIteratorBase<_Value>::memberName(char const** end) const { const char* cname = (*current_).first.data(); if (!cname) { *end = NULL; @@ -138,20 +138,20 @@ char const* ValueIteratorBase::memberName(char const** end) const { // ////////////////////////////////////////////////////////////////// template -ValueConstIterator::ValueConstIterator() {} +ValueConstIterator<_Value>::ValueConstIterator() {} template -ValueConstIterator::ValueConstIterator( - const Value::ObjectValues::iterator& current) - : ValueIteratorBase(current) {} +ValueConstIterator<_Value>::ValueConstIterator( + const typename _Value::ObjectValues::iterator& current) + : ValueIteratorBase<_Value>(current) {} template -ValueConstIterator::ValueConstIterator(ValueIterator const& other) - : ValueIteratorBase(other) {} +ValueConstIterator<_Value>::ValueConstIterator(ValueIterator<_Value> const& other) + : ValueIteratorBase<_Value>(other) {} template -ValueConstIterator& ValueConstIterator:: -operator=(const ValueIteratorBase& other) { +ValueConstIterator<_Value>& ValueConstIterator<_Value>:: +operator=(const ValueIteratorBase<_Value>& other) { copy(other); return *this; } @@ -165,24 +165,24 @@ operator=(const ValueIteratorBase& other) { // ////////////////////////////////////////////////////////////////// template -ValueIterator::ValueIterator() {} +ValueIterator<_Value>::ValueIterator() {} template -ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) - : ValueIteratorBase(current) {} +ValueIterator<_Value>::ValueIterator(const typename _Value::ObjectValues::iterator& current) + : ValueIteratorBase<_Value>(current) {} template -ValueIterator::ValueIterator(const ValueConstIterator& other) - : ValueIteratorBase(other) { +ValueIterator<_Value>::ValueIterator(const ValueConstIterator<_Value>& other) + : ValueIteratorBase<_Value>(other) { throwRuntimeError("ConstIterator to Iterator should never be allowed."); } template -ValueIterator::ValueIterator(const ValueIterator& other) - : ValueIteratorBase(other) {} +ValueIterator<_Value>::ValueIterator(const ValueIterator<_Value>& other) + : ValueIteratorBase<_Value>(other) {} template -ValueIterator& ValueIterator::operator=(const SelfType& other) { +ValueIterator<_Value>& ValueIterator<_Value>::operator=(const SelfType& other) { copy(other); return *this; } diff --git a/include/json/writer.h b/include/json/writer.h index fc7c19cd8..56067a464 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -23,8 +23,7 @@ namespace Json { namespace detail { -template class _Alloc = std::allocator, - class _String = std::basic_string, std::allocator>> +template class Value; /** @@ -53,7 +52,7 @@ class JSON_API StreamWriter { \return zero on success (For now, we always return zero, so check the stream instead.) \throw std::exception possibly, depending on configuration */ - virtual int write(Value const& root, std::ostream* sout) = 0; + virtual int write(_Value const& root, std::ostream* sout) = 0; /** \brief A simple abstract factory. */ @@ -71,7 +70,7 @@ class JSON_API StreamWriter { * A StreamWriter will be created from the factory, used, and then deleted. */ template -std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); +std::string JSON_API writeString(typename StreamWriter<_Value>::Factory const& factory, _Value const& root); /** \brief Build a StreamWriter implementation. @@ -90,7 +89,7 @@ std::string JSON_API writeString(StreamWriter::Factory const& factory, Value con \endcode */ template -class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +class JSON_API StreamWriterBuilder : public StreamWriter<_Value>::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. @@ -115,7 +114,7 @@ class JSON_API StreamWriterBuilder : public StreamWriter::Factory { JSON Value. \sa setDefaults() */ - Json::Value settings_; + _Value settings_; StreamWriterBuilder(); ~StreamWriterBuilder() override; @@ -123,22 +122,22 @@ class JSON_API StreamWriterBuilder : public StreamWriter::Factory { /** * \throw std::exception if something goes wrong (e.g. invalid settings) */ - StreamWriter* newStreamWriter() const override; + StreamWriter<_Value>* newStreamWriter() const override; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. */ - bool validate(Json::Value* invalid) const; + bool validate(_Value* invalid) const; /** A simple way to update a specific setting. */ - Value& operator[](std::string key); + _Value& operator[](std::string key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) * \remark Defaults: * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults */ - static void setDefaults(Json::Value* settings); + static void setDefaults(_Value* settings); }; /** \brief Abstract class for writers. @@ -149,7 +148,7 @@ class JSON_API Writer { public: virtual ~Writer(); - virtual std::string write(const Value& root) = 0; + virtual std::string write(const _Value& root) = 0; }; /** \brief Outputs a Value in JSON format @@ -162,7 +161,7 @@ class JSON_API Writer { * \deprecated Use StreamWriterBuilder. */ template -class JSON_API FastWriter : public Writer { +class JSON_API FastWriter : public Writer<_Value> { public: FastWriter(); @@ -180,10 +179,10 @@ class JSON_API FastWriter : public Writer { void omitEndingLineFeed(); public: // overridden from Writer - std::string write(const Value& root) override; + std::string write(const _Value& root) override; private: - void writeValue(const Value& value); + void writeValue(const _Value& value); std::string document_; bool yamlCompatiblityEnabled_; @@ -216,7 +215,7 @@ class JSON_API FastWriter : public Writer { * \deprecated Use StreamWriterBuilder. */ template -class JSON_API StyledWriter : public Writer { +class JSON_API StyledWriter : public Writer<_Value> { public: StyledWriter(); ~StyledWriter() override {} @@ -226,20 +225,20 @@ class JSON_API StyledWriter : public Writer { * \param root Value to serialize. * \return String containing the JSON document that represents the root value. */ - std::string write(const Value& root) override; + std::string write(const _Value& root) override; private: - void writeValue(const Value& value); - void writeArrayValue(const Value& value); - bool isMultineArray(const Value& value); + void writeValue(const _Value& value); + void writeArrayValue(const _Value& value); + bool isMultineArray(const _Value& value); void pushValue(const std::string& value); void writeIndent(); void writeWithIndent(const std::string& value); void indent(); void unindent(); - void writeCommentBeforeValue(const Value& root); - void writeCommentAfterValueOnSameLine(const Value& root); - bool hasCommentForValue(const Value& value); + void writeCommentBeforeValue(const _Value& root); + void writeCommentAfterValueOnSameLine(const _Value& root); + bool hasCommentForValue(const _Value& value); static std::string normalizeEOL(const std::string& text); typedef std::vector ChildValues; @@ -291,20 +290,20 @@ class JSON_API StyledStreamWriter { * \note There is no point in deriving from Writer, since write() should not * return a value. */ - void write(std::ostream& out, const Value& root); + void write(std::ostream& out, const _Value& root); private: - void writeValue(const Value& value); - void writeArrayValue(const Value& value); - bool isMultineArray(const Value& value); + void writeValue(const _Value& value); + void writeArrayValue(const _Value& value); + bool isMultineArray(const _Value& value); void pushValue(const std::string& value); void writeIndent(); void writeWithIndent(const std::string& value); void indent(); void unindent(); - void writeCommentBeforeValue(const Value& root); - void writeCommentAfterValueOnSameLine(const Value& root); - bool hasCommentForValue(const Value& value); + void writeCommentBeforeValue(const _Value& root); + void writeCommentAfterValueOnSameLine(const _Value& root); + bool hasCommentForValue(const _Value& value); static std::string normalizeEOL(const std::string& text); typedef std::vector ChildValues; @@ -335,11 +334,6 @@ std::string JSON_API valueToString(bool value); template std::string JSON_API valueToQuotedString(const char* value); -/// \brief Output using the StyledStreamWriter. -/// \see Json::operator>>() -template -JSON_API std::ostream& operator<<(std::ostream&, const Value& root); - } // namespace detail typedef detail::FastWriter> FastWriter; // class Json::FastWriter diff --git a/include/json/writer.inl b/include/json/writer.inl index 26b842069..1b0833931 100644 --- a/include/json/writer.inl +++ b/include/json/writer.inl @@ -5,7 +5,7 @@ #if !defined(JSON_IS_AMALGAMATION) #include -#include "json_tool.h" +#include "tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include @@ -74,18 +74,17 @@ namespace Json { namespace detail { -#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr StreamWriterPtr; -#else -typedef std::auto_ptr StreamWriterPtr; -#endif +bool containsControlCharacter(const char* str); +bool containsControlCharacter0(const char* str, unsigned len); +// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp +char const* strnpbrk(char const* s, char const* accept, size_t n); template std::string valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); - if (value == Value::minLargestInt) { - uintToString(LargestUInt(Value::maxLargestInt) + 1, current); + if (value == _Value::minLargestInt) { + uintToString(LargestUInt(_Value::maxLargestInt) + 1, current); *--current = '-'; } else if (value < 0) { uintToString(LargestUInt(-value), current); @@ -110,12 +109,12 @@ std::string valueToString(LargestUInt value) { template std::string valueToString(Int value) { - return valueToString(LargestInt(value)); + return valueToString<_Value>(LargestInt(value)); } template std::string valueToString(UInt value) { - return valueToString(LargestUInt(value)); + return valueToString<_Value>(LargestUInt(value)); } #endif // # if defined(JSON_HAS_INT64) @@ -152,7 +151,7 @@ std::string valueToString(double value, bool useSpecialFloats, unsigned int prec } template -std::string valueToString(double value) { return valueToString(value, false, 17); } +std::string valueToString(double value) { return valueToString<_Value>(value, false, 17); } template std::string valueToString(bool value) { return value ? "true" : "false"; } @@ -287,27 +286,27 @@ static std::string valueToQuotedStringN(const char* value, unsigned length) { // Class Writer // ////////////////////////////////////////////////////////////////// template -Writer::~Writer() {} +Writer<_Value>::~Writer() {} // Class FastWriter // ////////////////////////////////////////////////////////////////// template -FastWriter::FastWriter() +FastWriter<_Value>::FastWriter() : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), omitEndingLineFeed_(false) {} template -void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } +void FastWriter<_Value>::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } template -void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } +void FastWriter<_Value>::dropNullPlaceholders() { dropNullPlaceholders_ = true; } template -void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } +void FastWriter<_Value>::omitEndingLineFeed() { omitEndingLineFeed_ = true; } template -std::string FastWriter::write(const Value& root) { +std::string FastWriter<_Value>::write(const _Value& root) { document_ = ""; writeValue(root); if (!omitEndingLineFeed_) @@ -316,20 +315,20 @@ std::string FastWriter::write(const Value& root) { } template -void FastWriter::writeValue(const Value& value) { +void FastWriter<_Value>::writeValue(const _Value& value) { switch (value.type()) { case nullValue: if (!dropNullPlaceholders_) document_ += "null"; break; case intValue: - document_ += valueToString(value.asLargestInt()); + document_ += valueToString<_Value>(value.asLargestInt()); break; case uintValue: - document_ += valueToString(value.asLargestUInt()); + document_ += valueToString<_Value>(value.asLargestUInt()); break; case realValue: - document_ += valueToString(value.asDouble()); + document_ += valueToString<_Value>(value.asDouble()); break; case stringValue: { @@ -337,11 +336,11 @@ void FastWriter::writeValue(const Value& value) { char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); + if (ok) document_ += valueToQuotedStringN<_Value>(str, static_cast(end-str)); break; } case booleanValue: - document_ += valueToString(value.asBool()); + document_ += valueToString<_Value>(value.asBool()); break; case arrayValue: { document_ += '['; @@ -354,14 +353,14 @@ void FastWriter::writeValue(const Value& value) { document_ += ']'; } break; case objectValue: { - Value::Members members(value.getMemberNames()); + typename _Value::Members members(value.getMemberNames()); document_ += '{'; - for (Value::Members::iterator it = members.begin(); it != members.end(); + for (typename _Value::Members::iterator it = members.begin(); it != members.end(); ++it) { const std::string& name = *it; if (it != members.begin()) document_ += ','; - document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); + document_ += valueToQuotedStringN<_Value>(name.data(), static_cast(name.length())); document_ += yamlCompatiblityEnabled_ ? ": " : ":"; writeValue(value[name]); } @@ -374,11 +373,11 @@ void FastWriter::writeValue(const Value& value) { // ////////////////////////////////////////////////////////////////// template -StyledWriter::StyledWriter() +StyledWriter<_Value>::StyledWriter() : rightMargin_(74), indentSize_(3), addChildValues_() {} template -std::string StyledWriter::write(const Value& root) { +std::string StyledWriter<_Value>::write(const _Value& root) { document_ = ""; addChildValues_ = false; indentString_ = ""; @@ -390,19 +389,19 @@ std::string StyledWriter::write(const Value& root) { } template -void StyledWriter::writeValue(const Value& value) { +void StyledWriter<_Value>::writeValue(const _Value& value) { switch (value.type()) { case nullValue: pushValue("null"); break; case intValue: - pushValue(valueToString(value.asLargestInt())); + pushValue(valueToString<_Value>(value.asLargestInt())); break; case uintValue: - pushValue(valueToString(value.asLargestUInt())); + pushValue(valueToString<_Value>(value.asLargestUInt())); break; case realValue: - pushValue(valueToString(value.asDouble())); + pushValue(valueToString<_Value>(value.asDouble())); break; case stringValue: { @@ -410,29 +409,29 @@ void StyledWriter::writeValue(const Value& value) { char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + if (ok) pushValue(valueToQuotedStringN<_Value>(str, static_cast(end-str))); else pushValue(""); break; } case booleanValue: - pushValue(valueToString(value.asBool())); + pushValue(valueToString<_Value>(value.asBool())); break; case arrayValue: writeArrayValue(value); break; case objectValue: { - Value::Members members(value.getMemberNames()); + typename _Value::Members members(value.getMemberNames()); if (members.empty()) pushValue("{}"); else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + typename _Value::Members::iterator it = members.begin(); for (;;) { const std::string& name = *it; - const Value& childValue = value[name]; + const _Value& childValue = value[name]; writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); + writeWithIndent(valueToQuotedString<_Value>(name.c_str())); document_ += " : "; writeValue(childValue); if (++it == members.end()) { @@ -450,7 +449,7 @@ void StyledWriter::writeValue(const Value& value) { } template -void StyledWriter::writeArrayValue(const Value& value) { +void StyledWriter<_Value>::writeArrayValue(const _Value& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); @@ -462,7 +461,7 @@ void StyledWriter::writeArrayValue(const Value& value) { bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { - const Value& childValue = value[index]; + const _Value& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); @@ -494,12 +493,12 @@ void StyledWriter::writeArrayValue(const Value& value) { } template -bool StyledWriter::isMultineArray(const Value& value) { +bool StyledWriter<_Value>::isMultineArray(const _Value& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (int index = 0; index < size && !isMultiLine; ++index) { - const Value& childValue = value[index]; + const _Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } @@ -522,7 +521,7 @@ bool StyledWriter::isMultineArray(const Value& value) { } template -void StyledWriter::pushValue(const std::string& value) { +void StyledWriter<_Value>::pushValue(const std::string& value) { if (addChildValues_) childValues_.push_back(value); else @@ -530,7 +529,7 @@ void StyledWriter::pushValue(const std::string& value) { } template -void StyledWriter::writeIndent() { +void StyledWriter<_Value>::writeIndent() { if (!document_.empty()) { char last = document_[document_.length() - 1]; if (last == ' ') // already indented @@ -542,22 +541,22 @@ void StyledWriter::writeIndent() { } template -void StyledWriter::writeWithIndent(const std::string& value) { +void StyledWriter<_Value>::writeWithIndent(const std::string& value) { writeIndent(); document_ += value; } template -void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } +void StyledWriter<_Value>::indent() { indentString_ += std::string(indentSize_, ' '); } template -void StyledWriter::unindent() { +void StyledWriter<_Value>::unindent() { assert(int(indentString_.size()) >= indentSize_); indentString_.resize(indentString_.size() - indentSize_); } template -void StyledWriter::writeCommentBeforeValue(const Value& root) { +void StyledWriter<_Value>::writeCommentBeforeValue(const _Value& root) { if (!root.hasComment(commentBefore)) return; @@ -578,7 +577,7 @@ void StyledWriter::writeCommentBeforeValue(const Value& root) { } template -void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { +void StyledWriter<_Value>::writeCommentAfterValueOnSameLine(const _Value& root) { if (root.hasComment(commentAfterOnSameLine)) document_ += " " + root.getComment(commentAfterOnSameLine); @@ -590,7 +589,7 @@ void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { } template -bool StyledWriter::hasCommentForValue(const Value& value) { +bool StyledWriter<_Value>::hasCommentForValue(const _Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); @@ -600,12 +599,12 @@ bool StyledWriter::hasCommentForValue(const Value& value) { // ////////////////////////////////////////////////////////////////// template -StyledStreamWriter::StyledStreamWriter(std::string indentation) +StyledStreamWriter<_Value>::StyledStreamWriter(std::string indentation) : document_(NULL), rightMargin_(74), indentation_(indentation), addChildValues_() {} template -void StyledStreamWriter::write(std::ostream& out, const Value& root) { +void StyledStreamWriter<_Value>::write(std::ostream& out, const _Value& root) { document_ = &out; addChildValues_ = false; indentString_ = ""; @@ -620,19 +619,19 @@ void StyledStreamWriter::write(std::ostream& out, const Value& root) { } template -void StyledStreamWriter::writeValue(const Value& value) { +void StyledStreamWriter<_Value>::writeValue(const _Value& value) { switch (value.type()) { case nullValue: pushValue("null"); break; case intValue: - pushValue(valueToString(value.asLargestInt())); + pushValue(valueToString<_Value>(value.asLargestInt())); break; case uintValue: - pushValue(valueToString(value.asLargestUInt())); + pushValue(valueToString<_Value>(value.asLargestUInt())); break; case realValue: - pushValue(valueToString(value.asDouble())); + pushValue(valueToString<_Value>(value.asDouble())); break; case stringValue: { @@ -640,29 +639,29 @@ void StyledStreamWriter::writeValue(const Value& value) { char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + if (ok) pushValue(valueToQuotedStringN<_Value>(str, static_cast(end-str))); else pushValue(""); break; } case booleanValue: - pushValue(valueToString(value.asBool())); + pushValue(valueToString<_Value>(value.asBool())); break; case arrayValue: writeArrayValue(value); break; case objectValue: { - Value::Members members(value.getMemberNames()); + typename _Value::Members members(value.getMemberNames()); if (members.empty()) pushValue("{}"); else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + typename _Value::Members::iterator it = members.begin(); for (;;) { const std::string& name = *it; - const Value& childValue = value[name]; + const _Value& childValue = value[name]; writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); + writeWithIndent(valueToQuotedString<_Value>(name.c_str())); *document_ << " : "; writeValue(childValue); if (++it == members.end()) { @@ -680,7 +679,7 @@ void StyledStreamWriter::writeValue(const Value& value) { } template -void StyledStreamWriter::writeArrayValue(const Value& value) { +void StyledStreamWriter<_Value>::writeArrayValue(const _Value& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); @@ -692,7 +691,7 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { - const Value& childValue = value[index]; + const _Value& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); @@ -726,12 +725,12 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { } template -bool StyledStreamWriter::isMultineArray(const Value& value) { +bool StyledStreamWriter<_Value>::isMultineArray(const _Value& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (int index = 0; index < size && !isMultiLine; ++index) { - const Value& childValue = value[index]; + const _Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } @@ -754,7 +753,7 @@ bool StyledStreamWriter::isMultineArray(const Value& value) { } template -void StyledStreamWriter::pushValue(const std::string& value) { +void StyledStreamWriter<_Value>::pushValue(const std::string& value) { if (addChildValues_) childValues_.push_back(value); else @@ -762,7 +761,7 @@ void StyledStreamWriter::pushValue(const std::string& value) { } template -void StyledStreamWriter::writeIndent() { +void StyledStreamWriter<_Value>::writeIndent() { // blep intended this to look at the so-far-written string // to determine whether we are already indented, but // with a stream we cannot do that. So we rely on some saved state. @@ -771,23 +770,23 @@ void StyledStreamWriter::writeIndent() { } template -void StyledStreamWriter::writeWithIndent(const std::string& value) { +void StyledStreamWriter<_Value>::writeWithIndent(const std::string& value) { if (!indented_) writeIndent(); *document_ << value; indented_ = false; } template -void StyledStreamWriter::indent() { indentString_ += indentation_; } +void StyledStreamWriter<_Value>::indent() { indentString_ += indentation_; } template -void StyledStreamWriter::unindent() { +void StyledStreamWriter<_Value>::unindent() { assert(indentString_.size() >= indentation_.size()); indentString_.resize(indentString_.size() - indentation_.size()); } template -void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { +void StyledStreamWriter<_Value>::writeCommentBeforeValue(const _Value& root) { if (!root.hasComment(commentBefore)) return; @@ -806,7 +805,7 @@ void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { } template -void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { +void StyledStreamWriter<_Value>::writeCommentAfterValueOnSameLine(const _Value& root) { if (root.hasComment(commentAfterOnSameLine)) *document_ << ' ' << root.getComment(commentAfterOnSameLine); @@ -818,7 +817,7 @@ void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { } template -bool StyledStreamWriter::hasCommentForValue(const Value& value) { +bool StyledStreamWriter<_Value>::hasCommentForValue(const _Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); @@ -838,7 +837,7 @@ struct CommentStyle { }; template -struct BuiltStyledStreamWriter : public StreamWriter +struct BuiltStyledStreamWriter : public StreamWriter<_Value> { BuiltStyledStreamWriter( std::string const& indentation, @@ -848,19 +847,19 @@ struct BuiltStyledStreamWriter : public StreamWriter std::string const& endingLineFeedSymbol, bool useSpecialFloats, unsigned int precision); - int write(Value const& root, std::ostream* sout) override; + int write(_Value const& root, std::ostream* sout) override; private: - void writeValue(Value const& value); - void writeArrayValue(Value const& value); - bool isMultineArray(Value const& value); + void writeValue(_Value const& value); + void writeArrayValue(_Value const& value); + bool isMultineArray(_Value const& value); void pushValue(std::string const& value); void writeIndent(); void writeWithIndent(std::string const& value); void indent(); void unindent(); - void writeCommentBeforeValue(Value const& root); - void writeCommentAfterValueOnSameLine(Value const& root); - static bool hasCommentForValue(const Value& value); + void writeCommentBeforeValue(_Value const& root); + void writeCommentAfterValueOnSameLine(_Value const& root); + static bool hasCommentForValue(const _Value& value); typedef std::vector ChildValues; @@ -878,7 +877,7 @@ private: unsigned int precision_; }; template -BuiltStyledStreamWriter::BuiltStyledStreamWriter( +BuiltStyledStreamWriter<_Value>::BuiltStyledStreamWriter( std::string const& indentation, CommentStyle::Enum cs, std::string const& colonSymbol, @@ -899,9 +898,9 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter( { } template -int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) +int BuiltStyledStreamWriter<_Value>::write(_Value const& root, std::ostream* sout) { - sout_ = sout; + StreamWriter<_Value>::sout_ = sout; addChildValues_ = false; indented_ = true; indentString_ = ""; @@ -910,24 +909,24 @@ int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); - *sout_ << endingLineFeedSymbol_; - sout_ = NULL; + *(StreamWriter<_Value>::sout_) << endingLineFeedSymbol_; + StreamWriter<_Value>::sout_ = NULL; return 0; } template -void BuiltStyledStreamWriter::writeValue(Value const& value) { +void BuiltStyledStreamWriter<_Value>::writeValue(_Value const& value) { switch (value.type()) { case nullValue: pushValue(nullSymbol_); break; case intValue: - pushValue(valueToString(value.asLargestInt())); + pushValue(valueToString<_Value>(value.asLargestInt())); break; case uintValue: - pushValue(valueToString(value.asLargestUInt())); + pushValue(valueToString<_Value>(value.asLargestUInt())); break; case realValue: - pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); + pushValue(valueToString<_Value>(value.asDouble(), useSpecialFloats_, precision_)); break; case stringValue: { @@ -935,36 +934,36 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + if (ok) pushValue(valueToQuotedStringN<_Value>(str, static_cast(end-str))); else pushValue(""); break; } case booleanValue: - pushValue(valueToString(value.asBool())); + pushValue(valueToString<_Value>(value.asBool())); break; case arrayValue: writeArrayValue(value); break; case objectValue: { - Value::Members members(value.getMemberNames()); + typename _Value::Members members(value.getMemberNames()); if (members.empty()) pushValue("{}"); else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + typename _Value::Members::iterator it = members.begin(); for (;;) { std::string const& name = *it; - Value const& childValue = value[name]; + _Value const& childValue = value[name]; writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); - *sout_ << colonSymbol_; + writeWithIndent(valueToQuotedStringN<_Value>(name.data(), static_cast(name.length()))); + *(StreamWriter<_Value>::sout_) << colonSymbol_; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); break; } - *sout_ << ","; + *(StreamWriter<_Value>::sout_) << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); @@ -975,7 +974,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { } template -void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { +void BuiltStyledStreamWriter<_Value>::writeArrayValue(_Value const& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); @@ -987,7 +986,7 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { - Value const& childValue = value[index]; + _Value const& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); @@ -1001,7 +1000,7 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { writeCommentAfterValueOnSameLine(childValue); break; } - *sout_ << ","; + *(StreamWriter<_Value>::sout_) << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); @@ -1009,26 +1008,26 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { } else // output on a single line { assert(childValues_.size() == size); - *sout_ << "["; - if (!indentation_.empty()) *sout_ << " "; + *(StreamWriter<_Value>::sout_) << "["; + if (!indentation_.empty()) *(StreamWriter<_Value>::sout_) << " "; for (unsigned index = 0; index < size; ++index) { if (index > 0) - *sout_ << ", "; - *sout_ << childValues_[index]; + *(StreamWriter<_Value>::sout_) << ", "; + *(StreamWriter<_Value>::sout_) << childValues_[index]; } - if (!indentation_.empty()) *sout_ << " "; - *sout_ << "]"; + if (!indentation_.empty()) *(StreamWriter<_Value>::sout_) << " "; + *(StreamWriter<_Value>::sout_) << "]"; } } } template -bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { +bool BuiltStyledStreamWriter<_Value>::isMultineArray(_Value const& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (int index = 0; index < size && !isMultiLine; ++index) { - Value const& childValue = value[index]; + _Value const& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } @@ -1051,15 +1050,15 @@ bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { } template -void BuiltStyledStreamWriter::pushValue(std::string const& value) { +void BuiltStyledStreamWriter<_Value>::pushValue(std::string const& value) { if (addChildValues_) childValues_.push_back(value); else - *sout_ << value; + *(StreamWriter<_Value>::sout_) << value; } template -void BuiltStyledStreamWriter::writeIndent() { +void BuiltStyledStreamWriter<_Value>::writeIndent() { // blep intended this to look at the so-far-written string // to determine whether we are already indented, but // with a stream we cannot do that. So we rely on some saved state. @@ -1067,28 +1066,28 @@ void BuiltStyledStreamWriter::writeIndent() { if (!indentation_.empty()) { // In this case, drop newlines too. - *sout_ << '\n' << indentString_; + *(StreamWriter<_Value>::sout_) << '\n' << indentString_; } } template -void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { +void BuiltStyledStreamWriter<_Value>::writeWithIndent(std::string const& value) { if (!indented_) writeIndent(); - *sout_ << value; + *(StreamWriter<_Value>::sout_) << value; indented_ = false; } template -void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } +void BuiltStyledStreamWriter<_Value>::indent() { indentString_ += indentation_; } template -void BuiltStyledStreamWriter::unindent() { +void BuiltStyledStreamWriter<_Value>::unindent() { assert(indentString_.size() >= indentation_.size()); indentString_.resize(indentString_.size() - indentation_.size()); } template -void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { +void BuiltStyledStreamWriter<_Value>::writeCommentBeforeValue(_Value const& root) { if (cs_ == CommentStyle::None) return; if (!root.hasComment(commentBefore)) return; @@ -1097,31 +1096,31 @@ void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { const std::string& comment = root.getComment(commentBefore); std::string::const_iterator iter = comment.begin(); while (iter != comment.end()) { - *sout_ << *iter; + *(StreamWriter<_Value>::sout_) << *iter; if (*iter == '\n' && (iter != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would write extra newline - *sout_ << indentString_; + *(StreamWriter<_Value>::sout_) << indentString_; ++iter; } indented_ = false; } template -void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { +void BuiltStyledStreamWriter<_Value>::writeCommentAfterValueOnSameLine(_Value const& root) { if (cs_ == CommentStyle::None) return; if (root.hasComment(commentAfterOnSameLine)) - *sout_ << " " + root.getComment(commentAfterOnSameLine); + *(StreamWriter<_Value>::sout_) << " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { writeIndent(); - *sout_ << root.getComment(commentAfter); + *(StreamWriter<_Value>::sout_) << root.getComment(commentAfter); } } // static template -bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { +bool BuiltStyledStreamWriter<_Value>::hasCommentForValue(const _Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); @@ -1131,27 +1130,27 @@ bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { // StreamWriter template -StreamWriter::StreamWriter() +StreamWriter<_Value>::StreamWriter() : sout_(NULL) { } template -StreamWriter::~StreamWriter() +StreamWriter<_Value>::~StreamWriter() { } template -StreamWriter::Factory::~Factory() +StreamWriter<_Value>::Factory::~Factory() {} template -StreamWriterBuilder::StreamWriterBuilder() +StreamWriterBuilder<_Value>::StreamWriterBuilder() { setDefaults(&settings_); } template -StreamWriterBuilder::~StreamWriterBuilder() +StreamWriterBuilder<_Value>::~StreamWriterBuilder() {} template -StreamWriter* StreamWriterBuilder::newStreamWriter() const +StreamWriter<_Value>* StreamWriterBuilder<_Value>::newStreamWriter() const { std::string indentation = settings_["indentation"].asString(); std::string cs_str = settings_["commentStyle"].asString(); @@ -1179,7 +1178,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const } if (pre > 17) pre = 17; std::string endingLineFeedSymbol = ""; - return new BuiltStyledStreamWriter( + return new BuiltStyledStreamWriter<_Value>( indentation, cs, colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); } @@ -1195,14 +1194,14 @@ static void getValidWriterKeys(std::set* valid_keys) valid_keys->insert("precision"); } template -bool StreamWriterBuilder::validate(Json::Value* invalid) const +bool StreamWriterBuilder<_Value>::validate(_Value* invalid) const { - Json::Value my_invalid; + _Value my_invalid; if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; + _Value& inv = *invalid; std::set valid_keys; - getValidWriterKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); + getValidWriterKeys<_Value>(&valid_keys); + typename _Value::Members keys = settings_.getMemberNames(); size_t n = keys.size(); for (size_t i = 0; i < n; ++i) { std::string const& key = keys[i]; @@ -1213,13 +1212,13 @@ bool StreamWriterBuilder::validate(Json::Value* invalid) const return 0u == inv.size(); } template -Value& StreamWriterBuilder::operator[](std::string key) +_Value& StreamWriterBuilder<_Value>::operator[](std::string key) { return settings_[key]; } // static template -void StreamWriterBuilder::setDefaults(Json::Value* settings) +void StreamWriterBuilder<_Value>::setDefaults(_Value* settings) { //! [StreamWriterBuilderDefaults] (*settings)["commentStyle"] = "All"; @@ -1232,16 +1231,30 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings) } template -std::string writeString(StreamWriter::Factory const& builder, Value const& root) { +std::string writeString(typename StreamWriter<_Value>::Factory const& builder, _Value const& root) { +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr> StreamWriterPtr; +#else +typedef std::auto_ptr> StreamWriterPtr; +#endif + std::ostringstream sout; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); return sout.str(); } +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() template -std::ostream& operator<<(std::ostream& sout, Value const& root) { - StreamWriterBuilder builder; +std::ostream& operator<<(std::ostream& sout, _Value const& root) { +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr> StreamWriterPtr; +#else +typedef std::auto_ptr> StreamWriterPtr; +#endif + + StreamWriterBuilder<_Value> builder; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); return sout; diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index c8bbd0d7e..a2e2912cd 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -82,13 +82,13 @@ printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { fprintf(fout, "%s=%s\n", path.c_str(), - Json::valueToString(value.asLargestInt()).c_str()); + Json::detail::valueToString(value.asLargestInt()).c_str()); break; case Json::uintValue: fprintf(fout, "%s=%s\n", path.c_str(), - Json::valueToString(value.asLargestUInt()).c_str()); + Json::detail::valueToString(value.asLargestUInt()).c_str()); break; case Json::realValue: fprintf(fout, @@ -185,7 +185,7 @@ static std::string useBuiltStyledStreamWriter( Json::Value const& root) { Json::StreamWriterBuilder builder; - return Json::writeString(builder, root); + return Json::detail::writeString(builder, root); } static int rewriteValueTree( const std::string& rewritePath, diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 5fafbdbf7..4c196b191 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -5,7 +5,7 @@ #if !defined(JSON_IS_AMALGAMATION) #include -#include +#include #include #include #endif // if !defined(JSON_IS_AMALGAMATION) @@ -42,9 +42,6 @@ #pragma warning(disable : 4996) #endif -static int const stackLimit_g = 1000; -static int stackDepth_g = 0; // see readValue() - namespace Json { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) @@ -71,9 +68,11 @@ Features Features::strictMode() { return features; } +namespace detail { // exact copy of Implementation of class Features // //////////////////////////////// OurFeatures OurFeatures::all() { return OurFeatures(); } +} // namespace detail } // namespace Json diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 479533371..32b1799cf 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -79,7 +79,9 @@ typedef std::unique_ptr StreamWriterPtr; typedef std::auto_ptr StreamWriterPtr; #endif -static bool containsControlCharacter(const char* str) { +namespace detail { + +bool containsControlCharacter(const char* str) { while (*str) { if (isControlCharacter(*(str++))) return true; @@ -87,7 +89,7 @@ static bool containsControlCharacter(const char* str) { return false; } -static bool containsControlCharacter0(const char* str, unsigned len) { +bool containsControlCharacter0(const char* str, unsigned len) { char const* end = str + len; while (end != str) { if (isControlCharacter(*str) || 0==*str) @@ -98,7 +100,7 @@ static bool containsControlCharacter0(const char* str, unsigned len) { } // https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp -static char const* strnpbrk(char const* s, char const* accept, size_t n) { +char const* strnpbrk(char const* s, char const* accept, size_t n) { assert((s || !n) && accept); char const* const end = s + n; @@ -113,4 +115,5 @@ static char const* strnpbrk(char const* s, char const* accept, size_t n) { return NULL; } +} // namespace detail } // namespace Json diff --git a/src/test_lib_json/jsontest.cpp b/src/test_lib_json/jsontest.cpp index bd9463fa5..2a43d3e61 100644 --- a/src/test_lib_json/jsontest.cpp +++ b/src/test_lib_json/jsontest.cpp @@ -203,11 +203,11 @@ TestResult& TestResult::addToLastFailure(const std::string& message) { } TestResult& TestResult::operator<<(Json::Int64 value) { - return addToLastFailure(Json::valueToString(value)); + return addToLastFailure(Json::detail::valueToString(value)); } TestResult& TestResult::operator<<(Json::UInt64 value) { - return addToLastFailure(Json::valueToString(value)); + return addToLastFailure(Json::detail::valueToString(value)); } TestResult& TestResult::operator<<(bool value) { diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index 30136b0fd..0a22f10c1 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -1565,7 +1565,7 @@ JSONTEST_FIXTURE(ValueTest, CommentBefore) { wbuilder.settings_["commentStyle"] = "All"; { char const expected[] = "// this comment should appear before\nnull"; - std::string result = Json::writeString(wbuilder, val); + std::string result = Json::detail::writeString(wbuilder, val); JSONTEST_ASSERT_STRING_EQUAL(expected, result); std::string res2 = val.toStyledString(); std::string exp2 = "\n"; @@ -1577,7 +1577,7 @@ JSONTEST_FIXTURE(ValueTest, CommentBefore) { val.swapPayload(other); { char const expected[] = "// this comment should appear before\n\"hello\""; - std::string result = Json::writeString(wbuilder, val); + std::string result = Json::detail::writeString(wbuilder, val); JSONTEST_ASSERT_STRING_EQUAL(expected, result); std::string res2 = val.toStyledString(); std::string exp2 = "\n"; @@ -1591,7 +1591,7 @@ JSONTEST_FIXTURE(ValueTest, CommentBefore) { // Assignment over-writes comments. { char const expected[] = "\"hello\""; - std::string result = Json::writeString(wbuilder, val); + std::string result = Json::detail::writeString(wbuilder, val); JSONTEST_ASSERT_STRING_EQUAL(expected, result); std::string res2 = val.toStyledString(); std::string exp2 = ""; @@ -1661,17 +1661,17 @@ JSONTEST_FIXTURE(ValueTest, specialFloats) { Json::Value v = std::numeric_limits::quiet_NaN(); std::string expected = "NaN"; - std::string result = Json::writeString(b, v); + std::string result = Json::detail::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); v = std::numeric_limits::infinity(); expected = "Infinity"; - result = Json::writeString(b, v); + result = Json::detail::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); v = -std::numeric_limits::infinity(); expected = "-Infinity"; - result = Json::writeString(b, v); + result = Json::detail::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); } @@ -1681,34 +1681,34 @@ JSONTEST_FIXTURE(ValueTest, precision) { Json::Value v = 100.0/3; std::string expected = "33.333"; - std::string result = Json::writeString(b, v); + std::string result = Json::detail::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); v = 0.25000000; expected = "0.25"; - result = Json::writeString(b, v); + result = Json::detail::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); v = 0.2563456; expected = "0.25635"; - result = Json::writeString(b, v); + result = Json::detail::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); b.settings_["precision"] = 1; expected = "0.3"; - result = Json::writeString(b, v); + result = Json::detail::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); b.settings_["precision"] = 17; v = 1234857476305.256345694873740545068; expected = "1234857476305.2563"; - result = Json::writeString(b, v); + result = Json::detail::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); b.settings_["precision"] = 24; v = 0.256345694873740545068; expected = "0.25634569487374054"; - result = Json::writeString(b, v); + result = Json::detail::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); } @@ -1729,9 +1729,9 @@ JSONTEST_FIXTURE(StreamWriterTest, dropNullPlaceholders) { Json::StreamWriterBuilder b; Json::Value nullValue; b.settings_["dropNullPlaceholders"] = false; - JSONTEST_ASSERT(Json::writeString(b, nullValue) == "null"); + JSONTEST_ASSERT(Json::detail::writeString(b, nullValue) == "null"); b.settings_["dropNullPlaceholders"] = true; - JSONTEST_ASSERT(Json::writeString(b, nullValue) == ""); + JSONTEST_ASSERT(Json::detail::writeString(b, nullValue) == ""); } JSONTEST_FIXTURE(StreamWriterTest, writeZeroes) { @@ -1743,7 +1743,7 @@ JSONTEST_FIXTURE(StreamWriterTest, writeZeroes) { Json::Value root; root = binary; JSONTEST_ASSERT_STRING_EQUAL(binary, root.asString()); - std::string out = Json::writeString(b, root); + std::string out = Json::detail::writeString(b, root); JSONTEST_ASSERT_EQUAL(expected.size(), out.size()); JSONTEST_ASSERT_STRING_EQUAL(expected, out); } @@ -1751,7 +1751,7 @@ JSONTEST_FIXTURE(StreamWriterTest, writeZeroes) { Json::Value root; root["top"] = binary; JSONTEST_ASSERT_STRING_EQUAL(binary, root["top"].asString()); - std::string out = Json::writeString(b, root["top"]); + std::string out = Json::detail::writeString(b, root["top"]); JSONTEST_ASSERT_STRING_EQUAL(expected, out); } } From 1ae3e95af22cdb40a55e28a1d4a3e7850beb5f3d Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 21:46:13 +0000 Subject: [PATCH 11/41] Move std::string -> String from value type Now starting to change over to the new memory allocation, care has been taken to leave exceptions in the original allocator --- include/json/reader.h | 14 ++-- include/json/reader.inl | 29 ++++---- include/json/tool.h | 5 +- include/json/value.h | 47 +++++++------ include/json/value.inl | 38 +++++----- include/json/valueiterator.inl | 6 +- include/json/writer.h | 57 ++++++++------- include/json/writer.inl | 125 +++++++++++++++++---------------- 8 files changed, 169 insertions(+), 152 deletions(-) diff --git a/include/json/reader.h b/include/json/reader.h index 2435f061d..0f9a38831 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -34,6 +34,7 @@ namespace detail { template class JSON_API Reader { public: + typedef typename _Value::String String; typedef char Char; typedef const Char* Location; @@ -74,7 +75,7 @@ class JSON_API Reader { * error occurred. */ bool - parse(const std::string& document, _Value& root, bool collectComments = true); + parse(const String& document, _Value& root, bool collectComments = true); /** \brief Read a Value from a JSON document. @@ -205,7 +206,7 @@ class JSON_API Reader { bool decodeNumber(Token& token); bool decodeNumber(Token& token, _Value& decoded); bool decodeString(Token& token); - bool decodeString(Token& token, std::string& decoded); + bool decodeString(Token& token, String& decoded); bool decodeDouble(Token& token); bool decodeDouble(Token& token, _Value& decoded); bool decodeUnicodeCodePoint(Token& token, @@ -226,20 +227,20 @@ class JSON_API Reader { Char getNextChar(); void getLocationLineAndColumn(Location location, int& line, int& column) const; - std::string getLocationLineAndColumn(Location location) const; + String getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); typedef std::stack<_Value*> Nodes; Nodes nodes_; Errors errors_; - std::string document_; + String document_; Location begin_; Location end_; Location current_; Location lastValueEnd_; _Value* lastValue_; - std::string commentsBefore_; + String commentsBefore_; Features features_; bool collectComments_; }; // Reader @@ -296,6 +297,7 @@ class JSON_API CharReader { template class JSON_API CharReaderBuilder : public CharReader<_Value>::Factory { public: + typedef typename _Value::String String; // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. /** Configuration of this builder. @@ -348,7 +350,7 @@ class JSON_API CharReaderBuilder : public CharReader<_Value>::Factory { /** A simple way to update a specific setting. */ - _Value& operator[](std::string key); + _Value& operator[](String key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) diff --git a/include/json/reader.inl b/include/json/reader.inl index d86309301..cd97b5f3b 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -76,7 +76,7 @@ Reader<_Value>::Reader(const Features& features) template bool -Reader<_Value>::parse(const std::string& document, _Value& root, bool collectComments) { +Reader<_Value>::parse(const String& document, _Value& root, bool collectComments) { document_ = document; const char* begin = document_.c_str(); const char* end = begin + document_.length(); @@ -92,7 +92,7 @@ bool Reader<_Value>::parse(std::istream& sin, _Value& root, bool collectComments // Since std::string is reference-counted, this at least does not // create an extra copy. - std::string doc; + String doc; std::getline(sin, doc, (char)EOF); return parse(doc, root, collectComments); } @@ -356,8 +356,8 @@ bool Reader<_Value>::readComment() { } template -static std::string normalizeEOL(typename Reader<_Value>::Location begin, typename Reader<_Value>::Location end) { - std::string normalized; +static typename _Value::String normalizeEOL(typename Reader<_Value>::Location begin, typename Reader<_Value>::Location end) { + typename _Value::String normalized; normalized.reserve(end - begin); typename Reader<_Value>::Location current = begin; while (current != end) { @@ -379,7 +379,7 @@ template void Reader<_Value>::addComment(Location begin, Location end, CommentPlacement placement) { assert(collectComments_); - const std::string& normalized = normalizeEOL<_Value>(begin, end); + const String& normalized = normalizeEOL<_Value>(begin, end); if (placement == commentAfterOnSameLine) { assert(lastValue_ != 0); lastValue_->setComment(normalized, placement); @@ -454,7 +454,7 @@ bool Reader<_Value>::readString() { template bool Reader<_Value>::readObject(Token& tokenStart) { Token tokenName; - std::string name; + String name; _Value init(objectValue); currentValue().swapPayload(init); currentValue().setOffsetStart(tokenStart.start_ - begin_); @@ -615,10 +615,10 @@ bool Reader<_Value>::decodeDouble(Token& token) { template bool Reader<_Value>::decodeDouble(Token& token, _Value& decoded) { double value = 0; - std::string buffer(token.start_, token.end_); + String buffer(token.start_, token.end_); std::istringstream is(buffer); if (!(is >> value)) - return addError("'" + std::string(token.start_, token.end_) + + return addError("'" + String(token.start_, token.end_) + "' is not a number.", token); decoded = value; @@ -627,7 +627,7 @@ bool Reader<_Value>::decodeDouble(Token& token, _Value& decoded) { template bool Reader<_Value>::decodeString(Token& token) { - std::string decoded_string; + String decoded_string; if (!decodeString(token, decoded_string)) return false; _Value decoded(decoded_string); @@ -638,7 +638,7 @@ bool Reader<_Value>::decodeString(Token& token) { } template -bool Reader<_Value>::decodeString(Token& token, std::string& decoded) { +bool Reader<_Value>::decodeString(Token& token, String& decoded) { decoded.reserve(token.end_ - token.start_ - 2); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' @@ -679,7 +679,7 @@ bool Reader<_Value>::decodeString(Token& token, std::string& decoded) { unsigned int unicode; if (!decodeUnicodeCodePoint(token, current, end, unicode)) return false; - decoded += codePointToUTF8(unicode); + decoded += codePointToUTF8(unicode); } break; default: return addError("Bad escape sequence in string", token, current); @@ -818,7 +818,7 @@ void Reader<_Value>::getLocationLineAndColumn(Location location, } template -std::string Reader<_Value>::getLocationLineAndColumn(Location location) const { +typename _Value::String Reader<_Value>::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; @@ -929,6 +929,7 @@ public: template class OurReader { public: + typedef typename _Value::String String; typedef char Char; typedef const Char* Location; struct StructuredError { @@ -1716,7 +1717,7 @@ bool OurReader<_Value>::decodeString(Token& token, std::string& decoded) { unsigned int unicode; if (!decodeUnicodeCodePoint(token, current, end, unicode)) return false; - decoded += codePointToUTF8(unicode); + decoded += codePointToUTF8(unicode); } break; default: return addError("Bad escape sequence in string", token, current); @@ -2019,7 +2020,7 @@ bool CharReaderBuilder<_Value>::validate(_Value* invalid) const return 0u == inv.size(); } template -_Value& CharReaderBuilder<_Value>::operator[](std::string key) +_Value& CharReaderBuilder<_Value>::operator[](String key) { return settings_[key]; } diff --git a/include/json/tool.h b/include/json/tool.h index e65e51b41..ea2f50aa2 100644 --- a/include/json/tool.h +++ b/include/json/tool.h @@ -15,8 +15,9 @@ namespace Json { /// Converts a unicode code-point to UTF-8. -static inline std::string codePointToUTF8(unsigned int cp) { - std::string result; +template +static inline _String codePointToUTF8(unsigned int cp) { + _String result; // based on description from http://en.wikipedia.org/wiki/UTF-8 diff --git a/include/json/value.h b/include/json/value.h index dd0fd4975..566ffff20 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -167,6 +167,8 @@ class JSON_API Value { template friend class ValueIteratorBase; public: + typedef _String String; + typedef _Alloc Allocator; typedef std::vector Members; typedef ValueIterator iterator; typedef ValueConstIterator const_iterator; @@ -295,7 +297,7 @@ Json::Value obj_value(Json::objectValue); // {} * \endcode */ Value(const Json::StaticString& value); - Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. + Value(const String& value); ///< Copy data() til size(). Embedded zeroes too. #ifdef JSON_USE_CPPTL Value(const CppTL::ConstString& value); #endif @@ -328,7 +330,7 @@ Json::Value obj_value(Json::objectValue); // {} int compare(const Value& other) const; const char* asCString() const; ///< Embedded zeroes could cause you trouble! - std::string asString() const; ///< Embedded zeroes are possible. + String asString() const; ///< Embedded zeroes are possible. /** Get raw char* of string-value. * \return false if !string. (Seg-fault if str or end are NULL.) */ @@ -432,11 +434,11 @@ Json::Value obj_value(Json::objectValue); // {} const Value& operator[](const char* key) const; /// Access an object value by name, create a null member if it does not exist. /// \param key may contain embedded nulls. - Value& operator[](const std::string& key); + Value& operator[](const String& key); /// Access an object value by name, returns null if there is no member with /// that name. /// \param key may contain embedded nulls. - const Value& operator[](const std::string& key) const; + const Value& operator[](const String& key) const; /** \brief Access an object value by name, create a null member if it does not exist. @@ -467,7 +469,7 @@ Json::Value obj_value(Json::objectValue); // {} /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy /// \param key may contain embedded nulls. - Value get(const std::string& key, const Value& defaultValue) const; + Value get(const String& key, const Value& defaultValue) const; #ifdef JSON_USE_CPPTL /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy @@ -492,7 +494,7 @@ Json::Value obj_value(Json::objectValue); // {} /// Same as removeMember(const char*) /// \param key may contain embedded nulls. /// \deprecated - Value removeMember(const std::string& key); + Value removeMember(const String& key); /// Same as removeMember(const char* begin, const char* end, Value* removed), /// but 'key' is null-terminated. bool removeMember(const char* key, Value* removed); @@ -502,8 +504,8 @@ Json::Value obj_value(Json::objectValue); // {} \param key may contain embedded nulls. \return true iff removed (no exceptions) */ - bool removeMember(std::string const& key, Value* removed); - /// Same as removeMember(std::string const& key, Value* removed) + bool removeMember(String const& key, Value* removed); + /// Same as removeMember(String const& key, Value* removed) bool removeMember(const char* begin, const char* end, Value* removed); /** \brief Remove the indexed array element. @@ -518,8 +520,8 @@ Json::Value obj_value(Json::objectValue); // {} bool isMember(const char* key) const; /// Return true if the object has a member named key. /// \param key may contain embedded nulls. - bool isMember(const std::string& key) const; - /// Same as isMember(std::string const& key)const + bool isMember(const String& key) const; + /// Same as isMember(String const& key)const bool isMember(const char* begin, const char* end) const; #ifdef JSON_USE_CPPTL /// Return true if the object has a member named key. @@ -539,17 +541,17 @@ Json::Value obj_value(Json::objectValue); // {} //# endif /// \deprecated Always pass len. - JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.") + JSONCPP_DEPRECATED("Use setComment(String const&) instead.") void setComment(const char* comment, CommentPlacement placement); /// Comments must be //... or /* ... */ void setComment(const char* comment, size_t len, CommentPlacement placement); /// Comments must be //... or /* ... */ - void setComment(const std::string& comment, CommentPlacement placement); + void setComment(const String& comment, CommentPlacement placement); bool hasComment(CommentPlacement placement) const; /// Include delimiters and embedded newlines. - std::string getComment(CommentPlacement placement) const; + String getComment(CommentPlacement placement) const; - std::string toStyledString() const; + String toStyledString() const; const_iterator begin() const; const_iterator end() const; @@ -613,13 +615,14 @@ Json::Value obj_value(Json::objectValue); // {} template class JSON_API PathArgument { public: + typedef typename _Value::String String; template friend class Path; PathArgument(); PathArgument(ArrayIndex index); PathArgument(const char* key); - PathArgument(const std::string& key); + PathArgument(const String& key); private: enum Kind { @@ -627,7 +630,7 @@ class JSON_API PathArgument { kindIndex, kindKey }; - std::string key_; + String key_; ArrayIndex index_; Kind kind_; }; @@ -646,7 +649,8 @@ class JSON_API PathArgument { template class JSON_API Path { public: - Path(const std::string& path, + typedef typename _Value::String String; + Path(const String& path, const PathArgument<_Value>& a1 = PathArgument<_Value>(), const PathArgument<_Value>& a2 = PathArgument<_Value>(), const PathArgument<_Value>& a3 = PathArgument<_Value>(), @@ -663,12 +667,12 @@ class JSON_API Path { typedef std::vector*> InArgs; typedef std::vector> Args; - void makePath(const std::string& path, const InArgs& in); - void addPathInArg(const std::string& path, + void makePath(const String& path, const InArgs& in); + void addPathInArg(const String& path, const InArgs& in, typename InArgs::const_iterator& itInArg, typename PathArgument<_Value>::Kind kind); - void invalidPath(const std::string& path, int location); + void invalidPath(const String& path, int location); Args args_; }; @@ -679,6 +683,7 @@ class JSON_API Path { template class JSON_API ValueIteratorBase { public: + typedef typename _Value::String String; typedef std::bidirectional_iterator_tag iterator_category; typedef unsigned int size_t; typedef int difference_type; @@ -702,7 +707,7 @@ class JSON_API ValueIteratorBase { /// Return the member name of the referenced Value, or "" if it is not an /// objectValue. /// \note Avoid `c_str()` on result, as embedded zeroes are possible. - std::string name() const; + String name() const; /// Return the member name of the referenced Value. "" if it is not an /// objectValue. diff --git a/include/json/value.inl b/include/json/value.inl index 9f1b343ea..01873ef8d 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -382,7 +382,7 @@ Value<_Alloc, _String>::Value(const char* beginValue, const char* endValue) { } template -Value<_Alloc, _String>::Value(const std::string& value) { +Value<_Alloc, _String>::Value(const String& value) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue>(value.data(), static_cast(value.length())); @@ -650,7 +650,7 @@ bool Value<_Alloc, _String>::getString(char const** str, char const** cend) cons } template -std::string Value<_Alloc, _String>::asString() const { +_String Value<_Alloc, _String>::asString() const { switch (type_) { case nullValue: return ""; @@ -660,7 +660,7 @@ std::string Value<_Alloc, _String>::asString() const { unsigned this_len; char const* this_str; decodePrefixedString>(this->allocated_, this->value_.string_, &this_len, &this_str); - return std::string(this_str, this_len); + return String(this_str, this_len); } case booleanValue: return value_.bool_ ? "true" : "false"; @@ -1097,7 +1097,7 @@ const Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const char* key return *found; } template -Value<_Alloc, _String> const& Value<_Alloc, _String>::operator[](std::string const& key) const +Value<_Alloc, _String> const& Value<_Alloc, _String>::operator[](String const& key) const { Value const* found = find(key.data(), key.data() + key.length()); if (!found) return nullRef; @@ -1110,7 +1110,7 @@ Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const char* key) { } template -Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const std::string& key) { +Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const String& key) { return resolveReference(key.data(), key.data() + key.length()); } @@ -1148,7 +1148,7 @@ Value<_Alloc, _String> Value<_Alloc, _String>::get(char const* key, Value<_Alloc return get(key, key + strlen(key), defaultValue); } template -Value<_Alloc, _String> Value<_Alloc, _String>::get(std::string const& key, Value<_Alloc, _String> const& defaultValue) const +Value<_Alloc, _String> Value<_Alloc, _String>::get(String const& key, Value<_Alloc, _String> const& defaultValue) const { return get(key.data(), key.data() + key.length(), defaultValue); } @@ -1174,7 +1174,7 @@ bool Value<_Alloc, _String>::removeMember(const char* key, Value<_Alloc, _String return removeMember(key, key + strlen(key), removed); } template -bool Value<_Alloc, _String>::removeMember(std::string const& key, Value<_Alloc, _String>* removed) +bool Value<_Alloc, _String>::removeMember(String const& key, Value<_Alloc, _String>* removed) { return removeMember(key.data(), key.data() + key.length(), removed); } @@ -1191,7 +1191,7 @@ Value<_Alloc, _String> Value<_Alloc, _String>::removeMember(const char* key) return removed; // still null if removeMember() did nothing } template -Value<_Alloc, _String> Value<_Alloc, _String>::removeMember(const std::string& key) +Value<_Alloc, _String> Value<_Alloc, _String>::removeMember(const String& key) { return removeMember(key.c_str()); } @@ -1240,7 +1240,7 @@ bool Value<_Alloc, _String>::isMember(char const* key) const return isMember(key, key + strlen(key)); } template -bool Value<_Alloc, _String>::isMember(std::string const& key) const +bool Value<_Alloc, _String>::isMember(String const& key) const { return isMember(key.data(), key.data() + key.length()); } @@ -1264,7 +1264,7 @@ typename Value<_Alloc, _String>::Members Value<_Alloc, _String>::getMemberNames( typename ObjectValues::const_iterator it = value_.map_->begin(); typename ObjectValues::const_iterator itEnd = value_.map_->end(); for (; it != itEnd; ++it) { - members.push_back(std::string((*it).first.data(), + members.push_back(String((*it).first.data(), (*it).first.length())); } return members; @@ -1421,7 +1421,7 @@ void Value<_Alloc, _String>::setComment(const char* comment, CommentPlacement pl } template -void Value<_Alloc, _String>::setComment(const std::string& comment, CommentPlacement placement) { +void Value<_Alloc, _String>::setComment(const String& comment, CommentPlacement placement) { setComment(comment.c_str(), comment.length(), placement); } @@ -1431,7 +1431,7 @@ bool Value<_Alloc, _String>::hasComment(CommentPlacement placement) const { } template -std::string Value<_Alloc, _String>::getComment(CommentPlacement placement) const { +_String Value<_Alloc, _String>::getComment(CommentPlacement placement) const { if (hasComment(placement)) return comments_[placement].comment_; return ""; @@ -1450,7 +1450,7 @@ template size_t Value<_Alloc, _String>::getOffsetLimit() const { return limit_; } template -std::string Value<_Alloc, _String>::toStyledString() const { +_String Value<_Alloc, _String>::toStyledString() const { StyledWriter> writer; return writer.write(*this); } @@ -1526,14 +1526,14 @@ PathArgument<_Value>::PathArgument(const char* key) : key_(key), index_(), kind_(kindKey) {} template -PathArgument<_Value>::PathArgument(const std::string& key) +PathArgument<_Value>::PathArgument(const String& key) : key_(key.c_str()), index_(), kind_(kindKey) {} // class Path // ////////////////////////////////////////////////////////////////// template -Path<_Value>::Path(const std::string& path, +Path<_Value>::Path(const String& path, const PathArgument<_Value>& a1, const PathArgument<_Value>& a2, const PathArgument<_Value>& a3, @@ -1549,7 +1549,7 @@ Path<_Value>::Path(const std::string& path, } template -void Path<_Value>::makePath(const std::string& path, const InArgs& in) { +void Path<_Value>::makePath(const String& path, const InArgs& in) { const char* current = path.c_str(); const char* end = current + path.length(); typename InArgs::const_iterator itInArg = in.begin(); @@ -1575,13 +1575,13 @@ void Path<_Value>::makePath(const std::string& path, const InArgs& in) { const char* beginName = current; while (current != end && !strchr("[.", *current)) ++current; - args_.push_back(std::string(beginName, current)); + args_.push_back(String(beginName, current)); } } } template -void Path<_Value>::addPathInArg(const std::string& /*path*/, +void Path<_Value>::addPathInArg(const String& /*path*/, const InArgs& in, typename InArgs::const_iterator& itInArg, typename PathArgument<_Value>::Kind kind) { @@ -1595,7 +1595,7 @@ void Path<_Value>::addPathInArg(const std::string& /*path*/, } template -void Path<_Value>::invalidPath(const std::string& /*path*/, int /*location*/) { +void Path<_Value>::invalidPath(const String& /*path*/, int /*location*/) { // Error: invalid path. } diff --git a/include/json/valueiterator.inl b/include/json/valueiterator.inl index adf513008..005ad7692 100644 --- a/include/json/valueiterator.inl +++ b/include/json/valueiterator.inl @@ -104,12 +104,12 @@ UInt ValueIteratorBase<_Value>::index() const { } template -std::string ValueIteratorBase<_Value>::name() const { +typename ValueIteratorBase<_Value>::String ValueIteratorBase<_Value>::name() const { char const* keey; char const* end; keey = memberName(&end); - if (!keey) return std::string(); - return std::string(keey, end); + if (!keey) return String(); + return String(keey, end); } template diff --git a/include/json/writer.h b/include/json/writer.h index 56067a464..8019dba47 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -70,7 +70,7 @@ class JSON_API StreamWriter { * A StreamWriter will be created from the factory, used, and then deleted. */ template -std::string JSON_API writeString(typename StreamWriter<_Value>::Factory const& factory, _Value const& root); +typename _Value::String JSON_API writeString(typename StreamWriter<_Value>::Factory const& factory, _Value const& root); /** \brief Build a StreamWriter implementation. @@ -91,6 +91,7 @@ std::string JSON_API writeString(typename StreamWriter<_Value>::Factory const& f template class JSON_API StreamWriterBuilder : public StreamWriter<_Value>::Factory { public: + typedef typename _Value::String String; // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. /** Configuration of this builder. @@ -130,7 +131,7 @@ class JSON_API StreamWriterBuilder : public StreamWriter<_Value>::Factory { bool validate(_Value* invalid) const; /** A simple way to update a specific setting. */ - _Value& operator[](std::string key); + _Value& operator[](String key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) @@ -146,9 +147,10 @@ class JSON_API StreamWriterBuilder : public StreamWriter<_Value>::Factory { template class JSON_API Writer { public: + typedef typename _Value::String String; virtual ~Writer(); - virtual std::string write(const _Value& root) = 0; + virtual String write(const _Value& root) = 0; }; /** \brief Outputs a Value in JSON format @@ -164,6 +166,7 @@ template class JSON_API FastWriter : public Writer<_Value> { public: + typedef typename _Value::String String; FastWriter(); ~FastWriter() override {} @@ -179,12 +182,12 @@ class JSON_API FastWriter : public Writer<_Value> { void omitEndingLineFeed(); public: // overridden from Writer - std::string write(const _Value& root) override; + String write(const _Value& root) override; private: void writeValue(const _Value& value); - std::string document_; + String document_; bool yamlCompatiblityEnabled_; bool dropNullPlaceholders_; bool omitEndingLineFeed_; @@ -217,6 +220,7 @@ class JSON_API FastWriter : public Writer<_Value> { template class JSON_API StyledWriter : public Writer<_Value> { public: + typedef typename _Value::String String; StyledWriter(); ~StyledWriter() override {} @@ -225,27 +229,27 @@ class JSON_API StyledWriter : public Writer<_Value> { * \param root Value to serialize. * \return String containing the JSON document that represents the root value. */ - std::string write(const _Value& root) override; + String write(const _Value& root) override; private: void writeValue(const _Value& value); void writeArrayValue(const _Value& value); bool isMultineArray(const _Value& value); - void pushValue(const std::string& value); + void pushValue(const String& value); void writeIndent(); - void writeWithIndent(const std::string& value); + void writeWithIndent(const String& value); void indent(); void unindent(); void writeCommentBeforeValue(const _Value& root); void writeCommentAfterValueOnSameLine(const _Value& root); bool hasCommentForValue(const _Value& value); - static std::string normalizeEOL(const std::string& text); + static String normalizeEOL(const String& text); - typedef std::vector ChildValues; + typedef std::vector ChildValues; ChildValues childValues_; - std::string document_; - std::string indentString_; + String document_; + String indentString_; int rightMargin_; int indentSize_; bool addChildValues_; @@ -280,7 +284,8 @@ class JSON_API StyledWriter : public Writer<_Value> { template class JSON_API StyledStreamWriter { public: - StyledStreamWriter(std::string indentation = "\t"); + typedef typename _Value::String String; + StyledStreamWriter(String indentation = "\t"); ~StyledStreamWriter() {} public: @@ -296,43 +301,43 @@ class JSON_API StyledStreamWriter { void writeValue(const _Value& value); void writeArrayValue(const _Value& value); bool isMultineArray(const _Value& value); - void pushValue(const std::string& value); + void pushValue(const String& value); void writeIndent(); - void writeWithIndent(const std::string& value); + void writeWithIndent(const String& value); void indent(); void unindent(); void writeCommentBeforeValue(const _Value& root); void writeCommentAfterValueOnSameLine(const _Value& root); bool hasCommentForValue(const _Value& value); - static std::string normalizeEOL(const std::string& text); + static String normalizeEOL(const String& text); - typedef std::vector ChildValues; + typedef std::vector ChildValues; ChildValues childValues_; std::ostream* document_; - std::string indentString_; + String indentString_; int rightMargin_; - std::string indentation_; + String indentation_; bool addChildValues_ : 1; bool indented_ : 1; }; #if defined(JSON_HAS_INT64) template -std::string JSON_API valueToString(Int value); +typename _Value::String JSON_API valueToString(Int value); template -std::string JSON_API valueToString(UInt value); +typename _Value::String JSON_API valueToString(UInt value); #endif // if defined(JSON_HAS_INT64) template -std::string JSON_API valueToString(LargestInt value); +typename _Value::String JSON_API valueToString(LargestInt value); template -std::string JSON_API valueToString(LargestUInt value); +typename _Value::String JSON_API valueToString(LargestUInt value); template -std::string JSON_API valueToString(double value); +typename _Value::String JSON_API valueToString(double value); template -std::string JSON_API valueToString(bool value); +typename _Value::String JSON_API valueToString(bool value); template -std::string JSON_API valueToQuotedString(const char* value); +typename _Value::String JSON_API valueToQuotedString(const char* value); } // namespace detail diff --git a/include/json/writer.inl b/include/json/writer.inl index 1b0833931..cfa831fd2 100644 --- a/include/json/writer.inl +++ b/include/json/writer.inl @@ -80,7 +80,7 @@ bool containsControlCharacter0(const char* str, unsigned len); char const* strnpbrk(char const* s, char const* accept, size_t n); template -std::string valueToString(LargestInt value) { +typename _Value::String valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); if (value == _Value::minLargestInt) { @@ -97,7 +97,7 @@ std::string valueToString(LargestInt value) { } template -std::string valueToString(LargestUInt value) { +typename _Value::String valueToString(LargestUInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); uintToString(value, current); @@ -108,19 +108,19 @@ std::string valueToString(LargestUInt value) { #if defined(JSON_HAS_INT64) template -std::string valueToString(Int value) { +typename _Value::String valueToString(Int value) { return valueToString<_Value>(LargestInt(value)); } template -std::string valueToString(UInt value) { +typename _Value::String valueToString(UInt value) { return valueToString<_Value>(LargestUInt(value)); } #endif // # if defined(JSON_HAS_INT64) template -std::string valueToString(double value, bool useSpecialFloats, unsigned int precision) { +typename _Value::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[32]; @@ -151,25 +151,26 @@ std::string valueToString(double value, bool useSpecialFloats, unsigned int prec } template -std::string valueToString(double value) { return valueToString<_Value>(value, false, 17); } +typename _Value::String valueToString(double value) { return valueToString<_Value>(value, false, 17); } template -std::string valueToString(bool value) { return value ? "true" : "false"; } +typename _Value::String valueToString(bool value) { return value ? "true" : "false"; } template -std::string valueToQuotedString(const char* value) { +typename _Value::String valueToQuotedString(const char* value) { + using String = typename _Value::String; if (value == NULL) return ""; // Not sure how to handle unicode... if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter(value)) - return std::string("\"") + value + "\""; + return String("\"") + value + "\""; // We have to walk value and escape any special characters. // Appending to std::string is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = + typename String::size_type maxsize = strlen(value) * 2 + 3; // allescaped+quotes+NULL - std::string result; + String result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; for (const char* c = value; *c != 0; ++c) { @@ -220,19 +221,20 @@ std::string valueToQuotedString(const char* value) { } template -static std::string valueToQuotedStringN(const char* value, unsigned length) { +static typename _Value::String valueToQuotedStringN(const char* value, unsigned length) { + using String = typename _Value::String; if (value == NULL) return ""; // Not sure how to handle unicode... if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && !containsControlCharacter0(value, length)) - return std::string("\"") + value + "\""; + return String("\"") + value + "\""; // We have to walk value and escape any special characters. // Appending to std::string is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = + typename String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL - std::string result; + String result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; char const* end = value + length; @@ -306,7 +308,7 @@ template void FastWriter<_Value>::omitEndingLineFeed() { omitEndingLineFeed_ = true; } template -std::string FastWriter<_Value>::write(const _Value& root) { +typename FastWriter<_Value>::String FastWriter<_Value>::write(const _Value& root) { document_ = ""; writeValue(root); if (!omitEndingLineFeed_) @@ -357,7 +359,7 @@ void FastWriter<_Value>::writeValue(const _Value& value) { document_ += '{'; for (typename _Value::Members::iterator it = members.begin(); it != members.end(); ++it) { - const std::string& name = *it; + const String& name = *it; if (it != members.begin()) document_ += ','; document_ += valueToQuotedStringN<_Value>(name.data(), static_cast(name.length())); @@ -377,7 +379,7 @@ StyledWriter<_Value>::StyledWriter() : rightMargin_(74), indentSize_(3), addChildValues_() {} template -std::string StyledWriter<_Value>::write(const _Value& root) { +typename StyledWriter<_Value>::String StyledWriter<_Value>::write(const _Value& root) { document_ = ""; addChildValues_ = false; indentString_ = ""; @@ -428,7 +430,7 @@ void StyledWriter<_Value>::writeValue(const _Value& value) { indent(); typename _Value::Members::iterator it = members.begin(); for (;;) { - const std::string& name = *it; + const String& name = *it; const _Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString<_Value>(name.c_str())); @@ -521,7 +523,7 @@ bool StyledWriter<_Value>::isMultineArray(const _Value& value) { } template -void StyledWriter<_Value>::pushValue(const std::string& value) { +void StyledWriter<_Value>::pushValue(const String& value) { if (addChildValues_) childValues_.push_back(value); else @@ -541,13 +543,13 @@ void StyledWriter<_Value>::writeIndent() { } template -void StyledWriter<_Value>::writeWithIndent(const std::string& value) { +void StyledWriter<_Value>::writeWithIndent(const String& value) { writeIndent(); document_ += value; } template -void StyledWriter<_Value>::indent() { indentString_ += std::string(indentSize_, ' '); } +void StyledWriter<_Value>::indent() { indentString_ += String(indentSize_, ' '); } template void StyledWriter<_Value>::unindent() { @@ -562,8 +564,8 @@ void StyledWriter<_Value>::writeCommentBeforeValue(const _Value& root) { document_ += "\n"; writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); + const String& comment = root.getComment(commentBefore); + typename String::const_iterator iter = comment.begin(); while (iter != comment.end()) { document_ += *iter; if (*iter == '\n' && @@ -599,7 +601,7 @@ bool StyledWriter<_Value>::hasCommentForValue(const _Value& value) { // ////////////////////////////////////////////////////////////////// template -StyledStreamWriter<_Value>::StyledStreamWriter(std::string indentation) +StyledStreamWriter<_Value>::StyledStreamWriter(String indentation) : document_(NULL), rightMargin_(74), indentation_(indentation), addChildValues_() {} @@ -658,7 +660,7 @@ void StyledStreamWriter<_Value>::writeValue(const _Value& value) { indent(); typename _Value::Members::iterator it = members.begin(); for (;;) { - const std::string& name = *it; + const String& name = *it; const _Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString<_Value>(name.c_str())); @@ -753,7 +755,7 @@ bool StyledStreamWriter<_Value>::isMultineArray(const _Value& value) { } template -void StyledStreamWriter<_Value>::pushValue(const std::string& value) { +void StyledStreamWriter<_Value>::pushValue(const String& value) { if (addChildValues_) childValues_.push_back(value); else @@ -770,7 +772,7 @@ void StyledStreamWriter<_Value>::writeIndent() { } template -void StyledStreamWriter<_Value>::writeWithIndent(const std::string& value) { +void StyledStreamWriter<_Value>::writeWithIndent(const String& value) { if (!indented_) writeIndent(); *document_ << value; indented_ = false; @@ -791,8 +793,8 @@ void StyledStreamWriter<_Value>::writeCommentBeforeValue(const _Value& root) { return; if (!indented_) writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); + const String& comment = root.getComment(commentBefore); + typename String::const_iterator iter = comment.begin(); while (iter != comment.end()) { *document_ << *iter; if (*iter == '\n' && @@ -839,12 +841,13 @@ struct CommentStyle { template struct BuiltStyledStreamWriter : public StreamWriter<_Value> { + typedef typename _Value::String String; BuiltStyledStreamWriter( - std::string const& indentation, + String const& indentation, CommentStyle::Enum cs, - std::string const& colonSymbol, - std::string const& nullSymbol, - std::string const& endingLineFeedSymbol, + String const& colonSymbol, + String const& nullSymbol, + String const& endingLineFeedSymbol, bool useSpecialFloats, unsigned int precision); int write(_Value const& root, std::ostream* sout) override; @@ -852,25 +855,25 @@ private: void writeValue(_Value const& value); void writeArrayValue(_Value const& value); bool isMultineArray(_Value const& value); - void pushValue(std::string const& value); + void pushValue(String const& value); void writeIndent(); - void writeWithIndent(std::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 ChildValues; + typedef std::vector ChildValues; ChildValues childValues_; - std::string indentString_; + String indentString_; int rightMargin_; - std::string indentation_; + String indentation_; CommentStyle::Enum cs_; - std::string colonSymbol_; - std::string nullSymbol_; - std::string endingLineFeedSymbol_; + String colonSymbol_; + String nullSymbol_; + String endingLineFeedSymbol_; bool addChildValues_ : 1; bool indented_ : 1; bool useSpecialFloats_ : 1; @@ -878,11 +881,11 @@ private: }; template BuiltStyledStreamWriter<_Value>::BuiltStyledStreamWriter( - std::string const& indentation, + String const& indentation, CommentStyle::Enum cs, - std::string const& colonSymbol, - std::string const& nullSymbol, - std::string const& endingLineFeedSymbol, + String const& colonSymbol, + String const& nullSymbol, + String const& endingLineFeedSymbol, bool useSpecialFloats, unsigned int precision) : rightMargin_(74) @@ -953,7 +956,7 @@ void BuiltStyledStreamWriter<_Value>::writeValue(_Value const& value) { indent(); typename _Value::Members::iterator it = members.begin(); for (;;) { - std::string const& name = *it; + String const& name = *it; _Value const& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedStringN<_Value>(name.data(), static_cast(name.length()))); @@ -1050,7 +1053,7 @@ bool BuiltStyledStreamWriter<_Value>::isMultineArray(_Value const& value) { } template -void BuiltStyledStreamWriter<_Value>::pushValue(std::string const& value) { +void BuiltStyledStreamWriter<_Value>::pushValue(String const& value) { if (addChildValues_) childValues_.push_back(value); else @@ -1071,7 +1074,7 @@ void BuiltStyledStreamWriter<_Value>::writeIndent() { } template -void BuiltStyledStreamWriter<_Value>::writeWithIndent(std::string const& value) { +void BuiltStyledStreamWriter<_Value>::writeWithIndent(String const& value) { if (!indented_) writeIndent(); *(StreamWriter<_Value>::sout_) << value; indented_ = false; @@ -1093,8 +1096,8 @@ void BuiltStyledStreamWriter<_Value>::writeCommentBeforeValue(_Value const& root return; if (!indented_) writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); + const String& comment = root.getComment(commentBefore); + typename String::const_iterator iter = comment.begin(); while (iter != comment.end()) { *(StreamWriter<_Value>::sout_) << *iter; if (*iter == '\n' && @@ -1152,8 +1155,8 @@ StreamWriterBuilder<_Value>::~StreamWriterBuilder() template StreamWriter<_Value>* StreamWriterBuilder<_Value>::newStreamWriter() const { - std::string indentation = settings_["indentation"].asString(); - std::string cs_str = settings_["commentStyle"].asString(); + String indentation = settings_["indentation"].asString(); + String cs_str = settings_["commentStyle"].asString(); bool eyc = settings_["enableYAMLCompatibility"].asBool(); bool dnp = settings_["dropNullPlaceholders"].asBool(); bool usf = settings_["useSpecialFloats"].asBool(); @@ -1166,24 +1169,24 @@ StreamWriter<_Value>* StreamWriterBuilder<_Value>::newStreamWriter() const } else { throwRuntimeError("commentStyle must be 'All' or 'None'"); } - std::string colonSymbol = " : "; + String colonSymbol = " : "; if (eyc) { colonSymbol = ": "; } else if (indentation.empty()) { colonSymbol = ":"; } - std::string nullSymbol = "null"; + String nullSymbol = "null"; if (dnp) { nullSymbol = ""; } if (pre > 17) pre = 17; - std::string endingLineFeedSymbol = ""; + String endingLineFeedSymbol = ""; return new BuiltStyledStreamWriter<_Value>( indentation, cs, colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); } template -static void getValidWriterKeys(std::set* valid_keys) +static void getValidWriterKeys(std::set* valid_keys) { valid_keys->clear(); valid_keys->insert("indentation"); @@ -1199,12 +1202,12 @@ bool StreamWriterBuilder<_Value>::validate(_Value* invalid) const _Value my_invalid; if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL _Value& inv = *invalid; - std::set valid_keys; + std::set valid_keys; getValidWriterKeys<_Value>(&valid_keys); typename _Value::Members keys = settings_.getMemberNames(); size_t n = keys.size(); for (size_t i = 0; i < n; ++i) { - std::string const& key = keys[i]; + String const& key = keys[i]; if (valid_keys.find(key) == valid_keys.end()) { inv[key] = settings_[key]; } @@ -1212,7 +1215,7 @@ bool StreamWriterBuilder<_Value>::validate(_Value* invalid) const return 0u == inv.size(); } template -_Value& StreamWriterBuilder<_Value>::operator[](std::string key) +_Value& StreamWriterBuilder<_Value>::operator[](String key) { return settings_[key]; } @@ -1231,7 +1234,7 @@ void StreamWriterBuilder<_Value>::setDefaults(_Value* settings) } template -std::string writeString(typename StreamWriter<_Value>::Factory const& builder, _Value const& root) { +typename _Value::String writeString(typename StreamWriter<_Value>::Factory const& builder, _Value const& root) { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) typedef std::unique_ptr> StreamWriterPtr; #else From fc8897a443a002e1235cfec2b33fa0d2f1748378 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 5 Feb 2016 23:10:21 +0000 Subject: [PATCH 12/41] Uses the appropriate allocator instead of malloc/free Remove malloc/free and instead use unique_ptr with vector and char to represent the old character array. --- include/json/value.h | 29 +++++- include/json/value.inl | 197 +++++++++++++++++++++++++++-------------- 2 files changed, 157 insertions(+), 69 deletions(-) diff --git a/include/json/value.h b/include/json/value.h index 566ffff20..8c9a67c2b 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -181,6 +181,12 @@ class JSON_API Value { typedef Json::LargestInt LargestInt; typedef Json::LargestUInt LargestUInt; typedef Json::ArrayIndex ArrayIndex; + typedef std::vector StringData; + #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) + typedef std::unique_ptr StringDataPtr; + #else + typedef std::auto_ptr StringDataPtr; + #endif static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null @@ -209,6 +215,23 @@ class JSON_API Value { private: #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class StringValueHolder { + public: + StringValueHolder(); + StringValueHolder(StringDataPtr&& value); + StringValueHolder(char* value); + ~StringValueHolder(); + char* GetString(); + const char* GetString() const; + void SetString(StringDataPtr&& value); + void SetString(char* value); + bool IsRaw() const; + + private: + char* valueStringRaw_ = nullptr; // the value that was passed in, this does not belong to value + StringDataPtr valueStringCopy_; // a copy of the value that was passed in + bool raw_ = true; + }; class CZString { public: enum DuplicationPolicy { @@ -240,7 +263,7 @@ class JSON_API Value { unsigned length_: 30; // 1GB max }; - char const* cstr_; // actually, a prefixed string, unless policy is noDup + StringValueHolder cstr_; // The string that's being stored union { ArrayIndex index_; StringStorage storage_; @@ -578,7 +601,7 @@ Json::Value obj_value(Json::objectValue); // {} void setComment(const char* text, size_t len); - char* comment_; + StringValueHolder comment_; }; // struct MemberNamesTransform @@ -595,9 +618,9 @@ Json::Value obj_value(Json::objectValue); // {} LargestUInt uint_; double real_; bool bool_; - char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ ObjectValues* map_; } value_; + StringValueHolder stringValue_; ValueType type_ : 8; unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. // If not allocated_, string_ must be null-terminated. diff --git a/include/json/value.inl b/include/json/value.inl index 01873ef8d..eec819218 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -92,28 +92,29 @@ static inline bool InRange(double d, T min, U max) { * @return Pointer on the duplicate instance of string. */ template -static inline char* duplicateStringValue(const char* value, +static inline typename _Value::StringDataPtr 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 >= (size_t)_Value::maxInt) length = _Value::maxInt - 1; - char* newString = static_cast(malloc(length + 1)); - if (newString == NULL) { + try { + typename _Value::StringDataPtr newString(new typename _Value::StringData(value, value + length)); + newString->push_back(0); + return newString; + } catch (...) { throwRuntimeError( "in Json::Value::duplicateStringValue(): " "Failed to allocate string value buffer"); } - memcpy(newString, value, length); - newString[length] = 0; - return newString; + return typename _Value::StringDataPtr(); //Not reachable but compiler warns if not here } /* Record the length as a prefix. */ template -static inline char* duplicateAndPrefixStringValue( +static inline typename _Value::StringDataPtr duplicateAndPrefixStringValue( const char* value, unsigned int length) { @@ -122,17 +123,20 @@ static inline char* duplicateAndPrefixStringValue( JSON_ASSERT_MESSAGE(length <= (unsigned)_Value::maxInt - sizeof(unsigned) - 1U, "in Json::Value::duplicateAndPrefixStringValue(): " "length too big for prefixing"); - unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; - char* newString = static_cast(malloc(actualLength)); - if (newString == 0) { + + try { + typename _Value::StringDataPtr newString(new typename _Value::StringData(value, value + length)); + for (unsigned int i=0; ipush_back(reinterpret_cast(&length)[i]); + newString->insert(newString->end(), value, value+length); + newString->push_back(0); + return newString; + } catch (...) { throwRuntimeError( "in Json::Value::duplicateAndPrefixStringValue(): " "Failed to allocate string value buffer"); } - *reinterpret_cast(newString) = length; - memcpy(newString + sizeof(unsigned), value, length); - newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later - return newString; + return typename _Value::StringDataPtr(); //Not reachable but compiler warns if not here } template inline static void decodePrefixedString( @@ -150,7 +154,7 @@ inline static void decodePrefixedString( /** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). */ template -static inline void releaseStringValue(char* value) { free(value); } +static inline void releaseStringValue(char* value) { /* Unused */ } //FIXME Remove! } // namespace detail } // namespace Json @@ -183,22 +187,22 @@ Value<_Alloc, _String>::CommentInfo::CommentInfo() : comment_(0) {} template Value<_Alloc, _String>::CommentInfo::~CommentInfo() { - if (comment_) - releaseStringValue>(comment_); + if (comment_.GetString()) + releaseStringValue>(comment_.GetString()); } template void Value<_Alloc, _String>::CommentInfo::setComment(const char* text, size_t len) { - if (comment_) { - releaseStringValue>(comment_); - comment_ = 0; + if (comment_.GetString()) { + releaseStringValue>(comment_.GetString()); + comment_.SetString(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); + comment_.SetString(duplicateStringValue>(text, len)); } // ////////////////////////////////////////////////////////////////// @@ -213,22 +217,24 @@ void Value<_Alloc, _String>::CommentInfo::setComment(const char* text, size_t le // a string is stored. template -Value<_Alloc, _String>::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} +Value<_Alloc, _String>::CZString::CZString(ArrayIndex aindex) : index_(aindex) {} template Value<_Alloc, _String>::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) - : cstr_(str) { + : cstr_(const_cast(str)) { // allocate != duplicate storage_.policy_ = allocate & 0x3; storage_.length_ = ulength & 0x3FFFFFFF; } template -Value<_Alloc, _String>::CZString::CZString(const CZString& other) - : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0 - ? duplicateStringValue>(other.cstr_, other.storage_.length_) - : other.cstr_) { - storage_.policy_ = (other.cstr_ +Value<_Alloc, _String>::CZString::CZString(const CZString& other) { + if (other.storage_.policy_ != noDuplication && other.cstr_.GetString() != 0) { + cstr_.SetString(std::move(duplicateStringValue>(other.cstr_.GetString(), other.storage_.length_))); + } else { + cstr_.SetString(const_cast(other.cstr_.GetString())); + } + storage_.policy_ = (other.cstr_.GetString() ? (static_cast(other.storage_.policy_) == noDuplication ? noDuplication : duplicate) : static_cast(other.storage_.policy_)); @@ -245,8 +251,8 @@ Value<_Alloc, _String>::CZString::CZString(CZString&& other) template Value<_Alloc, _String>::CZString::~CZString() { - if (cstr_ && storage_.policy_ == duplicate) - releaseStringValue>(const_cast(cstr_)); + if (cstr_.GetString() && storage_.policy_ == duplicate) + releaseStringValue>(const_cast(cstr_.GetString())); } template @@ -263,13 +269,13 @@ typename Value<_Alloc, _String>::CZString& Value<_Alloc, _String>::CZString::ope template bool Value<_Alloc, _String>::CZString::operator<(const CZString& other) const { - if (!cstr_) return index_ < other.index_; + if (!cstr_.GetString()) 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(this_len, other_len); - int comp = memcmp(this->cstr_, other.cstr_, min_len); + int comp = memcmp(this->cstr_.GetString(), other.cstr_.GetString(), min_len); if (comp < 0) return true; if (comp > 0) return false; return (this_len < other_len); @@ -277,13 +283,13 @@ bool Value<_Alloc, _String>::CZString::operator<(const CZString& other) const { template bool Value<_Alloc, _String>::CZString::operator==(const CZString& other) const { - if (!cstr_) return index_ == other.index_; + if (!cstr_.GetString()) 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; - int comp = memcmp(this->cstr_, other.cstr_, this_len); + int comp = memcmp(this->cstr_.GetString(), other.cstr_.GetString(), this_len); return comp == 0; } @@ -292,7 +298,7 @@ ArrayIndex Value<_Alloc, _String>::CZString::index() const { return index_; } //const char* Value::CZString::c_str() const { return cstr_; } template -const char* Value<_Alloc, _String>::CZString::data() const { return cstr_; } +const char* Value<_Alloc, _String>::CZString::data() const { return cstr_.GetString(); } template unsigned Value<_Alloc, _String>::CZString::length() const { return storage_.length_; } template @@ -324,7 +330,7 @@ Value<_Alloc, _String>::Value(ValueType vtype) { value_.real_ = 0.0; break; case stringValue: - value_.string_ = 0; + //Unused break; case arrayValue: case objectValue: @@ -371,27 +377,27 @@ Value<_Alloc, _String>::Value(double value) { template Value<_Alloc, _String>::Value(const char* value) { initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue>(value, static_cast(strlen(value))); + stringValue_.SetString(duplicateAndPrefixStringValue>(value, static_cast(strlen(value)))); } template Value<_Alloc, _String>::Value(const char* beginValue, const char* endValue) { initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue>(beginValue, static_cast(endValue - beginValue)); + stringValue_.SetString( + duplicateAndPrefixStringValue>(beginValue, static_cast(endValue - beginValue))); } template Value<_Alloc, _String>::Value(const String& value) { initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue>(value.data(), static_cast(value.length())); + stringValue_.SetString( + duplicateAndPrefixStringValue>(value.data(), static_cast(value.length()))); } template Value<_Alloc, _String>::Value(const StaticString& value) { initBasic(stringValue); - value_.string_ = const_cast(value.c_str()); + stringValue_.SetString(const_cast(value.c_str())); } #ifdef JSON_USE_CPPTL @@ -423,15 +429,15 @@ Value<_Alloc, _String>::Value(Value const& other) value_ = other.value_; break; case stringValue: - if (other.value_.string_ && other.allocated_) { + if (other.stringValue_.GetString() && other.allocated_) { unsigned len; char const* str; - decodePrefixedString>(other.allocated_, other.value_.string_, + decodePrefixedString>(other.allocated_, other.stringValue_.GetString(), &len, &str); - value_.string_ = duplicateAndPrefixStringValue>(str, len); + stringValue_.SetString(duplicateAndPrefixStringValue>(str, len)); allocated_ = true; } else { - value_.string_ = other.value_.string_; + stringValue_.SetString(const_cast(other.stringValue_.GetString())); allocated_ = false; } break; @@ -446,9 +452,9 @@ Value<_Alloc, _String>::Value(Value const& other) comments_ = new CommentInfo[numberOfCommentPlacement]; for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { const CommentInfo& otherComment = other.comments_[comment]; - if (otherComment.comment_) + if (otherComment.comment_.GetString()) comments_[comment].setComment( - otherComment.comment_, strlen(otherComment.comment_)); + otherComment.comment_.GetString(), strlen(otherComment.comment_.GetString())); } } } @@ -472,8 +478,8 @@ Value<_Alloc, _String>::~Value() { case booleanValue: break; case stringValue: - if (allocated_) - releaseStringValue>(value_.string_); + if (allocated_ && !(stringValue_.IsRaw())) + releaseStringValue>(stringValue_.GetString()); break; case arrayValue: case objectValue: @@ -542,16 +548,16 @@ bool Value<_Alloc, _String>::operator<(const Value<_Alloc, _String>& other) cons return value_.bool_ < other.value_.bool_; case stringValue: { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { - if (other.value_.string_) return true; + if ((stringValue_.GetString() == 0) || (other.stringValue_.GetString() == 0)) { + if (other.stringValue_.GetString()) return true; else return false; } 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->allocated_, this->stringValue_.GetString(), &this_len, &this_str); + decodePrefixedString>(other.allocated_, other.stringValue_.GetString(), &other_len, &other_str); unsigned min_len = std::min(this_len, other_len); int comp = memcmp(this_str, other_str, min_len); if (comp < 0) return true; @@ -602,15 +608,15 @@ bool Value<_Alloc, _String>::operator==(const Value<_Alloc, _String>& other) con return value_.bool_ == other.value_.bool_; case stringValue: { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { - return (value_.string_ == other.value_.string_); + if ((stringValue_.GetString() == 0) || (other.stringValue_.GetString() == 0)) { + return (stringValue_.GetString() == other.stringValue_.GetString()); } 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->allocated_, this->stringValue_.GetString(), &this_len, &this_str); + decodePrefixedString>(other.allocated_, other.stringValue_.GetString(), &other_len, &other_str); if (this_len != other_len) return false; int comp = memcmp(this_str, other_str, this_len); return comp == 0; @@ -632,19 +638,19 @@ template const char* Value<_Alloc, _String>::asCString() const { JSON_ASSERT_MESSAGE(type_ == stringValue, "in Json::Value::asCString(): requires stringValue"); - if (value_.string_ == 0) return 0; + if (stringValue_.GetString() == 0) return 0; unsigned this_len; char const* this_str; - decodePrefixedString>(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString>(this->allocated_, this->stringValue_.GetString(), &this_len, &this_str); return this_str; } template bool Value<_Alloc, _String>::getString(char const** str, char const** cend) const { if (type_ != stringValue) return false; - if (value_.string_ == 0) return false; + if (stringValue_.GetString() == 0) return false; unsigned length; - decodePrefixedString>(this->allocated_, this->value_.string_, &length, str); + decodePrefixedString>(this->allocated_, stringValue_.GetString(), &length, str); *cend = *str + length; return true; } @@ -656,10 +662,10 @@ _String Value<_Alloc, _String>::asString() const { return ""; case stringValue: { - if (value_.string_ == 0) return ""; + if (stringValue_.GetString() == 0) return ""; unsigned this_len; char const* this_str; - decodePrefixedString>(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString>(this->allocated_, this->stringValue_.GetString(), &this_len, &this_str); return String(this_str, this_len); } case booleanValue: @@ -1427,13 +1433,13 @@ void Value<_Alloc, _String>::setComment(const String& comment, CommentPlacement template bool Value<_Alloc, _String>::hasComment(CommentPlacement placement) const { - return comments_ != 0 && comments_[placement].comment_ != 0; + return comments_ != 0 && comments_[placement].comment_.GetString() != 0; } template _String Value<_Alloc, _String>::getComment(CommentPlacement placement) const { if (hasComment(placement)) - return comments_[placement].comment_; + return comments_[placement].comment_.GetString(); return ""; } @@ -1511,6 +1517,65 @@ typename Value<_Alloc, _String>::iterator Value<_Alloc, _String>::end() { return iterator(); } +template +Value<_Alloc, _String>::StringValueHolder::StringValueHolder() { + /* Not used */ +} + +template +Value<_Alloc, _String>::StringValueHolder::StringValueHolder(StringDataPtr&& value) { + SetString(std::move(value)); +} + +template +Value<_Alloc, _String>::StringValueHolder::StringValueHolder(char* value) { + SetString(value); +} + +template +Value<_Alloc, _String>::StringValueHolder::~StringValueHolder() { + /* Not used */ +} + +template +char* Value<_Alloc, _String>::StringValueHolder::GetString() { + if (raw_) { + return valueStringRaw_; + } else if (valueStringCopy_) { + return valueStringCopy_->data(); + } else { + return nullptr; + } +} + +template +const char* Value<_Alloc, _String>::StringValueHolder::GetString() const { + if (raw_) { + return valueStringRaw_; + } else if (valueStringCopy_) { + return valueStringCopy_->data(); + } else { + return nullptr; + } +} + +template +void Value<_Alloc, _String>::StringValueHolder::SetString(char* value) { + valueStringRaw_ = value; + raw_ = true; +} + +template +void Value<_Alloc, _String>::StringValueHolder::SetString(StringDataPtr&& value) { + std::swap(valueStringCopy_, value); + raw_ = false; +} + +template +bool Value<_Alloc, _String>::StringValueHolder::IsRaw() const { + return raw_; +} + // class PathArgument // ////////////////////////////////////////////////////////////////// From 1729e9c6957859e7a19b8fbefdfa3c4cecc748e1 Mon Sep 17 00:00:00 2001 From: Andrew Whatson Date: Sun, 7 Feb 2016 00:38:36 +1000 Subject: [PATCH 13/41] Fix some compilation errors - add missing include for std::unique_ptr - fix global operator<< template to be specific to Json::Value - add missing include guards to header implementation files - include the template implementations in tests --- include/json/reader.inl | 5 +++++ include/json/value.h | 4 ++-- include/json/value.inl | 5 +++++ include/json/writer.inl | 15 ++++++++++----- src/test_lib_json/jsontest.h | 4 ++-- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/include/json/reader.inl b/include/json/reader.inl index cd97b5f3b..9d6a7f400 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -3,6 +3,9 @@ // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE +#ifndef CPPTL_JSON_READER_INL_INCLUDED +#define CPPTL_JSON_READER_INL_INCLUDED + #if !defined(JSON_IS_AMALGAMATION) #include "assertions.h" #include "reader.h" @@ -2100,3 +2103,5 @@ std::istream& operator>>(std::istream& sin, _Value& root) { } // namespace detail } // namespace Json + +#endif // CPPTL_JSON_READER_INL_INCLUDED diff --git a/include/json/value.h b/include/json/value.h index 8c9a67c2b..945157ce3 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -9,6 +9,7 @@ #if !defined(JSON_IS_AMALGAMATION) #include "forwards.h" #endif // if !defined(JSON_IS_AMALGAMATION) +#include #include #include #include @@ -887,8 +888,7 @@ typedef detail::ValueIteratorBase> ValueIteratorBase; // class namespace std { /// Specialize std::swap() for Json::Value. -template, - class _String = std::basic_string, std::allocator>> +template inline void swap(Json::detail::Value<_Alloc, _String>& a, Json::detail::Value<_Alloc, _String>& b) { a.swap(b); } } diff --git a/include/json/value.inl b/include/json/value.inl index eec819218..b6392bbff 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -3,6 +3,9 @@ // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE +#ifndef CPPTL_JSON_VALUE_INL_INCLUDED +#define CPPTL_JSON_VALUE_INL_INCLUDED + #if !defined(JSON_IS_AMALGAMATION) #include "assertions.h" #include "value.h" @@ -1730,3 +1733,5 @@ _Value& Path<_Value>::make(_Value& root) const { } // namespace detail } // namespace Json + +#endif // CPPTL_JSON_VALUE_INL_INCLUDED diff --git a/include/json/writer.inl b/include/json/writer.inl index cfa831fd2..fb1fb14a4 100644 --- a/include/json/writer.inl +++ b/include/json/writer.inl @@ -3,6 +3,9 @@ // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE +#ifndef JSON_WRITER_INL_INCLUDED +#define JSON_WRITER_INL_INCLUDED + #if !defined(JSON_IS_AMALGAMATION) #include #include "tool.h" @@ -1249,15 +1252,15 @@ typedef std::auto_ptr> StreamWriterPtr; /// \brief Output using the StyledStreamWriter. /// \see Json::operator>>() -template -std::ostream& operator<<(std::ostream& sout, _Value const& root) { +template +std::ostream& operator<<(std::ostream& sout, Value<_Alloc, _String> const& root) { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr> StreamWriterPtr; +typedef std::unique_ptr>> StreamWriterPtr; #else -typedef std::auto_ptr> StreamWriterPtr; +typedef std::auto_ptr>> StreamWriterPtr; #endif - StreamWriterBuilder<_Value> builder; + StreamWriterBuilder> builder; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); return sout; @@ -1265,3 +1268,5 @@ typedef std::auto_ptr> StreamWriterPtr; } // namespace detail } // namespace Json + +#endif // JSON_WRITER_INL_INCLUDED diff --git a/src/test_lib_json/jsontest.h b/src/test_lib_json/jsontest.h index 01b9c40dd..8ebdeea93 100644 --- a/src/test_lib_json/jsontest.h +++ b/src/test_lib_json/jsontest.h @@ -7,8 +7,8 @@ #define JSONTEST_H_INCLUDED #include -#include -#include +#include +#include #include #include #include From ecf832b41e0ca4bc0f32434a0926a0be17dcdd07 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sat, 6 Feb 2016 19:33:26 +0000 Subject: [PATCH 14/41] Fix issue with incorrect clone causing seg fault --- include/json/value.inl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/json/value.inl b/include/json/value.inl index b6392bbff..65fdee42c 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -36,7 +36,7 @@ namespace detail { #define ALIGNAS(byte_alignment) #endif static const unsigned char ALIGNAS(8) kNull[2048] = { 0 }; //FIXME no sizeof(Value) exists -const unsigned char& kNullRef = kNull[0]; +static const unsigned char& kNullRef = kNull[0]; template const Value& Value::null = reinterpret_cast&>(kNullRef); template @@ -128,7 +128,7 @@ static inline typename _Value::StringDataPtr duplicateAndPrefixStringValue( "length too big for prefixing"); try { - typename _Value::StringDataPtr newString(new typename _Value::StringData(value, value + length)); + typename _Value::StringDataPtr newString(new typename _Value::StringData()); for (unsigned int i=0; ipush_back(reinterpret_cast(&length)[i]); newString->insert(newString->end(), value, value+length); @@ -157,7 +157,7 @@ inline static void decodePrefixedString( /** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). */ template -static inline void releaseStringValue(char* value) { /* Unused */ } //FIXME Remove! +static inline void releaseStringValue(char* value) { (void)(value);/* Unused */ } //FIXME Remove! } // namespace detail } // namespace Json From 30d4cf0e71e5e669f5db0b229d2ae5fa4108c56e Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sat, 6 Feb 2016 19:52:50 +0000 Subject: [PATCH 15/41] All tests fixed, issue with missing swap of string value --- include/json/value.h | 7 +++++++ include/json/value.inl | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/include/json/value.h b/include/json/value.h index 945157ce3..9b67dd2ba 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -219,6 +219,8 @@ class JSON_API Value { class StringValueHolder { public: StringValueHolder(); + StringValueHolder(const StringValueHolder& other); + StringValueHolder(StringValueHolder&& other); StringValueHolder(StringDataPtr&& value); StringValueHolder(char* value); ~StringValueHolder(); @@ -228,6 +230,11 @@ class JSON_API Value { void SetString(char* value); bool IsRaw() const; + StringValueHolder& operator=(const StringValueHolder& other); + StringValueHolder& operator=(StringValueHolder&& other); + void copy(const StringValueHolder& other); + void swap(StringValueHolder&& other); + private: char* valueStringRaw_ = nullptr; // the value that was passed in, this does not belong to value StringDataPtr valueStringCopy_; // a copy of the value that was passed in diff --git a/include/json/value.inl b/include/json/value.inl index 65fdee42c..2f5e71eba 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -511,6 +511,7 @@ void Value<_Alloc, _String>::swapPayload(Value<_Alloc, _String>& other) { int temp2 = allocated_; allocated_ = other.allocated_; other.allocated_ = temp2 & 0x1; + std::swap(stringValue_, other.stringValue_); } template @@ -1525,6 +1526,16 @@ Value<_Alloc, _String>::StringValueHolder::StringValueHolder() { /* Not used */ } +template +Value<_Alloc, _String>::StringValueHolder::StringValueHolder(const StringValueHolder& other) { + copy(other); +} + +template +Value<_Alloc, _String>::StringValueHolder::StringValueHolder(StringValueHolder&& other) { + swap(std::move(other)); +} + template Value<_Alloc, _String>::StringValueHolder::StringValueHolder(StringDataPtr&& value) { SetString(std::move(value)); @@ -1579,6 +1590,33 @@ bool Value<_Alloc, _String>::StringValueHolder::IsRaw() const { return raw_; } +template +typename Value<_Alloc, _String>::StringValueHolder& Value<_Alloc, _String>::StringValueHolder::operator=(const StringValueHolder& other) { + copy(other); + return *this; +} + +template +typename Value<_Alloc, _String>::StringValueHolder& Value<_Alloc, _String>::StringValueHolder::operator=(StringValueHolder&& other) { + swap(std::move(other)); + return *this; +} + +template +void Value<_Alloc, _String>::StringValueHolder::copy(const StringValueHolder& other) { + valueStringRaw_ = other.valueStringRaw_; + if (other.valueStringCopy_) + valueStringCopy_ = StringDataPtr(new StringData(*other.valueStringCopy_)); + raw_ = other.raw_; +} + +template +void Value<_Alloc, _String>::StringValueHolder::swap(StringValueHolder&& other) { + std::swap(valueStringRaw_, other.valueStringRaw_); + std::swap(valueStringCopy_, other.valueStringCopy_); + std::swap(raw_, other.raw_); +} + // class PathArgument // ////////////////////////////////////////////////////////////////// From b905735a73fe85c880d6d715984bd976073d8c0e Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sat, 6 Feb 2016 20:33:32 +0000 Subject: [PATCH 16/41] Fix up merge --- include/json/reader.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/json/reader.inl b/include/json/reader.inl index cd113972c..91584711a 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -752,7 +752,7 @@ bool Reader<_Value>::decodeUnicodeEscapeSequence(Token& token, token, current); } - ret_unicode = unicode; + ret_unicode = static_cast(unicode); return true; } @@ -1599,7 +1599,7 @@ bool OurReader<_Value>::decodeNumber(Token& token, _Value& decoded) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); - typename _Value::UInt digit(c - '0'); + typename _Value::UInt digit(static_cast(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 From 6147e5995ce438c719e7cfb46234bcc73600a920 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sat, 6 Feb 2016 22:30:08 +0000 Subject: [PATCH 17/41] isfinite global declaration conflicted with boost --- include/json/writer.h | 2 ++ include/json/writer.inl | 33 +-------------------------------- src/lib_json/json_writer.cpp | 23 +++++------------------ 3 files changed, 8 insertions(+), 50 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index e4c63405d..7a9840ccd 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -341,6 +341,8 @@ typename _Value::String JSON_API valueToQuotedString(const char* value); } // namespace detail +bool jsonIsFinite(double value); + typedef detail::FastWriter> FastWriter; // class Json::FastWriter typedef detail::StreamWriter> StreamWriter; // class Json::StreamWriter typedef detail::StreamWriterBuilder> StreamWriterBuilder; // class Json::StreamWriterBuilder diff --git a/include/json/writer.inl b/include/json/writer.inl index d0cddd55f..2339d0619 100644 --- a/include/json/writer.inl +++ b/include/json/writer.inl @@ -19,36 +19,6 @@ #include #include -#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 -#include -#define isfinite _finite -#elif defined(__sun) && defined(__SVR4) //Solaris -#if !defined(isfinite) -#include -#define isfinite finite -#endif -#elif defined(_AIX) -#if !defined(isfinite) -#include -#define isfinite finite -#endif -#elif defined(__hpux) -#if !defined(isfinite) -#if defined(__ia64) && !defined(finite) -#define isfinite(x) ((sizeof(x) == sizeof(float) ? \ - _Isfinitef(x) : _IsFinite(x))) -#else -#include -#define isfinite finite -#endif -#endif -#else -#include -#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 @@ -67,7 +37,6 @@ #if defined(__BORLANDC__) #include -#define isfinite _finite #define snprintf _snprintf #endif @@ -137,7 +106,7 @@ typename _Value::String valueToString(double value, bool useSpecialFloats, unsig // Print into the buffer. We need not request the alternative representation // that always has a decimal point because JSON doesn't distingish the // concepts of reals and integers. - if (isfinite(value)) { + if (jsonIsFinite(value)) { len = snprintf(buffer, sizeof(buffer), formatString, value); } else { // IEEE standard states that NaN values will not compare to themselves diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 373ffaf0b..2f845d811 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -46,26 +46,9 @@ #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 -#endif -#elif defined(__ANDROID__) || defined(__QNXNTO__) -#define snprintf snprintf -#elif __cplusplus >= 201103L -#if !defined(__MINGW32__) -#define snprintf std::snprintf -#endif -#endif - -#if defined(__BORLANDC__) +#if defined(__BORLANDC__) #include #define isfinite _finite -#define snprintf _snprintf #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 @@ -81,6 +64,10 @@ typedef std::unique_ptr StreamWriterPtr; typedef std::auto_ptr StreamWriterPtr; #endif +bool jsonIsFinite(double value) { + return isfinite(value); +} + namespace detail { bool containsControlCharacter(const char* str) { From eb9a949aff77cbecf96986e9339e86ff5ef10428 Mon Sep 17 00:00:00 2001 From: Jan Bilek Date: Sun, 7 Feb 2016 15:11:59 +1000 Subject: [PATCH 18/41] Added typedef alias Value_ for a type with accessible allocator --- include/json/value.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/json/value.h b/include/json/value.h index 0b6171d56..8746d9dcf 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -885,6 +885,8 @@ class JSON_API ValueIterator : public ValueIteratorBase<_Value> { typedef detail::Path> Path; // class Json::Path typedef detail::PathArgument> PathArgument; // class Json::PathArgument +template // class Json::Value_ type with accessible allocator + using Value_ = Json::detail::Value<_Alloc, _String>; typedef detail::Value<> Value; // class Json::Value typedef detail::ValueConstIterator> ValueConstIterator; // class Json::ValueConstIterator typedef detail::ValueIterator> ValueIterator; // class Json::ValueIterator From 74147f7a8196b0a25be8e640f7f62685394ac82f Mon Sep 17 00:00:00 2001 From: Jan Bilek Date: Sun, 7 Feb 2016 17:14:05 +1000 Subject: [PATCH 19/41] Added typedef aliases to Writer & Reader --- include/json/reader.h | 3 +++ include/json/value.h | 7 ++++--- include/json/writer.h | 8 +++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/json/reader.h b/include/json/reader.h index b9d2e4057..21de93283 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -407,7 +407,10 @@ JSON_API std::istream& operator>>(std::istream&, _Value&); typedef detail::CharReader> CharReader; // class Json::CharReader typedef detail::CharReaderBuilder> CharReaderBuilder; // class Json::CharReaderBuilder + typedef detail::Reader> Reader; // class Json::Reader +template // class Json::Reader_ type with accessible allocator + using Reader_ = detail::Reader>; } // namespace Json diff --git a/include/json/value.h b/include/json/value.h index 8746d9dcf..1ff02793d 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -885,13 +885,14 @@ class JSON_API ValueIterator : public ValueIteratorBase<_Value> { typedef detail::Path> Path; // class Json::Path typedef detail::PathArgument> PathArgument; // class Json::PathArgument -template // class Json::Value_ type with accessible allocator - using Value_ = Json::detail::Value<_Alloc, _String>; -typedef detail::Value<> Value; // class Json::Value typedef detail::ValueConstIterator> ValueConstIterator; // class Json::ValueConstIterator typedef detail::ValueIterator> ValueIterator; // class Json::ValueIterator typedef detail::ValueIteratorBase> ValueIteratorBase; // class Json::ValueIteratorBase +typedef detail::Value<> Value; // class Json::Value +template // class Json::Value_ type with accessible allocator + using Value_ = detail::Value<_Alloc, _String>; + } // namespace Json diff --git a/include/json/writer.h b/include/json/writer.h index 7a9840ccd..7bcc47f43 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -343,12 +343,18 @@ typename _Value::String JSON_API valueToQuotedString(const char* value); bool jsonIsFinite(double value); -typedef detail::FastWriter> FastWriter; // class Json::FastWriter typedef detail::StreamWriter> StreamWriter; // class Json::StreamWriter typedef detail::StreamWriterBuilder> StreamWriterBuilder; // class Json::StreamWriterBuilder typedef detail::StyledStreamWriter> StyledStreamWriter; // class Json::StyledStreamWriter typedef detail::StyledWriter> StyledWriter; // class Json::StyledWriter + +typedef detail::FastWriter> FastWriter; // class Json::FastWriter +template // class Json::FastWriter type with accessible allocator + using FastWriter_ = detail::FastWriter>; + typedef detail::Writer> Writer; // class Json::Writer +template // class Json::Writer_ type with accessible allocator + using Writer_ = detail::Writer>; } // namespace Json From ce5b9f7726b1281d6be9f0bcc4f8ec01e2cbac62 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sun, 7 Feb 2016 12:15:58 +0000 Subject: [PATCH 20/41] Revert "Added typedef aliases to Writer & Reader" This reverts commit 74147f7a8196b0a25be8e640f7f62685394ac82f. --- include/json/reader.h | 3 --- include/json/value.h | 7 +++---- include/json/writer.h | 8 +------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/include/json/reader.h b/include/json/reader.h index 21de93283..b9d2e4057 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -407,10 +407,7 @@ JSON_API std::istream& operator>>(std::istream&, _Value&); typedef detail::CharReader> CharReader; // class Json::CharReader typedef detail::CharReaderBuilder> CharReaderBuilder; // class Json::CharReaderBuilder - typedef detail::Reader> Reader; // class Json::Reader -template // class Json::Reader_ type with accessible allocator - using Reader_ = detail::Reader>; } // namespace Json diff --git a/include/json/value.h b/include/json/value.h index 1ff02793d..8746d9dcf 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -885,14 +885,13 @@ class JSON_API ValueIterator : public ValueIteratorBase<_Value> { typedef detail::Path> Path; // class Json::Path typedef detail::PathArgument> PathArgument; // class Json::PathArgument +template // class Json::Value_ type with accessible allocator + using Value_ = Json::detail::Value<_Alloc, _String>; +typedef detail::Value<> Value; // class Json::Value typedef detail::ValueConstIterator> ValueConstIterator; // class Json::ValueConstIterator typedef detail::ValueIterator> ValueIterator; // class Json::ValueIterator typedef detail::ValueIteratorBase> ValueIteratorBase; // class Json::ValueIteratorBase -typedef detail::Value<> Value; // class Json::Value -template // class Json::Value_ type with accessible allocator - using Value_ = detail::Value<_Alloc, _String>; - } // namespace Json diff --git a/include/json/writer.h b/include/json/writer.h index 7bcc47f43..7a9840ccd 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -343,18 +343,12 @@ typename _Value::String JSON_API valueToQuotedString(const char* value); bool jsonIsFinite(double value); +typedef detail::FastWriter> FastWriter; // class Json::FastWriter typedef detail::StreamWriter> StreamWriter; // class Json::StreamWriter typedef detail::StreamWriterBuilder> StreamWriterBuilder; // class Json::StreamWriterBuilder typedef detail::StyledStreamWriter> StyledStreamWriter; // class Json::StyledStreamWriter typedef detail::StyledWriter> StyledWriter; // class Json::StyledWriter - -typedef detail::FastWriter> FastWriter; // class Json::FastWriter -template // class Json::FastWriter type with accessible allocator - using FastWriter_ = detail::FastWriter>; - typedef detail::Writer> Writer; // class Json::Writer -template // class Json::Writer_ type with accessible allocator - using Writer_ = detail::Writer>; } // namespace Json From 70f323e2212d3225e6937f5e8532300f82683538 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sun, 7 Feb 2016 12:16:06 +0000 Subject: [PATCH 21/41] Revert "Added typedef alias Value_ for a type with accessible allocator" This reverts commit eb9a949aff77cbecf96986e9339e86ff5ef10428. --- include/json/value.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/json/value.h b/include/json/value.h index 8746d9dcf..0b6171d56 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -885,8 +885,6 @@ class JSON_API ValueIterator : public ValueIteratorBase<_Value> { typedef detail::Path> Path; // class Json::Path typedef detail::PathArgument> PathArgument; // class Json::PathArgument -template // class Json::Value_ type with accessible allocator - using Value_ = Json::detail::Value<_Alloc, _String>; typedef detail::Value<> Value; // class Json::Value typedef detail::ValueConstIterator> ValueConstIterator; // class Json::ValueConstIterator typedef detail::ValueIterator> ValueIterator; // class Json::ValueIterator From 32ffc7f10a9a65eaa74c6ddf616f62d4bec6db93 Mon Sep 17 00:00:00 2001 From: Jan Bilek Date: Sun, 7 Feb 2016 23:49:35 +1000 Subject: [PATCH 22/41] addError needs to use String --- include/json/reader.h | 4 ++-- include/json/reader.inl | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/json/reader.h b/include/json/reader.h index b9d2e4057..a2408139c 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -217,9 +217,9 @@ class JSON_API Reader { Location& current, Location end, unsigned int& unicode); - bool addError(const std::string& message, Token& token, Location extra = 0); + bool addError(String message, Token& token, Location extra = 0); bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const std::string& message, + bool addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken); void skipUntilSpace(); diff --git a/include/json/reader.inl b/include/json/reader.inl index 91584711a..08301667c 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -758,7 +758,7 @@ bool Reader<_Value>::decodeUnicodeEscapeSequence(Token& token, template bool -Reader<_Value>::addError(const std::string& message, Token& token, Location extra) { +Reader<_Value>::addError(String message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -782,7 +782,7 @@ bool Reader<_Value>::recoverFromError(TokenType skipUntilToken) { } template -bool Reader<_Value>::addErrorAndRecover(const std::string& message, +bool Reader<_Value>::addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken) { addError(message, token); @@ -1021,7 +1021,7 @@ private: Location& current, Location end, unsigned int& unicode); - bool addError(const std::string& message, Token& token, Location extra = 0); + bool addError(String message, Token& token, Location extra = 0); bool recoverFromError(TokenType skipUntilToken); bool addErrorAndRecover(const std::string& message, Token& token, @@ -1798,7 +1798,7 @@ bool OurReader<_Value>::decodeUnicodeEscapeSequence(Token& token, template bool -OurReader<_Value>::addError(const std::string& message, Token& token, Location extra) { +OurReader<_Value>::addError(String message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -1822,7 +1822,7 @@ bool OurReader<_Value>::recoverFromError(TokenType skipUntilToken) { } template -bool OurReader<_Value>::addErrorAndRecover(const std::string& message, +bool OurReader<_Value>::addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken) { addError(message, token); From 0a3d8eea0bf8284b9cc905dfeb314de8992137f2 Mon Sep 17 00:00:00 2001 From: Jan Bilek Date: Mon, 8 Feb 2016 00:05:41 +1000 Subject: [PATCH 23/41] More String uses --- include/json/reader.h | 2 +- include/json/reader.inl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/json/reader.h b/include/json/reader.h index a2408139c..94c5f05c6 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -186,7 +186,7 @@ class JSON_API Reader { class ErrorInfo { public: Token token_; - std::string message_; + String message_; Location extra_; }; diff --git a/include/json/reader.inl b/include/json/reader.inl index 08301667c..9f1d7b4f1 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -989,7 +989,7 @@ private: class ErrorInfo { public: Token token_; - std::string message_; + String message_; Location extra_; }; From 8140a0f3c368ea95946f830e87b1ea6ab495e3dc Mon Sep 17 00:00:00 2001 From: Jan Bilek Date: Mon, 8 Feb 2016 00:08:49 +1000 Subject: [PATCH 24/41] Compilation fix --- include/json/reader.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/json/reader.inl b/include/json/reader.inl index 9f1d7b4f1..27261399e 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -1023,7 +1023,7 @@ private: unsigned int& unicode); bool addError(String message, Token& token, Location extra = 0); bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const std::string& message, + bool addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken); void skipUntilSpace(); From 1f2d46d85bd92475cd31c0e20dd7e28ff0ac0cda Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sun, 7 Feb 2016 15:21:59 +0000 Subject: [PATCH 25/41] Fixes for allocators that aren't std::allocator Add a unit test for secure allocation Change the streams to basic streams with the correct allocator Strange change to have to instantiate string then move it due to compiler issue --- include/json/reader.inl | 5 ++- include/json/value.h | 2 +- include/json/writer.inl | 4 +- src/test_lib_json/main.cpp | 84 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/include/json/reader.inl b/include/json/reader.inl index 27261399e..db31c0f34 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -479,7 +479,8 @@ bool Reader<_Value>::readObject(Token& tokenStart) { _Value numberName; if (!decodeNumber(tokenName, numberName)) return recoverFromError(tokenObjectEnd); - name = numberName.asString(); + String tmp = numberName.asString(); + name = tmp; } else { break; } @@ -621,7 +622,7 @@ template bool Reader<_Value>::decodeDouble(Token& token, _Value& decoded) { double value = 0; String buffer(token.start_, token.end_); - std::istringstream is(buffer); + std::basic_istringstream is(buffer); if (!(is >> value)) return addError("'" + String(token.start_, token.end_) + "' is not a number.", diff --git a/include/json/value.h b/include/json/value.h index 0b6171d56..2ac2a69ea 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -170,7 +170,7 @@ class JSON_API Value { public: typedef _String String; typedef _Alloc Allocator; - typedef std::vector Members; + typedef std::vector<_String> Members; typedef ValueIterator iterator; typedef ValueConstIterator const_iterator; typedef Json::UInt UInt; diff --git a/include/json/writer.inl b/include/json/writer.inl index 2339d0619..b77d10fd8 100644 --- a/include/json/writer.inl +++ b/include/json/writer.inl @@ -180,7 +180,7 @@ typename _Value::String valueToQuotedString(const char* value) { // sequence from occurring. default: if (isControlCharacter(*c)) { - std::ostringstream oss; + std::basic_ostringstream oss; oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast(*c); result += oss.str(); @@ -245,7 +245,7 @@ static typename _Value::String valueToQuotedStringN(const char* value, unsigned // sequence from occurring. default: if ((isControlCharacter(*c)) || (*c == 0)) { - std::ostringstream oss; + std::basic_ostringstream oss; oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast(*c); result += oss.str(); diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index 0a22f10c1..aa827c4d6 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -2510,6 +2510,88 @@ JSONTEST_FIXTURE(RValueTest, moveConstruction) { #endif } +struct AllocatorTest : JsonTest::TestCase {}; + +template +class SecureAllocator { + public: + // Type definitions + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + /** + * Allocate memory for N items using the standard allocator. + */ + pointer allocate(size_type n) { + // allocate using "global operator new" + return static_cast(::operator new(n * sizeof(T))); + } + + /** + * Release memory which was allocated for N items at pointer P. + * + * The memory block is filled with zeroes before being released. + * The pointer argument is tagged as "volatile" to prevent the + * compiler optimizing out this critical step. + */ + void deallocate(volatile pointer p, size_type n) { + std::memset(p, 0, n * sizeof(T)); + // free using "global operator delete" + ::operator delete(p); + } + + /** + * Construct an item in-place at pointer P. + */ + template + void construct(pointer p, Args&&... args) { + // construct using "placement new" and "perfect forwarding" + ::new (static_cast(p)) T(std::forward(args)...); + } + + /** + * Destroy an item in-place at pointer P. + */ + void destroy(pointer p) { + // destroy using "explicit destructor" + p->~T(); + } + + // Boilerplate + SecureAllocator() {} + template SecureAllocator(const SecureAllocator&) {} + template struct rebind { using other = SecureAllocator; }; +}; +#include +JSONTEST_FIXTURE(AllocatorTest, otherAllocator) { + using MyString = std::basic_string, SecureAllocator>; + using Value = Json::detail::Value, MyString>; + using FastWriter = Json::detail::FastWriter ; + using StyledWriter = Json::detail::StyledWriter ; + using Reader = Json::detail::Reader; + + Value testValue = MyString("1234"); + JSONTEST_ASSERT_EQUAL(MyString("1234"), testValue.asString()); + + FastWriter fwriter; + auto fastoutput = fwriter.write(testValue); + std::cout << fastoutput << std::endl; + + StyledWriter swriter; + auto styledoutput = swriter.write(testValue); +std::cout << styledoutput << std::endl; + + Reader reader; + Value node; + reader.parse(styledoutput, node); + JSONTEST_ASSERT_EQUAL(MyString("1234"), node.asString()); +} + int main(int argc, const char* argv[]) { JsonTest::Runner runner; JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr); @@ -2585,5 +2667,7 @@ int main(int argc, const char* argv[]) { JSONTEST_REGISTER_FIXTURE(runner, RValueTest, moveConstruction); + JSONTEST_REGISTER_FIXTURE(runner, AllocatorTest, otherAllocator); + return runner.runCommandLine(argc, argv); } From 2872f443b9374dfaa60a31e6ee19e86f55e88fd3 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sun, 7 Feb 2016 16:44:50 +0000 Subject: [PATCH 26/41] Add support for easy creation fetching of Json in any allocator format --- include/json/value.h | 5 ++++- include/json/value.inl | 30 +++++++++++++++++++++++++++++- src/test_lib_json/main.cpp | 6 +++--- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/include/json/value.h b/include/json/value.h index 2ac2a69ea..6a23345d3 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -328,7 +328,8 @@ Json::Value obj_value(Json::objectValue); // {} * \endcode */ Value(const Json::StaticString& value); - Value(const String& value); ///< Copy data() til size(). Embedded zeroes too. + template class BasicString> + Value(const BasicString& value); ///< Copy data() til size(). Embedded zeroes too. #ifdef JSON_USE_CPPTL Value(const CppTL::ConstString& value); #endif @@ -362,6 +363,8 @@ Json::Value obj_value(Json::objectValue); // {} const char* asCString() const; ///< Embedded zeroes could cause you trouble! String asString() const; ///< Embedded zeroes are possible. + template + RString asTemplateString() const; ///< Embedded zeroes are possible. /** Get raw char* of string-value. * \return false if !string. (Seg-fault if str or end are NULL.) */ diff --git a/include/json/value.inl b/include/json/value.inl index 30488844b..36f97bb88 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -394,7 +394,8 @@ Value<_Alloc, _String>::Value(const char* beginValue, const char* endValue) { } template -Value<_Alloc, _String>::Value(const String& value) { +template class BasicString> +Value<_Alloc, _String>::Value(const BasicString& value) { initBasic(stringValue, true); stringValue_.SetString( duplicateAndPrefixStringValue>(value.data(), static_cast(value.length()))); @@ -688,6 +689,33 @@ _String Value<_Alloc, _String>::asString() const { } } +template +template +RString Value<_Alloc, _String>::asTemplateString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + { + if (stringValue_.GetString() == 0) return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString>(this->allocated_, this->stringValue_.GetString(), &this_len, &this_str); + return RString(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString>(value_.int_); + case uintValue: + return valueToString>(value_.uint_); + case realValue: + return valueToString>(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + #ifdef JSON_USE_CPPTL template CppTL::ConstString Value<_Alloc, _String>::asConstString() const { diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index aa827c4d6..53506701d 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -2567,7 +2567,7 @@ class SecureAllocator { template SecureAllocator(const SecureAllocator&) {} template struct rebind { using other = SecureAllocator; }; }; -#include + JSONTEST_FIXTURE(AllocatorTest, otherAllocator) { using MyString = std::basic_string, SecureAllocator>; using Value = Json::detail::Value, MyString>; @@ -2580,16 +2580,16 @@ JSONTEST_FIXTURE(AllocatorTest, otherAllocator) { FastWriter fwriter; auto fastoutput = fwriter.write(testValue); - std::cout << fastoutput << std::endl; StyledWriter swriter; auto styledoutput = swriter.write(testValue); -std::cout << styledoutput << std::endl; Reader reader; Value node; reader.parse(styledoutput, node); JSONTEST_ASSERT_EQUAL(MyString("1234"), node.asString()); + + JSONTEST_ASSERT_EQUAL(std::string("1234"), node.asTemplateString()); } int main(int argc, const char* argv[]) { From 56256c322f9d36a7e4e382eea67d126f360e0c6b Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sun, 7 Feb 2016 17:02:15 +0000 Subject: [PATCH 27/41] Support parse from just const char* rather than copying into string first --- include/json/reader.h | 22 +++++++++++++++++++++- include/json/reader.inl | 10 +++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/include/json/reader.h b/include/json/reader.h index 94c5f05c6..a93c37aae 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -74,8 +74,28 @@ class JSON_API Reader { * \return \c true if the document was successfully parsed, \c false if an * error occurred. */ + template class BasicString> bool - parse(const String& document, _Value& root, bool collectComments = true); + parse(const BasicString& document, _Value& root, bool collectComments = true); + + /** \brief Read a Value from a JSON + document. + * \param doc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* doc, + _Value& root, + bool collectComments = true); /** \brief Read a Value from a JSON document. diff --git a/include/json/reader.inl b/include/json/reader.inl index db31c0f34..cb616cb4b 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -80,8 +80,9 @@ Reader<_Value>::Reader(const Features& features) } template +template class BasicString> bool -Reader<_Value>::parse(const String& document, _Value& root, bool collectComments) { +Reader<_Value>::parse(const BasicString& document, _Value& root, bool collectComments) { document_ = document; const char* begin = document_.c_str(); const char* end = begin + document_.length(); @@ -102,6 +103,13 @@ bool Reader<_Value>::parse(std::istream& sin, _Value& root, bool collectComments return parse(doc, root, collectComments); } +template +bool Reader<_Value>::parse(const char* doc, + _Value& root, + bool collectComments) { + return parse(doc, doc + strlen(doc), root, collectComments); +} + template bool Reader<_Value>::parse(const char* beginDoc, const char* endDoc, From d75f8794937cd11ec15352621a0d93858a21ac18 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sun, 7 Feb 2016 17:15:46 +0000 Subject: [PATCH 28/41] Document was incorrectly typed --- include/json/reader.inl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/json/reader.inl b/include/json/reader.inl index cb616cb4b..ee24a212d 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -83,7 +83,8 @@ template template class BasicString> bool Reader<_Value>::parse(const BasicString& document, _Value& root, bool collectComments) { - document_ = document; + String stronglyTypedDocument(document.begin(), document.end()); + std::swap(stronglyTypedDocument, document_); const char* begin = document_.c_str(); const char* end = begin + document_.length(); return parse(begin, end, root, collectComments); From d296cce4e71a7e58246ca4c01df4acca60d78e4b Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sun, 7 Feb 2016 21:29:00 +0000 Subject: [PATCH 29/41] toStyledString to support an output in a specific format --- include/json/value.h | 2 ++ include/json/value.inl | 8 ++++++++ src/test_lib_json/main.cpp | 3 +++ 3 files changed, 13 insertions(+) diff --git a/include/json/value.h b/include/json/value.h index 6a23345d3..1a1c96920 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -586,6 +586,8 @@ Json::Value obj_value(Json::objectValue); // {} String getComment(CommentPlacement placement) const; String toStyledString() const; + template + RString toStyledTemplateString() const; const_iterator begin() const; const_iterator end() const; diff --git a/include/json/value.inl b/include/json/value.inl index 36f97bb88..a5f515cd1 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -1496,6 +1496,14 @@ _String Value<_Alloc, _String>::toStyledString() const { return writer.write(*this); } +template +template +RString Value<_Alloc, _String>::toStyledTemplateString() const { + StyledWriter> writer; + auto written = writer.write(*this); + return RString(written.begin(), written.end()); +} + template typename Value<_Alloc, _String>::const_iterator Value<_Alloc, _String>::begin() const { switch (type_) { diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index 53506701d..bc67b0556 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -2580,10 +2580,13 @@ JSONTEST_FIXTURE(AllocatorTest, otherAllocator) { FastWriter fwriter; auto fastoutput = fwriter.write(testValue); + std::string fastoutputString(fastoutput.begin(), fastoutput.end()); StyledWriter swriter; auto styledoutput = swriter.write(testValue); + JSONTEST_ASSERT_EQUAL(fastoutputString, testValue.toStyledTemplateString()); + Reader reader; Value node; reader.parse(styledoutput, node); From 08df25833821fde0b76dcb7bf0f29e61527dd018 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Sun, 7 Feb 2016 23:19:16 +0000 Subject: [PATCH 30/41] SecureAllocator needs a operator == to compile on ubuntu --- src/test_lib_json/main.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index bc67b0556..af4f41ed1 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -2568,6 +2568,16 @@ class SecureAllocator { template struct rebind { using other = SecureAllocator; }; }; +template +bool operator==(const SecureAllocator&, const SecureAllocator&) { + return true; +} + +template +bool operator!=(const SecureAllocator&, const SecureAllocator&) { + return false; +} + JSONTEST_FIXTURE(AllocatorTest, otherAllocator) { using MyString = std::basic_string, SecureAllocator>; using Value = Json::detail::Value, MyString>; From f6e89d46c8a284d8e7f6f829727093b9189a3c2d Mon Sep 17 00:00:00 2001 From: Andrew Whatson Date: Mon, 8 Feb 2016 11:21:03 +1000 Subject: [PATCH 31/41] Commit gitattributes enforced line endings --- devtools/agent_vmw7.json | 66 ++-- devtools/agent_vmxp.json | 52 +-- makefiles/msvc2010/jsoncpp.sln | 84 ++--- makefiles/msvc2010/jsontest.vcxproj | 190 +++++------ makefiles/msvc2010/lib_json.vcxproj | 284 ++++++++-------- makefiles/msvc2010/test_lib_json.vcxproj | 216 ++++++------ makefiles/vs71/jsoncpp.sln | 92 ++--- makefiles/vs71/jsontest.vcproj | 238 ++++++------- makefiles/vs71/lib_json.vcproj | 410 +++++++++++------------ makefiles/vs71/test_lib_json.vcproj | 260 +++++++------- 10 files changed, 946 insertions(+), 946 deletions(-) diff --git a/devtools/agent_vmw7.json b/devtools/agent_vmw7.json index 95d62ba36..cd7b777fe 100644 --- a/devtools/agent_vmw7.json +++ b/devtools/agent_vmw7.json @@ -1,33 +1,33 @@ -{ - "cmake_variants" : [ - {"name": "generator", - "generators": [ - {"generator": [ - "Visual Studio 7 .NET 2003", - "Visual Studio 9 2008", - "Visual Studio 9 2008 Win64", - "Visual Studio 10", - "Visual Studio 10 Win64", - "Visual Studio 11", - "Visual Studio 11 Win64" - ] - }, - {"generator": ["MinGW Makefiles"], - "env_prepend": [{"path": "c:/wut/prg/MinGW/bin"}] - } - ] - }, - {"name": "shared_dll", - "variables": [ - ["BUILD_SHARED_LIBS=true"], - ["BUILD_SHARED_LIBS=false"] - ] - }, - {"name": "build_type", - "build_types": [ - "debug", - "release" - ] - } - ] -} +{ + "cmake_variants" : [ + {"name": "generator", + "generators": [ + {"generator": [ + "Visual Studio 7 .NET 2003", + "Visual Studio 9 2008", + "Visual Studio 9 2008 Win64", + "Visual Studio 10", + "Visual Studio 10 Win64", + "Visual Studio 11", + "Visual Studio 11 Win64" + ] + }, + {"generator": ["MinGW Makefiles"], + "env_prepend": [{"path": "c:/wut/prg/MinGW/bin"}] + } + ] + }, + {"name": "shared_dll", + "variables": [ + ["BUILD_SHARED_LIBS=true"], + ["BUILD_SHARED_LIBS=false"] + ] + }, + {"name": "build_type", + "build_types": [ + "debug", + "release" + ] + } + ] +} diff --git a/devtools/agent_vmxp.json b/devtools/agent_vmxp.json index 39d5e53a9..f82a0773a 100644 --- a/devtools/agent_vmxp.json +++ b/devtools/agent_vmxp.json @@ -1,26 +1,26 @@ -{ - "cmake_variants" : [ - {"name": "generator", - "generators": [ - {"generator": [ - "Visual Studio 6", - "Visual Studio 7", - "Visual Studio 8 2005" - ] - } - ] - }, - {"name": "shared_dll", - "variables": [ - ["BUILD_SHARED_LIBS=true"], - ["BUILD_SHARED_LIBS=false"] - ] - }, - {"name": "build_type", - "build_types": [ - "debug", - "release" - ] - } - ] -} +{ + "cmake_variants" : [ + {"name": "generator", + "generators": [ + {"generator": [ + "Visual Studio 6", + "Visual Studio 7", + "Visual Studio 8 2005" + ] + } + ] + }, + {"name": "shared_dll", + "variables": [ + ["BUILD_SHARED_LIBS=true"], + ["BUILD_SHARED_LIBS=false"] + ] + }, + {"name": "build_type", + "build_types": [ + "debug", + "release" + ] + } + ] +} diff --git a/makefiles/msvc2010/jsoncpp.sln b/makefiles/msvc2010/jsoncpp.sln index fb1889810..c4ecb9070 100644 --- a/makefiles/msvc2010/jsoncpp.sln +++ b/makefiles/msvc2010/jsoncpp.sln @@ -1,42 +1,42 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_json", "lib_json.vcxproj", "{1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jsontest", "jsontest.vcxproj", "{25AF2DD2-D396-4668-B188-488C33B8E620}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_lib_json", "test_lib_json.vcxproj", "{B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|Win32.ActiveCfg = Debug|Win32 - {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|Win32.Build.0 = Debug|Win32 - {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|x64.ActiveCfg = Debug|x64 - {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|x64.Build.0 = Debug|x64 - {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|Win32.ActiveCfg = Release|Win32 - {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|Win32.Build.0 = Release|Win32 - {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|x64.ActiveCfg = Release|x64 - {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|x64.Build.0 = Release|x64 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug|Win32.ActiveCfg = Debug|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug|Win32.Build.0 = Debug|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug|x64.ActiveCfg = Debug|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Release|Win32.ActiveCfg = Release|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Release|Win32.Build.0 = Release|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Release|x64.ActiveCfg = Release|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug|Win32.ActiveCfg = Debug|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug|Win32.Build.0 = Debug|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug|x64.ActiveCfg = Debug|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release|Win32.ActiveCfg = Release|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release|Win32.Build.0 = Release|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release|x64.ActiveCfg = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_json", "lib_json.vcxproj", "{1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jsontest", "jsontest.vcxproj", "{25AF2DD2-D396-4668-B188-488C33B8E620}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_lib_json", "test_lib_json.vcxproj", "{B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|Win32.ActiveCfg = Debug|Win32 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|Win32.Build.0 = Debug|Win32 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|x64.ActiveCfg = Debug|x64 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|x64.Build.0 = Debug|x64 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|Win32.ActiveCfg = Release|Win32 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|Win32.Build.0 = Release|Win32 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|x64.ActiveCfg = Release|x64 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|x64.Build.0 = Release|x64 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug|Win32.ActiveCfg = Debug|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug|Win32.Build.0 = Debug|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug|x64.ActiveCfg = Debug|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Release|Win32.ActiveCfg = Release|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Release|Win32.Build.0 = Release|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Release|x64.ActiveCfg = Release|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug|Win32.ActiveCfg = Debug|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug|Win32.Build.0 = Debug|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug|x64.ActiveCfg = Debug|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release|Win32.ActiveCfg = Release|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release|Win32.Build.0 = Release|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release|x64.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/makefiles/msvc2010/jsontest.vcxproj b/makefiles/msvc2010/jsontest.vcxproj index 4c301e50a..939d440dd 100644 --- a/makefiles/msvc2010/jsontest.vcxproj +++ b/makefiles/msvc2010/jsontest.vcxproj @@ -1,96 +1,96 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {25AF2DD2-D396-4668-B188-488C33B8E620} - Win32Proj - - - - Application - MultiByte - - - Application - MultiByte - - - - - - - - - - - - - <_ProjectFileVersion>10.0.40219.1 - ../../build/vs71/debug/jsontest\ - ../../build/vs71/debug/jsontest\ - true - ../../build/vs71/release/jsontest\ - ../../build/vs71/release/jsontest\ - false - - - - Disabled - ../../include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - - - Level3 - EditAndContinue - - - $(OutDir)jsontest.exe - true - $(OutDir)jsontest.pdb - Console - MachineX86 - - - - - ../../include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - Level3 - ProgramDatabase - - - $(OutDir)jsontest.exe - true - Console - true - true - MachineX86 - - - - - - - - {1e6c2c1c-6453-4129-ae3f-0ee8e6599c89} - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {25AF2DD2-D396-4668-B188-488C33B8E620} + Win32Proj + + + + Application + MultiByte + + + Application + MultiByte + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + ../../build/vs71/debug/jsontest\ + ../../build/vs71/debug/jsontest\ + true + ../../build/vs71/release/jsontest\ + ../../build/vs71/release/jsontest\ + false + + + + Disabled + ../../include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + $(OutDir)jsontest.exe + true + $(OutDir)jsontest.pdb + Console + MachineX86 + + + + + ../../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + + + Level3 + ProgramDatabase + + + $(OutDir)jsontest.exe + true + Console + true + true + MachineX86 + + + + + + + + {1e6c2c1c-6453-4129-ae3f-0ee8e6599c89} + + + + + \ No newline at end of file diff --git a/makefiles/msvc2010/lib_json.vcxproj b/makefiles/msvc2010/lib_json.vcxproj index e435f8685..3cfd0f936 100644 --- a/makefiles/msvc2010/lib_json.vcxproj +++ b/makefiles/msvc2010/lib_json.vcxproj @@ -1,143 +1,143 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - - - - - - - - - - - {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89} - Win32Proj - jsoncpp - - - - StaticLibrary - true - Unicode - - - StaticLibrary - true - Unicode - - - StaticLibrary - false - true - Unicode - - - StaticLibrary - false - true - Unicode - - - - - - - - - - - - - - - - - - - - - NotUsing - Level3 - Disabled - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - ../../include - MultiThreadedDebug - - - Windows - true - - - - - NotUsing - Level3 - Disabled - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - ../../include - MultiThreadedDebug - - - Windows - true - - - - - Level3 - NotUsing - MaxSpeed - true - true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - ../../include - MultiThreaded - - - Windows - true - true - true - - - - - Level3 - NotUsing - MaxSpeed - true - true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - ../../include - MultiThreaded - - - Windows - true - true - true - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89} + Win32Proj + jsoncpp + + + + StaticLibrary + true + Unicode + + + StaticLibrary + true + Unicode + + + StaticLibrary + false + true + Unicode + + + StaticLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ../../include + MultiThreadedDebug + + + Windows + true + + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ../../include + MultiThreadedDebug + + + Windows + true + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + ../../include + MultiThreaded + + + Windows + true + true + true + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + ../../include + MultiThreaded + + + Windows + true + true + true + + + + + \ No newline at end of file diff --git a/makefiles/msvc2010/test_lib_json.vcxproj b/makefiles/msvc2010/test_lib_json.vcxproj index 08c4d44ec..068af613e 100644 --- a/makefiles/msvc2010/test_lib_json.vcxproj +++ b/makefiles/msvc2010/test_lib_json.vcxproj @@ -1,109 +1,109 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D} - test_lib_json - Win32Proj - - - - Application - MultiByte - - - Application - MultiByte - - - - - - - - - - - - - <_ProjectFileVersion>10.0.40219.1 - ../../build/vs71/debug/test_lib_json\ - ../../build/vs71/debug/test_lib_json\ - true - ../../build/vs71/release/test_lib_json\ - ../../build/vs71/release/test_lib_json\ - false - - - - Disabled - ../../include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - - - Level3 - EditAndContinue - - - $(OutDir)test_lib_json.exe - true - $(OutDir)test_lib_json.pdb - Console - MachineX86 - - - Running all unit tests - $(TargetPath) - - - - - ../../include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - Level3 - ProgramDatabase - - - $(OutDir)test_lib_json.exe - true - Console - true - true - MachineX86 - - - Running all unit tests - $(TargetPath) - - - - - - - - - - - - {1e6c2c1c-6453-4129-ae3f-0ee8e6599c89} - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D} + test_lib_json + Win32Proj + + + + Application + MultiByte + + + Application + MultiByte + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + ../../build/vs71/debug/test_lib_json\ + ../../build/vs71/debug/test_lib_json\ + true + ../../build/vs71/release/test_lib_json\ + ../../build/vs71/release/test_lib_json\ + false + + + + Disabled + ../../include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + $(OutDir)test_lib_json.exe + true + $(OutDir)test_lib_json.pdb + Console + MachineX86 + + + Running all unit tests + $(TargetPath) + + + + + ../../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + + + Level3 + ProgramDatabase + + + $(OutDir)test_lib_json.exe + true + Console + true + true + MachineX86 + + + Running all unit tests + $(TargetPath) + + + + + + + + + + + + {1e6c2c1c-6453-4129-ae3f-0ee8e6599c89} + + + + + \ No newline at end of file diff --git a/makefiles/vs71/jsoncpp.sln b/makefiles/vs71/jsoncpp.sln index 5bfa36654..dd2f91b44 100644 --- a/makefiles/vs71/jsoncpp.sln +++ b/makefiles/vs71/jsoncpp.sln @@ -1,46 +1,46 @@ -Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_json", "lib_json.vcproj", "{B84F7231-16CE-41D8-8C08-7B523FF4225B}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jsontest", "jsontest.vcproj", "{25AF2DD2-D396-4668-B188-488C33B8E620}" - ProjectSection(ProjectDependencies) = postProject - {B84F7231-16CE-41D8-8C08-7B523FF4225B} = {B84F7231-16CE-41D8-8C08-7B523FF4225B} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_lib_json", "test_lib_json.vcproj", "{B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}" - ProjectSection(ProjectDependencies) = postProject - {B84F7231-16CE-41D8-8C08-7B523FF4225B} = {B84F7231-16CE-41D8-8C08-7B523FF4225B} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfiguration) = preSolution - Debug = Debug - dummy = dummy - Release = Release - EndGlobalSection - GlobalSection(ProjectConfiguration) = postSolution - {B84F7231-16CE-41D8-8C08-7B523FF4225B}.Debug.ActiveCfg = Debug|Win32 - {B84F7231-16CE-41D8-8C08-7B523FF4225B}.Debug.Build.0 = Debug|Win32 - {B84F7231-16CE-41D8-8C08-7B523FF4225B}.dummy.ActiveCfg = dummy|Win32 - {B84F7231-16CE-41D8-8C08-7B523FF4225B}.dummy.Build.0 = dummy|Win32 - {B84F7231-16CE-41D8-8C08-7B523FF4225B}.Release.ActiveCfg = Release|Win32 - {B84F7231-16CE-41D8-8C08-7B523FF4225B}.Release.Build.0 = Release|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug.ActiveCfg = Debug|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug.Build.0 = Debug|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.dummy.ActiveCfg = Debug|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.dummy.Build.0 = Debug|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Release.ActiveCfg = Release|Win32 - {25AF2DD2-D396-4668-B188-488C33B8E620}.Release.Build.0 = Release|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug.ActiveCfg = Debug|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug.Build.0 = Debug|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.dummy.ActiveCfg = Debug|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.dummy.Build.0 = Debug|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release.ActiveCfg = Release|Win32 - {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection -EndGlobal +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_json", "lib_json.vcproj", "{B84F7231-16CE-41D8-8C08-7B523FF4225B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jsontest", "jsontest.vcproj", "{25AF2DD2-D396-4668-B188-488C33B8E620}" + ProjectSection(ProjectDependencies) = postProject + {B84F7231-16CE-41D8-8C08-7B523FF4225B} = {B84F7231-16CE-41D8-8C08-7B523FF4225B} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_lib_json", "test_lib_json.vcproj", "{B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}" + ProjectSection(ProjectDependencies) = postProject + {B84F7231-16CE-41D8-8C08-7B523FF4225B} = {B84F7231-16CE-41D8-8C08-7B523FF4225B} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + dummy = dummy + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {B84F7231-16CE-41D8-8C08-7B523FF4225B}.Debug.ActiveCfg = Debug|Win32 + {B84F7231-16CE-41D8-8C08-7B523FF4225B}.Debug.Build.0 = Debug|Win32 + {B84F7231-16CE-41D8-8C08-7B523FF4225B}.dummy.ActiveCfg = dummy|Win32 + {B84F7231-16CE-41D8-8C08-7B523FF4225B}.dummy.Build.0 = dummy|Win32 + {B84F7231-16CE-41D8-8C08-7B523FF4225B}.Release.ActiveCfg = Release|Win32 + {B84F7231-16CE-41D8-8C08-7B523FF4225B}.Release.Build.0 = Release|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug.ActiveCfg = Debug|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Debug.Build.0 = Debug|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.dummy.ActiveCfg = Debug|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.dummy.Build.0 = Debug|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Release.ActiveCfg = Release|Win32 + {25AF2DD2-D396-4668-B188-488C33B8E620}.Release.Build.0 = Release|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug.ActiveCfg = Debug|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Debug.Build.0 = Debug|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.dummy.ActiveCfg = Debug|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.dummy.Build.0 = Debug|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release.ActiveCfg = Release|Win32 + {B7A96B78-2782-40D2-8F37-A2DEF2B9C26D}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/makefiles/vs71/jsontest.vcproj b/makefiles/vs71/jsontest.vcproj index 99a4dd697..562c71f61 100644 --- a/makefiles/vs71/jsontest.vcproj +++ b/makefiles/vs71/jsontest.vcproj @@ -1,119 +1,119 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/makefiles/vs71/lib_json.vcproj b/makefiles/vs71/lib_json.vcproj index fe66d8ac5..24c5dd411 100644 --- a/makefiles/vs71/lib_json.vcproj +++ b/makefiles/vs71/lib_json.vcproj @@ -1,205 +1,205 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/makefiles/vs71/test_lib_json.vcproj b/makefiles/vs71/test_lib_json.vcproj index df36700bd..9ebb986a6 100644 --- a/makefiles/vs71/test_lib_json.vcproj +++ b/makefiles/vs71/test_lib_json.vcproj @@ -1,130 +1,130 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 580f92e8e5dd8c11b807367219d6476b190ff32b Mon Sep 17 00:00:00 2001 From: Andrew Whatson Date: Mon, 8 Feb 2016 16:16:01 +1000 Subject: [PATCH 32/41] Fix string constructor template --- include/json/value.h | 4 ++-- include/json/value.inl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/json/value.h b/include/json/value.h index 1a1c96920..cc5773505 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -328,8 +328,8 @@ Json::Value obj_value(Json::objectValue); // {} * \endcode */ Value(const Json::StaticString& value); - template class BasicString> - Value(const BasicString& value); ///< Copy data() til size(). Embedded zeroes too. + template + Value(const std::basic_string& value); ///< Copy data() til size(). Embedded zeroes too. #ifdef JSON_USE_CPPTL Value(const CppTL::ConstString& value); #endif diff --git a/include/json/value.inl b/include/json/value.inl index a5f515cd1..97aff09bf 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -394,8 +394,8 @@ Value<_Alloc, _String>::Value(const char* beginValue, const char* endValue) { } template -template class BasicString> -Value<_Alloc, _String>::Value(const BasicString& value) { +template +Value<_Alloc, _String>::Value(const std::basic_string& value) { initBasic(stringValue, true); stringValue_.SetString( duplicateAndPrefixStringValue>(value.data(), static_cast(value.length()))); From 5cdb42c4e53616aa4d60022a2ea07f5254b97405 Mon Sep 17 00:00:00 2001 From: Andrew Whatson Date: Mon, 8 Feb 2016 18:14:36 +1000 Subject: [PATCH 33/41] Accept any basic_string as key in operator[] and get() --- include/json/value.h | 9 ++++++--- include/json/value.inl | 13 ++++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/include/json/value.h b/include/json/value.h index cc5773505..55d41e200 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -468,11 +468,13 @@ Json::Value obj_value(Json::objectValue); // {} const Value& operator[](const char* key) const; /// Access an object value by name, create a null member if it does not exist. /// \param key may contain embedded nulls. - Value& operator[](const String& key); + template + Value& operator[](const std::basic_string& key); /// Access an object value by name, returns null if there is no member with /// that name. /// \param key may contain embedded nulls. - const Value& operator[](const String& key) const; + template + const Value& operator[](const std::basic_string& key) const; /** \brief Access an object value by name, create a null member if it does not exist. @@ -503,7 +505,8 @@ Json::Value obj_value(Json::objectValue); // {} /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy /// \param key may contain embedded nulls. - Value get(const String& key, const Value& defaultValue) const; + template + Value get(const std::basic_string& key, const Value& defaultValue) const; #ifdef JSON_USE_CPPTL /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy diff --git a/include/json/value.inl b/include/json/value.inl index 97aff09bf..3e69b654e 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -1130,6 +1130,7 @@ Value<_Alloc, _String> const* Value<_Alloc, _String>::find(char const* key, char if (it == value_.map_->end()) return NULL; return &(*it).second; } + template const Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const char* key) const { @@ -1137,8 +1138,10 @@ const Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const char* key if (!found) return nullRef; return *found; } + template -Value<_Alloc, _String> const& Value<_Alloc, _String>::operator[](String const& key) const +template +Value<_Alloc, _String> const& Value<_Alloc, _String>::operator[](std::basic_string const& key) const { Value const* found = find(key.data(), key.data() + key.length()); if (!found) return nullRef; @@ -1151,7 +1154,8 @@ Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const char* key) { } template -Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const String& key) { +template +Value<_Alloc, _String>& Value<_Alloc, _String>::operator[](const std::basic_string& key) { return resolveReference(key.data(), key.data() + key.length()); } @@ -1183,13 +1187,16 @@ Value<_Alloc, _String> Value<_Alloc, _String>::get(char const* key, char const* Value const* found = find(key, cend); return !found ? defaultValue : *found; } + template Value<_Alloc, _String> Value<_Alloc, _String>::get(char const* key, Value<_Alloc, _String> const& defaultValue) const { return get(key, key + strlen(key), defaultValue); } + template -Value<_Alloc, _String> Value<_Alloc, _String>::get(String const& key, Value<_Alloc, _String> const& defaultValue) const +template +Value<_Alloc, _String> Value<_Alloc, _String>::get(std::basic_string const& key, Value<_Alloc, _String> const& defaultValue) const { return get(key.data(), key.data() + key.length(), defaultValue); } From ecbc0acb10da45b1d4ddeb257b8c400b55506a71 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 12 Feb 2016 10:21:27 +0000 Subject: [PATCH 34/41] Remove IsIntegral compiler warning about not used By adding it to the class we remove the unused warning but this is a compiler error since the method actually is used. --- include/json/value.h | 1 + include/json/value.inl | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/json/value.h b/include/json/value.h index 55d41e200..b57cbfe50 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -606,6 +606,7 @@ Json::Value obj_value(Json::objectValue); // {} ptrdiff_t getOffsetLimit() const; private: + static bool IsIntegral(double d); void initBasic(ValueType type, bool allocated = false); Value& resolveReference(const char* key); diff --git a/include/json/value.inl b/include/json/value.inl index acffdef21..5b83fa015 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -1344,7 +1344,8 @@ typename Value<_Alloc, _String>::Members Value<_Alloc, _String>::getMemberNames( // //# endif -static bool IsIntegral(double d) { +template +bool Value<_Alloc, _String>::IsIntegral(double d) { double integral_part; return modf(d, &integral_part) == 0.0; } From 4f8eb0353864c4447cb2a2c6c903a1c414595de3 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 12 Feb 2016 10:31:50 +0000 Subject: [PATCH 35/41] Fix travis, the override added has caused compilation to fail on clang --- include/json/reader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/json/reader.h b/include/json/reader.h index a93c37aae..c8128ece2 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -359,9 +359,9 @@ class JSON_API CharReaderBuilder : public CharReader<_Value>::Factory { _Value settings_; CharReaderBuilder(); - ~CharReaderBuilder() override; + virtual ~CharReaderBuilder() override; - CharReader<_Value>* newCharReader() const override; + virtual CharReader<_Value>* newCharReader() const override; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. From 09a70c686de8da2d0b4fcb5d1ede903f7b195850 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 12 Feb 2016 10:37:05 +0000 Subject: [PATCH 36/41] Another virtual in clang++ causing issues --- include/json/reader.inl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/json/reader.inl b/include/json/reader.inl index 17191e164..f0a2d3720 100644 --- a/include/json/reader.inl +++ b/include/json/reader.inl @@ -1969,7 +1969,8 @@ public: : collectComments_(collectComments) , reader_(features) {} - bool parse( + virtual ~OurCharReader() {} + virtual bool parse( char const* beginDoc, char const* endDoc, _Value* root, std::string* errs) override { bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); From b27a16e019913a51bbdeae418f2baaf8875b0c96 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 12 Feb 2016 10:46:09 +0000 Subject: [PATCH 37/41] More override on clang++ fixes --- include/json/writer.h | 12 ++++++------ include/json/writer.inl | 3 ++- src/test_lib_json/jsontest.h | 6 ++++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index 7a9840ccd..8047f1eb1 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -118,12 +118,12 @@ class JSON_API StreamWriterBuilder : public StreamWriter<_Value>::Factory { _Value settings_; StreamWriterBuilder(); - ~StreamWriterBuilder() override; + virtual ~StreamWriterBuilder() override; /** * \throw std::exception if something goes wrong (e.g. invalid settings) */ - StreamWriter<_Value>* newStreamWriter() const override; + virtual StreamWriter<_Value>* newStreamWriter() const override; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. @@ -168,7 +168,7 @@ class JSON_API FastWriter : public Writer<_Value> { public: typedef typename _Value::String String; FastWriter(); - ~FastWriter() override {} + virtual ~FastWriter() override {} void enableYAMLCompatibility(); @@ -182,7 +182,7 @@ class JSON_API FastWriter : public Writer<_Value> { void omitEndingLineFeed(); public: // overridden from Writer - String write(const _Value& root) override; + virtual String write(const _Value& root) override; private: void writeValue(const _Value& value); @@ -222,14 +222,14 @@ class JSON_API StyledWriter : public Writer<_Value> { public: typedef typename _Value::String String; StyledWriter(); - ~StyledWriter() override {} + virtual ~StyledWriter() override {} public: // overridden from Writer /** \brief Serialize a Value in JSON format. * \param root Value to serialize. * \return String containing the JSON document that represents the root value. */ - String write(const _Value& root) override; + virtual String write(const _Value& root) override; private: void writeValue(const _Value& value); diff --git a/include/json/writer.inl b/include/json/writer.inl index f1eff0db5..2ce93f4d9 100644 --- a/include/json/writer.inl +++ b/include/json/writer.inl @@ -824,7 +824,8 @@ struct BuiltStyledStreamWriter : public StreamWriter<_Value> String const& endingLineFeedSymbol, bool useSpecialFloats, unsigned int precision); - int write(_Value const& root, std::ostream* sout) override; + virtual ~BuiltStyledStreamWriter() {} + virtual int write(_Value const& root, std::ostream* sout) override; private: void writeValue(_Value const& value); void writeArrayValue(_Value const& value); diff --git a/src/test_lib_json/jsontest.h b/src/test_lib_json/jsontest.h index 8ebdeea93..0194a78ee 100644 --- a/src/test_lib_json/jsontest.h +++ b/src/test_lib_json/jsontest.h @@ -265,8 +265,10 @@ TestResult& checkStringEqual(TestResult& result, } \ \ public: /* overidden from TestCase */ \ - const char* testName() const override { return #FixtureType "/" #name; } \ - void runTestCase() override; \ + virtual ~Test##FixtureType##name() {} \ + virtual const char* testName() const override \ + { return #FixtureType "/" #name; } \ + virtual void runTestCase() override; \ }; \ \ void Test##FixtureType##name::runTestCase() From 3933c84f9b9d7f0437b39be6e30ad9a674c295f6 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 12 Feb 2016 10:55:17 +0000 Subject: [PATCH 38/41] Implicit string conversion reported by travis clang++ --- include/json/value.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/json/value.inl b/include/json/value.inl index 5b83fa015..4a7709353 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -103,7 +103,7 @@ static inline typename _Value::StringDataPtr duplicateStringValue(const char* va // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. if (length >= (size_t)_Value::maxInt) - length = _Value::maxInt - 1; + length = static_cast(_Value::maxInt - 1); try { typename _Value::StringDataPtr newString(new typename _Value::StringData(value, value + length)); From 7ee007e4f6b4bf78f48b1c4e22699fe049503f1f Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 12 Feb 2016 12:13:07 +0000 Subject: [PATCH 39/41] Attempt to fix the travis error about max_size --- include/json/value.inl | 1 - src/lib_json/json_value.cpp | 1 - src/test_lib_json/main.cpp | 4 ++++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/json/value.inl b/include/json/value.inl index 4a7709353..4764ded9a 100644 --- a/include/json/value.inl +++ b/include/json/value.inl @@ -19,7 +19,6 @@ #ifdef JSON_USE_CPPTL #include #endif -#include // size_t #include // min() #define JSON_ASSERT_UNREACHABLE assert(false) diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 6cfe428f6..1d4dddac6 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -16,7 +16,6 @@ #ifdef JSON_USE_CPPTL #include #endif -#include // size_t #include // min() #define JSON_ASSERT_UNREACHABLE assert(false) diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index af4f41ed1..1c09b7eb4 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -2554,6 +2554,10 @@ class SecureAllocator { ::new (static_cast(p)) T(std::forward(args)...); } + size_type max_size() const { + return size_t(-1) / sizeof(T); + } + /** * Destroy an item in-place at pointer P. */ From 3f5a69fc4d692e888d56199acd21cbe5b5cf6655 Mon Sep 17 00:00:00 2001 From: Christopher Dawes Date: Fri, 12 Feb 2016 12:15:10 +0000 Subject: [PATCH 40/41] Also support address for fuller compliance with allocator --- src/test_lib_json/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index 1c09b7eb4..1e3a6ee3a 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -2558,6 +2558,14 @@ class SecureAllocator { return size_t(-1) / sizeof(T); } + pointer address( reference x ) const { + return std::__addressof(x); + } + + const_pointer address( const_reference x ) const { + return std::__addressof(x); + } + /** * Destroy an item in-place at pointer P. */ From e920567c0b7b7bf679c23cebf53a674ef92a486f Mon Sep 17 00:00:00 2001 From: dawesc Date: Sat, 13 Feb 2016 10:19:25 +0000 Subject: [PATCH 41/41] Not installing enough files --- include/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 591bf7bc9..37f23c722 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,3 +1,3 @@ -FILE(GLOB INCLUDE_FILES "json/*.h") +FILE(GLOB HEADER_FILES "json/*.h") FILE(GLOB INCLUDE_FILES "json/*.inl") -INSTALL(FILES ${INCLUDE_FILES} DESTINATION ${INCLUDE_INSTALL_DIR}/json) +INSTALL(FILES ${HEADER_FILES} ${INCLUDE_FILES} DESTINATION ${INCLUDE_INSTALL_DIR}/json)