JsonCpp 1.0.0 (reduced)
Extract upstream JsonCpp using the following shell code. url=https://github.com/open-source-parsers/jsoncpp && v=1.0.0 && r=7165f6ac && paths=" LICENSE include/json src/lib_json " && remove=" include/json/autolink.h src/lib_json/CMakeLists.txt src/lib_json/sconscript src/lib_json/version.h.in " && mkdir jsoncpp-$v-g$r-reduced && git clone $url jsoncpp-git && date=$(cd jsoncpp-git && git log -n 1 --format='%cd' $r) && (cd jsoncpp-git && git checkout $r && git archive --format=tar $r -- $paths) | (cd jsoncpp-$v-g$r-reduced && tar xv && rm -f $remove) && echo "g$r date: $date"
This commit is contained in:
commit
53f6ccb0ee
|
@ -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.
|
|
@ -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 <stdlib.h>
|
||||||
|
|
||||||
|
#if !defined(JSON_IS_AMALGAMATION)
|
||||||
|
#include "config.h"
|
||||||
|
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||||
|
|
||||||
|
#if JSON_USE_EXCEPTION
|
||||||
|
#include <stdexcept>
|
||||||
|
#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<char*>(666), message); \
|
||||||
|
exit(123); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define JSON_ASSERT_MESSAGE(condition, message) \
|
||||||
|
if (!(condition)) { \
|
||||||
|
JSON_FAIL_MESSAGE(message) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
|
|
@ -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 <cpptl/config.h>
|
||||||
|
#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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 <deque>
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <stack>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Disable warning C4251: <data member>: <type> 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 <a HREF="http://www.json.org">JSON</a> 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 <a HREF="http://www.json.org">JSON</a>
|
||||||
|
* 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 <a HREF="http://www.json.org">JSON</a>
|
||||||
|
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<StructuredError> 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<ErrorInfo> 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<Value*> 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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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 <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Disable warning C4251: <data member>: <type> 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 <a HREF="http://www.json.org">JSON</a> 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 <a HREF="http://www.json.org">JSON</a> 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 <a HREF="http://www.json.org">JSON</a> 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<std::string> ChildValues;
|
||||||
|
|
||||||
|
ChildValues childValues_;
|
||||||
|
std::string document_;
|
||||||
|
std::string indentString_;
|
||||||
|
int rightMargin_;
|
||||||
|
int indentSize_;
|
||||||
|
bool addChildValues_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> 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 <a HREF="http://www.json.org">JSON</a> 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<std::string> 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
|
|
@ -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 <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#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 <typename AllocatedType, const unsigned int objectPerAllocation>
|
||||||
|
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<BatchInfo*>(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
|
|
@ -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<Value**>(newIndexes);
|
||||||
|
}
|
||||||
|
virtual void releaseArrayPageIndex(Value** indexes,
|
||||||
|
ValueInternalArray::PageIndex indexCount) {
|
||||||
|
if (indexes)
|
||||||
|
free(indexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Value* allocateArrayPage() {
|
||||||
|
return static_cast<Value*>(
|
||||||
|
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<Value**>(newIndexes);
|
||||||
|
}
|
||||||
|
virtual void releaseArrayPageIndex(Value** indexes,
|
||||||
|
ValueInternalArray::PageIndex indexCount) {
|
||||||
|
if (indexes)
|
||||||
|
free(indexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Value* allocateArrayPage() {
|
||||||
|
return static_cast<Value*>(pagesAllocator_.allocate());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void releaseArrayPage(Value* value) {
|
||||||
|
if (value)
|
||||||
|
pagesAllocator_.release(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BatchAllocator<ValueInternalArray, 1> arraysAllocator_;
|
||||||
|
BatchAllocator<Value, ValueInternalArray::itemsPerPage> 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<ValueInternalArray*>(this);
|
||||||
|
it.currentItemIndex_ = 0;
|
||||||
|
it.currentPageIndex_ = pages_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueInternalArray::makeIterator(IteratorState& it,
|
||||||
|
ArrayIndex index) const {
|
||||||
|
it.array_ = const_cast<ValueInternalArray*>(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
|
|
@ -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<ValueInternalMap, 1> mapsAllocator_;
|
||||||
|
BatchAllocator<ValueInternalLink, 1> 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<Value*>(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<ValueInternalMap*>(this);
|
||||||
|
it.bucketIndex_ = 0;
|
||||||
|
it.itemIndex_ = 0;
|
||||||
|
it.link_ = buckets_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueInternalMap::makeEndIterator(IteratorState& it) const {
|
||||||
|
it.map_ = const_cast<ValueInternalMap*>(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
|
|
@ -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 <json/assertions.h>
|
||||||
|
#include <json/reader.h>
|
||||||
|
#include <json/value.h>
|
||||||
|
#include "json_tool.h"
|
||||||
|
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||||
|
#include <utility>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <istream>
|
||||||
|
|
||||||
|
#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<char> begin(sin);
|
||||||
|
// std::istream_iterator<char> 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::StructuredError> Reader::getStructuredErrors() const {
|
||||||
|
std::vector<Reader::StructuredError> 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
|
|
@ -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<char>(cp);
|
||||||
|
} else if (cp <= 0x7FF) {
|
||||||
|
result.resize(2);
|
||||||
|
result[1] = static_cast<char>(0x80 | (0x3f & cp));
|
||||||
|
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
|
||||||
|
} else if (cp <= 0xFFFF) {
|
||||||
|
result.resize(3);
|
||||||
|
result[2] = static_cast<char>(0x80 | (0x3f & cp));
|
||||||
|
result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
|
||||||
|
result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
|
||||||
|
} else if (cp <= 0x10FFFF) {
|
||||||
|
result.resize(4);
|
||||||
|
result[3] = static_cast<char>(0x80 | (0x3f & cp));
|
||||||
|
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
|
||||||
|
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
|
||||||
|
result[0] = static_cast<char>(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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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 <json/writer.h>
|
||||||
|
#include "json_tool.h"
|
||||||
|
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||||
|
#include <utility>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
|
||||||
|
#include <float.h>
|
||||||
|
#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 </
|
||||||
|
// sequence.
|
||||||
|
// Should add a flag to allow this compatibility mode and prevent this
|
||||||
|
// sequence from occurring.
|
||||||
|
default:
|
||||||
|
if (isControlCharacter(*c)) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
|
||||||
|
<< std::setw(4) << static_cast<int>(*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
|
Loading…
Reference in New Issue