diff --git a/Utilities/cmjsoncpp/LICENSE b/Utilities/cmjsoncpp/LICENSE new file mode 100644 index 000000000..ca2bfe1a0 --- /dev/null +++ b/Utilities/cmjsoncpp/LICENSE @@ -0,0 +1,55 @@ +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. diff --git a/Utilities/cmjsoncpp/include/json/assertions.h b/Utilities/cmjsoncpp/include/json/assertions.h new file mode 100644 index 000000000..5ef7e7bb7 --- /dev/null +++ b/Utilities/cmjsoncpp/include/json/assertions.h @@ -0,0 +1,41 @@ +// 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_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +#if JSON_USE_EXCEPTION +#include +#define JSON_ASSERT(condition) \ + assert(condition); // @todo <= change this into an exception throw +#define JSON_FAIL_MESSAGE(message) throw std::runtime_error(message); +#else // JSON_USE_EXCEPTION +#define JSON_ASSERT(condition) assert(condition); + +// The call to assert() will show the failure message in debug builds. In +// release bugs we write to invalid memory in order to crash hard, so that a +// debugger or crash reporter gets the chance to take over. We still call exit() +// afterward in order to tell the compiler that this macro doesn't return. +#define JSON_FAIL_MESSAGE(message) \ + { \ + assert(false&& message); \ + strcpy(reinterpret_cast(666), message); \ + exit(123); \ + } + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message) \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED diff --git a/Utilities/cmjsoncpp/include/json/config.h b/Utilities/cmjsoncpp/include/json/config.h new file mode 100644 index 000000000..afd3a4560 --- /dev/null +++ b/Utilities/cmjsoncpp/include/json/config.h @@ -0,0 +1,112 @@ +// 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_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 +/// If defined, indicates that Json specific container should be used +/// (hash table & simple deque container with customizable allocator). +/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 +//# define JSON_VALUE_USE_INTERNAL_MAP 1 +/// Force usage of standard new/malloc based allocator instead of memory pool +/// based allocator. +/// The memory pools allocator used optimization (initializing Value and +/// ValueInternalLink +/// as if it was a POD) that may cause some validation tool to report errors. +/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. +//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED diff --git a/Utilities/cmjsoncpp/include/json/features.h b/Utilities/cmjsoncpp/include/json/features.h new file mode 100644 index 000000000..1bb7bb614 --- /dev/null +++ b/Utilities/cmjsoncpp/include/json/features.h @@ -0,0 +1,57 @@ +// 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_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_; + + /// \c true if dropped null placeholders are allowed. Default: \c false. + bool allowDroppedNullPlaceholders_; + + /// \c true if numeric object key are allowed. Default: \c false. + bool allowNumericKeys_; +}; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/Utilities/cmjsoncpp/include/json/forwards.h b/Utilities/cmjsoncpp/include/json/forwards.h new file mode 100644 index 000000000..84a26cd2f --- /dev/null +++ b/Utilities/cmjsoncpp/include/json/forwards.h @@ -0,0 +1,43 @@ +// 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_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; +#ifdef JSON_VALUE_USE_INTERNAL_MAP +class ValueMapAllocator; +class ValueInternalLink; +class ValueInternalArray; +class ValueInternalMap; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED diff --git a/Utilities/cmjsoncpp/include/json/json.h b/Utilities/cmjsoncpp/include/json/json.h new file mode 100644 index 000000000..8f10ac2bf --- /dev/null +++ b/Utilities/cmjsoncpp/include/json/json.h @@ -0,0 +1,15 @@ +// 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_JSON_H_INCLUDED +#define JSON_JSON_H_INCLUDED + +#include "autolink.h" +#include "value.h" +#include "reader.h" +#include "writer.h" +#include "features.h" + +#endif // JSON_JSON_H_INCLUDED diff --git a/Utilities/cmjsoncpp/include/json/reader.h b/Utilities/cmjsoncpp/include/json/reader.h new file mode 100644 index 000000000..98814d50e --- /dev/null +++ b/Utilities/cmjsoncpp/include/json/reader.h @@ -0,0 +1,276 @@ +// 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_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#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. + * + */ +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 expectToken(TokenType type, Token& token, const char* message); + 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_; +}; + +/** \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 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<<() +*/ +JSON_API std::istream& operator>>(std::istream&, Value&); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/Utilities/cmjsoncpp/include/json/value.h b/Utilities/cmjsoncpp/include/json/value.h new file mode 100644 index 000000000..197a85614 --- /dev/null +++ b/Utilities/cmjsoncpp/include/json/value.h @@ -0,0 +1,1088 @@ +// 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_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#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 { + +/** \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 +}; + +//# 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) : str_(czstring) {} + + operator const char*() const { return str_; } + + const char* c_str() const { return str_; } + +private: + const char* 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 resize and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtanis 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. + */ +class JSON_API Value { + friend class ValueIteratorBase; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + friend class ValueInternalLink; + friend class ValueInternalMap; +#endif +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; + /// 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) + +private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +#ifndef JSON_VALUE_USE_INTERNAL_MAP + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(const char* cstr, DuplicationPolicy allocate); + CZString(const CZString& other); + ~CZString(); + CZString& operator=(CZString other); + bool operator<(const CZString& other) const; + bool operator==(const CZString& other) const; + ArrayIndex index() const; + const char* c_str() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + const char* cstr_; + ArrayIndex index_; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +#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); + Value(const char* beginValue, const char* endValue); + /** \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. + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * \endcode + */ + Value(const StaticString& value); + Value(const std::string& value); +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + Value(const Value& other); + ~Value(); + + Value& operator=(Value other); + /// Swap values. + /// \note Currently, comments are intentionally not swapped, for + /// both logic and efficiency. + void swap(Value& other); + + ValueType type() 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; + bool operator!=(const Value& other) const; + + int compare(const Value& other) const; + + const char* asCString() const; + std::string asString() 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.) + 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. + 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. + Value& operator[](const std::string& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + 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 as 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); +#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; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const std::string& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// \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 + Value removeMember(const char* key); + /// Same as removeMember(const char*) + Value removeMember(const std::string& key); + + /// Return true if the object has a member named key. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + bool isMember(const std::string& key) 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 + + /// Comments must be //... or /* ... */ + void setComment(const char* comment, 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, bool isStatic); + +#ifdef JSON_VALUE_USE_INTERNAL_MAP + inline bool isItemAvailable() const { return itemIsUsed_ == 0; } + + inline void setItemUsed(bool isUsed = true) { itemIsUsed_ = isUsed ? 1 : 0; } + + inline bool isMemberNameStatic() const { return memberNameIsStatic_ == 0; } + + inline void setMemberNameIsStatic(bool isStatic) { + memberNameIsStatic_ = isStatic ? 1 : 0; + } +#endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP + +private: + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text); + + 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_; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + ValueInternalArray* array_; + ValueInternalMap* map_; +#else + ObjectValues* map_; +#endif + } value_; + ValueType type_ : 8; + int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. +#ifdef JSON_VALUE_USE_INTERNAL_MAP + unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. + int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. +#endif + 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_; +}; + +#ifdef JSON_VALUE_USE_INTERNAL_MAP +/** \brief Allocator to customize Value internal map. + * Below is an example of a simple implementation (default implementation + actually + * use memory pool for speed). + * \code + class DefaultValueMapAllocator : public ValueMapAllocator + { + public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } + }; + * \endcode + */ +class JSON_API ValueMapAllocator { +public: + virtual ~ValueMapAllocator(); + virtual ValueInternalMap* newMap() = 0; + virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) = 0; + virtual void destructMap(ValueInternalMap* map) = 0; + virtual ValueInternalLink* allocateMapBuckets(unsigned int size) = 0; + virtual void releaseMapBuckets(ValueInternalLink* links) = 0; + virtual ValueInternalLink* allocateMapLink() = 0; + virtual void releaseMapLink(ValueInternalLink* link) = 0; +}; + +/** \brief ValueInternalMap hash-map bucket chain link (for internal use only). + * \internal previous_ & next_ allows for bidirectional traversal. + */ +class JSON_API ValueInternalLink { +public: + enum { + itemPerLink = 6 + }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. + enum InternalFlags { + flagAvailable = 0, + flagUsed = 1 + }; + + ValueInternalLink(); + + ~ValueInternalLink(); + + Value items_[itemPerLink]; + char* keys_[itemPerLink]; + ValueInternalLink* previous_; + ValueInternalLink* next_; +}; + +/** \brief A linked page based hash-table implementation used internally by + *Value. + * \internal ValueInternalMap is a tradional bucket based hash-table, with a + *linked + * list in each bucket to handle collision. There is an addional twist in that + * each node of the collision linked list is a page containing a fixed amount of + * value. This provides a better compromise between memory usage and speed. + * + * Each bucket is made up of a chained list of ValueInternalLink. The last + * link of a given bucket can be found in the 'previous_' field of the following + *bucket. + * The last link of the last bucket is stored in tailLink_ as it has no + *following bucket. + * Only the last link of a bucket may contains 'available' item. The last link + *always + * contains at least one element unless is it the bucket one very first link. + */ +class JSON_API ValueInternalMap { + friend class ValueIteratorBase; + friend class Value; + +public: + typedef unsigned int HashKey; + typedef unsigned int BucketIndex; + +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState { + IteratorState() : map_(0), link_(0), itemIndex_(0), bucketIndex_(0) {} + ValueInternalMap* map_; + ValueInternalLink* link_; + BucketIndex itemIndex_; + BucketIndex bucketIndex_; + }; +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalMap(); + ValueInternalMap(const ValueInternalMap& other); + ValueInternalMap& operator=(ValueInternalMap other); + ~ValueInternalMap(); + + void swap(ValueInternalMap& other); + + BucketIndex size() const; + + void clear(); + + bool reserveDelta(BucketIndex growth); + + bool reserve(BucketIndex newItemCount); + + const Value* find(const char* key) const; + + Value* find(const char* key); + + Value& resolveReference(const char* key, bool isStatic); + + void remove(const char* key); + + void doActualRemove(ValueInternalLink* link, + BucketIndex index, + BucketIndex bucketIndex); + + ValueInternalLink*& getLastLinkInBucket(BucketIndex bucketIndex); + + Value& setNewItem(const char* key, + bool isStatic, + ValueInternalLink* link, + BucketIndex index); + + Value& unsafeAdd(const char* key, bool isStatic, HashKey hashedKey); + + HashKey hash(const char* key) const; + + int compare(const ValueInternalMap& other) const; + +private: + void makeBeginIterator(IteratorState& it) const; + void makeEndIterator(IteratorState& it) const; + static bool equals(const IteratorState& x, const IteratorState& other); + static void increment(IteratorState& iterator); + static void incrementBucket(IteratorState& iterator); + static void decrement(IteratorState& iterator); + static const char* key(const IteratorState& iterator); + static const char* key(const IteratorState& iterator, bool& isStatic); + static Value& value(const IteratorState& iterator); + static int distance(const IteratorState& x, const IteratorState& y); + +private: + ValueInternalLink* buckets_; + ValueInternalLink* tailLink_; + BucketIndex bucketsSize_; + BucketIndex itemCount_; +}; + +/** \brief A simplified deque implementation used internally by Value. +* \internal +* It is based on a list of fixed "page", each page contains a fixed number of +*items. +* Instead of using a linked-list, a array of pointer is used for fast item +*look-up. +* Look-up for an element is as follow: +* - compute page index: pageIndex = itemIndex / itemsPerPage +* - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] +* +* Insertion is amortized constant time (only the array containing the index of +*pointers +* need to be reallocated when items are appended). +*/ +class JSON_API ValueInternalArray { + friend class Value; + friend class ValueIteratorBase; + +public: + enum { + itemsPerPage = 8 + }; // should be a power of 2 for fast divide and modulo. + typedef Value::ArrayIndex ArrayIndex; + typedef unsigned int PageIndex; + +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState // Must be a POD + { + IteratorState() : array_(0), currentPageIndex_(0), currentItemIndex_(0) {} + ValueInternalArray* array_; + Value** currentPageIndex_; + unsigned int currentItemIndex_; + }; +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalArray(); + ValueInternalArray(const ValueInternalArray& other); + ValueInternalArray& operator=(ValueInternalArray other); + ~ValueInternalArray(); + void swap(ValueInternalArray& other); + + void clear(); + void resize(ArrayIndex newSize); + + Value& resolveReference(ArrayIndex index); + + Value* find(ArrayIndex index) const; + + ArrayIndex size() const; + + int compare(const ValueInternalArray& other) const; + +private: + static bool equals(const IteratorState& x, const IteratorState& other); + static void increment(IteratorState& iterator); + static void decrement(IteratorState& iterator); + static Value& dereference(const IteratorState& iterator); + static Value& unsafeDereference(const IteratorState& iterator); + static int distance(const IteratorState& x, const IteratorState& y); + static ArrayIndex indexOf(const IteratorState& iterator); + void makeBeginIterator(IteratorState& it) const; + void makeEndIterator(IteratorState& it) const; + void makeIterator(IteratorState& it, ArrayIndex index) const; + + void makeIndexValid(ArrayIndex index); + + Value** pages_; + ArrayIndex size_; + PageIndex pageCount_; +}; + +/** \brief Experimental: do not use. Allocator to customize Value internal +array. + * Below is an example of a simple implementation (actual implementation use + * memory pool). + \code +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator +virtual ~DefaultValueArrayAllocator() +{ +} + +virtual ValueInternalArray *newArray() +{ + return new ValueInternalArray(); +} + +virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) +{ + return new ValueInternalArray( other ); +} + +virtual void destruct( ValueInternalArray *array ) +{ + delete array; +} + +virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex +&indexCount, + ValueInternalArray::PageIndex +minNewIndexCount ) +{ + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); +} +virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) +{ + if ( indexes ) + free( indexes ); +} + +virtual Value *allocateArrayPage() +{ + return static_cast( malloc( sizeof(Value) * +ValueInternalArray::itemsPerPage ) ); +} + +virtual void releaseArrayPage( Value *value ) +{ + if ( value ) + free( value ); +} +}; + \endcode + */ +class JSON_API ValueArrayAllocator { +public: + virtual ~ValueArrayAllocator(); + virtual ValueInternalArray* newArray() = 0; + virtual ValueInternalArray* newArrayCopy(const ValueInternalArray& other) = 0; + virtual void destructArray(ValueInternalArray* array) = 0; + /** \brief Reallocate array page index. + * Reallocates an array of pointer on each page. + * \param indexes [input] pointer on the current index. May be \c NULL. + * [output] pointer on the new index of at least + * \a minNewIndexCount pages. + * \param indexCount [input] current number of pages in the index. + * [output] number of page the reallocated index can handle. + * \b MUST be >= \a minNewIndexCount. + * \param minNewIndexCount Minimum number of page the new index must be able + * to + * handle. + */ + virtual void + reallocateArrayPageIndex(Value**& indexes, + ValueInternalArray::PageIndex& indexCount, + ValueInternalArray::PageIndex minNewIndexCount) = 0; + virtual void + releaseArrayPageIndex(Value** indexes, + ValueInternalArray::PageIndex indexCount) = 0; + virtual Value* allocateArrayPage() = 0; + virtual void releaseArrayPage(Value* value) = 0; +}; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +/** \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; + + ValueIteratorBase(); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +#else + ValueIteratorBase(const ValueInternalArray::IteratorState& state); + ValueIteratorBase(const ValueInternalMap::IteratorState& state); +#endif + + 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 computeDistance(other); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value. -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + const char* memberName() const; + +protected: + Value& 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: +#ifndef JSON_VALUE_USE_INTERNAL_MAP + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; +#else + union { + ValueInternalArray::IteratorState array_; + ValueInternalMap::IteratorState map_; + } iterator_; + bool isArray_; +#endif +}; + +/** \brief const iterator for object and array value. + * + */ +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(); + +private: +/*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); +#else + ValueConstIterator(const ValueInternalArray::IteratorState& state); + ValueConstIterator(const ValueInternalMap::IteratorState& state); +#endif +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \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(); + ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIterator(const Value::ObjectValues::iterator& current); +#else + ValueIterator(const ValueInternalArray::IteratorState& state); + ValueIterator(const ValueInternalMap::IteratorState& state); +#endif +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--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED diff --git a/Utilities/cmjsoncpp/include/json/version.h b/Utilities/cmjsoncpp/include/json/version.h new file mode 100644 index 000000000..6fe06824d --- /dev/null +++ b/Utilities/cmjsoncpp/include/json/version.h @@ -0,0 +1,14 @@ +// DO NOT EDIT. This file is generated by CMake from "version" +// and "version.h.in" files. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +# define JSON_VERSION_H_INCLUDED + +# define JSONCPP_VERSION_STRING "1.0.0" +# define JSONCPP_VERSION_MAJOR 1 +# define JSONCPP_VERSION_MINOR 0 +# define JSONCPP_VERSION_PATCH 0 +# define JSONCPP_VERSION_QUALIFIER +# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) + +#endif // JSON_VERSION_H_INCLUDED diff --git a/Utilities/cmjsoncpp/include/json/writer.h b/Utilities/cmjsoncpp/include/json/writer.h new file mode 100644 index 000000000..dc9e46f4b --- /dev/null +++ b/Utilities/cmjsoncpp/include/json/writer.h @@ -0,0 +1,213 @@ +// 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_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#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; + +/** \brief Abstract class for writers. + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual std::string write(const Value& 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 + */ +class JSON_API FastWriter : public Writer { +public: + FastWriter(); + virtual ~FastWriter() {} + + 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 + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + + std::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() + */ +class JSON_API StyledWriter : public Writer { +public: + StyledWriter(); + virtual ~StyledWriter() {} + +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. + */ + virtual std::string write(const Value& root); + +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); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::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() + */ +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); + +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); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; +}; + +#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); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED diff --git a/Utilities/cmjsoncpp/src/lib_json/json_batchallocator.h b/Utilities/cmjsoncpp/src/lib_json/json_batchallocator.h new file mode 100644 index 000000000..2fbef7a86 --- /dev/null +++ b/Utilities/cmjsoncpp/src/lib_json/json_batchallocator.h @@ -0,0 +1,121 @@ +// 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 JSONCPP_BATCHALLOCATOR_H_INCLUDED +#define JSONCPP_BATCHALLOCATOR_H_INCLUDED + +#include +#include + +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +namespace Json { + +/* Fast memory allocator. + * + * This memory allocator allocates memory for a batch of object (specified by + * the page size, the number of object in each page). + * + * It does not allow the destruction of a single object. All the allocated + * objects can be destroyed at once. The memory can be either released or reused + * for future allocation. + * + * The in-place new operator must be used to construct the object using the + * pointer returned by allocate. + */ +template +class BatchAllocator { +public: + BatchAllocator(unsigned int objectsPerPage = 255) + : freeHead_(0), objectsPerPage_(objectsPerPage) { + // printf( "Size: %d => %s\n", sizeof(AllocatedType), + // typeid(AllocatedType).name() ); + assert(sizeof(AllocatedType) * objectPerAllocation >= + sizeof(AllocatedType*)); // We must be able to store a slist in the + // object free space. + assert(objectsPerPage >= 16); + batches_ = allocateBatch(0); // allocated a dummy page + currentBatch_ = batches_; + } + + ~BatchAllocator() { + for (BatchInfo* batch = batches_; batch;) { + BatchInfo* nextBatch = batch->next_; + free(batch); + batch = nextBatch; + } + } + + /// allocate space for an array of objectPerAllocation object. + /// @warning it is the responsability of the caller to call objects + /// constructors. + AllocatedType* allocate() { + if (freeHead_) // returns node from free list. + { + AllocatedType* object = freeHead_; + freeHead_ = *(AllocatedType**)object; + return object; + } + if (currentBatch_->used_ == currentBatch_->end_) { + currentBatch_ = currentBatch_->next_; + while (currentBatch_ && currentBatch_->used_ == currentBatch_->end_) + currentBatch_ = currentBatch_->next_; + + if (!currentBatch_) // no free batch found, allocate a new one + { + currentBatch_ = allocateBatch(objectsPerPage_); + currentBatch_->next_ = batches_; // insert at the head of the list + batches_ = currentBatch_; + } + } + AllocatedType* allocated = currentBatch_->used_; + currentBatch_->used_ += objectPerAllocation; + return allocated; + } + + /// Release the object. + /// @warning it is the responsability of the caller to actually destruct the + /// object. + void release(AllocatedType* object) { + assert(object != 0); + *(AllocatedType**)object = freeHead_; + freeHead_ = object; + } + +private: + struct BatchInfo { + BatchInfo* next_; + AllocatedType* used_; + AllocatedType* end_; + AllocatedType buffer_[objectPerAllocation]; + }; + + // disabled copy constructor and assignement operator. + BatchAllocator(const BatchAllocator&); + void operator=(const BatchAllocator&); + + static BatchInfo* allocateBatch(unsigned int objectsPerPage) { + const unsigned int mallocSize = + sizeof(BatchInfo) - sizeof(AllocatedType) * objectPerAllocation + + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; + BatchInfo* batch = static_cast(malloc(mallocSize)); + batch->next_ = 0; + batch->used_ = batch->buffer_; + batch->end_ = batch->buffer_ + objectsPerPage; + return batch; + } + + BatchInfo* batches_; + BatchInfo* currentBatch_; + /// Head of a single linked list within the allocated space of freeed object + AllocatedType* freeHead_; + unsigned int objectsPerPage_; +}; + +} // namespace Json + +#endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION + +#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED diff --git a/Utilities/cmjsoncpp/src/lib_json/json_internalarray.inl b/Utilities/cmjsoncpp/src/lib_json/json_internalarray.inl new file mode 100644 index 000000000..9ee15e9db --- /dev/null +++ b/Utilities/cmjsoncpp/src/lib_json/json_internalarray.inl @@ -0,0 +1,360 @@ +// 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 + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueArrayAllocator::~ValueArrayAllocator() {} + +// ////////////////////////////////////////////////////////////////// +// class DefaultValueArrayAllocator +// ////////////////////////////////////////////////////////////////// +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueArrayAllocator : public ValueArrayAllocator { +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() {} + + virtual ValueInternalArray* newArray() { return new ValueInternalArray(); } + + virtual ValueInternalArray* newArrayCopy(const ValueInternalArray& other) { + return new ValueInternalArray(other); + } + + virtual void destructArray(ValueInternalArray* array) { delete array; } + + virtual void + reallocateArrayPageIndex(Value**& indexes, + ValueInternalArray::PageIndex& indexCount, + ValueInternalArray::PageIndex minNewIndexCount) { + ValueInternalArray::PageIndex newIndexCount = (indexCount * 3) / 2 + 1; + if (minNewIndexCount > newIndexCount) + newIndexCount = minNewIndexCount; + void* newIndexes = realloc(indexes, sizeof(Value*) * newIndexCount); + JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc."); + indexCount = newIndexCount; + indexes = static_cast(newIndexes); + } + virtual void releaseArrayPageIndex(Value** indexes, + ValueInternalArray::PageIndex indexCount) { + if (indexes) + free(indexes); + } + + virtual Value* allocateArrayPage() { + return static_cast( + malloc(sizeof(Value) * ValueInternalArray::itemsPerPage)); + } + + virtual void releaseArrayPage(Value* value) { + if (value) + free(value); + } +}; + +#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueArrayAllocator : public ValueArrayAllocator { +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() {} + + virtual ValueInternalArray* newArray() { + ValueInternalArray* array = arraysAllocator_.allocate(); + new (array) ValueInternalArray(); // placement new + return array; + } + + virtual ValueInternalArray* newArrayCopy(const ValueInternalArray& other) { + ValueInternalArray* array = arraysAllocator_.allocate(); + new (array) ValueInternalArray(other); // placement new + return array; + } + + virtual void destructArray(ValueInternalArray* array) { + if (array) { + array->~ValueInternalArray(); + arraysAllocator_.release(array); + } + } + + virtual void + reallocateArrayPageIndex(Value**& indexes, + ValueInternalArray::PageIndex& indexCount, + ValueInternalArray::PageIndex minNewIndexCount) { + ValueInternalArray::PageIndex newIndexCount = (indexCount * 3) / 2 + 1; + if (minNewIndexCount > newIndexCount) + newIndexCount = minNewIndexCount; + void* newIndexes = realloc(indexes, sizeof(Value*) * newIndexCount); + JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc."); + indexCount = newIndexCount; + indexes = static_cast(newIndexes); + } + virtual void releaseArrayPageIndex(Value** indexes, + ValueInternalArray::PageIndex indexCount) { + if (indexes) + free(indexes); + } + + virtual Value* allocateArrayPage() { + return static_cast(pagesAllocator_.allocate()); + } + + virtual void releaseArrayPage(Value* value) { + if (value) + pagesAllocator_.release(value); + } + +private: + BatchAllocator arraysAllocator_; + BatchAllocator pagesAllocator_; +}; +#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR + +static ValueArrayAllocator*& arrayAllocator() { + static DefaultValueArrayAllocator defaultAllocator; + static ValueArrayAllocator* arrayAllocator = &defaultAllocator; + return arrayAllocator; +} + +static struct DummyArrayAllocatorInitializer { + DummyArrayAllocatorInitializer() { + arrayAllocator(); // ensure arrayAllocator() statics are initialized before + // main(). + } +} dummyArrayAllocatorInitializer; + +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +bool ValueInternalArray::equals(const IteratorState& x, + const IteratorState& other) { + return x.array_ == other.array_ && + x.currentItemIndex_ == other.currentItemIndex_ && + x.currentPageIndex_ == other.currentPageIndex_; +} + +void ValueInternalArray::increment(IteratorState& it) { + JSON_ASSERT_MESSAGE( + it.array_ && (it.currentPageIndex_ - it.array_->pages_) * itemsPerPage + + it.currentItemIndex_ != + it.array_->size_, + "ValueInternalArray::increment(): moving iterator beyond end"); + ++(it.currentItemIndex_); + if (it.currentItemIndex_ == itemsPerPage) { + it.currentItemIndex_ = 0; + ++(it.currentPageIndex_); + } +} + +void ValueInternalArray::decrement(IteratorState& it) { + JSON_ASSERT_MESSAGE( + it.array_ && it.currentPageIndex_ == it.array_->pages_ && + it.currentItemIndex_ == 0, + "ValueInternalArray::decrement(): moving iterator beyond end"); + if (it.currentItemIndex_ == 0) { + it.currentItemIndex_ = itemsPerPage - 1; + --(it.currentPageIndex_); + } else { + --(it.currentItemIndex_); + } +} + +Value& ValueInternalArray::unsafeDereference(const IteratorState& it) { + return (*(it.currentPageIndex_))[it.currentItemIndex_]; +} + +Value& ValueInternalArray::dereference(const IteratorState& it) { + JSON_ASSERT_MESSAGE( + it.array_ && (it.currentPageIndex_ - it.array_->pages_) * itemsPerPage + + it.currentItemIndex_ < + it.array_->size_, + "ValueInternalArray::dereference(): dereferencing invalid iterator"); + return unsafeDereference(it); +} + +void ValueInternalArray::makeBeginIterator(IteratorState& it) const { + it.array_ = const_cast(this); + it.currentItemIndex_ = 0; + it.currentPageIndex_ = pages_; +} + +void ValueInternalArray::makeIterator(IteratorState& it, + ArrayIndex index) const { + it.array_ = const_cast(this); + it.currentItemIndex_ = index % itemsPerPage; + it.currentPageIndex_ = pages_ + index / itemsPerPage; +} + +void ValueInternalArray::makeEndIterator(IteratorState& it) const { + makeIterator(it, size_); +} + +ValueInternalArray::ValueInternalArray() : pages_(0), size_(0), pageCount_(0) {} + +ValueInternalArray::ValueInternalArray(const ValueInternalArray& other) + : pages_(0), size_(other.size_), pageCount_(0) { + PageIndex minNewPages = other.size_ / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex(pages_, pageCount_, minNewPages); + JSON_ASSERT_MESSAGE(pageCount_ >= minNewPages, + "ValueInternalArray::reserve(): bad reallocation"); + IteratorState itOther; + other.makeBeginIterator(itOther); + Value* value; + for (ArrayIndex index = 0; index < size_; ++index, increment(itOther)) { + if (index % itemsPerPage == 0) { + PageIndex pageIndex = index / itemsPerPage; + value = arrayAllocator()->allocateArrayPage(); + pages_[pageIndex] = value; + } + new (value) Value(dereference(itOther)); + } +} + +ValueInternalArray& ValueInternalArray::operator=(ValueInternalArray other) { + swap(other); + return *this; +} + +ValueInternalArray::~ValueInternalArray() { + // destroy all constructed items + IteratorState it; + IteratorState itEnd; + makeBeginIterator(it); + makeEndIterator(itEnd); + for (; !equals(it, itEnd); increment(it)) { + Value* value = &dereference(it); + value->~Value(); + } + // release all pages + PageIndex lastPageIndex = size_ / itemsPerPage; + for (PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex) + arrayAllocator()->releaseArrayPage(pages_[pageIndex]); + // release pages index + arrayAllocator()->releaseArrayPageIndex(pages_, pageCount_); +} + +void ValueInternalArray::swap(ValueInternalArray& other) { + Value** tempPages = pages_; + pages_ = other.pages_; + other.pages_ = tempPages; + ArrayIndex tempSize = size_; + size_ = other.size_; + other.size_ = tempSize; + PageIndex tempPageCount = pageCount_; + pageCount_ = other.pageCount_; + other.pageCount_ = tempPageCount; +} + +void ValueInternalArray::clear() { + ValueInternalArray dummy; + swap(dummy); +} + +void ValueInternalArray::resize(ArrayIndex newSize) { + if (newSize == 0) + clear(); + else if (newSize < size_) { + IteratorState it; + IteratorState itEnd; + makeIterator(it, newSize); + makeIterator(itEnd, size_); + for (; !equals(it, itEnd); increment(it)) { + Value* value = &dereference(it); + value->~Value(); + } + PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; + PageIndex lastPageIndex = size_ / itemsPerPage; + for (; pageIndex < lastPageIndex; ++pageIndex) + arrayAllocator()->releaseArrayPage(pages_[pageIndex]); + size_ = newSize; + } else if (newSize > size_) + resolveReference(newSize); +} + +void ValueInternalArray::makeIndexValid(ArrayIndex index) { + // Need to enlarge page index ? + if (index >= pageCount_ * itemsPerPage) { + PageIndex minNewPages = (index + 1) / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex(pages_, pageCount_, minNewPages); + JSON_ASSERT_MESSAGE(pageCount_ >= minNewPages, + "ValueInternalArray::reserve(): bad reallocation"); + } + + // Need to allocate new pages ? + ArrayIndex nextPageIndex = (size_ % itemsPerPage) != 0 + ? size_ - (size_ % itemsPerPage) + itemsPerPage + : size_; + if (nextPageIndex <= index) { + PageIndex pageIndex = nextPageIndex / itemsPerPage; + PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; + for (; pageToAllocate-- > 0; ++pageIndex) + pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); + } + + // Initialize all new entries + IteratorState it; + IteratorState itEnd; + makeIterator(it, size_); + size_ = index + 1; + makeIterator(itEnd, size_); + for (; !equals(it, itEnd); increment(it)) { + Value* value = &dereference(it); + new (value) Value(); // Construct a default value using placement new + } +} + +Value& ValueInternalArray::resolveReference(ArrayIndex index) { + if (index >= size_) + makeIndexValid(index); + return pages_[index / itemsPerPage][index % itemsPerPage]; +} + +Value* ValueInternalArray::find(ArrayIndex index) const { + if (index >= size_) + return 0; + return &(pages_[index / itemsPerPage][index % itemsPerPage]); +} + +ValueInternalArray::ArrayIndex ValueInternalArray::size() const { + return size_; +} + +int ValueInternalArray::distance(const IteratorState& x, + const IteratorState& y) { + return indexOf(y) - indexOf(x); +} + +ValueInternalArray::ArrayIndex +ValueInternalArray::indexOf(const IteratorState& iterator) { + if (!iterator.array_) + return ArrayIndex(-1); + return ArrayIndex((iterator.currentPageIndex_ - iterator.array_->pages_) * + itemsPerPage + + iterator.currentItemIndex_); +} + +int ValueInternalArray::compare(const ValueInternalArray& other) const { + int sizeDiff(size_ - other.size_); + if (sizeDiff != 0) + return sizeDiff; + + for (ArrayIndex index = 0; index < size_; ++index) { + int diff = pages_[index / itemsPerPage][index % itemsPerPage].compare( + other.pages_[index / itemsPerPage][index % itemsPerPage]); + if (diff != 0) + return diff; + } + return 0; +} + +} // namespace Json diff --git a/Utilities/cmjsoncpp/src/lib_json/json_internalmap.inl b/Utilities/cmjsoncpp/src/lib_json/json_internalmap.inl new file mode 100644 index 000000000..ef3f3302d --- /dev/null +++ b/Utilities/cmjsoncpp/src/lib_json/json_internalmap.inl @@ -0,0 +1,473 @@ +// 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 + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalMap +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/** \internal MUST be safely initialized using memset( this, 0, + * sizeof(ValueInternalLink) ); + * This optimization is used by the fast allocator. + */ +ValueInternalLink::ValueInternalLink() : previous_(0), next_(0) {} + +ValueInternalLink::~ValueInternalLink() { + for (int index = 0; index < itemPerLink; ++index) { + if (!items_[index].isItemAvailable()) { + if (!items_[index].isMemberNameStatic()) + free(keys_[index]); + } else + break; + } +} + +ValueMapAllocator::~ValueMapAllocator() {} + +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueMapAllocator : public ValueMapAllocator { +public: // overridden from ValueMapAllocator + virtual ValueInternalMap* newMap() { return new ValueInternalMap(); } + + virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) { + return new ValueInternalMap(other); + } + + virtual void destructMap(ValueInternalMap* map) { delete map; } + + virtual ValueInternalLink* allocateMapBuckets(unsigned int size) { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; } + + virtual ValueInternalLink* allocateMapLink() { + return new ValueInternalLink(); + } + + virtual void releaseMapLink(ValueInternalLink* link) { delete link; } +}; +#else +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueMapAllocator : public ValueMapAllocator { +public: // overridden from ValueMapAllocator + virtual ValueInternalMap* newMap() { + ValueInternalMap* map = mapsAllocator_.allocate(); + new (map) ValueInternalMap(); // placement new + return map; + } + + virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) { + ValueInternalMap* map = mapsAllocator_.allocate(); + new (map) ValueInternalMap(other); // placement new + return map; + } + + virtual void destructMap(ValueInternalMap* map) { + if (map) { + map->~ValueInternalMap(); + mapsAllocator_.release(map); + } + } + + virtual ValueInternalLink* allocateMapBuckets(unsigned int size) { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; } + + virtual ValueInternalLink* allocateMapLink() { + ValueInternalLink* link = linksAllocator_.allocate(); + memset(link, 0, sizeof(ValueInternalLink)); + return link; + } + + virtual void releaseMapLink(ValueInternalLink* link) { + link->~ValueInternalLink(); + linksAllocator_.release(link); + } + +private: + BatchAllocator mapsAllocator_; + BatchAllocator linksAllocator_; +}; +#endif + +static ValueMapAllocator*& mapAllocator() { + static DefaultValueMapAllocator defaultAllocator; + static ValueMapAllocator* mapAllocator = &defaultAllocator; + return mapAllocator; +} + +static struct DummyMapAllocatorInitializer { + DummyMapAllocatorInitializer() { + mapAllocator(); // ensure mapAllocator() statics are initialized before + // main(). + } +} dummyMapAllocatorInitializer; + +// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. + +/* +use linked list hash map. +buckets array is a container. +linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) +value have extra state: valid, available, deleted +*/ + +ValueInternalMap::ValueInternalMap() + : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) {} + +ValueInternalMap::ValueInternalMap(const ValueInternalMap& other) + : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) { + reserve(other.itemCount_); + IteratorState it; + IteratorState itEnd; + other.makeBeginIterator(it); + other.makeEndIterator(itEnd); + for (; !equals(it, itEnd); increment(it)) { + bool isStatic; + const char* memberName = key(it, isStatic); + const Value& aValue = value(it); + resolveReference(memberName, isStatic) = aValue; + } +} + +ValueInternalMap& ValueInternalMap::operator=(ValueInternalMap other) { + swap(other); + return *this; +} + +ValueInternalMap::~ValueInternalMap() { + if (buckets_) { + for (BucketIndex bucketIndex = 0; bucketIndex < bucketsSize_; + ++bucketIndex) { + ValueInternalLink* link = buckets_[bucketIndex].next_; + while (link) { + ValueInternalLink* linkToRelease = link; + link = link->next_; + mapAllocator()->releaseMapLink(linkToRelease); + } + } + mapAllocator()->releaseMapBuckets(buckets_); + } +} + +void ValueInternalMap::swap(ValueInternalMap& other) { + ValueInternalLink* tempBuckets = buckets_; + buckets_ = other.buckets_; + other.buckets_ = tempBuckets; + ValueInternalLink* tempTailLink = tailLink_; + tailLink_ = other.tailLink_; + other.tailLink_ = tempTailLink; + BucketIndex tempBucketsSize = bucketsSize_; + bucketsSize_ = other.bucketsSize_; + other.bucketsSize_ = tempBucketsSize; + BucketIndex tempItemCount = itemCount_; + itemCount_ = other.itemCount_; + other.itemCount_ = tempItemCount; +} + +void ValueInternalMap::clear() { + ValueInternalMap dummy; + swap(dummy); +} + +ValueInternalMap::BucketIndex ValueInternalMap::size() const { + return itemCount_; +} + +bool ValueInternalMap::reserveDelta(BucketIndex growth) { + return reserve(itemCount_ + growth); +} + +bool ValueInternalMap::reserve(BucketIndex newItemCount) { + if (!buckets_ && newItemCount > 0) { + buckets_ = mapAllocator()->allocateMapBuckets(1); + bucketsSize_ = 1; + tailLink_ = &buckets_[0]; + } + // BucketIndex idealBucketCount = (newItemCount + + // ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; + return true; +} + +const Value* ValueInternalMap::find(const char* key) const { + if (!bucketsSize_) + return 0; + HashKey hashedKey = hash(key); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for (const ValueInternalLink* current = &buckets_[bucketIndex]; current != 0; + current = current->next_) { + for (BucketIndex index = 0; index < ValueInternalLink::itemPerLink; + ++index) { + if (current->items_[index].isItemAvailable()) + return 0; + if (strcmp(key, current->keys_[index]) == 0) + return ¤t->items_[index]; + } + } + return 0; +} + +Value* ValueInternalMap::find(const char* key) { + const ValueInternalMap* constThis = this; + return const_cast(constThis->find(key)); +} + +Value& ValueInternalMap::resolveReference(const char* key, bool isStatic) { + HashKey hashedKey = hash(key); + if (bucketsSize_) { + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink** previous = 0; + BucketIndex index; + for (ValueInternalLink* current = &buckets_[bucketIndex]; current != 0; + previous = ¤t->next_, current = current->next_) { + for (index = 0; index < ValueInternalLink::itemPerLink; ++index) { + if (current->items_[index].isItemAvailable()) + return setNewItem(key, isStatic, current, index); + if (strcmp(key, current->keys_[index]) == 0) + return current->items_[index]; + } + } + } + + reserveDelta(1); + return unsafeAdd(key, isStatic, hashedKey); +} + +void ValueInternalMap::remove(const char* key) { + HashKey hashedKey = hash(key); + if (!bucketsSize_) + return; + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for (ValueInternalLink* link = &buckets_[bucketIndex]; link != 0; + link = link->next_) { + BucketIndex index; + for (index = 0; index < ValueInternalLink::itemPerLink; ++index) { + if (link->items_[index].isItemAvailable()) + return; + if (strcmp(key, link->keys_[index]) == 0) { + doActualRemove(link, index, bucketIndex); + return; + } + } + } +} + +void ValueInternalMap::doActualRemove(ValueInternalLink* link, + BucketIndex index, + BucketIndex bucketIndex) { + // find last item of the bucket and swap it with the 'removed' one. + // set removed items flags to 'available'. + // if last page only contains 'available' items, then desallocate it (it's + // empty) + ValueInternalLink*& lastLink = getLastLinkInBucket(index); + BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 + for (; lastItemIndex < ValueInternalLink::itemPerLink; + ++lastItemIndex) // may be optimized with dicotomic search + { + if (lastLink->items_[lastItemIndex].isItemAvailable()) + break; + } + + BucketIndex lastUsedIndex = lastItemIndex - 1; + Value* valueToDelete = &link->items_[index]; + Value* valueToPreserve = &lastLink->items_[lastUsedIndex]; + if (valueToDelete != valueToPreserve) + valueToDelete->swap(*valueToPreserve); + if (lastUsedIndex == 0) // page is now empty + { // remove it from bucket linked list and delete it. + ValueInternalLink* linkPreviousToLast = lastLink->previous_; + if (linkPreviousToLast != 0) // can not deleted bucket link. + { + mapAllocator()->releaseMapLink(lastLink); + linkPreviousToLast->next_ = 0; + lastLink = linkPreviousToLast; + } + } else { + Value dummy; + valueToPreserve->swap(dummy); // restore deleted to default Value. + valueToPreserve->setItemUsed(false); + } + --itemCount_; +} + +ValueInternalLink*& +ValueInternalMap::getLastLinkInBucket(BucketIndex bucketIndex) { + if (bucketIndex == bucketsSize_ - 1) + return tailLink_; + ValueInternalLink*& previous = buckets_[bucketIndex + 1].previous_; + if (!previous) + previous = &buckets_[bucketIndex]; + return previous; +} + +Value& ValueInternalMap::setNewItem(const char* key, + bool isStatic, + ValueInternalLink* link, + BucketIndex index) { + char* duplicatedKey = makeMemberName(key); + ++itemCount_; + link->keys_[index] = duplicatedKey; + link->items_[index].setItemUsed(); + link->items_[index].setMemberNameIsStatic(isStatic); + return link->items_[index]; // items already default constructed. +} + +Value& +ValueInternalMap::unsafeAdd(const char* key, bool isStatic, HashKey hashedKey) { + JSON_ASSERT_MESSAGE(bucketsSize_ > 0, + "ValueInternalMap::unsafeAdd(): internal logic error."); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink*& previousLink = getLastLinkInBucket(bucketIndex); + ValueInternalLink* link = previousLink; + BucketIndex index; + for (index = 0; index < ValueInternalLink::itemPerLink; ++index) { + if (link->items_[index].isItemAvailable()) + break; + } + if (index == ValueInternalLink::itemPerLink) // need to add a new page + { + ValueInternalLink* newLink = mapAllocator()->allocateMapLink(); + index = 0; + link->next_ = newLink; + previousLink = newLink; + link = newLink; + } + return setNewItem(key, isStatic, link, index); +} + +ValueInternalMap::HashKey ValueInternalMap::hash(const char* key) const { + HashKey hash = 0; + while (*key) + hash += *key++ * 37; + return hash; +} + +int ValueInternalMap::compare(const ValueInternalMap& other) const { + int sizeDiff(itemCount_ - other.itemCount_); + if (sizeDiff != 0) + return sizeDiff; + // Strict order guaranty is required. Compare all keys FIRST, then compare + // values. + IteratorState it; + IteratorState itEnd; + makeBeginIterator(it); + makeEndIterator(itEnd); + for (; !equals(it, itEnd); increment(it)) { + if (!other.find(key(it))) + return 1; + } + + // All keys are equals, let's compare values + makeBeginIterator(it); + for (; !equals(it, itEnd); increment(it)) { + const Value* otherValue = other.find(key(it)); + int valueDiff = value(it).compare(*otherValue); + if (valueDiff != 0) + return valueDiff; + } + return 0; +} + +void ValueInternalMap::makeBeginIterator(IteratorState& it) const { + it.map_ = const_cast(this); + it.bucketIndex_ = 0; + it.itemIndex_ = 0; + it.link_ = buckets_; +} + +void ValueInternalMap::makeEndIterator(IteratorState& it) const { + it.map_ = const_cast(this); + it.bucketIndex_ = bucketsSize_; + it.itemIndex_ = 0; + it.link_ = 0; +} + +bool ValueInternalMap::equals(const IteratorState& x, + const IteratorState& other) { + return x.map_ == other.map_ && x.bucketIndex_ == other.bucketIndex_ && + x.link_ == other.link_ && x.itemIndex_ == other.itemIndex_; +} + +void ValueInternalMap::incrementBucket(IteratorState& iterator) { + ++iterator.bucketIndex_; + JSON_ASSERT_MESSAGE( + iterator.bucketIndex_ <= iterator.map_->bucketsSize_, + "ValueInternalMap::increment(): attempting to iterate beyond end."); + if (iterator.bucketIndex_ == iterator.map_->bucketsSize_) + iterator.link_ = 0; + else + iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); + iterator.itemIndex_ = 0; +} + +void ValueInternalMap::increment(IteratorState& iterator) { + JSON_ASSERT_MESSAGE(iterator.map_, + "Attempting to iterator using invalid iterator."); + ++iterator.itemIndex_; + if (iterator.itemIndex_ == ValueInternalLink::itemPerLink) { + JSON_ASSERT_MESSAGE( + iterator.link_ != 0, + "ValueInternalMap::increment(): attempting to iterate beyond end."); + iterator.link_ = iterator.link_->next_; + if (iterator.link_ == 0) + incrementBucket(iterator); + } else if (iterator.link_->items_[iterator.itemIndex_].isItemAvailable()) { + incrementBucket(iterator); + } +} + +void ValueInternalMap::decrement(IteratorState& iterator) { + if (iterator.itemIndex_ == 0) { + JSON_ASSERT_MESSAGE(iterator.map_, + "Attempting to iterate using invalid iterator."); + if (iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_]) { + JSON_ASSERT_MESSAGE(iterator.bucketIndex_ > 0, + "Attempting to iterate beyond beginning."); + --(iterator.bucketIndex_); + } + iterator.link_ = iterator.link_->previous_; + iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; + } +} + +const char* ValueInternalMap::key(const IteratorState& iterator) { + JSON_ASSERT_MESSAGE(iterator.link_, + "Attempting to iterate using invalid iterator."); + return iterator.link_->keys_[iterator.itemIndex_]; +} + +const char* ValueInternalMap::key(const IteratorState& iterator, + bool& isStatic) { + JSON_ASSERT_MESSAGE(iterator.link_, + "Attempting to iterate using invalid iterator."); + isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); + return iterator.link_->keys_[iterator.itemIndex_]; +} + +Value& ValueInternalMap::value(const IteratorState& iterator) { + JSON_ASSERT_MESSAGE(iterator.link_, + "Attempting to iterate using invalid iterator."); + return iterator.link_->items_[iterator.itemIndex_]; +} + +int ValueInternalMap::distance(const IteratorState& x, const IteratorState& y) { + int offset = 0; + IteratorState it = x; + while (!equals(it, y)) + increment(it); + return offset; +} + +} // namespace Json diff --git a/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp new file mode 100644 index 000000000..c5111f8d7 --- /dev/null +++ b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp @@ -0,0 +1,885 @@ +// Copyright 2007-2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_(true), strictRoot_(false), + allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} + +Features Features::all() { return Features(); } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +static inline bool in(Reader::Char c, + Reader::Char c1, + Reader::Char c2, + Reader::Char c3, + Reader::Char c4) { + return c == c1 || c == c2 || c == c3 || c == c4; +} + +static inline bool in(Reader::Char c, + Reader::Char c1, + Reader::Char c2, + Reader::Char c3, + Reader::Char c4, + Reader::Char c5) { + return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; +} + +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); + + 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() { + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + // Remove newline characters at the end of the comments + size_t lastNonNewline = commentsBefore_.find_last_not_of("\r\n"); + if (lastNonNewline != std::string::npos) { + commentsBefore_.erase(lastNonNewline + 1); + } else { + commentsBefore_.clear(); + } + + 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: + currentValue() = true; + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + break; + case tokenFalse: + currentValue() = false; + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + break; + case tokenNull: + currentValue() = Value(); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + break; + case tokenArraySeparator: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + currentValue() = Value(); + 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(); + } + + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::expectToken(TokenType type, Token& token, const char* message) { + readToken(token); + if (token.type_ != type) + return addError(message, token); + return true; +} + +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; +} + +void +Reader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(std::string(begin, end), placement); + } else { + commentsBefore_ += std::string(begin, end); + } +} + +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 == '\r' || c == '\n') + break; + } + return true; +} + +void Reader::readNumber() { + while (current_ != end_) { + if (!(*current_ >= '0' && *current_ <= '9') && + !in(*current_, '.', 'e', 'E', '+', '-')) + break; + ++current_; + } +} + +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; + currentValue() = Value(objectValue); + 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) { + currentValue() = Value(arrayValue); + 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() = decoded; + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + bool isDouble = false; + for (Location inspect = token.start_; inspect != token.end_; ++inspect) { + isDouble = isDouble || in(*inspect, '.', 'e', 'E', '+') || + (*inspect == '-' && inspect != token.start_); + } + if (isDouble) + return decodeDouble(token, 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; + 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 addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + 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 Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue() = decoded; + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::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 Reader::decodeString(Token& token) { + std::string decoded; + if (!decodeString(token, decoded)) + return false; + currentValue() = 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]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) +#if defined(WINCE) + _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#else + sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif +#else + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif + 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(); +} + +std::istream& operator>>(std::istream& sin, Value& root) { + Json::Reader reader; + bool ok = reader.parse(sin, root, true); + if (!ok) { + fprintf(stderr, + "Error from reader: %s", + reader.getFormattedErrorMessages().c_str()); + + JSON_FAIL_MESSAGE("reader error"); + } + return sin; +} + +} // namespace Json diff --git a/Utilities/cmjsoncpp/src/lib_json/json_tool.h b/Utilities/cmjsoncpp/src/lib_json/json_tool.h new file mode 100644 index 000000000..f9b61c38c --- /dev/null +++ b/Utilities/cmjsoncpp/src/lib_json/json_tool.h @@ -0,0 +1,87 @@ +// 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 LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { + +/// Converts a unicode code-point to UTF-8. +static inline std::string codePointToUTF8(unsigned int cp) { + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +/// Returns true if ch is a control character (in range [0,32[). +static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + +/** 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 inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = char(value % 10) + '0'; + value /= 10; + } while (value != 0); +} + +/** 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 inline void fixNumericLocale(char* begin, char* end) { + while (begin < end) { + if (*begin == ',') { + *begin = '.'; + } + ++begin; + } +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED diff --git a/Utilities/cmjsoncpp/src/lib_json/json_value.cpp b/Utilities/cmjsoncpp/src/lib_json/json_value.cpp new file mode 100644 index 000000000..b73deac17 --- /dev/null +++ b/Utilities/cmjsoncpp/src/lib_json/json_value.cpp @@ -0,0 +1,1478 @@ +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#include "json_batchallocator.h" +#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +#include +#endif +#include // size_t + +#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 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); + +/// Unknown size marker +static const unsigned int unknown = (unsigned)-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, + unsigned int length = unknown) { + if (length == unknown) + length = (unsigned int)strlen(value); + + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= (unsigned)Value::maxInt) + length = Value::maxInt - 1; + + char* newString = static_cast(malloc(length + 1)); + JSON_ASSERT_MESSAGE(newString != 0, + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/** Free the string duplicated by duplicateStringValue(). + */ +static inline void releaseStringValue(char* value) { free(value); } + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) +#ifdef JSON_VALUE_USE_INTERNAL_MAP +#include "json_internalarray.inl" +#include "json_internalmap.inl" +#endif // JSON_VALUE_USE_INTERNAL_MAP + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() : comment_(0) {} + +Value::CommentInfo::~CommentInfo() { + if (comment_) + releaseStringValue(comment_); +} + +void Value::CommentInfo::setComment(const char* text) { + if (comment_) + releaseStringValue(comment_); + 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); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#ifndef JSON_VALUE_USE_INTERNAL_MAP + +// Notes: index_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {} + +Value::CZString::CZString(const char* cstr, DuplicationPolicy allocate) + : cstr_(allocate == duplicate ? duplicateStringValue(cstr) : cstr), + index_(allocate) {} + +Value::CZString::CZString(const CZString& other) + : cstr_(other.index_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue(other.cstr_) + : other.cstr_), + index_(other.cstr_ + ? static_cast(other.index_ == noDuplication + ? noDuplication : duplicate) + : other.index_) {} + +Value::CZString::~CZString() { + if (cstr_ && index_ == 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 strcmp(cstr_, other.cstr_) < 0; + return index_ < other.index_; +} + +bool Value::CZString::operator==(const CZString& other) const { + if (cstr_) + return strcmp(cstr_, other.cstr_) == 0; + return index_ == other.index_; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +const char* Value::CZString::c_str() const { return cstr_; } + +bool Value::CZString::isStaticString() const { return index_ == noDuplication; } + +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// 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 type) { + initBasic(type); + switch (type) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArray(); + break; + case objectValue: + value_.map_ = mapAllocator()->newMap(); + break; +#endif + 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_ = duplicateStringValue(value); +} + +Value::Value(const char* beginValue, const char* endValue) { + initBasic(stringValue, true); + value_.string_ = + duplicateStringValue(beginValue, (unsigned int)(endValue - beginValue)); +} + +Value::Value(const std::string& value) { + initBasic(stringValue, true); + value_.string_ = + duplicateStringValue(value.c_str(), (unsigned int)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_ = duplicateStringValue(value, value.length()); +} +#endif + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(const Value& other) + : type_(other.type_), allocated_(false) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , + itemIsUsed_(0) +#endif + , + 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_) { + value_.string_ = duplicateStringValue(other.value_.string_); + allocated_ = true; + } else { + value_.string_ = 0; + allocated_ = false; + } + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArrayCopy(*other.value_.array_); + break; + case objectValue: + value_.map_ = mapAllocator()->newMapCopy(*other.value_.map_); + break; +#endif + 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_); + } + } +} + +Value::~Value() { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (allocated_) + releaseStringValue(value_.string_); + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + delete value_.map_; + break; +#else + case arrayValue: + arrayAllocator()->destructArray(value_.array_); + break; + case objectValue: + mapAllocator()->destructMap(value_.map_); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + + if (comments_) + delete[] comments_; +} + +Value& Value::operator=(Value other) { + swap(other); + return *this; +} + +void Value::swap(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; + 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: + return (value_.string_ == 0 && other.value_.string_) || + (other.value_.string_ && value_.string_ && + strcmp(value_.string_, other.value_.string_) < 0); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + 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_); + } +#else + case arrayValue: + return value_.array_->compare(*(other.value_.array_)) < 0; + case objectValue: + return value_.map_->compare(*(other.value_.map_)) < 0; +#endif + 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: + return (value_.string_ == other.value_.string_) || + (other.value_.string_ && value_.string_ && + strcmp(value_.string_, other.value_.string_) == 0); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); +#else + case arrayValue: + return value_.array_->compare(*(other.value_.array_)) == 0; + case objectValue: + return value_.map_->compare(*(other.value_.map_)) == 0; +#endif + 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"); + return value_.string_; +} + +std::string Value::asString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + return value_.string_ ? value_.string_ : ""; + 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 { + return CppTL::ConstString(asString().c_str()); +} +#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: + return value_.real_ ? 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; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + 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()); +#else + case arrayValue: + return Int(value_.array_->size()); + case objectValue: + return Int(value_.map_->size()); +#endif + } + 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_) { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_->clear(); + break; +#else + case arrayValue: + value_.array_->clear(); + break; + case objectValue: + value_.map_->clear(); + break; +#endif + 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); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + 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); + } +#else + value_.array_->resize(newSize); +#endif +} + +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); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + 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, null); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +#else + return value_.array_->resolveReference(index); +#endif +} + +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 null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return null; + return (*it).second; +#else + Value* value = value_.array_->find(index); + return value ? *value : null; +#endif +} + +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)]; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, false); +} + +void Value::initBasic(ValueType type, bool allocated) { + type_ = type; + allocated_ = allocated; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + itemIsUsed_ = 0; +#endif + comments_ = 0; + start_ = 0; + limit_ = 0; +} + +Value& Value::resolveReference(const char* key, bool isStatic) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( + key, isStatic ? CZString::noDuplication : 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, null); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +#else + return value_.map_->resolveReference(key, isStatic); +#endif +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &null ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +const Value& Value::operator[](const char* key) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::operator[](char const*)const: requires objectValue"); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return null; + return (*it).second; +#else + const Value* value = value_.map_->find(key); + return value ? *value : null; +#endif +} + +Value& Value::operator[](const std::string& key) { + return (*this)[key.c_str()]; +} + +const Value& Value::operator[](const std::string& key) const { + return (*this)[key.c_str()]; +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key, true); +} + +#ifdef JSON_USE_CPPTL +Value& Value::operator[](const CppTL::ConstString& key) { + return (*this)[key.c_str()]; +} + +const Value& Value::operator[](const CppTL::ConstString& key) const { + return (*this)[key.c_str()]; +} +#endif + +Value& Value::append(const Value& value) { return (*this)[size()] = value; } + +Value Value::get(const char* key, const Value& defaultValue) const { + const Value* value = &((*this)[key]); + return value == &null ? defaultValue : *value; +} + +Value Value::get(const std::string& key, const Value& defaultValue) const { + return get(key.c_str(), defaultValue); +} + +Value Value::removeMember(const char* key) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return null; + Value old(it->second); + value_.map_->erase(it); + return old; +#else + Value* value = value_.map_->find(key); + if (value) { + Value old(*value); + value_.map_.remove(key); + return old; + } else { + return null; + } +#endif +} + +Value Value::removeMember(const std::string& key) { + return removeMember(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value Value::get(const CppTL::ConstString& key, + const Value& defaultValue) const { + return get(key.c_str(), defaultValue); +} +#endif + +bool Value::isMember(const char* key) const { + const Value* value = &((*this)[key]); + return value != &null; +} + +bool Value::isMember(const std::string& key) const { + return isMember(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +bool Value::isMember(const CppTL::ConstString& key) const { + return isMember(key.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()); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + 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.c_str())); +#else + ValueInternalMap::IteratorState it; + ValueInternalMap::IteratorState itEnd; + value_.map_->makeBeginIterator(it); + value_.map_->makeEndIterator(itEnd); + for (; !ValueInternalMap::equals(it, itEnd); ValueInternalMap::increment(it)) + members.push_back(std::string(ValueInternalMap::key(it))); +#endif + 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, CommentPlacement placement) { + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + comments_[placement].setComment(comment); +} + +void Value::setComment(const std::string& comment, CommentPlacement placement) { + setComment(comment.c_str(), 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_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator(it); + return const_iterator(it); + } + break; + case objectValue: + if (value_.map_) { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator(it); + return const_iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator(it); + return const_iterator(it); + } + break; + case objectValue: + if (value_.map_) { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator(it); + return const_iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator(it); + return iterator(it); + } + break; + case objectValue: + if (value_.map_) { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator(it); + return iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; +#endif + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator(it); + return iterator(it); + } + break; + case objectValue: + if (value_.map_) { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator(it); + return iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; +#endif + 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::null) { + // 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::null) + 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/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl b/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl new file mode 100644 index 000000000..a9f7df63a --- /dev/null +++ b/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl @@ -0,0 +1,241 @@ +// 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 + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() +#ifndef JSON_VALUE_USE_INTERNAL_MAP + : current_(), isNull_(true) { +} +#else + : isArray_(true), isNull_(true) { + iterator_.array_ = ValueInternalArray::IteratorState(); +} +#endif + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} +#else +ValueIteratorBase::ValueIteratorBase( + const ValueInternalArray::IteratorState& state) + : isArray_(true) { + iterator_.array_ = state; +} + +ValueIteratorBase::ValueIteratorBase( + const ValueInternalMap::IteratorState& state) + : isArray_(false) { + iterator_.map_ = state; +} +#endif + +Value& ValueIteratorBase::deref() const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + return current_->second; +#else + if (isArray_) + return ValueInternalArray::dereference(iterator_.array_); + return ValueInternalMap::value(iterator_.map_); +#endif +} + +void ValueIteratorBase::increment() { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ++current_; +#else + if (isArray_) + ValueInternalArray::increment(iterator_.array_); + ValueInternalMap::increment(iterator_.map_); +#endif +} + +void ValueIteratorBase::decrement() { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + --current_; +#else + if (isArray_) + ValueInternalArray::decrement(iterator_.array_); + ValueInternalMap::decrement(iterator_.map_); +#endif +} + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP +#ifdef JSON_USE_CPPTL_SMALLMAP + return current_ - other.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 +#else + if (isArray_) + return ValueInternalArray::distance(iterator_.array_, + other.iterator_.array_); + return ValueInternalMap::distance(iterator_.map_, other.iterator_.map_); +#endif +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +#else + if (isArray_) + return ValueInternalArray::equals(iterator_.array_, other.iterator_.array_); + return ValueInternalMap::equals(iterator_.map_, other.iterator_.map_); +#endif +} + +void ValueIteratorBase::copy(const SelfType& other) { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + current_ = other.current_; + isNull_ = other.isNull_; +#else + if (isArray_) + iterator_.array_ = other.iterator_.array_; + iterator_.map_ = other.iterator_.map_; +#endif +} + +Value ValueIteratorBase::key() const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if (czstring.c_str()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.c_str())); + return Value(czstring.c_str()); + } + return Value(czstring.index()); +#else + if (isArray_) + return Value(ValueInternalArray::indexOf(iterator_.array_)); + bool isStatic; + const char* memberName = ValueInternalMap::key(iterator_.map_, isStatic); + if (isStatic) + return Value(StaticString(memberName)); + return Value(memberName); +#endif +} + +UInt ValueIteratorBase::index() const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if (!czstring.c_str()) + return czstring.index(); + return Value::UInt(-1); +#else + if (isArray_) + return Value::UInt(ValueInternalArray::indexOf(iterator_.array_)); + return Value::UInt(-1); +#endif +} + +const char* ValueIteratorBase::memberName() const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const char* name = (*current_).first.c_str(); + return name ? name : ""; +#else + if (!isArray_) + return ValueInternalMap::key(iterator_.map_); + return ""; +#endif +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() {} + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} +#else +ValueConstIterator::ValueConstIterator( + const ValueInternalArray::IteratorState& state) + : ValueIteratorBase(state) {} + +ValueConstIterator::ValueConstIterator( + const ValueInternalMap::IteratorState& state) + : ValueIteratorBase(state) {} +#endif + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() {} + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} +#else +ValueIterator::ValueIterator(const ValueInternalArray::IteratorState& state) + : ValueIteratorBase(state) {} + +ValueIterator::ValueIterator(const ValueInternalMap::IteratorState& state) + : ValueIteratorBase(state) {} +#endif + +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator::ValueIterator(const ValueIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + +} // namespace Json diff --git a/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp new file mode 100644 index 000000000..89964ea32 --- /dev/null +++ b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp @@ -0,0 +1,690 @@ +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#include +#define isfinite _finite +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +static bool containsControlCharacter(const char* str) { + while (*str) { + if (isControlCharacter(*(str++))) + return true; + } + return false; +} + +std::string valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if (isNegative) + value = -value; + uintToString(LargestUInt(value), current); + if (isNegative) + *--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) { + // Allocate a buffer that is more than large enough to store the 16 digits of + // precision requested below. + char buffer[32]; + int len = -1; + +// 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 defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with + // visual studio 2005 to + // avoid warning. +#if defined(WINCE) + len = _snprintf(buffer, sizeof(buffer), "%.16g", value); +#else + len = sprintf_s(buffer, sizeof(buffer), "%.16g", value); +#endif +#else + if (isfinite(value)) { + len = snprintf(buffer, sizeof(buffer), "%.16g", value); + } else { + // IEEE standard states that NaN values will not compare to themselves + if (value != value) { + len = snprintf(buffer, sizeof(buffer), "null"); + } else if (value < 0) { + len = snprintf(buffer, sizeof(buffer), "-1e+9999"); + } else { + len = snprintf(buffer, sizeof(buffer), "1e+9999"); + } + // For those, we do not need to call fixNumLoc, but it is fast. + } +#endif + assert(len >= 0); + fixNumericLocale(buffer, buffer + len); + return buffer; +} + +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; +} + +// 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: + document_ += valueToQuotedString(value.asCString()); + 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_ += valueToQuotedString(name.c_str()); + 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: + pushValue(valueToQuotedString(value.asCString())); + 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 = + 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) { + 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(); + std::string normalizedComment = normalizeEOL(root.getComment(commentBefore)); + std::string::const_iterator iter = normalizedComment.begin(); + while (iter != normalizedComment.end()) { + document_ += *iter; + if (*iter == '\n' && *(iter + 1) == '/') + writeIndent(); + ++iter; + } + + // Comments are stripped of newlines, so add one here + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += normalizeEOL(root.getComment(commentAfter)); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +std::string StyledWriter::normalizeEOL(const std::string& text) { + std::string normalized; + normalized.reserve(text.length()); + const char* begin = text.c_str(); + const char* end = begin + text.length(); + const char* current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') // mac or dos EOL + { + if (*current == '\n') // convert dos EOL + ++current; + normalized += '\n'; + } else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + +// 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_ = ""; + writeCommentBeforeValue(root); + 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: + pushValue(valueToQuotedString(value.asCString())); + 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 { + 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 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 = + 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) { + 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() { + /* + Some comments in this method would have been nice. ;-) + + 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_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const std::string& value) { + writeIndent(); + *document_ << value; +} + +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; + *document_ << normalizeEOL(root.getComment(commentBefore)); + *document_ << "\n"; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + + if (root.hasComment(commentAfter)) { + *document_ << "\n"; + *document_ << normalizeEOL(root.getComment(commentAfter)); + *document_ << "\n"; + } +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +std::string StyledStreamWriter::normalizeEOL(const std::string& text) { + std::string normalized; + normalized.reserve(text.length()); + const char* begin = text.c_str(); + const char* end = begin + text.length(); + const char* current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') // mac or dos EOL + { + if (*current == '\n') // convert dos EOL + ++current; + normalized += '\n'; + } else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + +std::ostream& operator<<(std::ostream& sout, const Value& root) { + Json::StyledStreamWriter writer; + writer.write(sout, root); + return sout; +} + +} // namespace Json