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 diff --git a/include/json/allocator.h b/include/json/allocator.h new file mode 100644 index 000000000..42db8baa0 --- /dev/null +++ b/include/json/allocator.h @@ -0,0 +1,92 @@ +// Copyright 2015 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at https://raw.githubusercontent.com/open-source-parsers/jsoncpp/master/LICENSE +#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED +#define CPPTL_JSON_ALLOCATOR_H_INCLUDED + +#include //std::filln +#include // std::memset +#include // std::size_t, std::ptrdiff_t +#include // std::forward + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +/** + * This class is used for de-allocating memory securely, allocation happens + * in the normal manner however it's important that de-allocation explicitly + * overwrites the memory with 0s to ensure the memory cannot be "sniffed" + */ +template +class JSON_API 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::fill_n((volatile char*)p, n * sizeof(value_type), 0); + // 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; }; +}; + +template +bool operator==(const SecureAllocator&, const SecureAllocator&) { + return true; +} + +template +bool operator!=(const SecureAllocator&, const SecureAllocator&) { + return false; +} + +} //end namespace Json + +#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED diff --git a/include/json/config.h b/include/json/config.h index 7201ba7df..a3c4c0692 100644 --- a/include/json/config.h +++ b/include/json/config.h @@ -113,6 +113,13 @@ #define JSONCPP_DEPRECATED(message) #endif // if !defined(JSONCPP_DEPRECATED) +//We may want to have different presets for the default allocator and +//string +//#define JSONCPP_DEFAULT_CHAR_TRAITS = std::char_traits +//#define JSONCPP_DEFAULT_ALLOCATOR = std::allocator +#define JSONCPP_DEFAULT_CHAR_TRAITS +#define JSONCPP_DEFAULT_ALLOCATOR + namespace Json { typedef int Int; typedef unsigned int UInt; diff --git a/include/json/forwards.h b/include/json/forwards.h index ccfe09abf..ddfe20743 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 +// reader_declaration.h +template class Reader; // features.h @@ -25,11 +28,17 @@ class Features; // value.h typedef unsigned int ArrayIndex; class StaticString; +template class Path; +template class PathArgument; +template class Value; +template class ValueIteratorBase; +template class ValueIterator; +template class ValueConstIterator; } // namespace Json diff --git a/include/json/json.h b/include/json/json.h index 8f10ac2bf..2a05603d1 100644 --- a/include/json/json.h +++ b/include/json/json.h @@ -6,6 +6,7 @@ #ifndef JSON_JSON_H_INCLUDED #define JSON_JSON_H_INCLUDED +#include "allocator.h" #include "autolink.h" #include "value.h" #include "reader.h" diff --git a/include/json/reader.h b/include/json/reader.h index 0a1574fad..855857976 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -7,367 +7,1894 @@ #define CPPTL_JSON_READER_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) -#include "features.h" -#include "value.h" +#include "reader_declaration.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#include -#include - -// Disable warning C4251: : needs to have dll-interface to -// be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(push) -#pragma warning(disable : 4251) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) namespace Json { -/** \brief Unserialize a JSON document into a - *Value. - * - * \deprecated Use CharReader and CharReaderBuilder. - */ -class JSON_API Reader { -public: - typedef char Char; - typedef const Char* Location; - - /** \brief An error tagged with where in the JSON text it was encountered. - * - * The offsets give the [start, limit) range of bytes within the text. Note - * that this is bytes, not codepoints. - * - */ - struct StructuredError { - size_t offset_start; - size_t offset_limit; - std::string message; - }; - - /** \brief Constructs a Reader allowing all features - * for parsing. - */ - Reader(); - - /** \brief Constructs a Reader allowing the specified feature set - * for parsing. - */ - Reader(const Features& features); - - /** \brief Read a Value from a JSON - * document. - * \param document UTF-8 encoded string containing 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 std::string& document, Value& root, bool collectComments = true); - - /** \brief Read a Value from a JSON - document. - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \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* beginDoc, - const char* endDoc, - 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); - - /** \brief Returns a user friendly string that list errors in the parsed - * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. - * \deprecated Use getFormattedErrorMessages() instead (typo fix). - */ - JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") - std::string getFormatedErrorMessages() const; - - /** \brief Returns a user friendly string that list errors in the parsed - * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. - */ - std::string getFormattedErrorMessages() const; - - /** \brief Returns a vector of structured erros encounted while parsing. - * \return A (possibly empty) vector of StructuredError objects. Currently - * only one error can be returned, but the caller should tolerate - * multiple - * errors. This can occur if the parser recovers from a non-fatal - * parse error and then encounters additional errors. - */ - std::vector getStructuredErrors() const; - - /** \brief Add a semantic error message. - * \param value JSON Value location associated with the error - * \param message The error message. - * \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); - - /** \brief Add a semantic error message with extra context. - * \param value JSON Value location associated with the error - * \param message The error message. - * \param extra Additional JSON Value location to contextualize the error - * \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); - - /** \brief Return whether there are any errors. - * \return \c true if there are no errors to report \c false if - * errors have occurred. - */ - bool good() const; - -private: - enum TokenType { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - 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(); - void readNumber(); - 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_; - Features features_; - bool collectComments_; -}; // Reader - -/** Interface for reading JSON from a char array. - */ -class JSON_API CharReader { -public: - virtual ~CharReader() {} - /** \brief Read a Value from a JSON - document. - * The document must be a UTF-8 encoded string containing the document to read. - * - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param errs [out] Formatted error messages (if not NULL) - * a user friendly string that lists errors in the parsed - * document. - * \return \c true if the document was successfully parsed, \c false if an - error occurred. - */ - virtual bool parse( - char const* beginDoc, char const* endDoc, - Value* root, std::string* errs) = 0; - - class JSON_API Factory { - public: - virtual ~Factory() {} - /** \brief Allocate a CharReader via operator new(). - * \throw std::exception if something goes wrong (e.g. invalid settings) - */ - virtual CharReader* newCharReader() const = 0; - }; // Factory -}; // CharReader - -/** \brief Build a CharReader implementation. - -Usage: -\code - using namespace Json; - CharReaderBuilder builder; - builder["collectComments"] = false; - Value value; - std::string errs; - bool ok = parseFromStream(builder, std::cin, &value, &errs); -\endcode -*/ -class JSON_API CharReaderBuilder : public CharReader::Factory { -public: - // 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. - These are case-sensitive. - Available settings (case-sensitive): - - `"collectComments": false or true` - - true to collect comment and allow writing them - back during serialization, false to discard comments. - This parameter is ignored if allowComments is false. - - `"allowComments": false or true` - - true if comments are allowed. - - `"strictRoot": false or true` - - true if root must be either an array or an object value - - `"allowDroppedNullPlaceholders": false or true` - - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) - - `"allowNumericKeys": false or true` - - true if numeric object keys are allowed. - - `"allowSingleQuotes": false or true` - - true if '' are allowed for strings (both keys and values) - - `"stackLimit": integer` - - Exceeding stackLimit (recursive depth of `readValue()`) will - cause an exception. - - This is a security issue (seg-faults caused by deeply nested JSON), - so the default is low. - - `"failIfExtra": false or true` - - If true, `parse()` returns false when extra non-whitespace trails - the JSON value in the input string. - - `"rejectDupKeys": false or true` - - If true, `parse()` returns false when a key is duplicated within an object. - - `"allowSpecialFloats": false or true` - - If true, special float values (NaNs and infinities) are allowed - and their values are lossfree restorable. - - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ - Json::Value settings_; - - CharReaderBuilder(); - ~CharReaderBuilder() override; - - CharReader* newCharReader() const override; - - /** \return true if 'settings' are legal and consistent; - * otherwise, indicate bad settings via 'invalid'. - */ - bool validate(Json::Value* invalid) const; - - /** A simple way to update a specific setting. - */ - 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); - /** 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); -}; +// Implementation of class Reader +// //////////////////////////////// + +template +int const Reader<_Traits, _Alloc>::stackLimit_g = 1000; +template +int Reader<_Traits, _Alloc>::stackDepth_g = 0; + +template +bool Reader<_Traits, _Alloc>::containsNewLine(typename Reader<_Traits, _Alloc>::Location begin, typename Reader<_Traits, _Alloc>::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// +template +Reader<_Traits, _Alloc>::Reader() + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(Features::all()), + collectComments_() {} + +template +Reader<_Traits, _Alloc>::Reader(const Features& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +template +bool +Reader<_Traits, _Alloc>::parse(const std::basic_string& document, Value<_Traits, _Alloc>& root, bool collectComments) { + document_ = document; + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +template +bool Reader<_Traits, _Alloc>::parse(std::istream& sin, Value<_Traits, _Alloc>& 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::basic_string is reference-counted, this at least does not + // create an extra copy. + std::basic_string doc; + std::getline(sin, doc, (char)EOF); + return parse(doc, root, collectComments); +} + + +template +bool Reader<_Traits, _Alloc>::parse(const char* beginDoc, + const char* endDoc, + Value<_Traits, _Alloc>& 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; +} + + +template +bool Reader<_Traits, _Alloc>::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<_Traits, _Alloc> v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value<_Traits, _Alloc> v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value<_Traits, _Alloc> 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<_Traits, _Alloc> v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + } // Else, fall through... + break; + 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; +} + +template +void Reader<_Traits, _Alloc>::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +template +bool Reader<_Traits, _Alloc>::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; +} + +template +void Reader<_Traits, _Alloc>::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +template +bool Reader<_Traits, _Alloc>::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; +} + +template +bool Reader<_Traits, _Alloc>::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; +} + +template +std::basic_string Reader<_Traits, _Alloc>::normalizeEOL(typename Reader<_Traits, _Alloc>::Location begin, typename Reader<_Traits, _Alloc>::Location end) { + std::basic_string normalized; + normalized.reserve(end - begin); + Reader<_Traits, _Alloc>::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; +} + +template +void +Reader<_Traits, _Alloc>::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const std::basic_string& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +template +bool Reader<_Traits, _Alloc>::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +template +bool Reader<_Traits, _Alloc>::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; +} + +template +void Reader<_Traits, _Alloc>::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; + } +} + +template +bool Reader<_Traits, _Alloc>::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +template +bool Reader<_Traits, _Alloc>::readObject(Token& tokenStart) { + Token tokenName; + std::basic_string name; + Value<_Traits, _Alloc> 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<_Traits, _Alloc> 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<_Traits, _Alloc>& 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); +} + +template +bool Reader<_Traits, _Alloc>::readArray(Token& tokenStart) { + Value<_Traits, _Alloc> 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<_Traits, _Alloc>& 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; +} + +template +bool Reader<_Traits, _Alloc>::decodeNumber(Token& token) { + Value<_Traits, _Alloc> decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +template +bool Reader<_Traits, _Alloc>::decodeNumber(Token& token, Value<_Traits, _Alloc>& 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. + typename Value<_Traits, _Alloc>::LargestUInt maxIntegerValue = + isNegative ? typename Value<_Traits, _Alloc>::LargestUInt(Value<_Traits, _Alloc>::maxLargestInt) + 1 + : Value<_Traits, _Alloc>::maxLargestUInt; + typename Value<_Traits, _Alloc>::LargestUInt threshold = maxIntegerValue / 10; + typename Value<_Traits, _Alloc>::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + typename Value<_Traits, _Alloc>::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<_Traits, _Alloc>::minLargestInt; + else if (isNegative) + decoded = - typename Value<_Traits, _Alloc>::LargestInt(value); + else if (value <= typename Value<_Traits, _Alloc>::LargestUInt(Value<_Traits, _Alloc>::maxInt)) + decoded = typename Value<_Traits, _Alloc>::LargestInt(value); + else + decoded = value; + return true; +} + +template +bool Reader<_Traits, _Alloc>::decodeDouble(Token& token) { + Value<_Traits, _Alloc> decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +template +bool Reader<_Traits, _Alloc>::decodeDouble(Token& token, Value<_Traits, _Alloc>& decoded) { + double value = 0; + std::basic_string buffer(token.start_, token.end_); + std::istringstream is(buffer); + if (!(is >> value)) + return addError("'" + std::basic_string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +template +bool Reader<_Traits, _Alloc>::decodeString(Token& token) { + std::basic_string decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value<_Traits, _Alloc> decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +template +bool Reader<_Traits, _Alloc>::decodeString(Token& token, std::basic_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 += WriterUtils::codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +template +bool Reader<_Traits, _Alloc>::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; +} + +template +bool Reader<_Traits, _Alloc>::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; +} + +template +bool +Reader<_Traits, _Alloc>::addError(const std::basic_string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +template +bool Reader<_Traits, _Alloc>::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; +} + +template +bool Reader<_Traits, _Alloc>::addErrorAndRecover(const std::basic_string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +template +Value<_Traits, _Alloc>& Reader<_Traits, _Alloc>::currentValue() { return *(nodes_.top()); } + +template +typename Reader<_Traits, _Alloc>::Char Reader<_Traits, _Alloc>::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +template +void Reader<_Traits, _Alloc>::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; +} + +template +std::basic_string Reader<_Traits, _Alloc>::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 +template +std::basic_string Reader<_Traits, _Alloc>::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +template +std::basic_string Reader<_Traits, _Alloc>::getFormattedErrorMessages() const { + std::basic_string formattedMessage; + for (typename 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; +} + +template +std::vector::StructuredError> Reader<_Traits, _Alloc>::getStructuredErrors() const { + std::vector::StructuredError> allErrors; + for (typename Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + Reader<_Traits, _Alloc>::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; +} + +template +bool Reader<_Traits, _Alloc>::pushError(const Value<_Traits, _Alloc>& value, const std::basic_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; +} + +template +bool Reader<_Traits, _Alloc>::pushError(const Value<_Traits, _Alloc>& value, const std::basic_string& message, const Value<_Traits, _Alloc>& 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; +} + +template +bool Reader<_Traits, _Alloc>::good() const { + return !errors_.size(); +} + +// complete copy of Read impl, for OurReader<_Traits, _Alloc> + +template +OurReader<_Traits, _Alloc>::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), + stackDepth_(0), + features_(features), collectComments_() { +} + +template +bool OurReader<_Traits, _Alloc>::parse(const char* beginDoc, + const char* endDoc, + Value<_Traits, _Alloc>& 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; +} + +template +bool OurReader<_Traits, _Alloc>::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<_Traits, _Alloc> v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value<_Traits, _Alloc> v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value<_Traits, _Alloc> v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNaN: + { + Value<_Traits, _Alloc> v(std::numeric_limits::quiet_NaN()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenPosInf: + { + Value<_Traits, _Alloc> v(std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNegInf: + { + Value<_Traits, _Alloc> 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<_Traits, _Alloc> 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; +} + +template +void OurReader<_Traits, _Alloc>::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +template +bool OurReader<_Traits, _Alloc>::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; +} + +template +void OurReader<_Traits, _Alloc>::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +template +bool OurReader<_Traits, _Alloc>::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; +} + +template +bool OurReader<_Traits, _Alloc>::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_ && !Reader<_Traits, _Alloc>::containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !Reader<_Traits, _Alloc>::containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +template +void +OurReader<_Traits, _Alloc>::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const std::basic_string& normalized = Reader<_Traits, _Alloc>::normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +template +bool OurReader<_Traits, _Alloc>::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +template +bool OurReader<_Traits, _Alloc>::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; +} + +template +bool OurReader<_Traits, _Alloc>::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; +} +template +bool OurReader<_Traits, _Alloc>::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + + +template +bool OurReader<_Traits, _Alloc>::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +template +bool OurReader<_Traits, _Alloc>::readObject(Token& tokenStart) { + Token tokenName; + std::basic_string name; + Value<_Traits, _Alloc> 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<_Traits, _Alloc> 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::basic_string msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover( + msg, tokenName, tokenObjectEnd); + } + Value<_Traits, _Alloc>& 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); +} + +template +bool OurReader<_Traits, _Alloc>::readArray(Token& tokenStart) { + Value<_Traits, _Alloc> 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<_Traits, _Alloc>& 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; +} + +template +bool OurReader<_Traits, _Alloc>::decodeNumber(Token& token) { + Value<_Traits, _Alloc> decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +template +bool OurReader<_Traits, _Alloc>::decodeNumber(Token& token, Value<_Traits, _Alloc>& 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. + typename Value<_Traits, _Alloc>::LargestUInt maxIntegerValue = + isNegative ? typename Value<_Traits, _Alloc>::LargestUInt(-Value<_Traits, _Alloc>::minLargestInt) + : Value<_Traits, _Alloc>::maxLargestUInt; + typename Value<_Traits, _Alloc>::LargestUInt threshold = maxIntegerValue / 10; + typename Value<_Traits, _Alloc>::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + typename Value<_Traits, _Alloc>::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 = - typename Value<_Traits, _Alloc>::LargestInt(value); + else if (value <= typename Value<_Traits, _Alloc>::LargestUInt(Value<_Traits, _Alloc>::maxInt)) + decoded = typename Value<_Traits, _Alloc>::LargestInt(value); + else + decoded = value; + return true; +} + +template +bool OurReader<_Traits, _Alloc>::decodeDouble(Token& token) { + Value<_Traits, _Alloc> decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +template +bool OurReader<_Traits, _Alloc>::decodeDouble(Token& token, Value<_Traits, _Alloc>& 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::basic_string buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError("'" + std::basic_string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +template +bool OurReader<_Traits, _Alloc>::decodeString(Token& token) { + std::basic_string decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value<_Traits, _Alloc> decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +template +bool OurReader<_Traits, _Alloc>::decodeString(Token& token, std::basic_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 += WriterUtils::codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +template +bool OurReader<_Traits, _Alloc>::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; +} + +template +bool OurReader<_Traits, _Alloc>::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; +} + +template +bool +OurReader<_Traits, _Alloc>::addError(const std::basic_string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +template +bool OurReader<_Traits, _Alloc>::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; +} + +template +bool OurReader<_Traits, _Alloc>::addErrorAndRecover(const std::basic_string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +template +Value<_Traits, _Alloc>& OurReader<_Traits, _Alloc>::currentValue() { return *(nodes_.top()); } + +template +typename OurReader<_Traits, _Alloc>::Char OurReader<_Traits, _Alloc>::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +template +void OurReader<_Traits, _Alloc>::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; +} + +template +std::basic_string OurReader<_Traits, _Alloc>::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; +} + +template +std::basic_string OurReader<_Traits, _Alloc>::getFormattedErrorMessages() const { + std::basic_string formattedMessage; + for (typename 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; +} + +template +std::vector::StructuredError> OurReader<_Traits, _Alloc>::getStructuredErrors() const { + std::vector::StructuredError> allErrors; + for (typename Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + OurReader<_Traits, _Alloc>::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; +} + +template +bool OurReader<_Traits, _Alloc>::pushError(const Value<_Traits, _Alloc>& value, const std::basic_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; +} + +template +bool OurReader<_Traits, _Alloc>::pushError(const Value<_Traits, _Alloc>& value, const std::basic_string& message, const Value<_Traits, _Alloc>& 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; +} + +template +bool OurReader<_Traits, _Alloc>::good() const { + return !errors_.size(); +} + +template +CharReaderBuilder<_Traits, _Alloc>::CharReaderBuilder() +{ + setDefaults(&settings_); +} +template +CharReaderBuilder<_Traits, _Alloc>::~CharReaderBuilder() +{} +template +CharReader<_Traits, _Alloc>* CharReaderBuilder<_Traits, _Alloc>::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<_Traits, _Alloc>(collectComments, features); +} +template +void CharReaderBuilder<_Traits, _Alloc>::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"); +} +template +bool CharReaderBuilder<_Traits, _Alloc>::validate(Json::Value<_Traits, _Alloc>* invalid) const +{ + Json::Value<_Traits, _Alloc> my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value<_Traits, _Alloc>& inv = *invalid; + std::set> valid_keys; + getValidReaderKeys(&valid_keys); + typename Value<_Traits, _Alloc>::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::basic_string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +template +Value<_Traits, _Alloc>& CharReaderBuilder<_Traits, _Alloc>::operator[](std::basic_string key) +{ + return settings_[key]; +} +// static +template +void CharReaderBuilder<_Traits, _Alloc>::strictMode(Json::Value<_Traits, _Alloc>* 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 +template +void CharReaderBuilder<_Traits, _Alloc>::setDefaults(Json::Value<_Traits, _Alloc>* 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 /** Consume entire stream and use its begin/end. * Someday we might have a real StreamReader, but for now this * is convenient. */ -bool JSON_API parseFromStream( - CharReader::Factory const&, - std::istream&, - Value* root, std::string* errs); +template +JSON_API bool parseFromStream( + typename CharReader<_Traits, _Alloc>::Factory const& fact, std::istream& sin, + Value<_Traits, _Alloc>* root, std::basic_string* errs) +{ + std::ostringstream ssin; + ssin << sin.rdbuf(); + std::basic_string doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +using CharReaderPtr = std::unique_ptr>; +#else +using CharReaderPtr = std::auto_ptr>; +#endif + + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} /** \brief Read from 'sin' into 'root'. @@ -376,7 +1903,7 @@ bool JSON_API parseFromStream( This can be used to read a file into a particular sub-object. For example: \code - Json::Value root; + Json::Value<_Traits, _Alloc> root; cin >> root["dir"]["file"]; cout << root; \endcode @@ -393,7 +1920,20 @@ bool JSON_API parseFromStream( \throw std::exception on parse error. \see Json::operator<<() */ -JSON_API std::istream& operator>>(std::istream&, Value&); +template +JSON_API std::istream& operator>>(std::istream& sin, Value<_Traits, _Alloc>& root) { + CharReaderBuilder<_Traits, _Alloc> b; + std::basic_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/include/json/reader_declaration.h b/include/json/reader_declaration.h new file mode 100644 index 000000000..a3552be3c --- /dev/null +++ b/include/json/reader_declaration.h @@ -0,0 +1,579 @@ +// Copyright 2007-2010 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 + +#ifndef CPPTL_JSON_READER_DECL_H_INCLUDED +#define CPPTL_JSON_READER_DECL_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value_declaration.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +/** \brief Unserialize a JSON document into a + *Value<_Traits, _Alloc>. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ +template +class JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief An error tagged with where in the JSON text it was encountered. + * + * The offsets give the [start, limit) range of bytes within the text. Note + * that this is bytes, not codepoints. + * + */ + struct StructuredError { + size_t offset_start; + size_t offset_limit; + std::basic_string message; + }; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features& features); + + /** \brief Read a Value<_Traits, _Alloc> from a JSON + * document. + * \param document UTF-8 encoded string containing 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 std::basic_string& document, Value<_Traits, _Alloc>& root, bool collectComments = true); + + /** \brief Read a Value<_Traits, _Alloc> from a JSON + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \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* beginDoc, + const char* endDoc, + Value<_Traits, _Alloc>& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value<_Traits, _Alloc>&). + bool parse(std::istream& is, Value<_Traits, _Alloc>& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + std::basic_string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + std::basic_string getFormattedErrorMessages() const; + + /** \brief Returns a vector of structured erros encounted while parsing. + * \return A (possibly empty) vector of StructuredError objects. Currently + * only one error can be returned, but the caller should tolerate + * multiple + * errors. This can occur if the parser recovers from a non-fatal + * parse error and then encounters additional errors. + */ + std::vector getStructuredErrors() const; + + /** \brief Add a semantic error message. + * \param value JSON Value<_Traits, _Alloc> location associated with the error + * \param message The error message. + * \return \c true if the error was successfully added, \c false if the + * Value<_Traits, _Alloc> offset exceeds the document size. + */ + bool pushError(const Value<_Traits, _Alloc>& value, const std::basic_string& message); + + /** \brief Add a semantic error message with extra context. + * \param value JSON Value<_Traits, _Alloc> location associated with the error + * \param message The error message. + * \param extra Additional JSON Value<_Traits, _Alloc> location to contextualize the error + * \return \c true if the error was successfully added, \c false if either + * Value<_Traits, _Alloc> offset exceeds the document size. + */ + bool pushError(const Value<_Traits, _Alloc>& value, const std::basic_string& message, const Value<_Traits, _Alloc>& extra); + + /** \brief Return whether there are any errors. + * \return \c true if there are no errors to report \c false if + * errors have occurred. + */ + bool good() const; + + static bool containsNewLine(typename Reader<_Traits, _Alloc>::Location begin, typename Reader<_Traits, _Alloc>::Location end); + static std::basic_string normalizeEOL(typename Reader<_Traits, _Alloc>::Location begin, typename Reader<_Traits, _Alloc>::Location end); + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::basic_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(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value<_Traits, _Alloc>& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::basic_string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value<_Traits, _Alloc>& 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::basic_string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::basic_string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value<_Traits, _Alloc>& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::basic_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::basic_string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value<_Traits, _Alloc>* lastValue_; + std::basic_string commentsBefore_; + Features features_; + bool collectComments_; + + static int const stackLimit_g; + static int stackDepth_g; // see readValue() +}; // Reader + +/** Interface for reading JSON from a char array. + */ +template +class JSON_API CharReader { +public: + virtual ~CharReader() {} + /** \brief Read a Value<_Traits, _Alloc> from a JSON + document. + * The document must be a UTF-8 encoded string containing the document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param errs [out] Formatted error messages (if not NULL) + * a user friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value<_Traits, _Alloc>* root, std::basic_string* errs) = 0; + + class JSON_API Factory { + public: + virtual ~Factory() {} + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + +Usage: +\code + using namespace Json; + CharReaderBuilder builder; + builder["collectComments"] = false; + Value<_Traits, _Alloc> value; + std::basic_string errs; + bool ok = parseFromStream(builder, std::cin, &value, &errs); +\endcode +*/ +template +class JSON_API CharReaderBuilder : public CharReader<_Traits, _Alloc>::Factory { +public: + // Note: We use a Json::Value<_Traits, _Alloc> so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + These are case-sensitive. + Available settings (case-sensitive): + - `"collectComments": false or true` + - true to collect comment and allow writing them + back during serialization, false to discard comments. + This parameter is ignored if allowComments is false. + - `"allowComments": false or true` + - true if comments are allowed. + - `"strictRoot": false or true` + - true if root must be either an array or an object value + - `"allowDroppedNullPlaceholders": false or true` + - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) + - `"allowNumericKeys": false or true` + - true if numeric object keys are allowed. + - `"allowSingleQuotes": false or true` + - true if '' are allowed for strings (both keys and values) + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. + - This is a security issue (seg-faults caused by deeply nested JSON), + so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. + - `"rejectDupKeys": false or true` + - If true, `parse()` returns false when a key is duplicated within an object. + - `"allowSpecialFloats": false or true` + - If true, special float values (NaNs and infinities) are allowed + and their values are lossfree restorable. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value<_Traits, _Alloc>. + \sa setDefaults() + */ + Json::Value<_Traits, _Alloc> settings_; + + CharReaderBuilder(); + ~CharReaderBuilder() override; + + CharReader<_Traits, _Alloc>* newCharReader() const override; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value<_Traits, _Alloc>* invalid) const; + + /** A simple way to update a specific setting. + */ + Value<_Traits, _Alloc>& operator[](std::basic_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<_Traits, _Alloc>* 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<_Traits, _Alloc>* settings); +private: + static void getValidReaderKeys(std::set>* valid_keys); +}; + +// 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 + + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +template +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + size_t offset_start; + size_t offset_limit; + std::basic_string message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value<_Traits, _Alloc>& root, + bool collectComments = true); + std::basic_string getFormattedErrorMessages() const; + std::vector getStructuredErrors() const; + bool pushError(const Value<_Traits, _Alloc>& value, const std::basic_string& message); + bool pushError(const Value<_Traits, _Alloc>& value, const std::basic_string& message, const Value<_Traits, _Alloc>& 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::basic_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<_Traits, _Alloc>& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::basic_string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value<_Traits, _Alloc>& 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::basic_string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::basic_string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value<_Traits, _Alloc>& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::basic_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::basic_string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value<_Traits, _Alloc>* lastValue_; + std::basic_string commentsBefore_; + int stackDepth_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +template +class OurCharReader : public CharReader<_Traits, _Alloc> { + bool const collectComments_; + OurReader<_Traits, _Alloc> reader_; +public: + OurCharReader( + bool collectComments, + OurFeatures const& features) + : collectComments_(collectComments) + , reader_(features) + {} + bool parse( + char const* beginDoc, char const* endDoc, + Value<_Traits, _Alloc>* root, std::basic_string* errs) override { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +////////////////////////////////// +// global functions + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +template +JSON_API bool parseFromStream( + typename CharReader<_Traits, _Alloc>::Factory const& fact, std::istream& sin, + Value<_Traits, _Alloc>* root, std::basic_string* errs); + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value<_Traits, _Alloc> root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +template +JSON_API std::istream& operator>>(std::istream& sin, Value<_Traits, _Alloc>& root); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_DECL_H_INCLUDED diff --git a/include/json/value.h b/include/json/value.h index 1cfda0774..649108dfe 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -7,838 +7,1945 @@ #define CPPTL_JSON_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) -#include "forwards.h" +#include "value_declaration.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include - -#ifndef JSON_USE_CPPTL_SMALLMAP -#include -#else -#include -#endif -#ifdef JSON_USE_CPPTL -#include -#endif - -// Disable warning C4251: : needs to have dll-interface to -// be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(push) -#pragma warning(disable : 4251) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) /** \brief JSON (JavaScript Object Notation). */ namespace Json { -/** Base class for all exceptions we throw. - * - * We use nothing but these internally. Of course, STL can throw others. - */ -class JSON_API Exception : public std::exception { -public: - Exception(std::string const& msg); - ~Exception() throw() override; - char const* what() const throw() override; -protected: - std::string msg_; -}; - -/** Exceptions which the user cannot easily avoid. - * - * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input - * - * \remark derived from Json::Exception - */ -class JSON_API RuntimeError : public Exception { -public: - RuntimeError(std::string const& msg); -}; - -/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. - * - * These are precondition-violations (user bugs) and internal errors (our bugs). - * - * \remark derived from Json::Exception - */ -class JSON_API LogicError : public Exception { -public: - LogicError(std::string const& msg); -}; +template +const unsigned char ALIGNAS(8) Value<_Traits, _Alloc>::kNull[2048] = {0}; //FIXME sizeof(Value<_Traits, _Alloc>) cannot be determined +template +const Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::null = reinterpret_cast&>(kNull[0]); +template +const Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::nullRef = null; + +template +const Int Value<_Traits, _Alloc>::minInt = Int(~(UInt(-1) / 2)); +template +const Int Value<_Traits, _Alloc>::maxInt = Int(UInt(-1) / 2); +template +const UInt Value<_Traits, _Alloc>::maxUInt = UInt(-1); +#if defined(JSON_HAS_INT64) +template +const Int64 Value<_Traits, _Alloc>::minInt64 = Int64(~(UInt64(-1) / 2)); +template +const Int64 Value<_Traits, _Alloc>::maxInt64 = Int64(UInt64(-1) / 2); +template +const UInt64 Value<_Traits, _Alloc>::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value<_Traits, _Alloc>::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +template +const double Value<_Traits, _Alloc>::maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +template +const LargestInt Value<_Traits, _Alloc>::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +template +const LargestInt Value<_Traits, _Alloc>::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +template +const LargestUInt Value<_Traits, _Alloc>::maxLargestUInt = LargestUInt(-1); + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +template +Value<_Traits, _Alloc>::CommentInfo::CommentInfo() {} + +template +Value<_Traits, _Alloc>::CommentInfo::~CommentInfo() {} + +template +void Value<_Traits, _Alloc>::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { + comment_ = string_data_type(); + } + JSON_ASSERT(text != 0); + JSON_ASSERT_MESSAGE( + text[0] == '\0' || text[0] == '/', + "in Json::Value<_Traits, _Alloc>::setComment(): Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text, len); +} -/// used internally -void throwRuntimeError(std::string const& msg); -/// used internally -void throwLogicError(std::string const& msg); +template +void Value<_Traits, _Alloc>::CommentInfo::setComment(const string_data_type& text, size_t len) { + if (comment_) { + comment_ = string_data_type(); + } + JSON_ASSERT(text); + JSON_ASSERT_MESSAGE( + text->data()[0] == '\0' || text->data()[0] == '/', + "in Json::Value<_Traits, _Alloc>::setComment(): Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text->data(), len); +} -/** \brief Type of the value held by a Value object. - */ -enum ValueType { - nullValue = 0, ///< 'null' value - intValue, ///< signed integer value - uintValue, ///< unsigned integer value - realValue, ///< double value - stringValue, ///< UTF-8 string value - booleanValue, ///< bool value - arrayValue, ///< array value (ordered list) - objectValue ///< object value (collection of name/value pairs). -}; - -enum CommentPlacement { - commentBefore = 0, ///< a comment placed on the line before a value - commentAfterOnSameLine, ///< a comment just after a value on the same line - commentAfter, ///< a comment on the line after a value (only make sense for - /// root value) - numberOfCommentPlacement -}; +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value<_Traits, _Alloc>::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +// Notes: policy_ indicates if the string was allocated when +// a string is stored. + +template +Value<_Traits, _Alloc>::CZString::CZString(ArrayIndex aindex) : cstrNoDup_(nullptr), index_(aindex) {} + +template +Value<_Traits, _Alloc>::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) + : cstrNoDup_(str) { + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = ulength & 0x3FFFFFFF; +} -//# ifdef JSON_USE_CPPTL -// typedef CppTL::AnyEnumerator EnumMemberNames; -// typedef CppTL::AnyEnumerator EnumValues; -//# endif +template +Value<_Traits, _Alloc>::CZString::CZString(const CZString& other) : cstrNoDup_(nullptr) { + switch (other.storage_.policy_) { + case noDuplication: + cstrNoDup_ = other.cstrNoDup_; + break; + case duplicate: + if (other.cstr_) + cstr_ = duplicateStringValue(other.cstr_->data(), other.storage_.length_); + break; + case duplicateOnCopy: + if (other.cstrNoDup_) + cstr_ = duplicateStringValue(other.cstrNoDup_, other.storage_.length_); + break; + } -/** \brief Lightweight wrapper to tag static string. - * - * Value constructor and objectValue member assignement takes advantage of the - * StaticString and avoid the cost of string duplication when storing the - * string or the member name. - * - * Example of usage: - * \code - * Json::Value aValue( StaticString("some text") ); - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ -class JSON_API StaticString { -public: - explicit StaticString(const char* czstring) : c_str_(czstring) {} - - operator const char*() const { return c_str_; } - - const char* c_str() const { return c_str_; } - -private: - const char* c_str_; -}; - -/** \brief Represents a JSON value. - * - * This class is a discriminated union wrapper that can represents a: - * - signed integer [range: Value::minInt - Value::maxInt] - * - unsigned integer (range: 0 - Value::maxUInt) - * - double - * - UTF-8 string - * - boolean - * - 'null' - * - an ordered list of Value - * - collection of name/value pairs (javascript object) - * - * The type of the held value is represented by a #ValueType and - * can be obtained using type(). - * - * Values of an #objectValue or #arrayValue can be accessed using operator[]() - * methods. - * Non-const methods will automatically create the a #nullValue element - * if it does not exist. - * The sequence of an #arrayValue will be automatically resized and initialized - * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. - * - * The get() methods can be used to obtain default value in the case the - * required element does not exist. - * - * It is possible to iterate over the list of a #objectValue values using - * the getMemberNames() method. - * - * \note #Value string-length fit in size_t, but keys must be < 2^30. - * (The reason is an implementation detail.) A #CharReader will raise an - * exception if a bound is exceeded to avoid security holes in your app, - * but the Value API does *not* check bounds. That is the responsibility - * of the caller. + storage_.policy_ = (other.cstr_ + ? (static_cast(other.storage_.policy_) == noDuplication + ? noDuplication : duplicate) + : static_cast(other.storage_.policy_)); + if (other.cstrNoDup_ && storage_.policy_ == duplicateOnCopy) //We've been copied, now the policy is just duplicate. + storage_.policy_ = duplicate; + storage_.length_ = other.storage_.length_; +} + +#if JSON_HAS_RVALUE_REFERENCES +template +Value<_Traits, _Alloc>::CZString::CZString(CZString&& other) + : cstrNoDup_(other.cstrNoDup_), index_(other.index_) { + std::swap(cstr_, other.cstr_); + other.cstrNoDup_ = nullptr; +} +#endif + +template +Value<_Traits, _Alloc>::CZString::~CZString() { + /* Not Used */ +} + +template +void Value<_Traits, _Alloc>::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(cstrNoDup_, other.cstrNoDup_); + std::swap(index_, other.index_); +} + +template +typename Value<_Traits, _Alloc>::CZString& Value<_Traits, _Alloc>::CZString::operator=(CZString other) { + swap(other); + return *this; +} + +template +bool Value<_Traits, _Alloc>::CZString::operator<(const CZString& other) const { + if (storage_.policy_ == duplicate) { + if (!cstr_) return index_ < other.index_; + } else { + if (!cstrNoDup_) 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); + const char* thisData = (storage_.policy_ == duplicate) ? this->cstr_->data() : this->cstrNoDup_; + const char* otherData = (other.storage_.policy_ == duplicate) ? other.cstr_->data() : other.cstrNoDup_; + int comp = memcmp(thisData, otherData, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); +} + +template +bool Value<_Traits, _Alloc>::CZString::operator==(const CZString& other) const { + if (storage_.policy_ == duplicate) { + if (!cstr_) return index_ == other.index_; + } else { + if (!cstrNoDup_) 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; + const char* thisData = (storage_.policy_ == duplicate) ? this->cstr_->data() : this->cstrNoDup_; + const char* otherData = (other.storage_.policy_ == duplicate) ? other.cstr_->data() : other.cstrNoDup_; + int comp = memcmp(thisData, otherData, this_len); + + return comp == 0; +} + +template +ArrayIndex Value<_Traits, _Alloc>::CZString::index() const { return index_; } + +//const char* Value<_Traits, _Alloc>::CZString::c_str() const { return cstr_; } +template +const char* Value<_Traits, _Alloc>::CZString::data() const { + if (storage_.policy_ == duplicate) { + if (!cstr_) + return nullptr; + return cstr_->data(); + } else { + return cstrNoDup_; + } +} + +template +unsigned Value<_Traits, _Alloc>::CZString::length() const { return storage_.length_; } +template +bool Value<_Traits, _Alloc>::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value<_Traits, _Alloc>::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value<_Traits, _Alloc>) ) + * This optimization is used in ValueInternalMap fast allocator. */ -class JSON_API Value { - friend class ValueIteratorBase; -public: - typedef std::vector Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; - typedef Json::UInt UInt; - typedef Json::Int Int; -#if defined(JSON_HAS_INT64) - typedef Json::UInt64 UInt64; - typedef Json::Int64 Int64; -#endif // defined(JSON_HAS_INT64) - typedef Json::LargestInt LargestInt; - typedef Json::LargestUInt LargestUInt; - typedef Json::ArrayIndex ArrayIndex; - - 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 - /// Minimum signed integer value that can be stored in a Json::Value. - static const LargestInt minLargestInt; - /// Maximum signed integer value that can be stored in a Json::Value. - static const LargestInt maxLargestInt; - /// Maximum unsigned integer value that can be stored in a Json::Value. - static const LargestUInt maxLargestUInt; - - /// Minimum signed int value that can be stored in a Json::Value. - static const Int minInt; - /// Maximum signed int value that can be stored in a Json::Value. - static const Int maxInt; - /// Maximum unsigned int value that can be stored in a Json::Value. - static const UInt maxUInt; +template +Value<_Traits, _Alloc>::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_.stringRaw_ = nullptr; + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +template +Value<_Traits, _Alloc>::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} +template +Value<_Traits, _Alloc>::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} #if defined(JSON_HAS_INT64) - /// Minimum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 minInt64; - /// Maximum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 maxInt64; - /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. - static const UInt64 maxUInt64; +template +Value<_Traits, _Alloc>::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +template +Value<_Traits, _Alloc>::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} #endif // defined(JSON_HAS_INT64) -private: -#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - class CZString { - public: - enum DuplicationPolicy { - noDuplication = 0, - duplicate, - duplicateOnCopy - }; - CZString(ArrayIndex index); - CZString(char const* str, unsigned length, DuplicationPolicy allocate); - CZString(CZString const& other); -#if JSON_HAS_RVALUE_REFERENCES - CZString(CZString&& other); -#endif - ~CZString(); - CZString& operator=(CZString other); - bool operator<(CZString const& other) const; - bool operator==(CZString const& other) const; - ArrayIndex index() const; - //const char* c_str() const; ///< \deprecated - char const* data() const; - unsigned length() const; - bool isStaticString() const; - - private: - void swap(CZString& other); - - struct StringStorage { - unsigned policy_: 2; - unsigned length_: 30; // 1GB max - }; - - char const* cstr_; // actually, a prefixed string, unless policy is noDup - union { - ArrayIndex index_; - StringStorage storage_; - }; - }; - -public: -#ifndef JSON_USE_CPPTL_SMALLMAP - typedef std::map ObjectValues; -#else - typedef CppTL::SmallMap ObjectValues; -#endif // ifndef JSON_USE_CPPTL_SMALLMAP -#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - -public: - /** \brief Create a default Value of the given type. - - This is a very useful constructor. - To create an empty array, pass arrayValue. - To create an empty object, pass objectValue. - Another Value can then be set to this one by assignment. -This is useful since clear() and resize() will not alter types. - - Examples: -\code -Json::Value null_value; // null -Json::Value arr_value(Json::arrayValue); // [] -Json::Value obj_value(Json::objectValue); // {} -\endcode - */ - Value(ValueType type = nullValue); - Value(Int value); - Value(UInt value); -#if defined(JSON_HAS_INT64) - Value(Int64 value); - Value(UInt64 value); -#endif // if defined(JSON_HAS_INT64) - Value(double value); - Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) - Value(const char* begin, const char* end); ///< Copy all, incl zeroes. - /** \brief Constructs a value from a static string. - - * Like other value string constructor but do not duplicate the string for - * internal storage. The given string must remain alive after the call to this - * constructor. - * \note This works only for null-terminated strings. (We cannot change the - * size of this class, so we have nowhere to store the length, - * which might be computed later for various operations.) - * - * Example of usage: - * \code - * static StaticString foo("some text"); - * Json::Value aValue(foo); - * \endcode - */ - Value(const StaticString& value); - Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. +template +Value<_Traits, _Alloc>::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +template +Value<_Traits, _Alloc>::Value(const char* value) { + initBasic(stringValue, true); + value_.stringDuplicate_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); +} + +template +Value<_Traits, _Alloc>::Value(const char* beginValue, const char* endValue) { + initBasic(stringValue, true); + value_.stringDuplicate_ = + duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); +} + +template +Value<_Traits, _Alloc>::Value(const std::basic_string::value_type, typename Value<_Traits, _Alloc>::traits_type, typename Value<_Traits, _Alloc>::allocator_type>& value) { + initBasic(stringValue, true); + value_.stringDuplicate_ = + duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); +} + +template +Value<_Traits, _Alloc>::Value(const StaticString& value) { + initBasic(stringValue); + value_.stringRaw_ = const_cast(value.c_str()); +} + #ifdef JSON_USE_CPPTL - Value(const CppTL::ConstString& value); +template +Value<_Traits, _Alloc>::Value(const CppTL::ConstString& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); +} #endif - Value(bool value); - /// Deep copy. - Value(const Value& other); + +template +Value<_Traits, _Alloc>::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +template +Value<_Traits, _Alloc>::Value(Value<_Traits, _Alloc> 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_.stringDuplicate_ && other.allocated_) { + unsigned len; + char const* str; + decodePrefixedString(other.allocated_, other.value_.stringDuplicate_->data(), &len, &str); + value_.stringDuplicate_ = duplicateAndPrefixStringValue(str, len); + allocated_ = true; + } else { + value_.stringRaw_ = other.value_.stringRaw_; + 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_->data())); + } + } +} + #if JSON_HAS_RVALUE_REFERENCES - /// Move constructor - Value(Value&& other); +// Move constructor +template +Value<_Traits, _Alloc>::Value(Value<_Traits, _Alloc>&& other) { + initBasic(nullValue); + swap(other); +} #endif - ~Value(); - - /// Deep copy, then swap(other). - /// \note Over-write existing comments. To preserve comments, use #swapPayload(). - Value& operator=(Value other); - /// Swap everything. - void swap(Value& other); - /// Swap values but leave comments and source offsets in place. - void swapPayload(Value& other); - - ValueType type() const; - - /// Compare payload only, not comments etc. - bool operator<(const Value& other) const; - bool operator<=(const Value& other) const; - bool operator>=(const Value& other) const; - bool operator>(const Value& other) const; - bool operator==(const Value& other) const; - bool operator!=(const Value& other) const; - int compare(const Value& other) const; - - const char* asCString() const; ///< Embedded zeroes could cause you trouble! - std::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.) - */ - bool getString( - char const** begin, char const** end) const; + +template +Value<_Traits, _Alloc>::~Value() { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } + + if (comments_) + delete[] comments_; +} + +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator=(Value<_Traits, _Alloc> other) { + swap(other); + return *this; +} + +template +void Value<_Traits, _Alloc>::swapPayload(Value<_Traits, _Alloc>& 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; +} + +template +void Value<_Traits, _Alloc>::swap(Value<_Traits, _Alloc>& other) { + swapPayload(other); + std::swap(comments_, other.comments_); + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +template +ValueType Value<_Traits, _Alloc>::type() const { return type_; } + +template +int Value<_Traits, _Alloc>::compare(const Value<_Traits, _Alloc>& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +template +bool Value<_Traits, _Alloc>::operator<(const Value<_Traits, _Alloc>& 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 (allocated_) { + if (!(value_.stringDuplicate_) || !(other.value_.stringDuplicate_)) { + if (other.value_.stringDuplicate_) return true; + else return false; + } + } else { + if ((value_.stringRaw_ == nullptr) || (other.value_.stringRaw_ == nullptr)) { + if (other.value_.stringRaw_) return true; + else return false; + } + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + if (allocated_) { + decodePrefixedString(this->allocated_, this->value_.stringDuplicate_->data(), &this_len, &this_str); + } else { + decodePrefixedString(this->allocated_, this->value_.stringRaw_, &this_len, &this_str); + } + if (other.allocated_) { + decodePrefixedString(other.allocated_, other.value_.stringDuplicate_->data(), &other_len, &other_str); + } else { + decodePrefixedString(other.allocated_, other.value_.stringRaw_, &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 +} + +template +bool Value<_Traits, _Alloc>::operator<=(const Value<_Traits, _Alloc>& other) const { return !(other < *this); } + +template +bool Value<_Traits, _Alloc>::operator>=(const Value<_Traits, _Alloc>& other) const { return !(*this < other); } + +template +bool Value<_Traits, _Alloc>::operator>(const Value<_Traits, _Alloc>& other) const { return other < *this; } + +template +bool Value<_Traits, _Alloc>::operator==(const Value<_Traits, _Alloc>& other) const { + // if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value<_Traits, _Alloc>::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 (allocated_) { + if (!(value_.stringDuplicate_) || !(other.value_.stringDuplicate_)) { + return (!(value_.stringDuplicate_) && !(other.value_.stringDuplicate_)); + } + } else { + if ((value_.stringRaw_ == nullptr) || (other.value_.stringRaw_ == nullptr)) { + return value_.stringRaw_ == other.value_.stringRaw_; + } + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + if (allocated_) { + decodePrefixedString(this->allocated_, this->value_.stringDuplicate_->data(), &this_len, &this_str); + } else { + decodePrefixedString(this->allocated_, this->value_.stringRaw_, &this_len, &this_str); + } + if (other.allocated_) { + decodePrefixedString(other.allocated_, other.value_.stringDuplicate_->data(), &other_len, &other_str); + } else { + decodePrefixedString(other.allocated_, other.value_.stringRaw_, &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 +} + +template +bool Value<_Traits, _Alloc>::operator!=(const Value<_Traits, _Alloc>& other) const { return !(*this == other); } + +template +const char* Value<_Traits, _Alloc>::asCString() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value<_Traits, _Alloc>::asCString(): requires stringValue"); + if (allocated_) { + if (!(value_.stringDuplicate_)) { + return nullptr; + } + } else { + if (value_.stringRaw_ == nullptr) { + return nullptr; + } + } + unsigned this_len; + char const* this_str; + if (allocated_) { + decodePrefixedString(this->allocated_, this->value_.stringDuplicate_->data(), &this_len, &this_str); + } else { + decodePrefixedString(this->allocated_, this->value_.stringRaw_, &this_len, &this_str); + } + return this_str; +} + +template +bool Value<_Traits, _Alloc>::getString(char const** str, char const** cend) const { + if (type_ != stringValue) return false; + if (allocated_) { + if (!(value_.stringDuplicate_)) return false; + } else { + if (value_.stringRaw_ == nullptr) return false; + } + unsigned length; + if (allocated_) { + decodePrefixedString(this->allocated_, this->value_.stringDuplicate_->data(), &length, str); + } else { + decodePrefixedString(this->allocated_, this->value_.stringRaw_, &length, str); + } + *cend = *str + length; + return true; +} + +template +typename Value<_Traits, _Alloc>::string_type Value<_Traits, _Alloc>::asString() const { + switch (type_) { + case nullValue: + return string_type(); + case stringValue: + { + if (allocated_) { + if (!(value_.stringDuplicate_)) return string_type(); + } else { + if (value_.stringRaw_ == nullptr) return string_type(); + } + unsigned this_len; + char const* this_str; + if (allocated_) { + decodePrefixedString(this->allocated_, this->value_.stringDuplicate_->data(), &this_len, &this_str); + } else { + decodePrefixedString(this->allocated_, this->value_.stringRaw_, &this_len, &this_str); + } + return string_type(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString<_Traits, _Alloc>(value_.int_); + case uintValue: + return valueToString<_Traits, _Alloc>(value_.uint_); + case realValue: + return valueToString<_Traits, _Alloc>(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + #ifdef JSON_USE_CPPTL - CppTL::ConstString asConstString() const; +CppTL::ConstString Value<_Traits, _Alloc>::asConstString() const { + unsigned len; + char const* str; + decodePrefixedString(allocated_, value_.string_, + &len, &str); + return CppTL::ConstString(str, len); +} #endif - Int asInt() const; - UInt asUInt() const; + +template +typename Value<_Traits, _Alloc>::Int Value<_Traits, _Alloc>::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<_Traits, _Alloc> is not convertible to Int."); +} + +template +typename Value<_Traits, _Alloc>::UInt Value<_Traits, _Alloc>::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<_Traits, _Alloc> is not convertible to UInt."); +} + #if defined(JSON_HAS_INT64) - Int64 asInt64() const; - UInt64 asUInt64() const; + +template +typename Value<_Traits, _Alloc>::Int64 Value<_Traits, _Alloc>::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<_Traits, _Alloc> is not convertible to Int64."); +} + +template +typename Value<_Traits, _Alloc>::UInt64 Value<_Traits, _Alloc>::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<_Traits, _Alloc> is not convertible to UInt64."); +} #endif // if defined(JSON_HAS_INT64) - LargestInt asLargestInt() const; - LargestUInt asLargestUInt() const; - float asFloat() const; - double asDouble() const; - bool asBool() const; - - bool isNull() const; - bool isBool() const; - bool isInt() const; - bool isInt64() const; - bool isUInt() const; - bool isUInt64() const; - bool isIntegral() const; - bool isDouble() const; - bool isNumeric() const; - bool isString() const; - bool isArray() const; - bool isObject() const; - - bool isConvertibleTo(ValueType other) const; - - /// Number of values in array or object - ArrayIndex size() const; - - /// \brief Return true if empty array, empty object, or null; - /// otherwise, false. - bool empty() const; - - /// Return isNull() - bool operator!() const; - - /// Remove all object members and array elements. - /// \pre type() is arrayValue, objectValue, or nullValue - /// \post type() is unchanged - void clear(); - - /// Resize the array to size elements. - /// New elements are initialized to null. - /// May only be called on nullValue or arrayValue. - /// \pre type() is arrayValue or nullValue - /// \post type() is arrayValue - void resize(ArrayIndex size); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value& operator[](ArrayIndex index); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value& operator[](int index); - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value& operator[](ArrayIndex index) const; - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value& operator[](int index) const; - - /// If the array contains at least index+1 elements, returns the element - /// value, - /// otherwise returns defaultValue. - Value get(ArrayIndex index, const Value& defaultValue) const; - /// Return true if index < size(). - bool isValidIndex(ArrayIndex index) const; - /// \brief Append value to array at the end. - /// - /// Equivalent to jsonvalue[jsonvalue.size()] = value; - Value& append(const Value& value); - - /// Access an object value by name, create a null member if it does not exist. - /// \note Because of our implementation, keys are limited to 2^30 -1 chars. - /// Exceeding that will cause an exception. - Value& operator[](const char* key); - /// Access an object value by name, returns null if there is no member with - /// that name. - 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); - /// 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; - /** \brief Access an object value by name, create a null member if it does not - exist. - - * If the object has no entry for that name, then the member name used to store - * the new entry is not duplicated. - * Example of use: - * \code - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ - Value& operator[](const StaticString& key); + +template +LargestInt Value<_Traits, _Alloc>::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +template +LargestUInt Value<_Traits, _Alloc>::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +template +double Value<_Traits, _Alloc>::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<_Traits, _Alloc> is not convertible to double."); +} + +template +float Value<_Traits, _Alloc>::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<_Traits, _Alloc> is not convertible to float."); +} + +template +bool Value<_Traits, _Alloc>::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<_Traits, _Alloc> is not convertible to bool."); +} + +template +bool Value<_Traits, _Alloc>::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 +template +ArrayIndex Value<_Traits, _Alloc>::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()) { + typename 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; +} + +template +bool Value<_Traits, _Alloc>::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +template +bool Value<_Traits, _Alloc>::operator!() const { return isNull(); } + +template +void Value<_Traits, _Alloc>::clear() { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || + type_ == objectValue, + "in Json::Value<_Traits, _Alloc>::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type_) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +template +void Value<_Traits, _Alloc>::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + "in Json::Value<_Traits, _Alloc>::resize(): requires arrayValue"); + if (type_ == nullValue) + *this = Value<_Traits, _Alloc>(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); + } +} + +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value<_Traits, _Alloc>::operator[](ArrayIndex): requires arrayValue"); + if (type_ == nullValue) + *this = Value<_Traits, _Alloc>(arrayValue); + CZString key(index); + typename ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + typename ObjectValues::value_type defaultValue(key, nullRef); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value<_Traits, _Alloc>::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +template +const Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value<_Traits, _Alloc>::operator[](ArrayIndex)const: requires arrayValue"); + if (type_ == nullValue) + return nullRef; + CZString key(index); + typename ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullRef; + return (*it).second; +} + +template +const Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value<_Traits, _Alloc>::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +template +void Value<_Traits, _Alloc>::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. +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::resolveReference(const char* key) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value<_Traits, _Alloc>::resolveReference(): requires objectValue"); + if (type_ == nullValue) + *this = Value<_Traits, _Alloc>(objectValue); + CZString actualKey( + key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! + typename ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + typename ObjectValues::value_type defaultValue(actualKey, nullRef); + it = value_.map_->insert(it, defaultValue); + Value<_Traits, _Alloc>& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::resolveReference(char const* key, char const* cend) +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value<_Traits, _Alloc>::resolveReference(key, end): requires objectValue"); + if (type_ == nullValue) + *this = Value<_Traits, _Alloc>(objectValue); + CZString actualKey( + key, static_cast(cend-key), CZString::duplicateOnCopy); + typename ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + typename ObjectValues::value_type defaultValue(actualKey, nullRef); + it = value_.map_->insert(it, defaultValue); + Value<_Traits, _Alloc>& value = (*it).second; + return value; +} + +template +Value<_Traits, _Alloc> Value<_Traits, _Alloc>::get(ArrayIndex index, const Value<_Traits, _Alloc>& defaultValue) const { + const Value<_Traits, _Alloc>* value = &((*this)[index]); + return value == &nullRef ? defaultValue : *value; +} + +template +bool Value<_Traits, _Alloc>::isValidIndex(ArrayIndex index) const { return index < size(); } + +template +Value<_Traits, _Alloc> const* Value<_Traits, _Alloc>::find(char const* key, char const* cend) const +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value<_Traits, _Alloc>::find(key, end, found): requires objectValue or nullValue"); + if (type_ == nullValue) return NULL; + CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); + typename ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) return NULL; + return &(*it).second; +} +template +const Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator[](const char* key) const +{ + Value<_Traits, _Alloc> const* found = find(key, key + strlen(key)); + if (!found) return nullRef; + return *found; +} +template +Value<_Traits, _Alloc> const& Value<_Traits, _Alloc>::operator[](typename Value<_Traits, _Alloc>::string_type const& key) const +{ + Value<_Traits, _Alloc> const* found = find(key.data(), key.data() + key.length()); + if (!found) return nullRef; + return *found; +} + +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator[](const typename Value<_Traits, _Alloc>::string_type& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator[](const StaticString& key) { + return resolveReference(key.c_str()); +} + #ifdef JSON_USE_CPPTL - /// Access an object value by name, create a null member if it does not exist. - Value& operator[](const CppTL::ConstString& key); - /// Access an object value by name, returns null if there is no member with - /// that name. - const Value& operator[](const CppTL::ConstString& key) const; +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::operator[](const CppTL::ConstString& key) { + return resolveReference(key.c_str(), key.end_c_str()); +} +template +Value<_Traits, _Alloc> const& Value<_Traits, _Alloc>::operator[](CppTL::ConstString const& key) const +{ + Value<_Traits, _Alloc> const* found = find(key.c_str(), key.end_c_str()); + if (!found) return nullRef; + return *found; +} #endif - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - Value get(const char* key, const Value& defaultValue) const; - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - /// \note key may contain embedded nulls. - Value get(const char* begin, const char* end, const Value& defaultValue) const; - /// 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; + +template +Value<_Traits, _Alloc>& Value<_Traits, _Alloc>::append(const Value<_Traits, _Alloc>& value) { return (*this)[size()] = value; } + +template +Value<_Traits, _Alloc> Value<_Traits, _Alloc>::get(char const* key, char const* cend, Value<_Traits, _Alloc> const& defaultValue) const +{ + Value<_Traits, _Alloc> const* found = find(key, cend); + return !found ? defaultValue : *found; +} +template +Value<_Traits, _Alloc> Value<_Traits, _Alloc>::get(char const* key, Value<_Traits, _Alloc> const& defaultValue) const +{ + return get(key, key + strlen(key), defaultValue); +} +template +Value<_Traits, _Alloc> Value<_Traits, _Alloc>::get(typename Value<_Traits, _Alloc>::string_type const& key, Value<_Traits, _Alloc> const& defaultValue) const +{ + return get(key.data(), key.data() + key.length(), defaultValue); +} + + +template +bool Value<_Traits, _Alloc>::removeMember(const char* key, const char* cend, Value<_Traits, _Alloc>* removed) +{ + if (type_ != objectValue) { + return false; + } + CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); + 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 +bool Value<_Traits, _Alloc>::removeMember(const char* key, Value<_Traits, _Alloc>* removed) +{ + return removeMember(key, key + strlen(key), removed); +} +template +bool Value<_Traits, _Alloc>::removeMember(typename Value<_Traits, _Alloc>::string_type const& key, Value<_Traits, _Alloc>* removed) +{ + return removeMember(key.data(), key.data() + key.length(), removed); +} +template +Value<_Traits, _Alloc> Value<_Traits, _Alloc>::removeMember(const char* key) +{ + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value<_Traits, _Alloc>::removeMember(): requires objectValue"); + if (type_ == nullValue) + return nullRef; + + Value<_Traits, _Alloc> removed; // null + removeMember(key, key + strlen(key), &removed); + return removed; // still null if removeMember() did nothing +} +template +Value<_Traits, _Alloc> Value<_Traits, _Alloc>::removeMember(const typename Value<_Traits, _Alloc>::string_type& key) +{ + return removeMember(key.c_str()); +} + +template +bool Value<_Traits, _Alloc>::removeIndex(ArrayIndex index, Value<_Traits, _Alloc>* removed) { + if (type_ != arrayValue) { + return false; + } + CZString key(index); + typename 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); + typename ObjectValues::iterator itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + #ifdef JSON_USE_CPPTL - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +template +Value<_Traits, _Alloc> Value<_Traits, _Alloc>::get(const CppTL::ConstString& key, + const Value<_Traits, _Alloc>& defaultValue) const { + return get(key.c_str(), key.end_c_str(), defaultValue); +} #endif - /// Most general and efficient version of isMember()const, get()const, - /// and operator[]const - /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 - Value const* find(char const* begin, char const* end) const; - /// Most general and efficient version of object-mutators. - /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 - /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. - Value const* demand(char const* begin, char const* end); - /// \brief Remove and return the named member. - /// - /// Do nothing if it did not exist. - /// \return the removed Value, or null. - /// \pre type() is objectValue or nullValue - /// \post type() is unchanged - /// \deprecated - Value removeMember(const char* key); - /// Same as removeMember(const char*) - /// \param key may contain embedded nulls. - /// \deprecated - Value removeMember(const std::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); - /** \brief Remove the named map member. - - Update 'removed' iff removed. - \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(const char* begin, const char* end, Value* removed); - /** \brief Remove the indexed array element. - - O(n) expensive operations. - Update 'removed' iff removed. - \return true iff removed (no exceptions) - */ - bool removeIndex(ArrayIndex i, Value* removed); - - /// Return true if the object has a member named key. - /// \note 'key' must be null-terminated. - 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 char* begin, const char* end) const; + +template +bool Value<_Traits, _Alloc>::isMember(char const* key, char const* cend) const +{ + Value<_Traits, _Alloc> const* value = find(key, cend); + return NULL != value; +} +template +bool Value<_Traits, _Alloc>::isMember(char const* key) const +{ + return isMember(key, key + strlen(key)); +} +template +bool Value<_Traits, _Alloc>::isMember(typename Value<_Traits, _Alloc>::string_type const& key) const +{ + return isMember(key.data(), key.data() + key.length()); +} + #ifdef JSON_USE_CPPTL - /// Return true if the object has a member named key. - bool isMember(const CppTL::ConstString& key) const; +template +bool Value<_Traits, _Alloc>::isMember(const CppTL::ConstString& key) const { + return isMember(key.c_str(), key.end_c_str()); +} #endif - /// \brief Return a list of the member names. - /// - /// If null, return an empty list. - /// \pre type() is objectValue or nullValue - /// \post if type() was nullValue, it remains nullValue - Members getMemberNames() const; - - //# ifdef JSON_USE_CPPTL - // EnumMemberNames enumMemberNames() const; - // EnumValues enumValues() const; - //# endif - - /// \deprecated Always pass len. - JSONCPP_DEPRECATED("Use setComment(std::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); - bool hasComment(CommentPlacement placement) const; - /// Include delimiters and embedded newlines. - std::string getComment(CommentPlacement placement) const; - - std::string toStyledString() const; - - const_iterator begin() const; - const_iterator end() const; - - iterator begin(); - iterator end(); - - // Accessors for the [start, limit) range of bytes within the JSON text from - // which this value was parsed, if any. - void setOffsetStart(size_t start); - void setOffsetLimit(size_t limit); - size_t getOffsetStart() const; - size_t getOffsetLimit() const; - -private: - void initBasic(ValueType type, bool allocated = false); - - Value& resolveReference(const char* key); - Value& resolveReference(const char* key, const char* end); - - struct CommentInfo { - CommentInfo(); - ~CommentInfo(); - - void setComment(const char* text, size_t len); - - char* comment_; - }; - - // struct MemberNamesTransform - //{ - // typedef const char *result_type; - // const char *operator()( const CZString &name ) const - // { - // return name.c_str(); - // } - //}; - - union ValueHolder { - LargestInt int_; - LargestUInt uint_; - double real_; - bool bool_; - char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ - ObjectValues* map_; - } value_; - ValueType type_ : 8; - unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. - // If not allocated_, string_ must be null-terminated. - CommentInfo* comments_; - - // [start, limit) byte offsets in the source JSON text from which this Value - // was extracted. - size_t start_; - size_t limit_; -}; - -/** \brief Experimental and untested: represents an element of the "path" to - * access a node. - */ -class JSON_API PathArgument { -public: - friend class Path; - - PathArgument(); - PathArgument(ArrayIndex index); - PathArgument(const char* key); - PathArgument(const std::string& key); - -private: - enum Kind { - kindNone = 0, - kindIndex, - kindKey - }; - std::string key_; - ArrayIndex index_; - Kind kind_; -}; - -/** \brief Experimental and untested: represents a "path" to access a node. - * - * Syntax: - * - "." => root node - * - ".[n]" => elements at index 'n' of root node (an array value) - * - ".name" => member named 'name' of root node (an object value) - * - ".name1.name2.name3" - * - ".[0][1][2].name1[3]" - * - ".%" => member name is provided as parameter - * - ".[%]" => index is provied as parameter - */ -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; - /// Creates the "path" to access the specified node and returns a reference on - /// the node. - Value& make(Value& root) const; - -private: - 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); - void invalidPath(const std::string& path, int location); - - Args args_; -}; - -/** \brief base class for Value iterators. - * - */ -class JSON_API ValueIteratorBase { -public: - typedef std::bidirectional_iterator_tag iterator_category; - typedef unsigned int size_t; - typedef int difference_type; - typedef ValueIteratorBase SelfType; +template +typename Value<_Traits, _Alloc>::Members Value<_Traits, _Alloc>::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value<_Traits, _Alloc>::getMemberNames(), value must be objectValue"); + if (type_ == nullValue) + return Value<_Traits, _Alloc>::Members(); + Members members; + members.reserve(value_.map_->size()); + typename ObjectValues::const_iterator it = value_.map_->begin(); + typename ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) { + members.push_back(string_type((*it).first.data(), + (*it).first.length())); + } + return members; +} +// +//# ifdef JSON_USE_CPPTL +// EnumMemberNames +// Value<_Traits, _Alloc>::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value<_Traits, _Alloc>::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; +} + +template +bool Value<_Traits, _Alloc>::isNull() const { return type_ == nullValue; } + +template +bool Value<_Traits, _Alloc>::isBool() const { return type_ == booleanValue; } + +template +bool Value<_Traits, _Alloc>::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; +} + +template +bool Value<_Traits, _Alloc>::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; +} + +template +bool Value<_Traits, _Alloc>::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; +} + +template +bool Value<_Traits, _Alloc>::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; +} + +template +bool Value<_Traits, _Alloc>::isIntegral() const { +#if defined(JSON_HAS_INT64) + return isInt64() || isUInt64(); +#else + return isInt() || isUInt(); +#endif +} + +template +bool Value<_Traits, _Alloc>::isDouble() const { return type_ == realValue || isIntegral(); } + +template +bool Value<_Traits, _Alloc>::isNumeric() const { return isIntegral() || isDouble(); } - bool operator==(const SelfType& other) const { return isEqual(other); } +template +bool Value<_Traits, _Alloc>::isString() const { return type_ == stringValue; } - bool operator!=(const SelfType& other) const { return !isEqual(other); } +template +bool Value<_Traits, _Alloc>::isArray() const { return type_ == arrayValue; } - difference_type operator-(const SelfType& other) const { - return other.computeDistance(*this); +template +bool Value<_Traits, _Alloc>::isObject() const { return type_ == objectValue; } + +template +void Value<_Traits, _Alloc>::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); +} - /// Return either the index or the member name of the referenced value as a - /// Value. - Value key() const; +template +void Value<_Traits, _Alloc>::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); +} - /// Return the index of the referenced Value, or -1 if it is not an arrayValue. - UInt index() const; +template +void Value<_Traits, _Alloc>::setComment(const string_type& comment, CommentPlacement placement) { + setComment(comment.c_str(), comment.length(), placement); +} - /// 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; +template +bool Value<_Traits, _Alloc>::hasComment(CommentPlacement placement) const { + return comments_ != 0 && comments_[placement].comment_; +} + +template +typename Value<_Traits, _Alloc>::string_type Value<_Traits, _Alloc>::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return Value<_Traits, _Alloc>::string_type(comments_[placement].comment_->begin(), comments_[placement].comment_->end()); + return ""; +} - /// Return the member name of the referenced Value. "" if it is not an - /// objectValue. - /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. - JSONCPP_DEPRECATED("Use `key = name();` instead.") - char const* memberName() const; - /// Return the member name of the referenced Value, or NULL if it is not an - /// objectValue. - /// \note Better version than memberName(). Allows embedded nulls. - char const* memberName(char const** end) const; +template +void Value<_Traits, _Alloc>::setOffsetStart(size_t start) { start_ = start; } -protected: - Value& deref() const; +template +void Value<_Traits, _Alloc>::setOffsetLimit(size_t limit) { limit_ = limit; } - void increment(); +template +size_t Value<_Traits, _Alloc>::getOffsetStart() const { return start_; } - void decrement(); +template +size_t Value<_Traits, _Alloc>::getOffsetLimit() const { return limit_; } - difference_type computeDistance(const SelfType& other) const; +template +typename Value<_Traits, _Alloc>::string_type Value<_Traits, _Alloc>::toStyledString() const { + StyledWriter<_Traits, _Alloc> writer; + return writer.write(*this); +} - bool isEqual(const SelfType& other) const; +template +typename Value<_Traits, _Alloc>::const_iterator Value<_Traits, _Alloc>::begin() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return const_iterator(); +} - void copy(const SelfType& other); +template +typename Value<_Traits, _Alloc>::const_iterator Value<_Traits, _Alloc>::end() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return const_iterator(); +} -private: - Value::ObjectValues::iterator current_; - // Indicates that iterator is for a null value. - bool isNull_; +template +typename Value<_Traits, _Alloc>::iterator Value<_Traits, _Alloc>::begin() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); +} -public: - // For some reason, BORLAND needs these at the end, rather - // than earlier. No idea why. - ValueIteratorBase(); - explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); -}; +template +typename Value<_Traits, _Alloc>::iterator Value<_Traits, _Alloc>::end() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); +} -/** \brief const iterator for object and array value. - * +/** 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. */ -class JSON_API ValueConstIterator : public ValueIteratorBase { - friend class Value; - -public: - typedef const Value value_type; - //typedef unsigned int size_t; - //typedef int difference_type; - typedef const Value& reference; - typedef const Value* pointer; - typedef ValueConstIterator SelfType; - - ValueConstIterator(); - ValueConstIterator(ValueIterator const& other); - -private: -/*! \internal Use by Value to create an iterator. +template +typename Value<_Traits, _Alloc>::string_data_type Value<_Traits, _Alloc>::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; + + Value<_Traits, _Alloc>::string_data_type newString(new string_data_type_in_ptr(value, value + length)); + newString->push_back(0); + return newString; +} + +/* Record the length as a prefix. */ - explicit ValueConstIterator(const Value::ObjectValues::iterator& current); -public: - SelfType& operator=(const ValueIteratorBase& other); +template +typename Value<_Traits, _Alloc>::string_data_type Value<_Traits, _Alloc>::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; + string_data_type newString = string_data_type(new string_data_type_in_ptr()); + try { + newString->reserve(actualLength); + } catch (...) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + newString->data()[actualLength-1] = 0; //Prevent overflow later. + for (unsigned int i=0; ipush_back(reinterpret_cast(&length)[i]); + newString->insert(newString->end(), value, value+length); + newString->push_back(static_cast(0)); + + return newString; +} + +template +void Value<_Traits, _Alloc>::decodePrefixedString( + bool isPrefixed, const char* prefixed, + unsigned* length, char const** value) +{ + if (!isPrefixed) { + *length = static_cast(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast(prefixed); + *value = prefixed + sizeof(unsigned); + } +} + +// class ValueHolder +// ////////////////////////////////////////////////////////////////// + +template +ValueHolder<_Traits, _Alloc>::ValueHolder() { /* Not used */ } //Now required for strings in the union +template +ValueHolder<_Traits, _Alloc>::ValueHolder(const ValueHolder& ref) { + copy(ref); +} +template +ValueHolder<_Traits, _Alloc>::ValueHolder(ValueHolder&& ref) { + swap(std::move(ref)); +} +template +ValueHolder<_Traits, _Alloc>::~ValueHolder() { /* Not used */ } + +template +ValueHolder<_Traits, _Alloc>& ValueHolder<_Traits, _Alloc>::operator=(const ValueHolder<_Traits, _Alloc>& value) { + copy(value); + return *this; +} + +template +ValueHolder<_Traits, _Alloc>& ValueHolder<_Traits, _Alloc>::operator=(ValueHolder<_Traits, _Alloc>&& value) { + swap(std::move(value)); + return *this; +} + +template +void ValueHolder<_Traits, _Alloc>::copy(const ValueHolder& value) { + int_=value.int_; + uint_=value.uint_; + real_=value.real_; + bool_=value.bool_; + stringRaw_=value.stringRaw_; + if (value.stringDuplicate_) { + stringDuplicate_=typename Value<_Traits, _Alloc>::string_data_type(new typename Value<_Traits, _Alloc>::string_data_type_in_ptr(*(value.stringDuplicate_))); + } + map_=value.map_; +} + +template +void ValueHolder<_Traits, _Alloc>::swap(ValueHolder&& value) { + int_=value.int_; + value.int_=0; + uint_=value.uint_; + value.uint_=0; + real_=value.real_; + value.real_=0; + bool_=value.bool_; + value.bool_=false; + stringRaw_=value.stringRaw_; + value.stringRaw_=nullptr; + std::swap(stringDuplicate_, value.stringDuplicate_); + map_=value.map_; + value.map_=nullptr; +} - SelfType operator++(int) { - SelfType temp(*this); - ++*this; - return temp; +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +template +PathArgument<_Traits, _Alloc>::PathArgument() : key_(), index_(), kind_(kindNone) {} + +template +PathArgument<_Traits, _Alloc>::PathArgument(ArrayIndex index) + : key_(), index_(index), kind_(kindIndex) {} + +template +PathArgument<_Traits, _Alloc>::PathArgument(const char* key) + : key_(key), index_(), kind_(kindKey) {} + +template +PathArgument<_Traits, _Alloc>::PathArgument(const std::string& key) + : key_(key.c_str()), index_(), kind_(kindKey) {} + + +// class Path +// ////////////////////////////////////////////////////////////////// + +template +Path<_Traits, _Alloc>::Path(const std::string& path, + const PathArgument<_Traits, _Alloc>& a1, + const PathArgument<_Traits, _Alloc>& a2, + const PathArgument<_Traits, _Alloc>& a3, + const PathArgument<_Traits, _Alloc>& a4, + const PathArgument<_Traits, _Alloc>& 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); +} + +template +void Path<_Traits, _Alloc>::makePath(const std::string& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + typename InArgs::const_iterator itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument<_Traits, _Alloc>::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<_Traits, _Alloc>::kindKey); + ++current; + } else if (*current == '.') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(std::string(beginName, current)); + } + } +} + +template +void Path<_Traits, _Alloc>::addPathInArg(const std::string& /*path*/, + const InArgs& in, + typename InArgs::const_iterator& itInArg, + typename PathArgument<_Traits, _Alloc>::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg); } +} + +template +void Path<_Traits, _Alloc>::invalidPath(const std::string& /*path*/, int /*location*/) { + // Error: invalid path. +} - SelfType operator--(int) { - SelfType temp(*this); - --*this; - return temp; +template +const Value<_Traits, _Alloc>& Path<_Traits, _Alloc>::resolve(const Value<_Traits, _Alloc>& root) const { + const Value<_Traits, _Alloc>* node = &root; + for (typename Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument<_Traits, _Alloc>& arg = *it; + if (arg.kind_ == PathArgument<_Traits, _Alloc>::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<_Traits, _Alloc>::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if (node == &Value<_Traits, _Alloc>::nullRef) { + // Error: unable to resolve path (object has no member named '' at + // position...) + } + } } + return *node; +} - SelfType& operator--() { - decrement(); - return *this; +template +Value<_Traits, _Alloc> Path<_Traits, _Alloc>::resolve(const Value<_Traits, _Alloc>& root, const Value<_Traits, _Alloc>& defaultValue) const { + const Value<_Traits, _Alloc>* node = &root; + for (typename Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument<_Traits, _Alloc>& arg = *it; + if (arg.kind_ == PathArgument<_Traits, _Alloc>::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument<_Traits, _Alloc>::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value<_Traits, _Alloc>::nullRef) + return defaultValue; + } } + return *node; +} - SelfType& operator++() { - increment(); - return *this; +template +Value<_Traits, _Alloc>& Path<_Traits, _Alloc>::make(Value<_Traits, _Alloc>& root) const { + Value<_Traits, _Alloc>* node = &root; + for (typename Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument<_Traits, _Alloc>& arg = *it; + if (arg.kind_ == PathArgument<_Traits, _Alloc>::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument<_Traits, _Alloc>::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } } + return *node; +} - reference operator*() const { return deref(); } +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +template +ValueIteratorBase<_Traits, _Alloc>::ValueIteratorBase() + : current_(), isNull_(true) { +} - pointer operator->() const { return &deref(); } -}; +template +ValueIteratorBase<_Traits, _Alloc>::ValueIteratorBase( + const typename Value<_Traits, _Alloc>::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} -/** \brief Iterator for object and array value. - */ -class JSON_API ValueIterator : public ValueIteratorBase { - friend class Value; - -public: - typedef Value value_type; - typedef unsigned int size_t; - typedef int difference_type; - typedef Value& reference; - typedef Value* pointer; - typedef ValueIterator SelfType; - - ValueIterator(); - explicit ValueIterator(const ValueConstIterator& other); - ValueIterator(const ValueIterator& other); - -private: -/*! \internal Use by Value to create an iterator. - */ - explicit ValueIterator(const Value::ObjectValues::iterator& current); -public: - SelfType& operator=(const SelfType& other); +template +Value<_Traits, _Alloc>& ValueIteratorBase<_Traits, _Alloc>::deref() const { + return current_->second; +} + +template +void ValueIteratorBase<_Traits, _Alloc>::increment() { + ++current_; +} + +template +void ValueIteratorBase<_Traits, _Alloc>::decrement() { + --current_; +} + +template +typename ValueIteratorBase<_Traits, _Alloc>::difference_type +ValueIteratorBase<_Traits, _Alloc>::computeDistance(const SelfType& other) const { +#ifdef JSON_USE_CPPTL_SMALLMAP + return other.current_ - current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } - SelfType operator++(int) { - SelfType temp(*this); - ++*this; - return temp; + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (typename Value<_Traits, _Alloc>::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; } + return myDistance; +#endif +} - SelfType operator--(int) { - SelfType temp(*this); - --*this; - return temp; +template +bool ValueIteratorBase<_Traits, _Alloc>::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; } + return current_ == other.current_; +} - SelfType& operator--() { - decrement(); - return *this; +template +void ValueIteratorBase<_Traits, _Alloc>::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +template +Value<_Traits, _Alloc> ValueIteratorBase<_Traits, _Alloc>::key() const { + const typename Value<_Traits, _Alloc>::CZString czstring = (*current_).first; + if (czstring.data()) { + if (czstring.isStaticString()) + return Value<_Traits, _Alloc>(StaticString(czstring.data())); + return Value<_Traits, _Alloc>(czstring.data(), czstring.data() + czstring.length()); } + return Value<_Traits, _Alloc>(czstring.index()); +} + +template +UInt ValueIteratorBase<_Traits, _Alloc>::index() const { + const typename Value<_Traits, _Alloc>::CZString czstring = (*current_).first; + if (!czstring.data()) + return czstring.index(); + return typename Value<_Traits, _Alloc>::UInt(-1); +} - SelfType& operator++() { - increment(); - return *this; +template +std::string ValueIteratorBase<_Traits, _Alloc>::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) return std::string(); + return std::string(keey, end); +} + +template +char const* ValueIteratorBase<_Traits, _Alloc>::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +template +char const* ValueIteratorBase<_Traits, _Alloc>::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = NULL; + return NULL; } + *end = cname + (*current_).first.length(); + return cname; +} - reference operator*() const { return deref(); } +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +template +ValueConstIterator<_Traits, _Alloc>::ValueConstIterator() {} + +template +ValueConstIterator<_Traits, _Alloc>::ValueConstIterator( + const typename Value<_Traits, _Alloc>::ObjectValues::iterator& current) + : ValueIteratorBase<_Traits, _Alloc>(current) {} + +template +ValueConstIterator<_Traits, _Alloc>::ValueConstIterator(ValueIterator<_Traits, _Alloc> const& other) + : ValueIteratorBase<_Traits, _Alloc>(other) {} + +template +ValueConstIterator<_Traits, _Alloc>& ValueConstIterator<_Traits, _Alloc>:: +operator=(const ValueIteratorBase<_Traits, _Alloc>& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +template +ValueIterator<_Traits, _Alloc>::ValueIterator() {} + +template +ValueIterator<_Traits, _Alloc>::ValueIterator(const typename Value<_Traits, _Alloc>::ObjectValues::iterator& current) + : ValueIteratorBase<_Traits, _Alloc>(current) {} + +template +ValueIterator<_Traits, _Alloc>::ValueIterator(const ValueConstIterator<_Traits, _Alloc>& other) + : ValueIteratorBase<_Traits, _Alloc>(other) { + throwRuntimeError("ConstIterator to Iterator should never be allowed."); +} - pointer operator->() const { return &deref(); } -}; +template +ValueIterator<_Traits, _Alloc>::ValueIterator(const ValueIterator<_Traits, _Alloc>& other) + : ValueIteratorBase<_Traits, _Alloc>(other) {} + +template +ValueIterator<_Traits, _Alloc>& ValueIterator<_Traits, _Alloc>::operator=(const SelfType& other) { + copy(other); + return *this; +} } // namespace Json namespace std { /// Specialize std::swap() for Json::Value. -template<> -inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } +template +inline void swap(Json::Value<_Traits, _Alloc>& a, Json::Value<_Traits, _Alloc>& b) { a.swap(b); } } diff --git a/include/json/value_declaration.h b/include/json/value_declaration.h new file mode 100644 index 000000000..bc35aa83b --- /dev/null +++ b/include/json/value_declaration.h @@ -0,0 +1,943 @@ +// Copyright 2007-2010 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 + +#ifndef CPPTL_JSON_DECL_H_INCLUDED +#define CPPTL_JSON_DECL_H_INCLUDED +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#include +#include +#endif // if !defined(JSON_IS_AMALGAMATION) + +#include +#include +#include +#include +#include +#include +#include // size_t +#include // min() + +#include +#include +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#include +#endif + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#define JSON_ASSERT_UNREACHABLE assert(false) + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +/// used internally +void throwRuntimeError(std::string const& msg); +/// used internally +void throwLogicError(std::string const& msg); + +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(std::string const& msg); + ~Exception() throw() override; + char const* what() const throw() override; +protected: + std::string msg_; +}; + +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(std::string const& msg); +}; + +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(std::string const& msg); +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +template +class ValueHolder; //Forward declaration + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +template +class JSON_API Value { + template + friend class ValueIteratorBase; +public: + typedef _Traits traits_type; + typedef typename _Traits::char_type value_type; + typedef _Alloc allocator_type; + typedef std::basic_string string_type; + typedef std::vector string_data_type_in_ptr; + typedef std::unique_ptr string_data_type; + typedef Value this_type; + + typedef std::vector Members; + typedef ValueIterator<_Traits, _Alloc> iterator; + typedef ValueConstIterator<_Traits, _Alloc> const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const this_type& null; ///< We regret this reference to a global instance; prefer the simpler Value(). + static const this_type& nullRef; ///< just a kludge for binary-compatibility; same as null + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + static const double maxUInt64AsDouble; + +private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); +#if JSON_HAS_RVALUE_REFERENCES + CZString(CZString&& other); +#endif + ~CZString(); + CZString& operator=(CZString other); + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + //const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_: 2; + unsigned length_: 30; // 1GB max + }; + + char const* cstrNoDup_; // actually, a prefixed string, unless policy is noDup + string_data_type cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map> ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, + * which might be computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const string_type& value); ///< Copy data() til size(). Embedded zeroes too. +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + /// Deep copy. + Value(const this_type& other); +#if JSON_HAS_RVALUE_REFERENCES + /// Move constructor + Value(this_type&& other); +#endif + ~Value(); + + /// Deep copy, then swap(other). + /// \note Over-write existing comments. To preserve comments, use #swapPayload(). + Value& operator=(this_type other); + /// Swap everything. + void swap(this_type& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(this_type& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const this_type& other) const; + bool operator<=(const this_type& other) const; + bool operator>=(const this_type& other) const; + bool operator>(const this_type& other) const; + bool operator==(const this_type& other) const; + bool operator!=(const this_type& other) const; + int compare(const this_type& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! + string_type asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString( + char const** begin, char const** end) const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + this_type& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + this_type& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const this_type& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const this_type& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + this_type get(ArrayIndex index, const this_type& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + this_type& append(const this_type& value); + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + this_type& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const this_type& 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. + this_type& operator[](const string_type& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const this_type& operator[](const string_type& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object has no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + this_type& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + this_type& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const this_type& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + this_type get(const char* key, const this_type& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + this_type get(const char* begin, const char* end, const this_type& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + this_type get(const string_type& key, const this_type& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + this_type get(const CppTL::ConstString& key, const this_type& defaultValue) const; +#endif + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + this_type const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + this_type const* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + /// \deprecated + this_type removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + /// \deprecated + this_type removeMember(const string_type& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, this_type* removed); + /** \brief Remove the named map member. + + Update 'removed' iff removed. + \param key may contain embedded nulls. + \return true iff removed (no exceptions) + */ + bool removeMember(string_type const& key, this_type* removed); + /// Same as removeMember(string_type const& key, this_type* removed) + bool removeMember(const char* begin, const char* end, this_type* removed); + /** \brief Remove the indexed array element. + + O(n) expensive operations. + Update 'removed' iff removed. + \return true iff removed (no exceptions) + */ + bool removeIndex(ArrayIndex i, this_type* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + 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 string_type& key) const; + /// Same as isMember(string_type 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. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(string_type 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 string_type& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + string_type getComment(CommentPlacement placement) const; + + string_type toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + // Accessors for the [start, limit) range of bytes within the JSON text from + // which this value was parsed, if any. + void setOffsetStart(size_t start); + void setOffsetLimit(size_t limit); + size_t getOffsetStart() const; + size_t getOffsetLimit() const; + +private: + void initBasic(ValueType type, bool allocated = false); + /** 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 string_data_type duplicateStringValue(const char* value, size_t length); + /** Record the length as a prefix. + */ + static string_data_type duplicateAndPrefixStringValue(const char* value, unsigned int length); + static void decodePrefixedString(bool isPrefixed, const char* prefixed, unsigned* length, char const** value); +#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) + this_type& resolveReference(const char* key); + this_type& resolveReference(const char* key, const char* end); + + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text, size_t len); + void setComment(const string_data_type& text, size_t len); + + string_data_type comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + ValueHolder<_Traits, _Alloc> value_; + ValueType type_ : 8; + unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. + // If not allocated_, string_ must be null-terminated. + CommentInfo* comments_; + + // [start, limit) byte offsets in the source JSON text from which this Value + // was extracted. + size_t start_; + size_t limit_; + + // 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[2048]; + const unsigned char& kNullRef = kNull[0]; +}; + +template +class ValueHolder { +public: + LargestInt int_ = 0; + LargestUInt uint_ = 0u; + double real_ = 0; + bool bool_ = false; + typename Value<_Traits, _Alloc>::string_data_type stringDuplicate_; // actually ptr to unsigned, followed by str (allocated) + const char* stringRaw_ = nullptr; // the raw string (!allocated) + typename Value<_Traits, _Alloc>::ObjectValues* map_ = nullptr; + + ValueHolder(); + ValueHolder(const ValueHolder& ref); + ValueHolder(ValueHolder&& ref); + ~ValueHolder(); + ValueHolder& operator=(const ValueHolder& value); + + ValueHolder& operator=(ValueHolder&& value); + + void copy(const ValueHolder& value); + void swap(ValueHolder&& value); +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +template +class JSON_API PathArgument { +public: + template + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const std::string& key); + +private: + enum Kind { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +template +class JSON_API Path { +public: + Path(const std::string& path, + const PathArgument<_Traits, _Alloc>& a1 = PathArgument<_Traits, _Alloc>(), + const PathArgument<_Traits, _Alloc>& a2 = PathArgument<_Traits, _Alloc>(), + const PathArgument<_Traits, _Alloc>& a3 = PathArgument<_Traits, _Alloc>(), + const PathArgument<_Traits, _Alloc>& a4 = PathArgument<_Traits, _Alloc>(), + const PathArgument<_Traits, _Alloc>& a5 = PathArgument<_Traits, _Alloc>()); + + const Value<_Traits, _Alloc>& resolve(const Value<_Traits, _Alloc>& root) const; + Value<_Traits, _Alloc> resolve(const Value<_Traits, _Alloc>& root, const Value<_Traits, _Alloc>& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value<_Traits, _Alloc>& make(Value<_Traits, _Alloc>& root) const; + +private: + using InArgs = std::vector*>; + using Args = std::vector>; + + void makePath(const std::string& path, const InArgs& in); + void addPathInArg(const std::string& path, + const InArgs& in, + typename InArgs::const_iterator& itInArg, + typename PathArgument<_Traits, _Alloc>::Kind kind); + void invalidPath(const std::string& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +template +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value<_Traits, _Alloc> key() const; + + /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + UInt index() const; + + /// 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; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value<_Traits, _Alloc>& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + typename Value<_Traits, _Alloc>::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const typename Value<_Traits, _Alloc>::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +template +class JSON_API ValueConstIterator : public ValueIteratorBase<_Traits, _Alloc> { + friend class Value<_Traits, _Alloc>; + +public: + typedef const Value<_Traits, _Alloc> value_type; + //typedef unsigned int size_t; + //typedef int difference_type; + typedef const Value<_Traits, _Alloc>& reference; + typedef const Value<_Traits, _Alloc>* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + ValueConstIterator(ValueIterator<_Traits, _Alloc> const& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const typename Value<_Traits, _Alloc>::ObjectValues::iterator& current); +public: + SelfType& operator=(const ValueIteratorBase<_Traits, _Alloc>& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + ValueIteratorBase<_Traits, _Alloc>::decrement(); + return *this; + } + + SelfType& operator++() { + ValueIteratorBase<_Traits, _Alloc>::increment(); + return *this; + } + + reference operator*() const { return ValueIteratorBase<_Traits, _Alloc>::deref(); } + + pointer operator->() const { return &ValueIteratorBase<_Traits, _Alloc>::deref(); } +}; + +/** \brief Iterator for object and array value. + */ +template +class JSON_API ValueIterator : public ValueIteratorBase<_Traits, _Alloc> { + friend class Value<_Traits, _Alloc>; + +public: + typedef Value<_Traits, _Alloc> value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value<_Traits, _Alloc>& reference; + typedef Value<_Traits, _Alloc>* pointer; + typedef ValueIterator<_Traits, _Alloc> SelfType; + + ValueIterator(); + explicit ValueIterator(const ValueConstIterator<_Traits, _Alloc>& other); + ValueIterator(const ValueIterator<_Traits, _Alloc>& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const typename Value<_Traits, _Alloc>::ObjectValues::iterator& current); +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + ValueIteratorBase<_Traits, _Alloc>::decrement(); + return *this; + } + + SelfType& operator++() { + ValueIteratorBase<_Traits, _Alloc>::increment(); + return *this; + } + + reference operator*() const { return ValueIteratorBase<_Traits, _Alloc>::deref(); } + + pointer operator->() const { return &ValueIteratorBase<_Traits, _Alloc>::deref(); } +}; + +} // namespace Json + + +namespace std { +/// Specialize std::swap() for Json::Value. +template +inline void swap(Json::Value<_Traits, _Alloc>& a, Json::Value<_Traits, _Alloc>& b); +} + + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_DECL_H_INCLUDED diff --git a/include/json/writer.h b/include/json/writer.h index f94aa1fe7..1aa6d3c82 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -7,320 +7,1191 @@ #define JSON_WRITER_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) -#include "value.h" +#include "writer_declaration.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include - -// Disable warning C4251: : needs to have dll-interface to -// be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(push) -#pragma warning(disable : 4251) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) namespace Json { -class Value; - -/** - -Usage: -\code - using namespace Json; - void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { - std::unique_ptr const writer( - factory.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush - } -\endcode -*/ -class JSON_API StreamWriter { -protected: - std::ostream* sout_; // not owned; will not delete -public: - StreamWriter(); - virtual ~StreamWriter(); - /** Write Value into document as configured in sub-class. - Do not take ownership of sout, but maintain a reference during function. - \pre sout != NULL - \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; - - /** \brief A simple abstract factory. - */ - class JSON_API Factory { - public: - virtual ~Factory(); - /** \brief Allocate a CharReader via operator new(). - * \throw std::exception if something goes wrong (e.g. invalid settings) - */ - virtual StreamWriter* newStreamWriter() const = 0; - }; // Factory -}; // StreamWriter +template +std::basic_string valueToQuotedStringN(const char* value, unsigned length) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (WriterUtils::strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && + !WriterUtils::containsControlCharacter0(value, length)) + return std::basic_string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::basic_string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + typename std::basic_string::size_type maxsize = + length * 2 + 3; // allescaped+quotes+NULL + std::basic_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 oss; + oss << "\\u" << std::hex << std::uppercase << std::setfill('0') + << std::setw(4) << static_cast(*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} -/** \brief Write into stringstream, then return string, for convenience. - * A StreamWriter will be created from the factory, used, and then deleted. - */ -std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); - - -/** \brief Build a StreamWriter implementation. - -Usage: -\code - using namespace Json; - Value value = ...; - StreamWriterBuilder builder; - builder["commentStyle"] = "None"; - builder["indentation"] = " "; // or whatever you like - std::unique_ptr writer( - builder.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush -\endcode -*/ -class JSON_API StreamWriterBuilder : public StreamWriter::Factory { -public: - // 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. - Available settings (case-sensitive): - - "commentStyle": "None" or "All" - - "indentation": "" - - "enableYAMLCompatibility": false or true - - slightly change the whitespace around colons - - "dropNullPlaceholders": false or true - - Drop the "null" string from the writer's output for nullValues. - Strictly speaking, this is not valid JSON. But when the output is being - fed to a browser's Javascript, it makes for smaller output and the - browser can handle the output just fine. - - "useSpecialFloats": false or true - - If true, outputs non-finite floating point values in the following way: - NaN values as "NaN", positive infinity as "Infinity", and negative infinity - as "-Infinity". - - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ - Json::Value settings_; - - StreamWriterBuilder(); - ~StreamWriterBuilder() override; - - /** - * \throw std::exception if something goes wrong (e.g. invalid settings) - */ - StreamWriter* newStreamWriter() const override; - - /** \return true if 'settings' are legal and consistent; - * otherwise, indicate bad settings via 'invalid'. - */ - bool validate(Json::Value* invalid) const; - /** A simple way to update a specific setting. - */ - 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); -}; +template +std::basic_string valueToString(LargestInt value) { + WriterUtils::UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + if (value == Value<_Traits, _Alloc>::minLargestInt) { + WriterUtils::uintToString(LargestUInt(Value<_Traits, _Alloc>::maxLargestInt) + 1, current); + *--current = '-'; + } else if (value < 0) { + WriterUtils::uintToString(LargestUInt(-value), current); + *--current = '-'; + } else { + WriterUtils::uintToString(LargestUInt(value), current); + } + assert(current >= buffer); + return current; +} -/** \brief Abstract class for writers. - * \deprecated Use StreamWriter. (And really, this is an implementation detail.) - */ -class JSON_API Writer { -public: - virtual ~Writer(); +template +std::basic_string valueToString(LargestUInt value) { + WriterUtils::UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + WriterUtils::uintToString(value, current); + assert(current >= buffer); + return current; +} - virtual std::string write(const Value& root) = 0; -}; +#if defined(JSON_HAS_INT64) -/** \brief Outputs a Value in JSON format - *without formatting (not human friendly). - * - * The JSON document is written in a single line. It is not intended for 'human' - *consumption, - * but may be usefull to support feature such as RPC where bandwith is limited. - * \sa Reader, Value - * \deprecated Use StreamWriterBuilder. - */ -class JSON_API FastWriter : public Writer { +template +std::basic_string valueToString(Int value) { + return valueToString<_Traits, _Alloc>(LargestInt(value)); +} -public: - FastWriter(); - ~FastWriter() override {} +template +std::basic_string valueToString(UInt value) { + return valueToString<_Traits, _Alloc>(LargestUInt(value)); +} +#endif // # if defined(JSON_HAS_INT64) - void enableYAMLCompatibility(); +template +std::basic_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; - /** \brief Drop the "null" string from the writer's output for nullValues. - * Strictly speaking, this is not valid JSON. But when the output is being - * fed to a browser's Javascript, it makes for smaller output and the - * browser can handle the output just fine. - */ - void dropNullPlaceholders(); + char formatString[6]; + sprintf(formatString, "%%.%dg", precision); - void omitEndingLineFeed(); + // 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); + WriterUtils::fixNumericLocale(buffer, buffer + len); + return buffer; +} -public: // overridden from Writer - std::string write(const Value& root) override; +template +std::basic_string valueToString(double value) { return valueToString<_Traits, _Alloc>(value, false, 17); } -private: - void writeValue(const Value& value); +template +std::basic_string valueToString(bool value) { return value ? "true" : "false"; } - std::string document_; - bool yamlCompatiblityEnabled_; - bool dropNullPlaceholders_; - bool omitEndingLineFeed_; -}; +template +std::basic_string valueToQuotedString(const char* value) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && + !WriterUtils::containsControlCharacter(value)) + return std::basic_string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::basic_string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + typename std::basic_string::size_type maxsize = + strlen(value) * 2 + 3; // allescaped+quotes+NULL + std::basic_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 oss; + oss << "\\u" << std::hex << std::uppercase << std::setfill('0') + << std::setw(4) << static_cast(*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} -/** \brief Writes a Value in JSON format in a - *human friendly way. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per - *line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value - *types, - * and all the values fit on one lines, then print the array on a single - *line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their - *#CommentPlacement. - * - * \sa Reader, Value, Value::setComment() - * \deprecated Use StreamWriterBuilder. - */ -class JSON_API StyledWriter : public Writer { -public: - StyledWriter(); - ~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. - */ - std::string write(const Value& root) override; +/////////////// +// StreamWriter -private: - 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); - static std::string normalizeEOL(const std::string& text); +template +StreamWriter<_Traits, _Alloc>::StreamWriter() + : sout_(NULL) +{ +} +template +StreamWriter<_Traits, _Alloc>::~StreamWriter() +{ +} +template +StreamWriter<_Traits, _Alloc>::Factory::~Factory() +{} - typedef std::vector ChildValues; +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +template +std::basic_string JSON_API writeString(typename StreamWriter<_Traits, _Alloc>::Factory const& builder, Value<_Traits, _Alloc> const& root) { +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) + typedef std::unique_ptr> StreamWriterPtr; +#else + typedef std::auto_ptr> StreamWriterPtr; +#endif + std::basic_ostringstream sout; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} - ChildValues childValues_; - std::string document_; - std::string indentString_; - int rightMargin_; - int indentSize_; - bool addChildValues_; +template +std::ostream& operator<<(std::ostream& sout, Value<_Traits, _Alloc> const& root) { +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) + typedef std::unique_ptr> StreamWriterPtr; +#else + typedef std::auto_ptr> StreamWriterPtr; +#endif + StreamWriterBuilder<_Traits, _Alloc> builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; +} + +/// 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. + }; }; -/** \brief Writes a Value in JSON format in a - human friendly way, - to a stream rather than to a string. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per - line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value - types, - * and all the values fit on one lines, then print the array on a single - line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their - #CommentPlacement. - * - * \param indentation Each level will be indented by this amount extra. - * \sa Reader, Value, Value::setComment() - * \deprecated Use StreamWriterBuilder. - */ -class JSON_API StyledStreamWriter { -public: - StyledStreamWriter(std::string indentation = "\t"); - ~StyledStreamWriter() {} - -public: - /** \brief Serialize a Value in JSON format. - * \param out Stream to write to. (Can be ostringstream, e.g.) - * \param root Value to serialize. - * \note There is no point in deriving from Writer, since write() should not - * return a value. - */ - void write(std::ostream& out, const Value& root); +template +struct BuiltStyledStreamWriter : public StreamWriter<_Traits, _Alloc> +{ + BuiltStyledStreamWriter( + std::basic_string const& indentation, + CommentStyle::Enum cs, + std::basic_string const& colonSymbol, + std::basic_string const& nullSymbol, + std::basic_string const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision); + int write(Value<_Traits, _Alloc> const& root, std::ostream* sout) override; private: - void writeValue(const Value& value); - void writeArrayValue(const Value& value); - bool isMultineArray(const Value& value); - void pushValue(const std::string& value); + void writeValue(Value<_Traits, _Alloc> const& value); + void writeArrayValue(Value<_Traits, _Alloc> const& value); + bool isMultineArray(Value<_Traits, _Alloc> const& value); + void pushValue(std::basic_string const& value); void writeIndent(); - void writeWithIndent(const std::string& value); + void writeWithIndent(std::basic_string const& 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); + void writeCommentBeforeValue(Value<_Traits, _Alloc> const& root); + void writeCommentAfterValueOnSameLine(Value<_Traits, _Alloc> const& root); + static bool hasCommentForValue(const Value<_Traits, _Alloc>& value); - typedef std::vector ChildValues; + typedef std::vector> ChildValues; ChildValues childValues_; - std::ostream* document_; - std::string indentString_; + std::basic_string indentString_; int rightMargin_; - std::string indentation_; + std::basic_string indentation_; + CommentStyle::Enum cs_; + std::basic_string colonSymbol_; + std::basic_string nullSymbol_; + std::basic_string endingLineFeedSymbol_; bool addChildValues_ : 1; bool indented_ : 1; + bool useSpecialFloats_ : 1; + unsigned int precision_; }; -#if defined(JSON_HAS_INT64) -std::string JSON_API valueToString(Int value); -std::string JSON_API valueToString(UInt value); -#endif // if defined(JSON_HAS_INT64) -std::string JSON_API valueToString(LargestInt value); -std::string JSON_API valueToString(LargestUInt value); -std::string JSON_API valueToString(double value); -std::string JSON_API valueToString(bool value); -std::string JSON_API valueToQuotedString(const char* value); - -/// \brief Output using the StyledStreamWriter. -/// \see Json::operator>>() -JSON_API std::ostream& operator<<(std::ostream&, const Value& root); +template +BuiltStyledStreamWriter<_Traits, _Alloc>::BuiltStyledStreamWriter( + std::basic_string const& indentation, + CommentStyle::Enum cs, + std::basic_string const& colonSymbol, + std::basic_string const& nullSymbol, + std::basic_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) +{ +} +template +int BuiltStyledStreamWriter<_Traits, _Alloc>::write(Value<_Traits, _Alloc> const& root, std::ostream* sout) +{ + StreamWriter<_Traits, _Alloc>::sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_ = ""; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *StreamWriter<_Traits, _Alloc>::sout_ << endingLineFeedSymbol_; + StreamWriter<_Traits, _Alloc>::sout_ = NULL; + return 0; +} +template +void BuiltStyledStreamWriter<_Traits, _Alloc>::writeValue(Value<_Traits, _Alloc> const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString<_Traits, _Alloc>(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString<_Traits, _Alloc>(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString<_Traits, _Alloc>(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<_Traits, _Alloc>(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString<_Traits, _Alloc>(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + typename Value<_Traits, _Alloc>::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + typename Value<_Traits, _Alloc>::Members::iterator it = members.begin(); + for (;;) { + std::basic_string const& name = *it; + Value<_Traits, _Alloc> const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN<_Traits, _Alloc>(name.data(), static_cast(name.length()))); + *StreamWriter<_Traits, _Alloc>::sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *StreamWriter<_Traits, _Alloc>::sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +template +void BuiltStyledStreamWriter<_Traits, _Alloc>::writeArrayValue(Value<_Traits, _Alloc> 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<_Traits, _Alloc> 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; + } + *StreamWriter<_Traits, _Alloc>::sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *StreamWriter<_Traits, _Alloc>::sout_ << "["; + if (!indentation_.empty()) *StreamWriter<_Traits, _Alloc>::sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *StreamWriter<_Traits, _Alloc>::sout_ << ", "; + *StreamWriter<_Traits, _Alloc>::sout_ << childValues_[index]; + } + if (!indentation_.empty()) *StreamWriter<_Traits, _Alloc>::sout_ << " "; + *StreamWriter<_Traits, _Alloc>::sout_ << "]"; + } + } +} + +template +bool BuiltStyledStreamWriter<_Traits, _Alloc>::isMultineArray(Value<_Traits, _Alloc> const& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + Value<_Traits, _Alloc> 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; +} + +template +void BuiltStyledStreamWriter<_Traits, _Alloc>::pushValue(std::basic_string const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *StreamWriter<_Traits, _Alloc>::sout_ << value; +} + +template +void BuiltStyledStreamWriter<_Traits, _Alloc>::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. + *StreamWriter<_Traits, _Alloc>::sout_ << '\n' << indentString_; + } +} + +template +void BuiltStyledStreamWriter<_Traits, _Alloc>::writeWithIndent(std::basic_string const& value) { + if (!indented_) writeIndent(); + *StreamWriter<_Traits, _Alloc>::sout_ << value; + indented_ = false; +} + +template +void BuiltStyledStreamWriter<_Traits, _Alloc>::indent() { indentString_ += indentation_; } + +template +void BuiltStyledStreamWriter<_Traits, _Alloc>::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +template +void BuiltStyledStreamWriter<_Traits, _Alloc>::writeCommentBeforeValue(Value<_Traits, _Alloc> const& root) { + if (cs_ == CommentStyle::None) return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const std::basic_string& comment = root.getComment(commentBefore); + typename std::basic_string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + if (*iter != 0) + *StreamWriter<_Traits, _Alloc>::sout_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *StreamWriter<_Traits, _Alloc>::sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +template +void BuiltStyledStreamWriter<_Traits, _Alloc>::writeCommentAfterValueOnSameLine(Value<_Traits, _Alloc> const& root) { + if (cs_ == CommentStyle::None) return; + if (root.hasComment(commentAfterOnSameLine)) + *StreamWriter<_Traits, _Alloc>::sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *StreamWriter<_Traits, _Alloc>::sout_ << root.getComment(commentAfter); + } +} + +// static +template +bool BuiltStyledStreamWriter<_Traits, _Alloc>::hasCommentForValue(const Value<_Traits, _Alloc>& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +template +StreamWriterBuilder<_Traits, _Alloc>::StreamWriterBuilder() +{ + setDefaults(&settings_); +} +template +StreamWriterBuilder<_Traits, _Alloc>::~StreamWriterBuilder() +{} +template +StreamWriter<_Traits, _Alloc>* StreamWriterBuilder<_Traits, _Alloc>::newStreamWriter() const +{ + std::basic_string indentation = settings_["indentation"].asString(); + std::basic_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::basic_string colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + std::basic_string nullSymbol = "null"; + if (dnp) { + nullSymbol = ""; + } + if (pre > 17) pre = 17; + std::basic_string endingLineFeedSymbol = ""; + return new BuiltStyledStreamWriter<_Traits, _Alloc>( + indentation, cs, + colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); +} +template +void StreamWriterBuilder<_Traits, _Alloc>::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"); +} +template +bool StreamWriterBuilder<_Traits, _Alloc>::validate(Json::Value<_Traits, _Alloc>* invalid) const +{ + Json::Value<_Traits, _Alloc> my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value<_Traits, _Alloc>& inv = *invalid; + std::set> valid_keys; + getValidWriterKeys(&valid_keys); + typename Value<_Traits, _Alloc>::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::basic_string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +template +Value<_Traits, _Alloc>& StreamWriterBuilder<_Traits, _Alloc>::operator[](std::basic_string key) +{ + return settings_[key]; +} +// static +template +void StreamWriterBuilder<_Traits, _Alloc>::setDefaults(Json::Value<_Traits, _Alloc>* settings) +{ + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + (*settings)["useSpecialFloats"] = false; + (*settings)["precision"] = 17; + //! [StreamWriterBuilderDefaults] +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +template +Writer<_Traits, _Alloc>::~Writer() {} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +template +FastWriter<_Traits, _Alloc>::FastWriter() + : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), + omitEndingLineFeed_(false) {} + +template +void FastWriter<_Traits, _Alloc>::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } + +template +void FastWriter<_Traits, _Alloc>::dropNullPlaceholders() { dropNullPlaceholders_ = true; } + +template +void FastWriter<_Traits, _Alloc>::omitEndingLineFeed() { omitEndingLineFeed_ = true; } + +template +std::basic_string FastWriter<_Traits, _Alloc>::write(const Value<_Traits, _Alloc>& root) { + document_ = ""; + writeValue(root); + if (!omitEndingLineFeed_) + document_ += "\n"; + return document_; +} + +template +void FastWriter<_Traits, _Alloc>::writeValue(const Value<_Traits, _Alloc>& value) { + switch (value.type()) { + case ValueType::nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case ValueType::intValue: + document_ += valueToString<_Traits, _Alloc>(value.asLargestInt()); + break; + case ValueType::uintValue: + document_ += valueToString<_Traits, _Alloc>(value.asLargestUInt()); + break; + case ValueType::realValue: + document_ += valueToString<_Traits, _Alloc>(value.asDouble()); + break; + case ValueType::stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) document_ += valueToQuotedStringN<_Traits, _Alloc>(str, static_cast(end-str)); + break; + } + case ValueType::booleanValue: + document_ += valueToString<_Traits, _Alloc>(value.asBool()); + break; + case ValueType::arrayValue: { + document_ += '['; + int size = value.size(); + for (int index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case ValueType::objectValue: { + typename Value<_Traits, _Alloc>::Members members(value.getMemberNames()); + document_ += '{'; + for (typename Value<_Traits, _Alloc>::Members::iterator it = members.begin(); it != members.end(); + ++it) { + const std::basic_string& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN<_Traits, _Alloc>(name.data(), static_cast(name.length())); + document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// +template +StyledWriter<_Traits, _Alloc>::StyledWriter() + : rightMargin_(74), indentSize_(3), addChildValues_() {} + +template +std::basic_string StyledWriter<_Traits, _Alloc>::write(const Value<_Traits, _Alloc>& root) { + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +template +void StyledWriter<_Traits, _Alloc>::writeValue(const Value<_Traits, _Alloc>& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString<_Traits, _Alloc>(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString<_Traits, _Alloc>(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString<_Traits, _Alloc>(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<_Traits, _Alloc>(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString<_Traits, _Alloc>(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + typename Value<_Traits, _Alloc>::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + typename Value<_Traits, _Alloc>::Members::iterator it = members.begin(); + for (;;) { + const std::basic_string& name = *it; + const Value<_Traits, _Alloc>& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString<_Traits, _Alloc>(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +template +void StyledWriter<_Traits, _Alloc>::writeArrayValue(const Value<_Traits, _Alloc>& 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<_Traits, _Alloc>& 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_ += " ]"; + } + } +} + +template +bool StyledWriter<_Traits, _Alloc>::isMultineArray(const Value<_Traits, _Alloc>& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value<_Traits, _Alloc>& 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; +} + +template +void StyledWriter<_Traits, _Alloc>::pushValue(const std::basic_string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +template +void StyledWriter<_Traits, _Alloc>::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_; +} + +template +void StyledWriter<_Traits, _Alloc>::writeWithIndent(const std::basic_string& value) { + writeIndent(); + document_ += value; +} + +template +void StyledWriter<_Traits, _Alloc>::indent() { indentString_ += std::basic_string(indentSize_, ' '); } + +template +void StyledWriter<_Traits, _Alloc>::unindent() { + assert(int(indentString_.size()) >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +template +void StyledWriter<_Traits, _Alloc>::writeCommentBeforeValue(const Value<_Traits, _Alloc>& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += "\n"; + writeIndent(); + const std::basic_string& comment = root.getComment(commentBefore); + typename std::basic_string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + if (*iter != 0) + document_ += *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += "\n"; +} + +template +void StyledWriter<_Traits, _Alloc>::writeCommentAfterValueOnSameLine(const Value<_Traits, _Alloc>& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += root.getComment(commentAfter); + document_ += "\n"; + } +} + +template +bool StyledWriter<_Traits, _Alloc>::hasCommentForValue(const Value<_Traits, _Alloc>& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +template +StyledStreamWriter<_Traits, _Alloc>::StyledStreamWriter(std::basic_string indentation) + : document_(NULL), rightMargin_(74), indentation_(indentation), + addChildValues_() {} + +template +void StyledStreamWriter<_Traits, _Alloc>::write(std::ostream& out, const Value<_Traits, _Alloc>& 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. +} + +template +void StyledStreamWriter<_Traits, _Alloc>::writeValue(const Value<_Traits, _Alloc>& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString<_Traits, _Alloc>(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString<_Traits, _Alloc>(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString<_Traits, _Alloc>(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<_Traits, _Alloc>(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString<_Traits, _Alloc>(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + typename Value<_Traits, _Alloc>::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + typename Value<_Traits, _Alloc>::Members::iterator it = members.begin(); + for (;;) { + const std::basic_string& name = *it; + const Value<_Traits, _Alloc>& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString<_Traits, _Alloc>(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +template +void StyledStreamWriter<_Traits, _Alloc>::writeArrayValue(const Value<_Traits, _Alloc>& 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<_Traits, _Alloc>& 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_ << " ]"; + } + } +} + +template +bool StyledStreamWriter<_Traits, _Alloc>::isMultineArray(const Value<_Traits, _Alloc>& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value<_Traits, _Alloc>& 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; +} + +template +void StyledStreamWriter<_Traits, _Alloc>::pushValue(const std::basic_string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +template +void StyledStreamWriter<_Traits, _Alloc>::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_; +} + +template +void StyledStreamWriter<_Traits, _Alloc>::writeWithIndent(const std::basic_string& value) { + if (!indented_) writeIndent(); + *document_ << value; + indented_ = false; +} + +template +void StyledStreamWriter<_Traits, _Alloc>::indent() { indentString_ += indentation_; } + +template +void StyledStreamWriter<_Traits, _Alloc>::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +template +void StyledStreamWriter<_Traits, _Alloc>::writeCommentBeforeValue(const Value<_Traits, _Alloc>& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const std::basic_string& comment = root.getComment(commentBefore); + typename std::basic_string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + if (*iter != 0) + *document_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +template +void StyledStreamWriter<_Traits, _Alloc>::writeCommentAfterValueOnSameLine(const Value<_Traits, _Alloc>& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +template +bool StyledStreamWriter<_Traits, _Alloc>::hasCommentForValue(const Value<_Traits, _Alloc>& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} } // namespace Json diff --git a/include/json/writer_declaration.h b/include/json/writer_declaration.h new file mode 100644 index 000000000..056f63055 --- /dev/null +++ b/include/json/writer_declaration.h @@ -0,0 +1,460 @@ +// Copyright 2007-2010 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 + +#ifndef JSON_WRITER_DECL_H_INCLUDED +#define JSON_WRITER_DECL_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value_declaration.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +#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 + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +template +class JSON_API StreamWriter; //Forward declare +template +class JSON_API StreamWriterBuilder; //Forward declare + +template +class Value; + + +template +std::basic_string valueToQuotedStringN(const char* value, unsigned length); + +template +std::basic_string valueToString(LargestInt value); + +template +std::basic_string valueToString(LargestUInt value); + +#if defined(JSON_HAS_INT64) + +template +std::basic_string valueToString(Int value); + +template +std::basic_string valueToString(UInt value); +#endif // # if defined(JSON_HAS_INT64) + +template +std::basic_string valueToString(double value, bool useSpecialFloats, unsigned int precision); + +template +std::basic_string valueToString(double value); + +template +std::basic_string valueToString(bool value); + +template +std::basic_string valueToQuotedString(const char* value); + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +template +std::basic_string JSON_API writeString(typename StreamWriter<_Traits, _Alloc>::Factory const& factory, Value<_Traits, _Alloc> const& root); + +template +std::ostream& operator<<(std::ostream& sout, Value<_Traits, _Alloc> const& root); + +/** + * Internal utilities for converting to output + */ +class WriterUtils { + public: + static bool containsControlCharacter(const char* str); + 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); + + /// Converts a unicode code-point to UTF-8. + static std::string codePointToUTF8(unsigned int cp); + /// Returns true if ch is a control character (in range [1,31]). + static bool isControlCharacter(char ch); + /** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ + static void uintToString(LargestUInt value, char*& current); + + /** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ + static void fixNumericLocale(char* begin, char* end); + + // Defines a char buffer for use with uintToString(). + typedef char UIntToStringBuffer[3 * sizeof(LargestUInt) + 1]; +}; + +/** + +Usage: +\code + using namespace Json; + void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { + std::unique_ptr const writer( + factory.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush + } +\endcode +*/ +template +class JSON_API StreamWriter { +protected: + std::ostream* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \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<_Traits, _Alloc> const& root, std::ostream* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Build a StreamWriter implementation. + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; // or whatever you like + std::unique_ptr writer( + builder.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush +\endcode +*/ +template +class JSON_API StreamWriterBuilder : public StreamWriter<_Traits, _Alloc>::Factory { +public: + // 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. + Available settings (case-sensitive): + - "commentStyle": "None" or "All" + - "indentation": "" + - "enableYAMLCompatibility": false or true + - slightly change the whitespace around colons + - "dropNullPlaceholders": false or true + - Drop the "null" string from the writer's output for nullValues. + Strictly speaking, this is not valid JSON. But when the output is being + fed to a browser's Javascript, it makes for smaller output and the + browser can handle the output just fine. + - "useSpecialFloats": false or true + - If true, outputs non-finite floating point values in the following way: + NaN values as "NaN", positive infinity as "Infinity", and negative infinity + as "-Infinity". + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value<_Traits, _Alloc> settings_; + + StreamWriterBuilder(); + ~StreamWriterBuilder() override; + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + StreamWriter<_Traits, _Alloc>* newStreamWriter() const override; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value<_Traits, _Alloc>* invalid) const; + /** A simple way to update a specific setting. + */ + Value<_Traits, _Alloc>& operator[](std::basic_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(Value<_Traits, _Alloc>* settings); + static void getValidWriterKeys(std::set>* valid_keys); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +template +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual std::basic_string write(const Value<_Traits, _Alloc>& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +template +class JSON_API FastWriter : public Writer<_Traits, _Alloc> { + +public: + FastWriter(); + ~FastWriter() override {} + + void enableYAMLCompatibility(); + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's Javascript, it makes for smaller output and the + * browser can handle the output just fine. + */ + void dropNullPlaceholders(); + + void omitEndingLineFeed(); + +public: // overridden from Writer + std::basic_string write(const Value<_Traits, _Alloc>& root) override; + +private: + void writeValue(const Value<_Traits, _Alloc>& value); + + std::basic_string document_; + bool yamlCompatiblityEnabled_; + bool dropNullPlaceholders_; + bool omitEndingLineFeed_; +}; + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +template +class JSON_API StyledWriter : public Writer<_Traits, _Alloc> { +public: + StyledWriter(); + ~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. + */ + std::basic_string write(const Value<_Traits, _Alloc>& root) override; + +private: + void writeValue(const Value<_Traits, _Alloc>& value); + void writeArrayValue(const Value<_Traits, _Alloc>& value); + bool isMultineArray(const Value<_Traits, _Alloc>& value); + void pushValue(const std::basic_string& value); + void writeIndent(); + void writeWithIndent(const std::basic_string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value<_Traits, _Alloc>& root); + void writeCommentAfterValueOnSameLine(const Value<_Traits, _Alloc>& root); + bool hasCommentForValue(const Value<_Traits, _Alloc>& value); + static std::basic_string normalizeEOL(const std::basic_string& text); + + typedef std::vector> ChildValues; + + ChildValues childValues_; + std::basic_string document_; + std::basic_string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +template +class JSON_API StyledStreamWriter { +public: + StyledStreamWriter(std::basic_string indentation = "\t"); + ~StyledStreamWriter() {} + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(std::ostream& out, const Value<_Traits, _Alloc>& root); + +private: + void writeValue(const Value<_Traits, _Alloc>& value); + void writeArrayValue(const Value<_Traits, _Alloc>& value); + bool isMultineArray(const Value<_Traits, _Alloc>& value); + void pushValue(const std::basic_string& value); + void writeIndent(); + void writeWithIndent(const std::basic_string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value<_Traits, _Alloc>& root); + void writeCommentAfterValueOnSameLine(const Value<_Traits, _Alloc>& root); + bool hasCommentForValue(const Value<_Traits, _Alloc>& value); + static std::basic_string normalizeEOL(const std::basic_string& text); + + typedef std::vector> ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::basic_string indentString_; + int rightMargin_; + std::basic_string indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_DECL_H_INCLUDED diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index c8bbd0d7e..ae2f9b9cb 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -15,12 +15,15 @@ #pragma warning(disable : 4996) // disable fopen deprecation warning #endif +#define USING_TRAITS_ALLOC std::char_traits, std::allocator +using TestValue = Json::Value; + struct Options { std::string path; Json::Features features; bool parseOnly; - typedef std::string (*writeFuncType)(Json::Value const&); + typedef std::string (*writeFuncType)(TestValue const&); writeFuncType write; }; @@ -70,7 +73,7 @@ static std::string readInputTestFile(const char* path) { } static void -printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { +printValueTree(FILE* fout, TestValue& value, const std::string& path = ".") { if (value.hasComment(Json::commentBefore)) { fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str()); } @@ -82,13 +85,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::valueToString(value.asLargestInt()).c_str()); break; case Json::uintValue: fprintf(fout, "%s=%s\n", path.c_str(), - Json::valueToString(value.asLargestUInt()).c_str()); + Json::valueToString(value.asLargestUInt()).c_str()); break; case Json::realValue: fprintf(fout, @@ -117,10 +120,10 @@ printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { } break; case Json::objectValue: { fprintf(fout, "%s={}\n", path.c_str()); - Json::Value::Members members(value.getMemberNames()); + TestValue::Members members(value.getMemberNames()); std::sort(members.begin(), members.end()); std::string suffix = *(path.end() - 1) == '.' ? "" : "."; - for (Json::Value::Members::iterator it = members.begin(); + for (TestValue::Members::iterator it = members.begin(); it != members.end(); ++it) { const std::string& name = *it; @@ -141,9 +144,9 @@ static int parseAndSaveValueTree(const std::string& input, const std::string& kind, const Json::Features& features, bool parseOnly, - Json::Value* root) + TestValue* root) { - Json::Reader reader(features); + Json::Reader reader(features); bool parsingSuccessful = reader.parse(input, *root); if (!parsingSuccessful) { printf("Failed to parse %s file: \n%s\n", @@ -162,34 +165,34 @@ static int parseAndSaveValueTree(const std::string& input, } return 0; } -// static std::string useFastWriter(Json::Value const& root) { +// static std::string useFastWriter(TestValue const& root) { // Json::FastWriter writer; // writer.enableYAMLCompatibility(); // return writer.write(root); // } static std::string useStyledWriter( - Json::Value const& root) + TestValue const& root) { - Json::StyledWriter writer; + Json::StyledWriter writer; return writer.write(root); } static std::string useStyledStreamWriter( - Json::Value const& root) + TestValue const& root) { - Json::StyledStreamWriter writer; + Json::StyledStreamWriter writer; std::ostringstream sout; writer.write(sout, root); return sout.str(); } static std::string useBuiltStyledStreamWriter( - Json::Value const& root) + TestValue const& root) { - Json::StreamWriterBuilder builder; + Json::StreamWriterBuilder builder; return Json::writeString(builder, root); } static int rewriteValueTree( const std::string& rewritePath, - const Json::Value& root, + const TestValue& root, Options::writeFuncType write, std::string* rewrite) { @@ -287,7 +290,7 @@ static int runTest(Options const& opts) std::string const rewritePath = basePath + ".rewrite"; std::string const rewriteActualPath = basePath + ".actual-rewrite"; - Json::Value root; + TestValue root; exitCode = parseAndSaveValueTree( input, actualPath, "input", opts.features, opts.parseOnly, &root); @@ -299,7 +302,7 @@ static int runTest(Options const& opts) if (exitCode) { return exitCode; } - Json::Value rewriteRoot; + TestValue rewriteRoot; exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath, "rewrite", opts.features, opts.parseOnly, &rewriteRoot); diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 2eae15de9..46ecc74aa 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -42,17 +42,8 @@ #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 // //////////////////////////////// @@ -71,1962 +62,10 @@ 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_tool.h b/src/lib_json/json_tool.h index e65e51b41..e0982b7a7 100644 --- a/src/lib_json/json_tool.h +++ b/src/lib_json/json_tool.h @@ -14,6 +14,8 @@ namespace Json { +class JsonTool { +public: /// Converts a unicode code-point to UTF-8. static inline std::string codePointToUTF8(unsigned int cp) { std::string result; @@ -81,7 +83,7 @@ static inline void fixNumericLocale(char* begin, char* end) { ++begin; } } - +}; } // namespace Json { #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 91b5f23f9..126294c3d 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -19,122 +19,8 @@ #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 @@ -176,1366 +62,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_valueiterator.inl b/src/lib_json/json_valueiterator.inl index ec9c85127..276dc6023 100644 --- a/src/lib_json/json_valueiterator.inl +++ b/src/lib_json/json_valueiterator.inl @@ -7,161 +7,4 @@ namespace Json { -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIteratorBase -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIteratorBase::ValueIteratorBase() - : current_(), isNull_(true) { -} - -ValueIteratorBase::ValueIteratorBase( - const Value::ObjectValues::iterator& current) - : current_(current), isNull_(false) {} - -Value& ValueIteratorBase::deref() const { - return current_->second; -} - -void ValueIteratorBase::increment() { - ++current_; -} - -void ValueIteratorBase::decrement() { - --current_; -} - -ValueIteratorBase::difference_type -ValueIteratorBase::computeDistance(const SelfType& other) const { -#ifdef JSON_USE_CPPTL_SMALLMAP - return other.current_ - current_; -#else - // Iterator for null value are initialized using the default - // constructor, which initialize current_ to the default - // std::map::iterator. As begin() and end() are two instance - // of the default std::map::iterator, they can not be compared. - // To allow this, we handle this comparison specifically. - if (isNull_ && other.isNull_) { - return 0; - } - - // Usage of std::distance is not portable (does not compile with Sun Studio 12 - // RogueWave STL, - // which is the one used by default). - // 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_; - ++it) { - ++myDistance; - } - return myDistance; -#endif -} - -bool ValueIteratorBase::isEqual(const SelfType& other) const { - if (isNull_) { - return other.isNull_; - } - return current_ == other.current_; -} - -void ValueIteratorBase::copy(const SelfType& other) { - current_ = other.current_; - isNull_ = other.isNull_; -} - -Value ValueIteratorBase::key() const { - const 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(czstring.index()); -} - -UInt ValueIteratorBase::index() const { - const Value::CZString czstring = (*current_).first; - if (!czstring.data()) - return czstring.index(); - return Value::UInt(-1); -} - -std::string ValueIteratorBase::name() const { - char const* keey; - char const* end; - keey = memberName(&end); - if (!keey) return std::string(); - return std::string(keey, end); -} - -char const* ValueIteratorBase::memberName() const { - const char* cname = (*current_).first.data(); - return cname ? cname : ""; -} - -char const* ValueIteratorBase::memberName(char const** end) const { - const char* cname = (*current_).first.data(); - if (!cname) { - *end = NULL; - return NULL; - } - *end = cname + (*current_).first.length(); - return cname; -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueConstIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueConstIterator::ValueConstIterator() {} - -ValueConstIterator::ValueConstIterator( - const Value::ObjectValues::iterator& current) - : ValueIteratorBase(current) {} - -ValueConstIterator::ValueConstIterator(ValueIterator const& other) - : ValueIteratorBase(other) {} - -ValueConstIterator& ValueConstIterator:: -operator=(const ValueIteratorBase& other) { - copy(other); - return *this; -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIterator::ValueIterator() {} - -ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) - : ValueIteratorBase(current) {} - -ValueIterator::ValueIterator(const ValueConstIterator& other) - : ValueIteratorBase(other) { - throwRuntimeError("ConstIterator to Iterator should never be allowed."); -} - -ValueIterator::ValueIterator(const ValueIterator& other) - : ValueIteratorBase(other) {} - -ValueIterator& ValueIterator::operator=(const SelfType& other) { - copy(other); - return *this; -} - } // namespace Json diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 0b2d7d5be..8642fee13 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -73,13 +73,7 @@ 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) { +bool WriterUtils::containsControlCharacter(const char* str) { while (*str) { if (isControlCharacter(*(str++))) return true; @@ -87,7 +81,7 @@ static bool containsControlCharacter(const char* str) { return false; } -static bool containsControlCharacter0(const char* str, unsigned len) { +bool WriterUtils::containsControlCharacter0(const char* str, unsigned len) { char const* end = str + len; while (end != str) { if (isControlCharacter(*str) || 0==*str) @@ -97,140 +91,8 @@ 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) { +char const* WriterUtils::strnpbrk(char const* s, char const* accept, size_t n) { assert((s || !n) && accept); char const* const end = s + n; @@ -244,971 +106,18 @@ 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; +std::string WriterUtils::codePointToUTF8(unsigned int cp) { + return JsonTool::codePointToUTF8(cp); } - -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()); +bool WriterUtils::isControlCharacter(char ch) { + return JsonTool::isControlCharacter(ch); } - -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 WriterUtils::uintToString(LargestUInt value, char*& current) { + JsonTool::uintToString(value, current); } - -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; +void WriterUtils::fixNumericLocale(char* begin, char* end) { + JsonTool::fixNumericLocale(begin, end); } } // namespace Json diff --git a/src/test_lib_json/jsontest.cpp b/src/test_lib_json/jsontest.cpp index bd9463fa5..a339bd0eb 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::valueToString, std::allocator>(value)); } TestResult& TestResult::operator<<(Json::UInt64 value) { - return addToLastFailure(Json::valueToString(value)); + return addToLastFailure(Json::valueToString, std::allocator>(value)); } TestResult& TestResult::operator<<(bool value) { diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index 30136b0fd..4e10f9c2f 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -12,14 +12,23 @@ #include #include +#define USING_TRAITS_ALLOC std::char_traits, std::allocator +using TestValue = Json::Value; +using TestValueIterator = Json::ValueIterator; +using TestStreamWriterBuilder = Json::StreamWriterBuilder; +using TestFastWriter = Json::FastWriter; +using TestReader = Json::Reader; +using TestCharReaderBuilder = Json::CharReaderBuilder; +using TestCharReader = Json::CharReader; + // Make numeric limits more convenient to talk about. // Assumes int type in 32 bits. -#define kint32max Json::Value::maxInt -#define kint32min Json::Value::minInt -#define kuint32max Json::Value::maxUInt -#define kint64max Json::Value::maxInt64 -#define kint64min Json::Value::minInt64 -#define kuint64max Json::Value::maxUInt64 +#define kint32max TestValue::maxInt +#define kint32min TestValue::minInt +#define kuint32max TestValue::maxUInt +#define kint64max TestValue::maxInt64 +#define kint64min TestValue::minInt64 +#define kuint64max TestValue::maxUInt64 //static const double kdint64max = double(kint64max); //static const float kfint64max = float(kint64max); @@ -44,26 +53,26 @@ static inline double uint64ToDouble(Json::UInt64 value) { #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) struct ValueTest : JsonTest::TestCase { - Json::Value null_; - Json::Value emptyArray_; - Json::Value emptyObject_; - Json::Value integer_; - Json::Value unsignedInteger_; - Json::Value smallUnsignedInteger_; - Json::Value real_; - Json::Value float_; - Json::Value array1_; - Json::Value object1_; - Json::Value emptyString_; - Json::Value string1_; - Json::Value string_; - Json::Value true_; - Json::Value false_; + TestValue null_; + TestValue emptyArray_; + TestValue emptyObject_; + TestValue integer_; + TestValue unsignedInteger_; + TestValue smallUnsignedInteger_; + TestValue real_; + TestValue float_; + TestValue array1_; + TestValue object1_; + TestValue emptyString_; + TestValue string1_; + TestValue string_; + TestValue true_; + TestValue false_; ValueTest() : emptyArray_(Json::arrayValue), emptyObject_(Json::objectValue), integer_(123456789), unsignedInteger_(34567890u), - smallUnsignedInteger_(Json::Value::UInt(Json::Value::maxInt)), + smallUnsignedInteger_(TestValue::UInt(TestValue::maxInt)), real_(1234.56789), float_(0.00390625f), emptyString_(""), string1_("a"), string_("sometext with space"), true_(true), false_(false) { array1_.append(1234); @@ -89,16 +98,16 @@ struct ValueTest : JsonTest::TestCase { bool isNumeric_; }; - void checkConstMemberCount(const Json::Value& value, + void checkConstMemberCount(const TestValue& value, unsigned int expectedCount); - void checkMemberCount(Json::Value& value, unsigned int expectedCount); + void checkMemberCount(TestValue& value, unsigned int expectedCount); - void checkIs(const Json::Value& value, const IsCheck& check); + void checkIs(const TestValue& value, const IsCheck& check); - void checkIsLess(const Json::Value& x, const Json::Value& y); + void checkIsLess(const TestValue& x, const TestValue& y); - void checkIsEqual(const Json::Value& x, const Json::Value& y); + void checkIsEqual(const TestValue& x, const TestValue& y); /// Normalize the representation of floating-point number by stripped leading /// 0 in exponent. @@ -191,28 +200,28 @@ JSONTEST_FIXTURE(ValueTest, objects) { JSONTEST_ASSERT(!emptyObject_.isConvertibleTo(Json::stringValue)); // Access through const reference - const Json::Value& constObject = object1_; + const TestValue& constObject = object1_; - JSONTEST_ASSERT_EQUAL(Json::Value(1234), constObject["id"]); - JSONTEST_ASSERT_EQUAL(Json::Value(), constObject["unknown id"]); + JSONTEST_ASSERT_EQUAL(TestValue(1234), constObject["id"]); + JSONTEST_ASSERT_EQUAL(TestValue(), constObject["unknown id"]); // Access through non-const reference - JSONTEST_ASSERT_EQUAL(Json::Value(1234), object1_["id"]); - JSONTEST_ASSERT_EQUAL(Json::Value(), object1_["unknown id"]); + JSONTEST_ASSERT_EQUAL(TestValue(1234), object1_["id"]); + JSONTEST_ASSERT_EQUAL(TestValue(), object1_["unknown id"]); object1_["some other id"] = "foo"; - JSONTEST_ASSERT_EQUAL(Json::Value("foo"), object1_["some other id"]); - JSONTEST_ASSERT_EQUAL(Json::Value("foo"), object1_["some other id"]); + JSONTEST_ASSERT_EQUAL(TestValue("foo"), object1_["some other id"]); + JSONTEST_ASSERT_EQUAL(TestValue("foo"), object1_["some other id"]); // Remove. - Json::Value got; + TestValue got; bool did; did = object1_.removeMember("some other id", &got); - JSONTEST_ASSERT_EQUAL(Json::Value("foo"), got); + JSONTEST_ASSERT_EQUAL(TestValue("foo"), got); JSONTEST_ASSERT_EQUAL(true, did); - got = Json::Value("bar"); + got = TestValue("bar"); did = object1_.removeMember("some other id", &got); - JSONTEST_ASSERT_EQUAL(Json::Value("bar"), got); + JSONTEST_ASSERT_EQUAL(TestValue("bar"), got); JSONTEST_ASSERT_EQUAL(false, did); } @@ -245,28 +254,28 @@ JSONTEST_FIXTURE(ValueTest, arrays) { JSONTEST_ASSERT(!emptyArray_.isConvertibleTo(Json::stringValue)); // Access through const reference - const Json::Value& constArray = array1_; - JSONTEST_ASSERT_EQUAL(Json::Value(1234), constArray[index0]); - JSONTEST_ASSERT_EQUAL(Json::Value(1234), constArray[0]); + const TestValue& constArray = array1_; + JSONTEST_ASSERT_EQUAL(TestValue(1234), constArray[index0]); + JSONTEST_ASSERT_EQUAL(TestValue(1234), constArray[0]); // Access through non-const reference - JSONTEST_ASSERT_EQUAL(Json::Value(1234), array1_[index0]); - JSONTEST_ASSERT_EQUAL(Json::Value(1234), array1_[0]); + JSONTEST_ASSERT_EQUAL(TestValue(1234), array1_[index0]); + JSONTEST_ASSERT_EQUAL(TestValue(1234), array1_[0]); - array1_[2] = Json::Value(17); - JSONTEST_ASSERT_EQUAL(Json::Value(), array1_[1]); - JSONTEST_ASSERT_EQUAL(Json::Value(17), array1_[2]); - Json::Value got; + array1_[2] = TestValue(17); + JSONTEST_ASSERT_EQUAL(TestValue(), array1_[1]); + JSONTEST_ASSERT_EQUAL(TestValue(17), array1_[2]); + TestValue got; JSONTEST_ASSERT_EQUAL(true, array1_.removeIndex(2, &got)); - JSONTEST_ASSERT_EQUAL(Json::Value(17), got); + JSONTEST_ASSERT_EQUAL(TestValue(17), got); JSONTEST_ASSERT_EQUAL(false, array1_.removeIndex(2, &got)); // gone now } JSONTEST_FIXTURE(ValueTest, arrayIssue252) { int count = 5; - Json::Value root; - Json::Value item; - root["array"] = Json::Value::nullRef; + TestValue root; + TestValue item; + root["array"] = TestValue::nullRef; for (int i = 0; i < count; i++) { item["a"] = i; @@ -300,7 +309,7 @@ JSONTEST_FIXTURE(ValueTest, null) { JSONTEST_ASSERT_EQUAL(0.0, null_.asFloat()); JSONTEST_ASSERT_STRING_EQUAL("", null_.asString()); - JSONTEST_ASSERT_EQUAL(Json::Value::null, null_); + JSONTEST_ASSERT_EQUAL(TestValue::null, null_); } JSONTEST_FIXTURE(ValueTest, strings) { @@ -376,29 +385,29 @@ JSONTEST_FIXTURE(ValueTest, bools) { JSONTEST_FIXTURE(ValueTest, integers) { IsCheck checks; - Json::Value val; + TestValue val; // Conversions that don't depend on the value. - JSONTEST_ASSERT(Json::Value(17).isConvertibleTo(Json::realValue)); - JSONTEST_ASSERT(Json::Value(17).isConvertibleTo(Json::stringValue)); - JSONTEST_ASSERT(Json::Value(17).isConvertibleTo(Json::booleanValue)); - JSONTEST_ASSERT(!Json::Value(17).isConvertibleTo(Json::arrayValue)); - JSONTEST_ASSERT(!Json::Value(17).isConvertibleTo(Json::objectValue)); - - JSONTEST_ASSERT(Json::Value(17U).isConvertibleTo(Json::realValue)); - JSONTEST_ASSERT(Json::Value(17U).isConvertibleTo(Json::stringValue)); - JSONTEST_ASSERT(Json::Value(17U).isConvertibleTo(Json::booleanValue)); - JSONTEST_ASSERT(!Json::Value(17U).isConvertibleTo(Json::arrayValue)); - JSONTEST_ASSERT(!Json::Value(17U).isConvertibleTo(Json::objectValue)); - - JSONTEST_ASSERT(Json::Value(17.0).isConvertibleTo(Json::realValue)); - JSONTEST_ASSERT(Json::Value(17.0).isConvertibleTo(Json::stringValue)); - JSONTEST_ASSERT(Json::Value(17.0).isConvertibleTo(Json::booleanValue)); - JSONTEST_ASSERT(!Json::Value(17.0).isConvertibleTo(Json::arrayValue)); - JSONTEST_ASSERT(!Json::Value(17.0).isConvertibleTo(Json::objectValue)); + JSONTEST_ASSERT(TestValue(17).isConvertibleTo(Json::realValue)); + JSONTEST_ASSERT(TestValue(17).isConvertibleTo(Json::stringValue)); + JSONTEST_ASSERT(TestValue(17).isConvertibleTo(Json::booleanValue)); + JSONTEST_ASSERT(!TestValue(17).isConvertibleTo(Json::arrayValue)); + JSONTEST_ASSERT(!TestValue(17).isConvertibleTo(Json::objectValue)); + + JSONTEST_ASSERT(TestValue(17U).isConvertibleTo(Json::realValue)); + JSONTEST_ASSERT(TestValue(17U).isConvertibleTo(Json::stringValue)); + JSONTEST_ASSERT(TestValue(17U).isConvertibleTo(Json::booleanValue)); + JSONTEST_ASSERT(!TestValue(17U).isConvertibleTo(Json::arrayValue)); + JSONTEST_ASSERT(!TestValue(17U).isConvertibleTo(Json::objectValue)); + + JSONTEST_ASSERT(TestValue(17.0).isConvertibleTo(Json::realValue)); + JSONTEST_ASSERT(TestValue(17.0).isConvertibleTo(Json::stringValue)); + JSONTEST_ASSERT(TestValue(17.0).isConvertibleTo(Json::booleanValue)); + JSONTEST_ASSERT(!TestValue(17.0).isConvertibleTo(Json::arrayValue)); + JSONTEST_ASSERT(!TestValue(17.0).isConvertibleTo(Json::objectValue)); // Default int - val = Json::Value(Json::intValue); + val = TestValue(Json::intValue); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); @@ -426,7 +435,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("0", val.asString()); // Default uint - val = Json::Value(Json::uintValue); + val = TestValue(Json::uintValue); JSONTEST_ASSERT_EQUAL(Json::uintValue, val.type()); @@ -454,7 +463,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("0", val.asString()); // Default real - val = Json::Value(Json::realValue); + val = TestValue(Json::realValue); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -482,7 +491,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("0", val.asString()); // Zero (signed constructor arg) - val = Json::Value(0); + val = TestValue(0); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); @@ -510,7 +519,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("0", val.asString()); // Zero (unsigned constructor arg) - val = Json::Value(0u); + val = TestValue(0u); JSONTEST_ASSERT_EQUAL(Json::uintValue, val.type()); @@ -538,7 +547,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("0", val.asString()); // Zero (floating-point constructor arg) - val = Json::Value(0.0); + val = TestValue(0.0); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -566,7 +575,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("0", val.asString()); // 2^20 (signed constructor arg) - val = Json::Value(1 << 20); + val = TestValue(1 << 20); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); checks = IsCheck(); @@ -593,7 +602,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("1048576", val.asString()); // 2^20 (unsigned constructor arg) - val = Json::Value(Json::UInt(1 << 20)); + val = TestValue(Json::UInt(1 << 20)); JSONTEST_ASSERT_EQUAL(Json::uintValue, val.type()); @@ -621,7 +630,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("1048576", val.asString()); // 2^20 (floating-point constructor arg) - val = Json::Value((1 << 20) / 1.0); + val = TestValue((1 << 20) / 1.0); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -650,7 +659,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { normalizeFloatingPointStr(val.asString())); // -2^20 - val = Json::Value(-(1 << 20)); + val = TestValue(-(1 << 20)); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); @@ -674,7 +683,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("-1048576", val.asString()); // int32 max - val = Json::Value(kint32max); + val = TestValue(kint32max); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); @@ -702,7 +711,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("2147483647", val.asString()); // int32 min - val = Json::Value(kint32min); + val = TestValue(kint32min); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); @@ -726,7 +735,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("-2147483648", val.asString()); // uint32 max - val = Json::Value(kuint32max); + val = TestValue(kuint32max); JSONTEST_ASSERT_EQUAL(Json::uintValue, val.type()); @@ -755,7 +764,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { #ifdef JSON_NO_INT64 // int64 max - val = Json::Value(double(kint64max)); + val = TestValue(double(kint64max)); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -774,7 +783,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("9.22337e+18", val.asString()); // int64 min - val = Json::Value(double(kint64min)); + val = TestValue(double(kint64min)); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -793,7 +802,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("-9.22337e+18", val.asString()); // uint64 max - val = Json::Value(double(kuint64max)); + val = TestValue(double(kuint64max)); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -812,7 +821,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("1.84467e+19", val.asString()); #else // ifdef JSON_NO_INT64 // 2^40 (signed constructor arg) - val = Json::Value(Json::Int64(1) << 40); + val = TestValue(Json::Int64(1) << 40); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); @@ -838,7 +847,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("1099511627776", val.asString()); // 2^40 (unsigned constructor arg) - val = Json::Value(Json::UInt64(1) << 40); + val = TestValue(Json::UInt64(1) << 40); JSONTEST_ASSERT_EQUAL(Json::uintValue, val.type()); @@ -864,7 +873,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("1099511627776", val.asString()); // 2^40 (floating-point constructor arg) - val = Json::Value((Json::Int64(1) << 40) / 1.0); + val = TestValue((Json::Int64(1) << 40) / 1.0); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -891,7 +900,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { normalizeFloatingPointStr(val.asString())); // -2^40 - val = Json::Value(-(Json::Int64(1) << 40)); + val = TestValue(-(Json::Int64(1) << 40)); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); @@ -914,7 +923,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_ASSERT_STRING_EQUAL("-1099511627776", val.asString()); // int64 max - val = Json::Value(Json::Int64(kint64max)); + val = TestValue(Json::Int64(kint64max)); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); @@ -941,7 +950,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { // int64 max (floating point constructor). Note that kint64max is not exactly // representable as a double, and will be rounded up to be higher. - val = Json::Value(double(kint64max)); + val = TestValue(double(kint64max)); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -966,7 +975,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { normalizeFloatingPointStr(val.asString())); // int64 min - val = Json::Value(Json::Int64(kint64min)); + val = TestValue(Json::Int64(kint64min)); JSONTEST_ASSERT_EQUAL(Json::intValue, val.type()); @@ -990,7 +999,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { // int64 min (floating point constructor). Note that kint64min *is* exactly // representable as a double. - val = Json::Value(double(kint64min)); + val = TestValue(double(kint64min)); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -1015,7 +1024,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { // 10^19 const Json::UInt64 ten_to_19 = static_cast(1e19); - val = Json::Value(Json::UInt64(ten_to_19)); + val = TestValue(Json::UInt64(ten_to_19)); JSONTEST_ASSERT_EQUAL(Json::uintValue, val.type()); @@ -1039,7 +1048,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { // 10^19 (double constructor). Note that 10^19 is not exactly representable // as a double. - val = Json::Value(uint64ToDouble(ten_to_19)); + val = TestValue(uint64ToDouble(ten_to_19)); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -1061,7 +1070,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { normalizeFloatingPointStr(val.asString())); // uint64 max - val = Json::Value(Json::UInt64(kuint64max)); + val = TestValue(Json::UInt64(kuint64max)); JSONTEST_ASSERT_EQUAL(Json::uintValue, val.type()); @@ -1085,7 +1094,7 @@ JSONTEST_FIXTURE(ValueTest, integers) { // uint64 max (floating point constructor). Note that kuint64max is not // exactly representable as a double, and will be rounded up to be higher. - val = Json::Value(uint64ToDouble(kuint64max)); + val = TestValue(uint64ToDouble(kuint64max)); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -1108,10 +1117,10 @@ JSONTEST_FIXTURE(ValueTest, integers) { JSONTEST_FIXTURE(ValueTest, nonIntegers) { IsCheck checks; - Json::Value val; + TestValue val; // Small positive number - val = Json::Value(1.5); + val = TestValue(1.5); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -1139,7 +1148,7 @@ JSONTEST_FIXTURE(ValueTest, nonIntegers) { JSONTEST_ASSERT_EQUAL("1.5", val.asString()); // Small negative number - val = Json::Value(-1.5); + val = TestValue(-1.5); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -1165,7 +1174,7 @@ JSONTEST_FIXTURE(ValueTest, nonIntegers) { JSONTEST_ASSERT_EQUAL("-1.5", val.asString()); // A bit over int32 max - val = Json::Value(kint32max + 0.5); + val = TestValue(kint32max + 0.5); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -1195,7 +1204,7 @@ JSONTEST_FIXTURE(ValueTest, nonIntegers) { normalizeFloatingPointStr(val.asString())); // A bit under int32 min - val = Json::Value(kint32min - 0.5); + val = TestValue(kint32min - 0.5); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -1223,7 +1232,7 @@ JSONTEST_FIXTURE(ValueTest, nonIntegers) { normalizeFloatingPointStr(val.asString())); // A bit over uint32 max - val = Json::Value(kuint32max + 0.5); + val = TestValue(kuint32max + 0.5); JSONTEST_ASSERT_EQUAL(Json::realValue, val.type()); @@ -1252,49 +1261,49 @@ JSONTEST_FIXTURE(ValueTest, nonIntegers) { JSONTEST_ASSERT_EQUAL("4294967295.5", normalizeFloatingPointStr(val.asString())); - val = Json::Value(1.2345678901234); + val = TestValue(1.2345678901234); JSONTEST_ASSERT_STRING_EQUAL("1.2345678901234001", normalizeFloatingPointStr(val.asString())); // A 16-digit floating point number. - val = Json::Value(2199023255552000.0f); + val = TestValue(2199023255552000.0f); JSONTEST_ASSERT_EQUAL(float(2199023255552000.0f), val.asFloat()); JSONTEST_ASSERT_STRING_EQUAL("2199023255552000", normalizeFloatingPointStr(val.asString())); // A very large floating point number. - val = Json::Value(3.402823466385289e38); + val = TestValue(3.402823466385289e38); JSONTEST_ASSERT_EQUAL(float(3.402823466385289e38), val.asFloat()); JSONTEST_ASSERT_STRING_EQUAL("3.402823466385289e+38", normalizeFloatingPointStr(val.asString())); // An even larger floating point number. - val = Json::Value(1.2345678e300); + val = TestValue(1.2345678e300); JSONTEST_ASSERT_EQUAL(double(1.2345678e300), val.asDouble()); JSONTEST_ASSERT_STRING_EQUAL("1.2345678e+300", normalizeFloatingPointStr(val.asString())); } -void ValueTest::checkConstMemberCount(const Json::Value& value, +void ValueTest::checkConstMemberCount(const TestValue& value, unsigned int expectedCount) { unsigned int count = 0; - Json::Value::const_iterator itEnd = value.end(); - for (Json::Value::const_iterator it = value.begin(); it != itEnd; ++it) { + TestValue::const_iterator itEnd = value.end(); + for (TestValue::const_iterator it = value.begin(); it != itEnd; ++it) { ++count; } - JSONTEST_ASSERT_EQUAL(expectedCount, count) << "Json::Value::const_iterator"; + JSONTEST_ASSERT_EQUAL(expectedCount, count) << "TestValue::const_iterator"; } -void ValueTest::checkMemberCount(Json::Value& value, +void ValueTest::checkMemberCount(TestValue& value, unsigned int expectedCount) { JSONTEST_ASSERT_EQUAL(expectedCount, value.size()); unsigned int count = 0; - Json::Value::iterator itEnd = value.end(); - for (Json::Value::iterator it = value.begin(); it != itEnd; ++it) { + TestValue::iterator itEnd = value.end(); + for (TestValue::iterator it = value.begin(); it != itEnd; ++it) { ++count; } - JSONTEST_ASSERT_EQUAL(expectedCount, count) << "Json::Value::iterator"; + JSONTEST_ASSERT_EQUAL(expectedCount, count) << "TestValue::iterator"; JSONTEST_ASSERT_PRED(checkConstMemberCount(value, expectedCount)); } @@ -1305,7 +1314,7 @@ ValueTest::IsCheck::IsCheck() isUInt64_(false), isIntegral_(false), isDouble_(false), isNumeric_(false) {} -void ValueTest::checkIs(const Json::Value& value, const IsCheck& check) { +void ValueTest::checkIs(const TestValue& value, const IsCheck& check) { JSONTEST_ASSERT_EQUAL(check.isObject_, value.isObject()); JSONTEST_ASSERT_EQUAL(check.isArray_, value.isArray()); JSONTEST_ASSERT_EQUAL(check.isBool_, value.isBool()); @@ -1327,7 +1336,7 @@ void ValueTest::checkIs(const Json::Value& value, const IsCheck& check) { } JSONTEST_FIXTURE(ValueTest, compareNull) { - JSONTEST_ASSERT_PRED(checkIsEqual(Json::Value(), Json::Value())); + JSONTEST_ASSERT_PRED(checkIsEqual(TestValue(), TestValue())); } JSONTEST_FIXTURE(ValueTest, compareInt) { @@ -1339,7 +1348,7 @@ JSONTEST_FIXTURE(ValueTest, compareInt) { JSONTEST_FIXTURE(ValueTest, compareUInt) { JSONTEST_ASSERT_PRED(checkIsLess(0u, 10u)); - JSONTEST_ASSERT_PRED(checkIsLess(0u, Json::Value::maxUInt)); + JSONTEST_ASSERT_PRED(checkIsLess(0u, TestValue::maxUInt)); JSONTEST_ASSERT_PRED(checkIsEqual(10u, 10u)); } @@ -1369,58 +1378,58 @@ JSONTEST_FIXTURE(ValueTest, compareBoolean) { JSONTEST_FIXTURE(ValueTest, compareArray) { // array compare size then content - Json::Value emptyArray(Json::arrayValue); - Json::Value l1aArray; + TestValue emptyArray(Json::arrayValue); + TestValue l1aArray; l1aArray.append(0); - Json::Value l1bArray; + TestValue l1bArray; l1bArray.append(10); - Json::Value l2aArray; + TestValue l2aArray; l2aArray.append(0); l2aArray.append(0); - Json::Value l2bArray; + TestValue l2bArray; l2bArray.append(0); l2bArray.append(10); JSONTEST_ASSERT_PRED(checkIsLess(emptyArray, l1aArray)); JSONTEST_ASSERT_PRED(checkIsLess(emptyArray, l2aArray)); JSONTEST_ASSERT_PRED(checkIsLess(l1aArray, l2aArray)); JSONTEST_ASSERT_PRED(checkIsLess(l2aArray, l2bArray)); - JSONTEST_ASSERT_PRED(checkIsEqual(emptyArray, Json::Value(emptyArray))); - JSONTEST_ASSERT_PRED(checkIsEqual(l1aArray, Json::Value(l1aArray))); - JSONTEST_ASSERT_PRED(checkIsEqual(l2bArray, Json::Value(l2bArray))); + JSONTEST_ASSERT_PRED(checkIsEqual(emptyArray, TestValue(emptyArray))); + JSONTEST_ASSERT_PRED(checkIsEqual(l1aArray, TestValue(l1aArray))); + JSONTEST_ASSERT_PRED(checkIsEqual(l2bArray, TestValue(l2bArray))); } JSONTEST_FIXTURE(ValueTest, compareObject) { // object compare size then content - Json::Value emptyObject(Json::objectValue); - Json::Value l1aObject; + TestValue emptyObject(Json::objectValue); + TestValue l1aObject; l1aObject["key1"] = 0; - Json::Value l1bObject; + TestValue l1bObject; l1aObject["key1"] = 10; - Json::Value l2aObject; + TestValue l2aObject; l2aObject["key1"] = 0; l2aObject["key2"] = 0; JSONTEST_ASSERT_PRED(checkIsLess(emptyObject, l1aObject)); JSONTEST_ASSERT_PRED(checkIsLess(emptyObject, l2aObject)); JSONTEST_ASSERT_PRED(checkIsLess(l1aObject, l2aObject)); - JSONTEST_ASSERT_PRED(checkIsEqual(emptyObject, Json::Value(emptyObject))); - JSONTEST_ASSERT_PRED(checkIsEqual(l1aObject, Json::Value(l1aObject))); - JSONTEST_ASSERT_PRED(checkIsEqual(l2aObject, Json::Value(l2aObject))); + JSONTEST_ASSERT_PRED(checkIsEqual(emptyObject, TestValue(emptyObject))); + JSONTEST_ASSERT_PRED(checkIsEqual(l1aObject, TestValue(l1aObject))); + JSONTEST_ASSERT_PRED(checkIsEqual(l2aObject, TestValue(l2aObject))); } JSONTEST_FIXTURE(ValueTest, compareType) { // object of different type are ordered according to their type - JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(), Json::Value(1))); - JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1), Json::Value(1u))); - JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1u), Json::Value(1.0))); - JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1.0), Json::Value("a"))); - JSONTEST_ASSERT_PRED(checkIsLess(Json::Value("a"), Json::Value(true))); + JSONTEST_ASSERT_PRED(checkIsLess(TestValue(), TestValue(1))); + JSONTEST_ASSERT_PRED(checkIsLess(TestValue(1), TestValue(1u))); + JSONTEST_ASSERT_PRED(checkIsLess(TestValue(1u), TestValue(1.0))); + JSONTEST_ASSERT_PRED(checkIsLess(TestValue(1.0), TestValue("a"))); + JSONTEST_ASSERT_PRED(checkIsLess(TestValue("a"), TestValue(true))); JSONTEST_ASSERT_PRED( - checkIsLess(Json::Value(true), Json::Value(Json::arrayValue))); - JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(Json::arrayValue), - Json::Value(Json::objectValue))); + checkIsLess(TestValue(true), TestValue(Json::arrayValue))); + JSONTEST_ASSERT_PRED(checkIsLess(TestValue(Json::arrayValue), + TestValue(Json::objectValue))); } -void ValueTest::checkIsLess(const Json::Value& x, const Json::Value& y) { +void ValueTest::checkIsLess(const TestValue& x, const TestValue& y) { JSONTEST_ASSERT(x < y); JSONTEST_ASSERT(y > x); JSONTEST_ASSERT(x <= y); @@ -1435,7 +1444,7 @@ void ValueTest::checkIsLess(const Json::Value& x, const Json::Value& y) { JSONTEST_ASSERT(y.compare(x) >= 0); } -void ValueTest::checkIsEqual(const Json::Value& x, const Json::Value& y) { +void ValueTest::checkIsEqual(const TestValue& x, const TestValue& y) { JSONTEST_ASSERT(x == y); JSONTEST_ASSERT(y == x); JSONTEST_ASSERT(x <= y); @@ -1453,10 +1462,10 @@ void ValueTest::checkIsEqual(const Json::Value& x, const Json::Value& y) { JSONTEST_FIXTURE(ValueTest, typeChecksThrowExceptions) { #if JSON_USE_EXCEPTION - Json::Value intVal(1); - Json::Value strVal("Test"); - Json::Value objVal(Json::objectValue); - Json::Value arrVal(Json::arrayValue); + TestValue intVal(1); + TestValue strVal("Test"); + TestValue objVal(Json::objectValue); + TestValue arrVal(Json::arrayValue); JSONTEST_ASSERT_THROWS(intVal["test"]); JSONTEST_ASSERT_THROWS(strVal["test"]); @@ -1517,17 +1526,17 @@ JSONTEST_FIXTURE(ValueTest, typeChecksThrowExceptions) { } JSONTEST_FIXTURE(ValueTest, offsetAccessors) { - Json::Value x; + TestValue x; JSONTEST_ASSERT(x.getOffsetStart() == 0); JSONTEST_ASSERT(x.getOffsetLimit() == 0); x.setOffsetStart(10); x.setOffsetLimit(20); JSONTEST_ASSERT(x.getOffsetStart() == 10); JSONTEST_ASSERT(x.getOffsetLimit() == 20); - Json::Value y(x); + TestValue y(x); JSONTEST_ASSERT(y.getOffsetStart() == 10); JSONTEST_ASSERT(y.getOffsetLimit() == 20); - Json::Value z; + TestValue z; z.swap(y); JSONTEST_ASSERT(z.getOffsetStart() == 10); JSONTEST_ASSERT(z.getOffsetLimit() == 20); @@ -1543,14 +1552,14 @@ JSONTEST_FIXTURE(ValueTest, StaticString) { JSONTEST_ASSERT_STRING_EQUAL("hallo", ss.c_str()); JSONTEST_ASSERT_STRING_EQUAL("hello", regular.c_str()); { - Json::Value root; + TestValue root; root["top"] = ss; JSONTEST_ASSERT_STRING_EQUAL("hallo", root["top"].asString()); mutant[1] = 'u'; JSONTEST_ASSERT_STRING_EQUAL("hullo", root["top"].asString()); } { - Json::Value root; + TestValue root; root["top"] = regular; JSONTEST_ASSERT_STRING_EQUAL("hello", root["top"].asString()); mutant[1] = 'u'; @@ -1559,9 +1568,9 @@ JSONTEST_FIXTURE(ValueTest, StaticString) { } JSONTEST_FIXTURE(ValueTest, CommentBefore) { - Json::Value val; // fill val + TestValue val; // fill val val.setComment(std::string("// this comment should appear before"), Json::commentBefore); - Json::StreamWriterBuilder wbuilder; + TestStreamWriterBuilder wbuilder; wbuilder.settings_["commentStyle"] = "All"; { char const expected[] = "// this comment should appear before\nnull"; @@ -1573,7 +1582,7 @@ JSONTEST_FIXTURE(ValueTest, CommentBefore) { exp2 += "\n"; JSONTEST_ASSERT_STRING_EQUAL(exp2, res2); } - Json::Value other = "hello"; + TestValue other = "hello"; val.swapPayload(other); { char const expected[] = "// this comment should appear before\n\"hello\""; @@ -1605,18 +1614,18 @@ JSONTEST_FIXTURE(ValueTest, zeroes) { char const cstr[] = "h\0i"; std::string binary(cstr, sizeof(cstr)); // include trailing 0 JSONTEST_ASSERT_EQUAL(4U, binary.length()); - Json::StreamWriterBuilder b; + TestStreamWriterBuilder b; { - Json::Value root; + TestValue root; root = binary; JSONTEST_ASSERT_STRING_EQUAL(binary, root.asString()); } { char const top[] = "top"; - Json::Value root; + TestValue root; root[top] = binary; JSONTEST_ASSERT_STRING_EQUAL(binary, root[top].asString()); - Json::Value removed; + TestValue removed; bool did; did = root.removeMember(top, top + sizeof(top) - 1U, &removed); @@ -1634,13 +1643,13 @@ JSONTEST_FIXTURE(ValueTest, zeroesInKeys) { std::string binary(cstr, sizeof(cstr)); // include trailing 0 JSONTEST_ASSERT_EQUAL(4U, binary.length()); { - Json::Value root; + TestValue root; root[binary] = "there"; JSONTEST_ASSERT_STRING_EQUAL("there", root[binary].asString()); JSONTEST_ASSERT(!root.isMember("h")); JSONTEST_ASSERT(root.isMember(binary)); - JSONTEST_ASSERT_STRING_EQUAL("there", root.get(binary, Json::Value::nullRef).asString()); - Json::Value removed; + JSONTEST_ASSERT_STRING_EQUAL("there", root.get(binary, TestValue::nullRef).asString()); + TestValue removed; bool did; did = root.removeMember(binary.data(), binary.data() + binary.length(), &removed); @@ -1651,15 +1660,15 @@ JSONTEST_FIXTURE(ValueTest, zeroesInKeys) { JSONTEST_ASSERT(!did); JSONTEST_ASSERT_STRING_EQUAL("there", removed.asString()); // still JSONTEST_ASSERT(!root.isMember(binary)); - JSONTEST_ASSERT_STRING_EQUAL("", root.get(binary, Json::Value::nullRef).asString()); + JSONTEST_ASSERT_STRING_EQUAL("", root.get(binary, TestValue::nullRef).asString()); } } JSONTEST_FIXTURE(ValueTest, specialFloats) { - Json::StreamWriterBuilder b; + TestStreamWriterBuilder b; b.settings_["useSpecialFloats"] = true; - Json::Value v = std::numeric_limits::quiet_NaN(); + TestValue v = std::numeric_limits::quiet_NaN(); std::string expected = "NaN"; std::string result = Json::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); @@ -1676,10 +1685,10 @@ JSONTEST_FIXTURE(ValueTest, specialFloats) { } JSONTEST_FIXTURE(ValueTest, precision) { - Json::StreamWriterBuilder b; + TestStreamWriterBuilder b; b.settings_["precision"] = 5; - Json::Value v = 100.0/3; + TestValue v = 100.0/3; std::string expected = "33.333"; std::string result = Json::writeString(b, v); JSONTEST_ASSERT_STRING_EQUAL(expected, result); @@ -1715,8 +1724,8 @@ JSONTEST_FIXTURE(ValueTest, precision) { struct WriterTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) { - Json::FastWriter writer; - Json::Value nullValue; + TestFastWriter writer; + TestValue nullValue; JSONTEST_ASSERT(writer.write(nullValue) == "null\n"); writer.dropNullPlaceholders(); @@ -1726,8 +1735,8 @@ JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) { struct StreamWriterTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(StreamWriterTest, dropNullPlaceholders) { - Json::StreamWriterBuilder b; - Json::Value nullValue; + TestStreamWriterBuilder b; + TestValue nullValue; b.settings_["dropNullPlaceholders"] = false; JSONTEST_ASSERT(Json::writeString(b, nullValue) == "null"); b.settings_["dropNullPlaceholders"] = true; @@ -1738,9 +1747,9 @@ JSONTEST_FIXTURE(StreamWriterTest, writeZeroes) { std::string binary("hi", 3); // include trailing 0 JSONTEST_ASSERT_EQUAL(3, binary.length()); std::string expected("\"hi\\u0000\""); // unicoded zero - Json::StreamWriterBuilder b; + TestStreamWriterBuilder b; { - Json::Value root; + TestValue root; root = binary; JSONTEST_ASSERT_STRING_EQUAL(binary, root.asString()); std::string out = Json::writeString(b, root); @@ -1748,7 +1757,7 @@ JSONTEST_FIXTURE(StreamWriterTest, writeZeroes) { JSONTEST_ASSERT_STRING_EQUAL(expected, out); } { - Json::Value root; + TestValue root; root["top"] = binary; JSONTEST_ASSERT_STRING_EQUAL(binary, root["top"].asString()); std::string out = Json::writeString(b, root["top"]); @@ -1759,8 +1768,8 @@ JSONTEST_FIXTURE(StreamWriterTest, writeZeroes) { struct ReaderTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) { - Json::Reader reader; - Json::Value root; + TestReader reader; + TestValue root; bool ok = reader.parse("{ \"property\" : \"value\" }", root); JSONTEST_ASSERT(ok); JSONTEST_ASSERT(reader.getFormattedErrorMessages().size() == 0); @@ -1768,8 +1777,8 @@ JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) { } JSONTEST_FIXTURE(ReaderTest, parseWithNoErrorsTestingOffsets) { - Json::Reader reader; - Json::Value root; + TestReader reader; + TestValue root; bool ok = reader.parse("{ \"property\" : [\"value\", \"value2\"], \"obj\" : " "{ \"nested\" : 123, \"bool\" : true}, \"null\" : " "null, \"false\" : false }", @@ -1798,14 +1807,14 @@ JSONTEST_FIXTURE(ReaderTest, parseWithNoErrorsTestingOffsets) { } JSONTEST_FIXTURE(ReaderTest, parseWithOneError) { - Json::Reader reader; - Json::Value root; + TestReader reader; + TestValue root; bool ok = reader.parse("{ \"property\" :: \"value\" }", root); JSONTEST_ASSERT(!ok); JSONTEST_ASSERT(reader.getFormattedErrorMessages() == "* Line 1, Column 15\n Syntax error: value, object or array " "expected.\n"); - std::vector errors = + std::vector errors = reader.getStructuredErrors(); JSONTEST_ASSERT(errors.size() == 1); JSONTEST_ASSERT(errors.at(0).offset_start == 14); @@ -1815,14 +1824,14 @@ JSONTEST_FIXTURE(ReaderTest, parseWithOneError) { } JSONTEST_FIXTURE(ReaderTest, parseChineseWithOneError) { - Json::Reader reader; - Json::Value root; + TestReader reader; + TestValue root; bool ok = reader.parse("{ \"pr佐藤erty\" :: \"value\" }", root); JSONTEST_ASSERT(!ok); JSONTEST_ASSERT(reader.getFormattedErrorMessages() == "* Line 1, Column 19\n Syntax error: value, object or array " "expected.\n"); - std::vector errors = + std::vector errors = reader.getStructuredErrors(); JSONTEST_ASSERT(errors.size() == 1); JSONTEST_ASSERT(errors.at(0).offset_start == 18); @@ -1832,14 +1841,14 @@ JSONTEST_FIXTURE(ReaderTest, parseChineseWithOneError) { } JSONTEST_FIXTURE(ReaderTest, parseWithDetailError) { - Json::Reader reader; - Json::Value root; + TestReader reader; + TestValue root; bool ok = reader.parse("{ \"property\" : \"v\\alue\" }", root); JSONTEST_ASSERT(!ok); JSONTEST_ASSERT(reader.getFormattedErrorMessages() == "* Line 1, Column 16\n Bad escape sequence in string\nSee " "Line 1, Column 20 for detail.\n"); - std::vector errors = + std::vector errors = reader.getStructuredErrors(); JSONTEST_ASSERT(errors.size() == 1); JSONTEST_ASSERT(errors.at(0).offset_start == 15); @@ -1850,10 +1859,10 @@ JSONTEST_FIXTURE(ReaderTest, parseWithDetailError) { struct CharReaderTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrors) { - Json::CharReaderBuilder b; - Json::CharReader* reader(b.newCharReader()); + TestCharReaderBuilder b; + TestCharReader* reader(b.newCharReader()); std::string errs; - Json::Value root; + TestValue root; char const doc[] = "{ \"property\" : \"value\" }"; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -1864,10 +1873,10 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrors) { } JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrorsTestingOffsets) { - Json::CharReaderBuilder b; - Json::CharReader* reader(b.newCharReader()); + TestCharReaderBuilder b; + TestCharReader* reader(b.newCharReader()); std::string errs; - Json::Value root; + TestValue root; char const doc[] = "{ \"property\" : [\"value\", \"value2\"], \"obj\" : " "{ \"nested\" : 123, \"bool\" : true}, \"null\" : " @@ -1881,10 +1890,10 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrorsTestingOffsets) { } JSONTEST_FIXTURE(CharReaderTest, parseWithOneError) { - Json::CharReaderBuilder b; - Json::CharReader* reader(b.newCharReader()); + TestCharReaderBuilder b; + TestCharReader* reader(b.newCharReader()); std::string errs; - Json::Value root; + TestValue root; char const doc[] = "{ \"property\" :: \"value\" }"; bool ok = reader->parse( @@ -1898,10 +1907,10 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithOneError) { } JSONTEST_FIXTURE(CharReaderTest, parseChineseWithOneError) { - Json::CharReaderBuilder b; - Json::CharReader* reader(b.newCharReader()); + TestCharReaderBuilder b; + TestCharReader* reader(b.newCharReader()); std::string errs; - Json::Value root; + TestValue root; char const doc[] = "{ \"pr佐藤erty\" :: \"value\" }"; bool ok = reader->parse( @@ -1915,10 +1924,10 @@ JSONTEST_FIXTURE(CharReaderTest, parseChineseWithOneError) { } JSONTEST_FIXTURE(CharReaderTest, parseWithDetailError) { - Json::CharReaderBuilder b; - Json::CharReader* reader(b.newCharReader()); + TestCharReaderBuilder b; + TestCharReader* reader(b.newCharReader()); std::string errs; - Json::Value root; + TestValue root; char const doc[] = "{ \"property\" : \"v\\alue\" }"; bool ok = reader->parse( @@ -1932,13 +1941,13 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithDetailError) { } JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) { - Json::CharReaderBuilder b; - Json::Value root; + TestCharReaderBuilder b; + TestValue root; char const doc[] = "{ \"property\" : \"value\" }"; { b.settings_["stackLimit"] = 2; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -1950,7 +1959,7 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) { } { b.settings_["stackLimit"] = 1; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; JSONTEST_ASSERT_THROWS(reader->parse( doc, doc + std::strlen(doc), @@ -1962,13 +1971,13 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) { struct CharReaderStrictModeTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(CharReaderStrictModeTest, dupKeys) { - Json::CharReaderBuilder b; - Json::Value root; + TestCharReaderBuilder b; + TestValue root; char const doc[] = "{ \"property\" : \"value\", \"key\" : \"val1\", \"key\" : \"val2\" }"; { b.strictMode(&b.settings_); - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -1986,13 +1995,13 @@ struct CharReaderFailIfExtraTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) { // This is interpreted as a string value followed by a colon. - Json::CharReaderBuilder b; - Json::Value root; + TestCharReaderBuilder b; + TestValue root; char const doc[] = " \"property\" : \"value\" }"; { b.settings_["failIfExtra"] = false; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -2004,7 +2013,7 @@ JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) { } { b.settings_["failIfExtra"] = true; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -2019,7 +2028,7 @@ JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) { { b.settings_["failIfExtra"] = false; b.strictMode(&b.settings_); - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -2034,12 +2043,12 @@ JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) { } JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue107) { // This is interpretted as an int value followed by a colon. - Json::CharReaderBuilder b; - Json::Value root; + TestCharReaderBuilder b; + TestValue root; char const doc[] = "1:2:3"; b.settings_["failIfExtra"] = true; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -2053,13 +2062,13 @@ JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue107) { delete reader; } JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterObject) { - Json::CharReaderBuilder b; - Json::Value root; + TestCharReaderBuilder b; + TestValue root; { char const doc[] = "{ \"property\" : \"value\" } //trailing\n//comment\n"; b.settings_["failIfExtra"] = true; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -2071,12 +2080,12 @@ JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterObject) { } } JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterArray) { - Json::CharReaderBuilder b; - Json::Value root; + TestCharReaderBuilder b; + TestValue root; char const doc[] = "[ \"property\" , \"value\" ] //trailing\n//comment\n"; b.settings_["failIfExtra"] = true; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -2087,12 +2096,12 @@ JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterArray) { delete reader; } JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) { - Json::CharReaderBuilder b; - Json::Value root; + TestCharReaderBuilder b; + TestValue root; char const doc[] = " true /*trailing\ncomment*/"; b.settings_["failIfExtra"] = true; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); std::string errs; bool ok = reader->parse( doc, doc + std::strlen(doc), @@ -2105,11 +2114,11 @@ JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) { struct CharReaderAllowDropNullTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(CharReaderAllowDropNullTest, issue178) { - Json::CharReaderBuilder b; + TestCharReaderBuilder b; b.settings_["allowDroppedNullPlaceholders"] = true; - Json::Value root; + TestValue root; std::string errs; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); { char const doc[] = "{\"a\":,\"b\":true}"; bool ok = reader->parse( @@ -2257,11 +2266,11 @@ JSONTEST_FIXTURE(CharReaderAllowDropNullTest, issue178) { struct CharReaderAllowSingleQuotesTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(CharReaderAllowSingleQuotesTest, issue182) { - Json::CharReaderBuilder b; + TestCharReaderBuilder b; b.settings_["allowSingleQuotes"] = true; - Json::Value root; + TestValue root; std::string errs; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); { char const doc[] = "{'a':true,\"b\":true}"; bool ok = reader->parse( @@ -2290,11 +2299,11 @@ JSONTEST_FIXTURE(CharReaderAllowSingleQuotesTest, issue182) { struct CharReaderAllowZeroesTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(CharReaderAllowZeroesTest, issue176) { - Json::CharReaderBuilder b; + TestCharReaderBuilder b; b.settings_["allowSingleQuotes"] = true; - Json::Value root; + TestValue root; std::string errs; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); { char const doc[] = "{'a':true,\"b\":true}"; bool ok = reader->parse( @@ -2323,11 +2332,11 @@ JSONTEST_FIXTURE(CharReaderAllowZeroesTest, issue176) { struct CharReaderAllowSpecialFloatsTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(CharReaderAllowSpecialFloatsTest, issue209) { - Json::CharReaderBuilder b; + TestCharReaderBuilder b; b.settings_["allowSpecialFloats"] = true; - Json::Value root; + TestValue root; std::string errs; - Json::CharReader* reader(b.newCharReader()); + TestCharReader* reader(b.newCharReader()); { char const doc[] = "{\"a\":NaN,\"b\":Infinity,\"c\":-Infinity}"; bool ok = reader->parse( @@ -2399,8 +2408,8 @@ struct BuilderTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(BuilderTest, settings) { { - Json::Value errs; - Json::CharReaderBuilder rb; + TestValue errs; + TestCharReaderBuilder rb; JSONTEST_ASSERT_EQUAL(false, rb.settings_.isMember("foo")); JSONTEST_ASSERT_EQUAL(true, rb.validate(&errs)); rb["foo"] = "bar"; @@ -2408,8 +2417,8 @@ JSONTEST_FIXTURE(BuilderTest, settings) { JSONTEST_ASSERT_EQUAL(false, rb.validate(&errs)); } { - Json::Value errs; - Json::StreamWriterBuilder wb; + TestValue errs; + TestStreamWriterBuilder wb; JSONTEST_ASSERT_EQUAL(false, wb.settings_.isMember("foo")); JSONTEST_ASSERT_EQUAL(true, wb.validate(&errs)); wb["foo"] = "bar"; @@ -2421,12 +2430,12 @@ JSONTEST_FIXTURE(BuilderTest, settings) { struct IteratorTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(IteratorTest, distance) { - Json::Value json; + TestValue json; json["k1"] = "a"; json["k2"] = "b"; int dist = 0; std::string str; - for (Json::ValueIterator it = json.begin(); it != json.end(); ++it) { + for (TestValueIterator it = json.begin(); it != json.end(); ++it) { dist = it - json.begin(); str = it->asString().c_str(); } @@ -2435,17 +2444,17 @@ JSONTEST_FIXTURE(IteratorTest, distance) { } JSONTEST_FIXTURE(IteratorTest, names) { - Json::Value json; + TestValue json; json["k1"] = "a"; json["k2"] = "b"; - Json::ValueIterator it = json.begin(); + TestValueIterator it = json.begin(); JSONTEST_ASSERT(it != json.end()); - JSONTEST_ASSERT_EQUAL(Json::Value("k1"), it.key()); + JSONTEST_ASSERT_EQUAL(TestValue("k1"), it.key()); JSONTEST_ASSERT_STRING_EQUAL("k1", it.name()); JSONTEST_ASSERT_EQUAL(-1, it.index()); ++it; JSONTEST_ASSERT(it != json.end()); - JSONTEST_ASSERT_EQUAL(Json::Value("k2"), it.key()); + JSONTEST_ASSERT_EQUAL(TestValue("k2"), it.key()); JSONTEST_ASSERT_STRING_EQUAL("k2", it.name()); JSONTEST_ASSERT_EQUAL(-1, it.index()); ++it; @@ -2453,17 +2462,17 @@ JSONTEST_FIXTURE(IteratorTest, names) { } JSONTEST_FIXTURE(IteratorTest, indexes) { - Json::Value json; + TestValue json; json[0] = "a"; json[1] = "b"; - Json::ValueIterator it = json.begin(); + TestValueIterator it = json.begin(); JSONTEST_ASSERT(it != json.end()); - JSONTEST_ASSERT_EQUAL(Json::Value(Json::ArrayIndex(0)), it.key()); + JSONTEST_ASSERT_EQUAL(TestValue(Json::ArrayIndex(0)), it.key()); JSONTEST_ASSERT_STRING_EQUAL("", it.name()); JSONTEST_ASSERT_EQUAL(0, it.index()); ++it; JSONTEST_ASSERT(it != json.end()); - JSONTEST_ASSERT_EQUAL(Json::Value(Json::ArrayIndex(1)), it.key()); + JSONTEST_ASSERT_EQUAL(TestValue(Json::ArrayIndex(1)), it.key()); JSONTEST_ASSERT_STRING_EQUAL("", it.name()); JSONTEST_ASSERT_EQUAL(1, it.index()); ++it; @@ -2471,12 +2480,12 @@ JSONTEST_FIXTURE(IteratorTest, indexes) { } JSONTEST_FIXTURE(IteratorTest, const) { - Json::Value const v; + TestValue const v; JSONTEST_ASSERT_THROWS( - Json::Value::iterator it(v.begin()) // Compile, but throw. + TestValue::iterator it(v.begin()) // Compile, but throw. ); - Json::Value value; + TestValue value; for(int i = 9; i < 12; ++i) { @@ -2488,7 +2497,7 @@ JSONTEST_FIXTURE(IteratorTest, const) { std::ostringstream out; //in old code, this will get a compile error - Json::Value::const_iterator iter = value.begin(); + TestValue::const_iterator iter = value.begin(); for(; iter != value.end(); ++iter) { out << *iter << ','; @@ -2501,15 +2510,29 @@ struct RValueTest : JsonTest::TestCase {}; JSONTEST_FIXTURE(RValueTest, moveConstruction) { #if JSON_HAS_RVALUE_REFERENCES - Json::Value json; + TestValue json; json["key"] = "value"; - Json::Value moved = std::move(json); + TestValue moved = std::move(json); JSONTEST_ASSERT(moved != json); // Possibly not nullValue; definitely not equal. JSONTEST_ASSERT_EQUAL(Json::objectValue, moved.type()); JSONTEST_ASSERT_EQUAL(Json::stringValue, moved["key"].type()); #endif } +struct TestSecureAllocator : JsonTest::TestCase {}; + +JSONTEST_FIXTURE(TestSecureAllocator, secureDestruction) { + using TestSecureValue = Json::Value, Json::SecureAllocator>; + + TestSecureValue myValue; + myValue["foo"] = "bar baz"; + JSONTEST_ASSERT_EQUAL(Json::stringValue, myValue["foo"]); + JSONTEST_ASSERT_EQUAL(TestSecureValue("bar baz"), myValue["foo"]); + + myValue["bar"] = 1234; + JSONTEST_ASSERT_EQUAL(TestSecureValue(1234), myValue["bar"]); +} + int main(int argc, const char* argv[]) { JsonTest::Runner runner; JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr); @@ -2585,5 +2608,7 @@ int main(int argc, const char* argv[]) { JSONTEST_REGISTER_FIXTURE(runner, RValueTest, moveConstruction); + JSONTEST_REGISTER_FIXTURE(runner, TestSecureAllocator, secureDestruction); + return runner.runCommandLine(argc, argv); }