mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-10-01 04:03:24 +03:00
Merge branch 'upstream-jsoncpp' into update-jsoncpp
* upstream-jsoncpp: jsoncpp 2017-08-27 (4cfae897)
This commit is contained in:
commit
e09819557f
19 files changed
+3438
-2293
No files matched your search
@@ -2,13 +2,13 @@ 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
|
||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim 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).
|
||||
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
|
||||
The JsonCpp Authors, 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
|
||||
@@ -23,7 +23,7 @@ described in clear, concise terms at:
|
||||
The full text of the MIT License follows:
|
||||
|
||||
========================================================================
|
||||
Copyright (c) 2007-2010 Baptiste Lepilleur
|
||||
Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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_ALLOCATOR_H_INCLUDED
|
||||
#define CPPTL_JSON_ALLOCATOR_H_INCLUDED
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#pragma pack(push, 8)
|
||||
|
||||
namespace Json {
|
||||
template<typename T>
|
||||
class SecureAllocator {
|
||||
public:
|
||||
// Type definitions
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
/**
|
||||
* Allocate memory for N items using the standard allocator.
|
||||
*/
|
||||
pointer allocate(size_type n) {
|
||||
// allocate using "global operator new"
|
||||
return static_cast<pointer>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Release memory which was allocated for N items at pointer P.
|
||||
*
|
||||
* The memory block is filled with zeroes before being released.
|
||||
* The pointer argument is tagged as "volatile" to prevent the
|
||||
* compiler optimizing out this critical step.
|
||||
*/
|
||||
void deallocate(volatile pointer p, size_type n) {
|
||||
std::memset(p, 0, n * sizeof(T));
|
||||
// free using "global operator delete"
|
||||
::operator delete(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an item in-place at pointer P.
|
||||
*/
|
||||
template<typename... Args>
|
||||
void construct(pointer p, Args&&... args) {
|
||||
// construct using "placement new" and "perfect forwarding"
|
||||
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
size_type max_size() const {
|
||||
return size_t(-1) / sizeof(T);
|
||||
}
|
||||
|
||||
pointer address( reference x ) const {
|
||||
return std::addressof(x);
|
||||
}
|
||||
|
||||
const_pointer address( const_reference x ) const {
|
||||
return std::addressof(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an item in-place at pointer P.
|
||||
*/
|
||||
void destroy(pointer p) {
|
||||
// destroy using "explicit destructor"
|
||||
p->~T();
|
||||
}
|
||||
|
||||
// Boilerplate
|
||||
SecureAllocator() {}
|
||||
template<typename U> SecureAllocator(const SecureAllocator<U>&) {}
|
||||
template<typename U> struct rebind { using other = SecureAllocator<U>; };
|
||||
};
|
||||
|
||||
|
||||
template<typename T, typename U>
|
||||
bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} //namespace Json
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -11,31 +11,44 @@
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sstream>
|
||||
|
||||
/** It should not be possible for a maliciously designed file to
|
||||
* cause an abort() or seg-fault, so these macros are used only
|
||||
* for pre-condition violations and internal logic errors.
|
||||
*/
|
||||
#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);
|
||||
|
||||
// @todo <= add detail about condition in exception
|
||||
# define JSON_ASSERT(condition) \
|
||||
{if (!(condition)) {Json::throwLogicError( "assert json failed" );}}
|
||||
|
||||
# define JSON_FAIL_MESSAGE(message) \
|
||||
{ \
|
||||
JSONCPP_OSTRINGSTREAM oss; oss << message; \
|
||||
Json::throwLogicError(oss.str()); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
#else // JSON_USE_EXCEPTION
|
||||
#define JSON_ASSERT(condition) assert(condition);
|
||||
|
||||
# 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) \
|
||||
// release builds we abort, for a core-dump or debugger.
|
||||
# define JSON_FAIL_MESSAGE(message) \
|
||||
{ \
|
||||
assert(false&& message); \
|
||||
strcpy(reinterpret_cast<char*>(666), message); \
|
||||
exit(123); \
|
||||
JSONCPP_OSTRINGSTREAM oss; oss << message; \
|
||||
assert(false && oss.str().c_str()); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#define JSON_ASSERT_MESSAGE(condition, message) \
|
||||
if (!(condition)) { \
|
||||
JSON_FAIL_MESSAGE(message) \
|
||||
JSON_FAIL_MESSAGE(message); \
|
||||
}
|
||||
|
||||
#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
|
||||
@@ -1,10 +1,13 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
#include <stddef.h>
|
||||
#include <string> //typedef String
|
||||
#include <stdint.h> //typedef int64_t, uint64_t
|
||||
|
||||
// Include KWSys Large File Support configuration.
|
||||
#include <cmsys/Configure.h>
|
||||
@@ -22,17 +25,6 @@
|
||||
/// 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.
|
||||
@@ -55,12 +47,12 @@
|
||||
#ifdef JSON_IN_CPPTL
|
||||
#define JSON_API CPPTL_API
|
||||
#elif defined(JSON_DLL_BUILD)
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define JSON_API __declspec(dllexport)
|
||||
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
|
||||
#endif // if defined(_MSC_VER)
|
||||
#elif defined(JSON_DLL)
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define JSON_API __declspec(dllimport)
|
||||
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
|
||||
#endif // if defined(_MSC_VER)
|
||||
@@ -74,26 +66,96 @@
|
||||
// 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) // MSVC
|
||||
# if _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 // 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))
|
||||
# if _MSC_VER >= 1500 // MSVC 2008
|
||||
/// Indicates that the following function is deprecated.
|
||||
# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
|
||||
# endif
|
||||
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
// In c++11 the override keyword allows you to explicity define that a function
|
||||
// is intended to override the base-class version. This makes the code more
|
||||
// managable and fixes a set of common hard-to-find bugs.
|
||||
#if __cplusplus >= 201103L
|
||||
# define JSONCPP_OVERRIDE override
|
||||
# define JSONCPP_NOEXCEPT noexcept
|
||||
#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900
|
||||
# define JSONCPP_OVERRIDE override
|
||||
# define JSONCPP_NOEXCEPT throw()
|
||||
#elif defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
# define JSONCPP_OVERRIDE override
|
||||
# define JSONCPP_NOEXCEPT noexcept
|
||||
#else
|
||||
# define JSONCPP_OVERRIDE
|
||||
# define JSONCPP_NOEXCEPT throw()
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_RVALUE_REFERENCES
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
|
||||
#define JSON_HAS_RVALUE_REFERENCES 1
|
||||
#endif // MSVC >= 2010
|
||||
|
||||
#ifdef __clang__
|
||||
#if __has_feature(cxx_rvalue_references)
|
||||
#define JSON_HAS_RVALUE_REFERENCES 1
|
||||
#endif // has_feature
|
||||
|
||||
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
|
||||
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
|
||||
#define JSON_HAS_RVALUE_REFERENCES 1
|
||||
#endif // GXX_EXPERIMENTAL
|
||||
|
||||
#endif // __clang__ || __GNUC__
|
||||
|
||||
#endif // not defined JSON_HAS_RVALUE_REFERENCES
|
||||
|
||||
#ifndef JSON_HAS_RVALUE_REFERENCES
|
||||
#define JSON_HAS_RVALUE_REFERENCES 0
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
# if __has_extension(attribute_deprecated_with_message)
|
||||
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
|
||||
# endif
|
||||
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
|
||||
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
|
||||
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
|
||||
# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
|
||||
# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
|
||||
# endif // GNUC version
|
||||
#endif // __clang__ || __GNUC__
|
||||
|
||||
#if !defined(JSONCPP_DEPRECATED)
|
||||
#define JSONCPP_DEPRECATED(message)
|
||||
#endif // if !defined(JSONCPP_DEPRECATED)
|
||||
|
||||
#if __GNUC__ >= 6
|
||||
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
|
||||
#endif
|
||||
|
||||
#if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
# include "version.h"
|
||||
|
||||
# if JSONCPP_USING_SECURE_MEMORY
|
||||
# include "allocator.h" //typedef Allocator
|
||||
# endif
|
||||
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
namespace Json {
|
||||
typedef int Int;
|
||||
typedef unsigned int UInt;
|
||||
@@ -107,13 +169,26 @@ typedef unsigned int LargestUInt;
|
||||
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;
|
||||
typedef int64_t Int64;
|
||||
typedef uint64_t UInt64;
|
||||
#endif // if defined(_MSC_VER)
|
||||
typedef Int64 LargestInt;
|
||||
typedef UInt64 LargestUInt;
|
||||
#define JSON_HAS_INT64
|
||||
#endif // if defined(JSON_NO_INT64)
|
||||
#if JSONCPP_USING_SECURE_MEMORY
|
||||
#define JSONCPP_STRING std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char> >
|
||||
#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
|
||||
#define JSONCPP_OSTREAM std::basic_ostream<char, std::char_traits<char>>
|
||||
#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
|
||||
#define JSONCPP_ISTREAM std::istream
|
||||
#else
|
||||
#define JSONCPP_STRING std::string
|
||||
#define JSONCPP_OSTRINGSTREAM std::ostringstream
|
||||
#define JSONCPP_OSTREAM std::ostream
|
||||
#define JSONCPP_ISTRINGSTREAM std::istringstream
|
||||
#define JSONCPP_ISTREAM std::istream
|
||||
#endif // if JSONCPP_USING_SECURE_MEMORY
|
||||
} // end namespace Json
|
||||
|
||||
#endif // JSON_CONFIG_H_INCLUDED
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "forwards.h"
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
#pragma pack(push, 8)
|
||||
|
||||
namespace Json {
|
||||
|
||||
/** \brief Configuration passed to reader and writer.
|
||||
@@ -54,4 +56,6 @@ public:
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // CPPTL_JSON_FEATURES_H_INCLUDED
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -31,12 +31,6 @@ 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
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <iosfwd>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <istream>
|
||||
|
||||
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
|
||||
// be used by...
|
||||
@@ -22,13 +23,16 @@
|
||||
#pragma warning(disable : 4251)
|
||||
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
|
||||
#pragma pack(push, 8)
|
||||
|
||||
namespace Json {
|
||||
|
||||
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
|
||||
*Value.
|
||||
*
|
||||
* \deprecated Use CharReader and CharReaderBuilder.
|
||||
*/
|
||||
class JSON_API Reader {
|
||||
class JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") JSON_API Reader {
|
||||
public:
|
||||
typedef char Char;
|
||||
typedef const Char* Location;
|
||||
@@ -40,9 +44,9 @@ public:
|
||||
*
|
||||
*/
|
||||
struct StructuredError {
|
||||
size_t offset_start;
|
||||
size_t offset_limit;
|
||||
std::string message;
|
||||
ptrdiff_t offset_start;
|
||||
ptrdiff_t offset_limit;
|
||||
JSONCPP_STRING message;
|
||||
};
|
||||
|
||||
/** \brief Constructs a Reader allowing all features
|
||||
@@ -78,7 +82,7 @@ public:
|
||||
document to read.
|
||||
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
|
||||
document to read.
|
||||
\ Must be >= beginDoc.
|
||||
* 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
|
||||
@@ -97,7 +101,19 @@ public:
|
||||
|
||||
/// \brief Parse from input stream.
|
||||
/// \see Json::operator>>(std::istream&, Json::Value&).
|
||||
bool parse(std::istream& is, Value& root, bool collectComments = true);
|
||||
bool parse(JSONCPP_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.")
|
||||
JSONCPP_STRING getFormatedErrorMessages() const;
|
||||
|
||||
/** \brief Returns a user friendly string that list errors in the parsed
|
||||
* document.
|
||||
@@ -107,17 +123,7 @@ public:
|
||||
* occurred
|
||||
* during parsing.
|
||||
*/
|
||||
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;
|
||||
JSONCPP_STRING getFormattedErrorMessages() const;
|
||||
|
||||
/** \brief Returns a vector of structured erros encounted while parsing.
|
||||
* \return A (possibly empty) vector of StructuredError objects. Currently
|
||||
@@ -134,7 +140,7 @@ public:
|
||||
* \return \c true if the error was successfully added, \c false if the
|
||||
* Value offset exceeds the document size.
|
||||
*/
|
||||
bool pushError(const Value& value, const std::string& message);
|
||||
bool pushError(const Value& value, const JSONCPP_STRING& message);
|
||||
|
||||
/** \brief Add a semantic error message with extra context.
|
||||
* \param value JSON Value location associated with the error
|
||||
@@ -143,7 +149,7 @@ public:
|
||||
* \return \c true if the error was successfully added, \c false if either
|
||||
* Value offset exceeds the document size.
|
||||
*/
|
||||
bool pushError(const Value& value, const std::string& message, const Value& extra);
|
||||
bool pushError(const Value& value, const JSONCPP_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
|
||||
@@ -179,13 +185,12 @@ private:
|
||||
class ErrorInfo {
|
||||
public:
|
||||
Token token_;
|
||||
std::string message_;
|
||||
JSONCPP_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);
|
||||
@@ -200,7 +205,7 @@ private:
|
||||
bool decodeNumber(Token& token);
|
||||
bool decodeNumber(Token& token, Value& decoded);
|
||||
bool decodeString(Token& token);
|
||||
bool decodeString(Token& token, std::string& decoded);
|
||||
bool decodeString(Token& token, JSONCPP_STRING& decoded);
|
||||
bool decodeDouble(Token& token);
|
||||
bool decodeDouble(Token& token, Value& decoded);
|
||||
bool decodeUnicodeCodePoint(Token& token,
|
||||
@@ -211,9 +216,9 @@ private:
|
||||
Location& current,
|
||||
Location end,
|
||||
unsigned int& unicode);
|
||||
bool addError(const std::string& message, Token& token, Location extra = 0);
|
||||
bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0);
|
||||
bool recoverFromError(TokenType skipUntilToken);
|
||||
bool addErrorAndRecover(const std::string& message,
|
||||
bool addErrorAndRecover(const JSONCPP_STRING& message,
|
||||
Token& token,
|
||||
TokenType skipUntilToken);
|
||||
void skipUntilSpace();
|
||||
@@ -221,24 +226,154 @@ private:
|
||||
Char getNextChar();
|
||||
void
|
||||
getLocationLineAndColumn(Location location, int& line, int& column) const;
|
||||
std::string getLocationLineAndColumn(Location location) const;
|
||||
JSONCPP_STRING getLocationLineAndColumn(Location location) const;
|
||||
void addComment(Location begin, Location end, CommentPlacement placement);
|
||||
void skipCommentTokens(Token& token);
|
||||
|
||||
static bool containsNewLine(Location begin, Location end);
|
||||
static JSONCPP_STRING normalizeEOL(Location begin, Location end);
|
||||
|
||||
typedef std::stack<Value*> Nodes;
|
||||
Nodes nodes_;
|
||||
Errors errors_;
|
||||
std::string document_;
|
||||
JSONCPP_STRING document_;
|
||||
Location begin_;
|
||||
Location end_;
|
||||
Location current_;
|
||||
Location lastValueEnd_;
|
||||
Value* lastValue_;
|
||||
std::string commentsBefore_;
|
||||
JSONCPP_STRING commentsBefore_;
|
||||
Features features_;
|
||||
bool collectComments_;
|
||||
}; // Reader
|
||||
|
||||
/** Interface for reading JSON from a char array.
|
||||
*/
|
||||
class JSON_API CharReader {
|
||||
public:
|
||||
virtual ~CharReader() {}
|
||||
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
|
||||
document.
|
||||
* The document must be a UTF-8 encoded string containing the document to read.
|
||||
*
|
||||
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
|
||||
document to read.
|
||||
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
|
||||
document to read.
|
||||
* Must be >= beginDoc.
|
||||
* \param root [out] Contains the root value of the document if it was
|
||||
* successfully parsed.
|
||||
* \param errs [out] Formatted error messages (if not NULL)
|
||||
* a user friendly string that lists errors in the parsed
|
||||
* document.
|
||||
* \return \c true if the document was successfully parsed, \c false if an
|
||||
error occurred.
|
||||
*/
|
||||
virtual bool parse(
|
||||
char const* beginDoc, char const* endDoc,
|
||||
Value* root, JSONCPP_STRING* errs) = 0;
|
||||
|
||||
class JSON_API Factory {
|
||||
public:
|
||||
virtual ~Factory() {}
|
||||
/** \brief Allocate a CharReader via operator new().
|
||||
* \throw std::exception if something goes wrong (e.g. invalid settings)
|
||||
*/
|
||||
virtual CharReader* newCharReader() const = 0;
|
||||
}; // Factory
|
||||
}; // CharReader
|
||||
|
||||
/** \brief Build a CharReader implementation.
|
||||
|
||||
Usage:
|
||||
\code
|
||||
using namespace Json;
|
||||
CharReaderBuilder builder;
|
||||
builder["collectComments"] = false;
|
||||
Value value;
|
||||
JSONCPP_STRING errs;
|
||||
bool ok = parseFromStream(builder, std::cin, &value, &errs);
|
||||
\endcode
|
||||
*/
|
||||
class JSON_API CharReaderBuilder : public CharReader::Factory {
|
||||
public:
|
||||
// Note: We use a Json::Value so that we can add data-members to this class
|
||||
// without a major version bump.
|
||||
/** Configuration of this builder.
|
||||
These are case-sensitive.
|
||||
Available settings (case-sensitive):
|
||||
- `"collectComments": false or true`
|
||||
- true to collect comment and allow writing them
|
||||
back during serialization, false to discard comments.
|
||||
This parameter is ignored if allowComments is false.
|
||||
- `"allowComments": false or true`
|
||||
- true if comments are allowed.
|
||||
- `"strictRoot": false or true`
|
||||
- true if root must be either an array or an object value
|
||||
- `"allowDroppedNullPlaceholders": false or true`
|
||||
- true if dropped null placeholders are allowed. (See StreamWriterBuilder.)
|
||||
- `"allowNumericKeys": false or true`
|
||||
- true if numeric object keys are allowed.
|
||||
- `"allowSingleQuotes": false or true`
|
||||
- true if '' are allowed for strings (both keys and values)
|
||||
- `"stackLimit": integer`
|
||||
- Exceeding stackLimit (recursive depth of `readValue()`) will
|
||||
cause an exception.
|
||||
- This is a security issue (seg-faults caused by deeply nested JSON),
|
||||
so the default is low.
|
||||
- `"failIfExtra": false or true`
|
||||
- If true, `parse()` returns false when extra non-whitespace trails
|
||||
the JSON value in the input string.
|
||||
- `"rejectDupKeys": false or true`
|
||||
- If true, `parse()` returns false when a key is duplicated within an object.
|
||||
- `"allowSpecialFloats": false or true`
|
||||
- If true, special float values (NaNs and infinities) are allowed
|
||||
and their values are lossfree restorable.
|
||||
|
||||
You can examine 'settings_` yourself
|
||||
to see the defaults. You can also write and read them just like any
|
||||
JSON Value.
|
||||
\sa setDefaults()
|
||||
*/
|
||||
Json::Value settings_;
|
||||
|
||||
CharReaderBuilder();
|
||||
~CharReaderBuilder() JSONCPP_OVERRIDE;
|
||||
|
||||
CharReader* newCharReader() const JSONCPP_OVERRIDE;
|
||||
|
||||
/** \return true if 'settings' are legal and consistent;
|
||||
* otherwise, indicate bad settings via 'invalid'.
|
||||
*/
|
||||
bool validate(Json::Value* invalid) const;
|
||||
|
||||
/** A simple way to update a specific setting.
|
||||
*/
|
||||
Value& operator[](JSONCPP_STRING key);
|
||||
|
||||
/** Called by ctor, but you can use this to reset settings_.
|
||||
* \pre 'settings' != NULL (but Json::null is fine)
|
||||
* \remark Defaults:
|
||||
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
|
||||
*/
|
||||
static void setDefaults(Json::Value* settings);
|
||||
/** Same as old Features::strictMode().
|
||||
* \pre 'settings' != NULL (but Json::null is fine)
|
||||
* \remark Defaults:
|
||||
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
|
||||
*/
|
||||
static void strictMode(Json::Value* settings);
|
||||
};
|
||||
|
||||
/** Consume entire stream and use its begin/end.
|
||||
* Someday we might have a real StreamReader, but for now this
|
||||
* is convenient.
|
||||
*/
|
||||
bool JSON_API parseFromStream(
|
||||
CharReader::Factory const&,
|
||||
JSONCPP_ISTREAM&,
|
||||
Value* root, std::string* errs);
|
||||
|
||||
/** \brief Read from 'sin' into 'root'.
|
||||
|
||||
Always keep comments from the input JSON.
|
||||
@@ -263,10 +398,12 @@ private:
|
||||
\throw std::exception on parse error.
|
||||
\see Json::operator<<()
|
||||
*/
|
||||
JSON_API std::istream& operator>>(std::istream&, Value&);
|
||||
JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&);
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
#pragma warning(pop)
|
||||
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -11,6 +11,7 @@
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
|
||||
#ifndef JSON_USE_CPPTL_SMALLMAP
|
||||
#include <map>
|
||||
@@ -21,6 +22,19 @@
|
||||
#include <cpptl/forwards.h>
|
||||
#endif
|
||||
|
||||
//Conditional NORETURN attribute on the throw functions would:
|
||||
// a) suppress false positives from static code analysis
|
||||
// b) possibly improve optimization opportunities.
|
||||
#if !defined(JSONCPP_NORETURN)
|
||||
# if defined(_MSC_VER)
|
||||
# define JSONCPP_NORETURN __declspec(noreturn)
|
||||
# elif defined(__GNUC__)
|
||||
# define JSONCPP_NORETURN __attribute__ ((__noreturn__))
|
||||
# else
|
||||
# define JSONCPP_NORETURN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
|
||||
// be used by...
|
||||
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
@@ -28,10 +42,52 @@
|
||||
#pragma warning(disable : 4251)
|
||||
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
|
||||
#pragma pack(push, 8)
|
||||
|
||||
/** \brief JSON (JavaScript Object Notation).
|
||||
*/
|
||||
namespace Json {
|
||||
|
||||
/** Base class for all exceptions we throw.
|
||||
*
|
||||
* We use nothing but these internally. Of course, STL can throw others.
|
||||
*/
|
||||
class JSON_API Exception : public std::exception {
|
||||
public:
|
||||
Exception(JSONCPP_STRING const& msg);
|
||||
~Exception() JSONCPP_NOEXCEPT JSONCPP_OVERRIDE;
|
||||
char const* what() const JSONCPP_NOEXCEPT JSONCPP_OVERRIDE;
|
||||
protected:
|
||||
JSONCPP_STRING msg_;
|
||||
};
|
||||
|
||||
/** Exceptions which the user cannot easily avoid.
|
||||
*
|
||||
* E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
|
||||
*
|
||||
* \remark derived from Json::Exception
|
||||
*/
|
||||
class JSON_API RuntimeError : public Exception {
|
||||
public:
|
||||
RuntimeError(JSONCPP_STRING const& msg);
|
||||
};
|
||||
|
||||
/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
|
||||
*
|
||||
* These are precondition-violations (user bugs) and internal errors (our bugs).
|
||||
*
|
||||
* \remark derived from Json::Exception
|
||||
*/
|
||||
class JSON_API LogicError : public Exception {
|
||||
public:
|
||||
LogicError(JSONCPP_STRING const& msg);
|
||||
};
|
||||
|
||||
/// used internally
|
||||
JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg);
|
||||
/// used internally
|
||||
JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg);
|
||||
|
||||
/** \brief Type of the value held by a Value object.
|
||||
*/
|
||||
enum ValueType {
|
||||
@@ -74,14 +130,14 @@ enum CommentPlacement {
|
||||
*/
|
||||
class JSON_API StaticString {
|
||||
public:
|
||||
explicit StaticString(const char* czstring) : str_(czstring) {}
|
||||
explicit StaticString(const char* czstring) : c_str_(czstring) {}
|
||||
|
||||
operator const char*() const { return str_; }
|
||||
operator const char*() const { return c_str_; }
|
||||
|
||||
const char* c_str() const { return str_; }
|
||||
const char* c_str() const { return c_str_; }
|
||||
|
||||
private:
|
||||
const char* str_;
|
||||
const char* c_str_;
|
||||
};
|
||||
|
||||
/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
|
||||
@@ -99,28 +155,29 @@ private:
|
||||
* The type of the held value is represented by a #ValueType and
|
||||
* can be obtained using type().
|
||||
*
|
||||
* values of an #objectValue or #arrayValue can be accessed using operator[]()
|
||||
*methods.
|
||||
* Non const methods will automatically create the a #nullValue element
|
||||
* Values of an #objectValue or #arrayValue can be accessed using operator[]()
|
||||
* methods.
|
||||
* Non-const methods will automatically create the a #nullValue element
|
||||
* if it does not exist.
|
||||
* The sequence of an #arrayValue will be automatically resize and initialized
|
||||
* The sequence of an #arrayValue will be automatically resized and initialized
|
||||
* with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
|
||||
*
|
||||
* The get() methods can be used to obtanis default value in the case the
|
||||
*required element
|
||||
* does not exist.
|
||||
* The get() methods can be used to obtain default value in the case the
|
||||
* required element does not exist.
|
||||
*
|
||||
* It is possible to iterate over the list of a #objectValue values using
|
||||
* the getMemberNames() method.
|
||||
*
|
||||
* \note #Value string-length fit in size_t, but keys must be < 2^30.
|
||||
* (The reason is an implementation detail.) A #CharReader will raise an
|
||||
* exception if a bound is exceeded to avoid security holes in your app,
|
||||
* but the Value API does *not* check bounds. That is the responsibility
|
||||
* of the caller.
|
||||
*/
|
||||
class JSON_API Value {
|
||||
friend class ValueIteratorBase;
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
friend class ValueInternalLink;
|
||||
friend class ValueInternalMap;
|
||||
#endif
|
||||
public:
|
||||
typedef std::vector<std::string> Members;
|
||||
typedef std::vector<JSONCPP_STRING> Members;
|
||||
typedef ValueIterator iterator;
|
||||
typedef ValueConstIterator const_iterator;
|
||||
typedef Json::UInt UInt;
|
||||
@@ -133,7 +190,10 @@ public:
|
||||
typedef Json::LargestUInt LargestUInt;
|
||||
typedef Json::ArrayIndex ArrayIndex;
|
||||
|
||||
static const Value& null;
|
||||
static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value().
|
||||
static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null
|
||||
static Value const& nullSingleton(); ///< Prefer this to null or nullRef.
|
||||
|
||||
/// Minimum signed integer value that can be stored in a Json::Value.
|
||||
static const LargestInt minLargestInt;
|
||||
/// Maximum signed integer value that can be stored in a Json::Value.
|
||||
@@ -159,7 +219,6 @@ public:
|
||||
|
||||
private:
|
||||
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
class CZString {
|
||||
public:
|
||||
enum DuplicationPolicy {
|
||||
@@ -168,20 +227,39 @@ private:
|
||||
duplicateOnCopy
|
||||
};
|
||||
CZString(ArrayIndex index);
|
||||
CZString(const char* cstr, DuplicationPolicy allocate);
|
||||
CZString(const CZString& other);
|
||||
CZString(char const* str, unsigned length, DuplicationPolicy allocate);
|
||||
CZString(CZString const& other);
|
||||
#if JSON_HAS_RVALUE_REFERENCES
|
||||
CZString(CZString&& other);
|
||||
#endif
|
||||
~CZString();
|
||||
CZString& operator=(CZString other);
|
||||
bool operator<(const CZString& other) const;
|
||||
bool operator==(const CZString& other) const;
|
||||
CZString& operator=(const CZString& other);
|
||||
|
||||
#if JSON_HAS_RVALUE_REFERENCES
|
||||
CZString& operator=(CZString&& other);
|
||||
#endif
|
||||
|
||||
bool operator<(CZString const& other) const;
|
||||
bool operator==(CZString const& other) const;
|
||||
ArrayIndex index() const;
|
||||
const char* c_str() const;
|
||||
//const char* c_str() const; ///< \deprecated
|
||||
char const* data() const;
|
||||
unsigned length() const;
|
||||
bool isStaticString() const;
|
||||
|
||||
private:
|
||||
void swap(CZString& other);
|
||||
const char* cstr_;
|
||||
ArrayIndex index_;
|
||||
|
||||
struct StringStorage {
|
||||
unsigned policy_: 2;
|
||||
unsigned length_: 30; // 1GB max
|
||||
};
|
||||
|
||||
char const* cstr_; // actually, a prefixed string, unless policy is noDup
|
||||
union {
|
||||
ArrayIndex index_;
|
||||
StringStorage storage_;
|
||||
};
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -190,7 +268,6 @@ public:
|
||||
#else
|
||||
typedef CppTL::SmallMap<CZString, Value> ObjectValues;
|
||||
#endif // ifndef JSON_USE_CPPTL_SMALLMAP
|
||||
#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
|
||||
|
||||
public:
|
||||
@@ -217,47 +294,72 @@ Json::Value obj_value(Json::objectValue); // {}
|
||||
Value(UInt64 value);
|
||||
#endif // if defined(JSON_HAS_INT64)
|
||||
Value(double value);
|
||||
Value(const char* value);
|
||||
Value(const char* beginValue, const char* endValue);
|
||||
Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
|
||||
Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
|
||||
/** \brief Constructs a value from a static string.
|
||||
|
||||
* Like other value string constructor but do not duplicate the string for
|
||||
* internal storage. The given string must remain alive after the call to this
|
||||
* constructor.
|
||||
* \note This works only for null-terminated strings. (We cannot change the
|
||||
* size of this class, so we have nowhere to store the length,
|
||||
* which might be computed later for various operations.)
|
||||
*
|
||||
* Example of usage:
|
||||
* \code
|
||||
* Json::Value aValue( StaticString("some text") );
|
||||
* static StaticString foo("some text");
|
||||
* Json::Value aValue(foo);
|
||||
* \endcode
|
||||
*/
|
||||
Value(const StaticString& value);
|
||||
Value(const std::string& value);
|
||||
Value(const JSONCPP_STRING& value); ///< Copy data() til size(). Embedded zeroes too.
|
||||
#ifdef JSON_USE_CPPTL
|
||||
Value(const CppTL::ConstString& value);
|
||||
#endif
|
||||
Value(bool value);
|
||||
/// Deep copy.
|
||||
Value(const Value& other);
|
||||
#if JSON_HAS_RVALUE_REFERENCES
|
||||
/// Move constructor
|
||||
Value(Value&& other);
|
||||
#endif
|
||||
~Value();
|
||||
|
||||
/// Deep copy, then swap(other).
|
||||
/// \note Over-write existing comments. To preserve comments, use #swapPayload().
|
||||
Value& operator=(Value other);
|
||||
/// Swap values.
|
||||
/// \note Currently, comments are intentionally not swapped, for
|
||||
/// both logic and efficiency.
|
||||
|
||||
/// Swap everything.
|
||||
void swap(Value& other);
|
||||
/// Swap values but leave comments and source offsets in place.
|
||||
void swapPayload(Value& other);
|
||||
|
||||
/// copy everything.
|
||||
void copy(const Value& other);
|
||||
/// copy values but leave comments and source offsets in place.
|
||||
void copyPayload(const Value& other);
|
||||
|
||||
ValueType type() const;
|
||||
|
||||
/// Compare payload only, not comments etc.
|
||||
bool operator<(const Value& other) const;
|
||||
bool operator<=(const Value& other) const;
|
||||
bool operator>=(const Value& other) const;
|
||||
bool operator>(const Value& other) const;
|
||||
|
||||
bool operator==(const Value& other) const;
|
||||
bool operator!=(const Value& other) const;
|
||||
|
||||
int compare(const Value& other) const;
|
||||
|
||||
const char* asCString() const;
|
||||
std::string asString() const;
|
||||
const char* asCString() const; ///< Embedded zeroes could cause you trouble!
|
||||
#if JSONCPP_USING_SECURE_MEMORY
|
||||
unsigned getCStringLength() const; //Allows you to understand the length of the CString
|
||||
#endif
|
||||
JSONCPP_STRING asString() const; ///< Embedded zeroes are possible.
|
||||
/** Get raw char* of string-value.
|
||||
* \return false if !string. (Seg-fault if str or end are NULL.)
|
||||
*/
|
||||
bool getString(
|
||||
char const** begin, char const** end) const;
|
||||
#ifdef JSON_USE_CPPTL
|
||||
CppTL::ConstString asConstString() const;
|
||||
#endif
|
||||
@@ -347,20 +449,28 @@ Json::Value obj_value(Json::objectValue); // {}
|
||||
/// Equivalent to jsonvalue[jsonvalue.size()] = value;
|
||||
Value& append(const Value& value);
|
||||
|
||||
#if JSON_HAS_RVALUE_REFERENCES
|
||||
Value& append(Value&& value);
|
||||
#endif
|
||||
|
||||
/// Access an object value by name, create a null member if it does not exist.
|
||||
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
|
||||
/// Exceeding that will cause an exception.
|
||||
Value& operator[](const char* key);
|
||||
/// Access an object value by name, returns null if there is no member with
|
||||
/// that name.
|
||||
const Value& operator[](const char* key) const;
|
||||
/// Access an object value by name, create a null member if it does not exist.
|
||||
Value& operator[](const std::string& key);
|
||||
/// \param key may contain embedded nulls.
|
||||
Value& operator[](const JSONCPP_STRING& key);
|
||||
/// Access an object value by name, returns null if there is no member with
|
||||
/// that name.
|
||||
const Value& operator[](const std::string& key) const;
|
||||
/// \param key may contain embedded nulls.
|
||||
const Value& operator[](const JSONCPP_STRING& key) const;
|
||||
/** \brief Access an object value by name, create a null member if it does not
|
||||
exist.
|
||||
|
||||
* If the object as no entry for that name, then the member name used to store
|
||||
* If the object has no entry for that name, then the member name used to store
|
||||
* the new entry is not duplicated.
|
||||
* Example of use:
|
||||
* \code
|
||||
@@ -378,27 +488,71 @@ Json::Value obj_value(Json::objectValue); // {}
|
||||
const Value& operator[](const CppTL::ConstString& key) const;
|
||||
#endif
|
||||
/// Return the member named key if it exist, defaultValue otherwise.
|
||||
/// \note deep copy
|
||||
Value get(const char* key, const Value& defaultValue) const;
|
||||
/// Return the member named key if it exist, defaultValue otherwise.
|
||||
Value get(const std::string& key, const Value& defaultValue) const;
|
||||
/// \note deep copy
|
||||
/// \note key may contain embedded nulls.
|
||||
Value get(const char* begin, const char* end, const Value& defaultValue) const;
|
||||
/// Return the member named key if it exist, defaultValue otherwise.
|
||||
/// \note deep copy
|
||||
/// \param key may contain embedded nulls.
|
||||
Value get(const JSONCPP_STRING& key, const Value& defaultValue) const;
|
||||
#ifdef JSON_USE_CPPTL
|
||||
/// Return the member named key if it exist, defaultValue otherwise.
|
||||
/// \note deep copy
|
||||
Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
|
||||
#endif
|
||||
/// Most general and efficient version of isMember()const, get()const,
|
||||
/// and operator[]const
|
||||
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
|
||||
Value const* find(char const* begin, char const* end) const;
|
||||
/// Most general and efficient version of object-mutators.
|
||||
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
|
||||
/// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
|
||||
Value const* demand(char const* begin, char const* end);
|
||||
/// \brief Remove and return the named member.
|
||||
///
|
||||
/// Do nothing if it did not exist.
|
||||
/// \return the removed Value, or null.
|
||||
/// \pre type() is objectValue or nullValue
|
||||
/// \post type() is unchanged
|
||||
/// \deprecated
|
||||
JSONCPP_DEPRECATED("")
|
||||
Value removeMember(const char* key);
|
||||
/// Same as removeMember(const char*)
|
||||
Value removeMember(const std::string& key);
|
||||
/// \param key may contain embedded nulls.
|
||||
/// \deprecated
|
||||
JSONCPP_DEPRECATED("")
|
||||
Value removeMember(const JSONCPP_STRING& key);
|
||||
/// Same as removeMember(const char* begin, const char* end, Value* removed),
|
||||
/// but 'key' is null-terminated.
|
||||
bool removeMember(const char* key, Value* removed);
|
||||
/** \brief Remove the named map member.
|
||||
|
||||
Update 'removed' iff removed.
|
||||
\param key may contain embedded nulls.
|
||||
\return true iff removed (no exceptions)
|
||||
*/
|
||||
bool removeMember(JSONCPP_STRING const& key, Value* removed);
|
||||
/// Same as removeMember(JSONCPP_STRING const& key, Value* removed)
|
||||
bool removeMember(const char* begin, const char* end, Value* removed);
|
||||
/** \brief Remove the indexed array element.
|
||||
|
||||
O(n) expensive operations.
|
||||
Update 'removed' iff removed.
|
||||
\return true iff removed (no exceptions)
|
||||
*/
|
||||
bool removeIndex(ArrayIndex i, Value* removed);
|
||||
|
||||
/// Return true if the object has a member named key.
|
||||
/// \note 'key' must be null-terminated.
|
||||
bool isMember(const char* key) const;
|
||||
/// Return true if the object has a member named key.
|
||||
bool isMember(const std::string& key) const;
|
||||
/// \param key may contain embedded nulls.
|
||||
bool isMember(const JSONCPP_STRING& key) const;
|
||||
/// Same as isMember(JSONCPP_STRING const& key)const
|
||||
bool isMember(const char* begin, const char* end) const;
|
||||
#ifdef JSON_USE_CPPTL
|
||||
/// Return true if the object has a member named key.
|
||||
bool isMember(const CppTL::ConstString& key) const;
|
||||
@@ -416,15 +570,18 @@ Json::Value obj_value(Json::objectValue); // {}
|
||||
// EnumValues enumValues() const;
|
||||
//# endif
|
||||
|
||||
/// Comments must be //... or /* ... */
|
||||
/// \deprecated Always pass len.
|
||||
JSONCPP_DEPRECATED("Use setComment(JSONCPP_STRING const&) instead.")
|
||||
void setComment(const char* comment, CommentPlacement placement);
|
||||
/// Comments must be //... or /* ... */
|
||||
void setComment(const std::string& comment, CommentPlacement placement);
|
||||
void setComment(const char* comment, size_t len, CommentPlacement placement);
|
||||
/// Comments must be //... or /* ... */
|
||||
void setComment(const JSONCPP_STRING& comment, CommentPlacement placement);
|
||||
bool hasComment(CommentPlacement placement) const;
|
||||
/// Include delimiters and embedded newlines.
|
||||
std::string getComment(CommentPlacement placement) const;
|
||||
JSONCPP_STRING getComment(CommentPlacement placement) const;
|
||||
|
||||
std::string toStyledString() const;
|
||||
JSONCPP_STRING toStyledString() const;
|
||||
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
@@ -434,34 +591,22 @@ Json::Value obj_value(Json::objectValue); // {}
|
||||
|
||||
// Accessors for the [start, limit) range of bytes within the JSON text from
|
||||
// which this value was parsed, if any.
|
||||
void setOffsetStart(size_t start);
|
||||
void setOffsetLimit(size_t limit);
|
||||
size_t getOffsetStart() const;
|
||||
size_t getOffsetLimit() const;
|
||||
void setOffsetStart(ptrdiff_t start);
|
||||
void setOffsetLimit(ptrdiff_t limit);
|
||||
ptrdiff_t getOffsetStart() const;
|
||||
ptrdiff_t getOffsetLimit() const;
|
||||
|
||||
private:
|
||||
void initBasic(ValueType type, bool allocated = false);
|
||||
|
||||
Value& resolveReference(const char* key, bool isStatic);
|
||||
Value& resolveReference(const char* key);
|
||||
Value& resolveReference(const char* key, const char* end);
|
||||
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
inline bool isItemAvailable() const { return itemIsUsed_ == 0; }
|
||||
|
||||
inline void setItemUsed(bool isUsed = true) { itemIsUsed_ = isUsed ? 1 : 0; }
|
||||
|
||||
inline bool isMemberNameStatic() const { return memberNameIsStatic_ == 0; }
|
||||
|
||||
inline void setMemberNameIsStatic(bool isStatic) {
|
||||
memberNameIsStatic_ = isStatic ? 1 : 0;
|
||||
}
|
||||
#endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
|
||||
private:
|
||||
struct CommentInfo {
|
||||
CommentInfo();
|
||||
~CommentInfo();
|
||||
|
||||
void setComment(const char* text);
|
||||
void setComment(const char* text, size_t len);
|
||||
|
||||
char* comment_;
|
||||
};
|
||||
@@ -480,26 +625,18 @@ private:
|
||||
LargestUInt uint_;
|
||||
double real_;
|
||||
bool bool_;
|
||||
char* string_;
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
ValueInternalArray* array_;
|
||||
ValueInternalMap* map_;
|
||||
#else
|
||||
char* string_; // actually ptr to unsigned, followed by str, unless !allocated_
|
||||
ObjectValues* map_;
|
||||
#endif
|
||||
} value_;
|
||||
ValueType type_ : 8;
|
||||
int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container.
|
||||
int memberNameIsStatic_ : 1; // used by the ValueInternalMap container.
|
||||
#endif
|
||||
unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
|
||||
// If not allocated_, string_ must be null-terminated.
|
||||
CommentInfo* comments_;
|
||||
|
||||
// [start, limit) byte offsets in the source JSON text from which this Value
|
||||
// was extracted.
|
||||
size_t start_;
|
||||
size_t limit_;
|
||||
ptrdiff_t start_;
|
||||
ptrdiff_t limit_;
|
||||
};
|
||||
|
||||
/** \brief Experimental and untested: represents an element of the "path" to
|
||||
@@ -512,7 +649,7 @@ public:
|
||||
PathArgument();
|
||||
PathArgument(ArrayIndex index);
|
||||
PathArgument(const char* key);
|
||||
PathArgument(const std::string& key);
|
||||
PathArgument(const JSONCPP_STRING& key);
|
||||
|
||||
private:
|
||||
enum Kind {
|
||||
@@ -520,7 +657,7 @@ private:
|
||||
kindIndex,
|
||||
kindKey
|
||||
};
|
||||
std::string key_;
|
||||
JSONCPP_STRING key_;
|
||||
ArrayIndex index_;
|
||||
Kind kind_;
|
||||
};
|
||||
@@ -538,7 +675,7 @@ private:
|
||||
*/
|
||||
class JSON_API Path {
|
||||
public:
|
||||
Path(const std::string& path,
|
||||
Path(const JSONCPP_STRING& path,
|
||||
const PathArgument& a1 = PathArgument(),
|
||||
const PathArgument& a2 = PathArgument(),
|
||||
const PathArgument& a3 = PathArgument(),
|
||||
@@ -555,355 +692,16 @@ private:
|
||||
typedef std::vector<const PathArgument*> InArgs;
|
||||
typedef std::vector<PathArgument> Args;
|
||||
|
||||
void makePath(const std::string& path, const InArgs& in);
|
||||
void addPathInArg(const std::string& path,
|
||||
void makePath(const JSONCPP_STRING& path, const InArgs& in);
|
||||
void addPathInArg(const JSONCPP_STRING& path,
|
||||
const InArgs& in,
|
||||
InArgs::const_iterator& itInArg,
|
||||
PathArgument::Kind kind);
|
||||
void invalidPath(const std::string& path, int location);
|
||||
void invalidPath(const JSONCPP_STRING& path, int location);
|
||||
|
||||
Args args_;
|
||||
};
|
||||
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
/** \brief Allocator to customize Value internal map.
|
||||
* Below is an example of a simple implementation (default implementation
|
||||
actually
|
||||
* use memory pool for speed).
|
||||
* \code
|
||||
class DefaultValueMapAllocator : public ValueMapAllocator
|
||||
{
|
||||
public: // overridden from ValueMapAllocator
|
||||
virtual ValueInternalMap *newMap()
|
||||
{
|
||||
return new ValueInternalMap();
|
||||
}
|
||||
|
||||
virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
|
||||
{
|
||||
return new ValueInternalMap( other );
|
||||
}
|
||||
|
||||
virtual void destructMap( ValueInternalMap *map )
|
||||
{
|
||||
delete map;
|
||||
}
|
||||
|
||||
virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
|
||||
{
|
||||
return new ValueInternalLink[size];
|
||||
}
|
||||
|
||||
virtual void releaseMapBuckets( ValueInternalLink *links )
|
||||
{
|
||||
delete [] links;
|
||||
}
|
||||
|
||||
virtual ValueInternalLink *allocateMapLink()
|
||||
{
|
||||
return new ValueInternalLink();
|
||||
}
|
||||
|
||||
virtual void releaseMapLink( ValueInternalLink *link )
|
||||
{
|
||||
delete link;
|
||||
}
|
||||
};
|
||||
* \endcode
|
||||
*/
|
||||
class JSON_API ValueMapAllocator {
|
||||
public:
|
||||
virtual ~ValueMapAllocator();
|
||||
virtual ValueInternalMap* newMap() = 0;
|
||||
virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) = 0;
|
||||
virtual void destructMap(ValueInternalMap* map) = 0;
|
||||
virtual ValueInternalLink* allocateMapBuckets(unsigned int size) = 0;
|
||||
virtual void releaseMapBuckets(ValueInternalLink* links) = 0;
|
||||
virtual ValueInternalLink* allocateMapLink() = 0;
|
||||
virtual void releaseMapLink(ValueInternalLink* link) = 0;
|
||||
};
|
||||
|
||||
/** \brief ValueInternalMap hash-map bucket chain link (for internal use only).
|
||||
* \internal previous_ & next_ allows for bidirectional traversal.
|
||||
*/
|
||||
class JSON_API ValueInternalLink {
|
||||
public:
|
||||
enum {
|
||||
itemPerLink = 6
|
||||
}; // sizeof(ValueInternalLink) = 128 on 32 bits architecture.
|
||||
enum InternalFlags {
|
||||
flagAvailable = 0,
|
||||
flagUsed = 1
|
||||
};
|
||||
|
||||
ValueInternalLink();
|
||||
|
||||
~ValueInternalLink();
|
||||
|
||||
Value items_[itemPerLink];
|
||||
char* keys_[itemPerLink];
|
||||
ValueInternalLink* previous_;
|
||||
ValueInternalLink* next_;
|
||||
};
|
||||
|
||||
/** \brief A linked page based hash-table implementation used internally by
|
||||
*Value.
|
||||
* \internal ValueInternalMap is a tradional bucket based hash-table, with a
|
||||
*linked
|
||||
* list in each bucket to handle collision. There is an addional twist in that
|
||||
* each node of the collision linked list is a page containing a fixed amount of
|
||||
* value. This provides a better compromise between memory usage and speed.
|
||||
*
|
||||
* Each bucket is made up of a chained list of ValueInternalLink. The last
|
||||
* link of a given bucket can be found in the 'previous_' field of the following
|
||||
*bucket.
|
||||
* The last link of the last bucket is stored in tailLink_ as it has no
|
||||
*following bucket.
|
||||
* Only the last link of a bucket may contains 'available' item. The last link
|
||||
*always
|
||||
* contains at least one element unless is it the bucket one very first link.
|
||||
*/
|
||||
class JSON_API ValueInternalMap {
|
||||
friend class ValueIteratorBase;
|
||||
friend class Value;
|
||||
|
||||
public:
|
||||
typedef unsigned int HashKey;
|
||||
typedef unsigned int BucketIndex;
|
||||
|
||||
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
|
||||
struct IteratorState {
|
||||
IteratorState() : map_(0), link_(0), itemIndex_(0), bucketIndex_(0) {}
|
||||
ValueInternalMap* map_;
|
||||
ValueInternalLink* link_;
|
||||
BucketIndex itemIndex_;
|
||||
BucketIndex bucketIndex_;
|
||||
};
|
||||
#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
|
||||
|
||||
ValueInternalMap();
|
||||
ValueInternalMap(const ValueInternalMap& other);
|
||||
ValueInternalMap& operator=(ValueInternalMap other);
|
||||
~ValueInternalMap();
|
||||
|
||||
void swap(ValueInternalMap& other);
|
||||
|
||||
BucketIndex size() const;
|
||||
|
||||
void clear();
|
||||
|
||||
bool reserveDelta(BucketIndex growth);
|
||||
|
||||
bool reserve(BucketIndex newItemCount);
|
||||
|
||||
const Value* find(const char* key) const;
|
||||
|
||||
Value* find(const char* key);
|
||||
|
||||
Value& resolveReference(const char* key, bool isStatic);
|
||||
|
||||
void remove(const char* key);
|
||||
|
||||
void doActualRemove(ValueInternalLink* link,
|
||||
BucketIndex index,
|
||||
BucketIndex bucketIndex);
|
||||
|
||||
ValueInternalLink*& getLastLinkInBucket(BucketIndex bucketIndex);
|
||||
|
||||
Value& setNewItem(const char* key,
|
||||
bool isStatic,
|
||||
ValueInternalLink* link,
|
||||
BucketIndex index);
|
||||
|
||||
Value& unsafeAdd(const char* key, bool isStatic, HashKey hashedKey);
|
||||
|
||||
HashKey hash(const char* key) const;
|
||||
|
||||
int compare(const ValueInternalMap& other) const;
|
||||
|
||||
private:
|
||||
void makeBeginIterator(IteratorState& it) const;
|
||||
void makeEndIterator(IteratorState& it) const;
|
||||
static bool equals(const IteratorState& x, const IteratorState& other);
|
||||
static void increment(IteratorState& iterator);
|
||||
static void incrementBucket(IteratorState& iterator);
|
||||
static void decrement(IteratorState& iterator);
|
||||
static const char* key(const IteratorState& iterator);
|
||||
static const char* key(const IteratorState& iterator, bool& isStatic);
|
||||
static Value& value(const IteratorState& iterator);
|
||||
static int distance(const IteratorState& x, const IteratorState& y);
|
||||
|
||||
private:
|
||||
ValueInternalLink* buckets_;
|
||||
ValueInternalLink* tailLink_;
|
||||
BucketIndex bucketsSize_;
|
||||
BucketIndex itemCount_;
|
||||
};
|
||||
|
||||
/** \brief A simplified deque implementation used internally by Value.
|
||||
* \internal
|
||||
* It is based on a list of fixed "page", each page contains a fixed number of
|
||||
*items.
|
||||
* Instead of using a linked-list, a array of pointer is used for fast item
|
||||
*look-up.
|
||||
* Look-up for an element is as follow:
|
||||
* - compute page index: pageIndex = itemIndex / itemsPerPage
|
||||
* - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage]
|
||||
*
|
||||
* Insertion is amortized constant time (only the array containing the index of
|
||||
*pointers
|
||||
* need to be reallocated when items are appended).
|
||||
*/
|
||||
class JSON_API ValueInternalArray {
|
||||
friend class Value;
|
||||
friend class ValueIteratorBase;
|
||||
|
||||
public:
|
||||
enum {
|
||||
itemsPerPage = 8
|
||||
}; // should be a power of 2 for fast divide and modulo.
|
||||
typedef Value::ArrayIndex ArrayIndex;
|
||||
typedef unsigned int PageIndex;
|
||||
|
||||
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
|
||||
struct IteratorState // Must be a POD
|
||||
{
|
||||
IteratorState() : array_(0), currentPageIndex_(0), currentItemIndex_(0) {}
|
||||
ValueInternalArray* array_;
|
||||
Value** currentPageIndex_;
|
||||
unsigned int currentItemIndex_;
|
||||
};
|
||||
#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
|
||||
|
||||
ValueInternalArray();
|
||||
ValueInternalArray(const ValueInternalArray& other);
|
||||
ValueInternalArray& operator=(ValueInternalArray other);
|
||||
~ValueInternalArray();
|
||||
void swap(ValueInternalArray& other);
|
||||
|
||||
void clear();
|
||||
void resize(ArrayIndex newSize);
|
||||
|
||||
Value& resolveReference(ArrayIndex index);
|
||||
|
||||
Value* find(ArrayIndex index) const;
|
||||
|
||||
ArrayIndex size() const;
|
||||
|
||||
int compare(const ValueInternalArray& other) const;
|
||||
|
||||
private:
|
||||
static bool equals(const IteratorState& x, const IteratorState& other);
|
||||
static void increment(IteratorState& iterator);
|
||||
static void decrement(IteratorState& iterator);
|
||||
static Value& dereference(const IteratorState& iterator);
|
||||
static Value& unsafeDereference(const IteratorState& iterator);
|
||||
static int distance(const IteratorState& x, const IteratorState& y);
|
||||
static ArrayIndex indexOf(const IteratorState& iterator);
|
||||
void makeBeginIterator(IteratorState& it) const;
|
||||
void makeEndIterator(IteratorState& it) const;
|
||||
void makeIterator(IteratorState& it, ArrayIndex index) const;
|
||||
|
||||
void makeIndexValid(ArrayIndex index);
|
||||
|
||||
Value** pages_;
|
||||
ArrayIndex size_;
|
||||
PageIndex pageCount_;
|
||||
};
|
||||
|
||||
/** \brief Experimental: do not use. Allocator to customize Value internal
|
||||
array.
|
||||
* Below is an example of a simple implementation (actual implementation use
|
||||
* memory pool).
|
||||
\code
|
||||
class DefaultValueArrayAllocator : public ValueArrayAllocator
|
||||
{
|
||||
public: // overridden from ValueArrayAllocator
|
||||
virtual ~DefaultValueArrayAllocator()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ValueInternalArray *newArray()
|
||||
{
|
||||
return new ValueInternalArray();
|
||||
}
|
||||
|
||||
virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
|
||||
{
|
||||
return new ValueInternalArray( other );
|
||||
}
|
||||
|
||||
virtual void destruct( ValueInternalArray *array )
|
||||
{
|
||||
delete array;
|
||||
}
|
||||
|
||||
virtual void reallocateArrayPageIndex( Value **&indexes,
|
||||
ValueInternalArray::PageIndex
|
||||
&indexCount,
|
||||
ValueInternalArray::PageIndex
|
||||
minNewIndexCount )
|
||||
{
|
||||
ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
|
||||
if ( minNewIndexCount > newIndexCount )
|
||||
newIndexCount = minNewIndexCount;
|
||||
void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
|
||||
if ( !newIndexes )
|
||||
throw std::bad_alloc();
|
||||
indexCount = newIndexCount;
|
||||
indexes = static_cast<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 );
|
||||
}
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
class JSON_API ValueArrayAllocator {
|
||||
public:
|
||||
virtual ~ValueArrayAllocator();
|
||||
virtual ValueInternalArray* newArray() = 0;
|
||||
virtual ValueInternalArray* newArrayCopy(const ValueInternalArray& other) = 0;
|
||||
virtual void destructArray(ValueInternalArray* array) = 0;
|
||||
/** \brief Reallocate array page index.
|
||||
* Reallocates an array of pointer on each page.
|
||||
* \param indexes [input] pointer on the current index. May be \c NULL.
|
||||
* [output] pointer on the new index of at least
|
||||
* \a minNewIndexCount pages.
|
||||
* \param indexCount [input] current number of pages in the index.
|
||||
* [output] number of page the reallocated index can handle.
|
||||
* \b MUST be >= \a minNewIndexCount.
|
||||
* \param minNewIndexCount Minimum number of page the new index must be able
|
||||
* to
|
||||
* handle.
|
||||
*/
|
||||
virtual void
|
||||
reallocateArrayPageIndex(Value**& indexes,
|
||||
ValueInternalArray::PageIndex& indexCount,
|
||||
ValueInternalArray::PageIndex minNewIndexCount) = 0;
|
||||
virtual void
|
||||
releaseArrayPageIndex(Value** indexes,
|
||||
ValueInternalArray::PageIndex indexCount) = 0;
|
||||
virtual Value* allocateArrayPage() = 0;
|
||||
virtual void releaseArrayPage(Value* value) = 0;
|
||||
};
|
||||
#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
|
||||
/** \brief base class for Value iterators.
|
||||
*
|
||||
*/
|
||||
@@ -914,32 +712,35 @@ public:
|
||||
typedef int difference_type;
|
||||
typedef ValueIteratorBase SelfType;
|
||||
|
||||
ValueIteratorBase();
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
|
||||
#else
|
||||
ValueIteratorBase(const ValueInternalArray::IteratorState& state);
|
||||
ValueIteratorBase(const ValueInternalMap::IteratorState& state);
|
||||
#endif
|
||||
|
||||
bool operator==(const SelfType& other) const { return isEqual(other); }
|
||||
|
||||
bool operator!=(const SelfType& other) const { return !isEqual(other); }
|
||||
|
||||
difference_type operator-(const SelfType& other) const {
|
||||
return computeDistance(other);
|
||||
return other.computeDistance(*this);
|
||||
}
|
||||
|
||||
/// Return either the index or the member name of the referenced value as a
|
||||
/// Value.
|
||||
Value key() const;
|
||||
|
||||
/// Return the index of the referenced Value. -1 if it is not an arrayValue.
|
||||
/// Return the index of the referenced Value, or -1 if it is not an arrayValue.
|
||||
UInt index() const;
|
||||
|
||||
/// Return the member name of the referenced Value, or "" if it is not an
|
||||
/// objectValue.
|
||||
/// \note Avoid `c_str()` on result, as embedded zeroes are possible.
|
||||
JSONCPP_STRING name() const;
|
||||
|
||||
/// Return the member name of the referenced Value. "" if it is not an
|
||||
/// objectValue.
|
||||
const char* memberName() const;
|
||||
/// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls.
|
||||
JSONCPP_DEPRECATED("Use `key = name();` instead.")
|
||||
char const* memberName() const;
|
||||
/// Return the member name of the referenced Value, or NULL if it is not an
|
||||
/// objectValue.
|
||||
/// \note Better version than memberName(). Allows embedded nulls.
|
||||
char const* memberName(char const** end) const;
|
||||
|
||||
protected:
|
||||
Value& deref() const;
|
||||
@@ -955,17 +756,15 @@ protected:
|
||||
void copy(const SelfType& other);
|
||||
|
||||
private:
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
Value::ObjectValues::iterator current_;
|
||||
// Indicates that iterator is for a null value.
|
||||
bool isNull_;
|
||||
#else
|
||||
union {
|
||||
ValueInternalArray::IteratorState array_;
|
||||
ValueInternalMap::IteratorState map_;
|
||||
} iterator_;
|
||||
bool isArray_;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// For some reason, BORLAND needs these at the end, rather
|
||||
// than earlier. No idea why.
|
||||
ValueIteratorBase();
|
||||
explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
|
||||
};
|
||||
|
||||
/** \brief const iterator for object and array value.
|
||||
@@ -976,23 +775,19 @@ class JSON_API ValueConstIterator : public ValueIteratorBase {
|
||||
|
||||
public:
|
||||
typedef const Value value_type;
|
||||
typedef unsigned int size_t;
|
||||
typedef int difference_type;
|
||||
//typedef unsigned int size_t;
|
||||
//typedef int difference_type;
|
||||
typedef const Value& reference;
|
||||
typedef const Value* pointer;
|
||||
typedef ValueConstIterator SelfType;
|
||||
|
||||
ValueConstIterator();
|
||||
ValueConstIterator(ValueIterator const& other);
|
||||
|
||||
private:
|
||||
/*! \internal Use by Value to create an iterator.
|
||||
*/
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
|
||||
#else
|
||||
ValueConstIterator(const ValueInternalArray::IteratorState& state);
|
||||
ValueConstIterator(const ValueInternalMap::IteratorState& state);
|
||||
#endif
|
||||
public:
|
||||
SelfType& operator=(const ValueIteratorBase& other);
|
||||
|
||||
@@ -1037,18 +832,13 @@ public:
|
||||
typedef ValueIterator SelfType;
|
||||
|
||||
ValueIterator();
|
||||
ValueIterator(const ValueConstIterator& other);
|
||||
explicit ValueIterator(const ValueConstIterator& other);
|
||||
ValueIterator(const ValueIterator& other);
|
||||
|
||||
private:
|
||||
/*! \internal Use by Value to create an iterator.
|
||||
*/
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
explicit ValueIterator(const Value::ObjectValues::iterator& current);
|
||||
#else
|
||||
ValueIterator(const ValueInternalArray::IteratorState& state);
|
||||
ValueIterator(const ValueInternalMap::IteratorState& state);
|
||||
#endif
|
||||
public:
|
||||
SelfType& operator=(const SelfType& other);
|
||||
|
||||
@@ -1081,6 +871,15 @@ public:
|
||||
|
||||
} // namespace Json
|
||||
|
||||
|
||||
namespace std {
|
||||
/// Specialize std::swap() for Json::Value.
|
||||
template<>
|
||||
inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); }
|
||||
}
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
#pragma warning(pop)
|
||||
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
// DO NOT EDIT. This file is generated by CMake from "version"
|
||||
// and "version.h.in" files.
|
||||
// DO NOT EDIT. This file (and "version") is generated by CMake.
|
||||
// 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_STRING "1.8.2"
|
||||
# define JSONCPP_VERSION_MAJOR 1
|
||||
# define JSONCPP_VERSION_MINOR 0
|
||||
# define JSONCPP_VERSION_PATCH 0
|
||||
# define JSONCPP_VERSION_MINOR 8
|
||||
# define JSONCPP_VERSION_PATCH 2
|
||||
# define JSONCPP_VERSION_QUALIFIER
|
||||
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
|
||||
|
||||
#ifdef JSONCPP_USING_SECURE_MEMORY
|
||||
#undef JSONCPP_USING_SECURE_MEMORY
|
||||
#endif
|
||||
#define JSONCPP_USING_SECURE_MEMORY 0
|
||||
// If non-zero, the library zeroes any memory that it has allocated before
|
||||
// it frees its memory.
|
||||
|
||||
#endif // JSON_VERSION_H_INCLUDED
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
|
||||
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
|
||||
// be used by...
|
||||
@@ -20,17 +21,131 @@
|
||||
#pragma warning(disable : 4251)
|
||||
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
|
||||
#pragma pack(push, 8)
|
||||
|
||||
namespace Json {
|
||||
|
||||
class Value;
|
||||
|
||||
/** \brief Abstract class for writers.
|
||||
/**
|
||||
|
||||
Usage:
|
||||
\code
|
||||
using namespace Json;
|
||||
void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {
|
||||
std::unique_ptr<StreamWriter> const writer(
|
||||
factory.newStreamWriter());
|
||||
writer->write(value, &std::cout);
|
||||
std::cout << std::endl; // add lf and flush
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
class JSON_API StreamWriter {
|
||||
protected:
|
||||
JSONCPP_OSTREAM* sout_; // not owned; will not delete
|
||||
public:
|
||||
StreamWriter();
|
||||
virtual ~StreamWriter();
|
||||
/** Write Value into document as configured in sub-class.
|
||||
Do not take ownership of sout, but maintain a reference during function.
|
||||
\pre sout != NULL
|
||||
\return zero on success (For now, we always return zero, so check the stream instead.)
|
||||
\throw std::exception possibly, depending on configuration
|
||||
*/
|
||||
virtual int write(Value const& root, JSONCPP_OSTREAM* sout) = 0;
|
||||
|
||||
/** \brief A simple abstract factory.
|
||||
*/
|
||||
class JSON_API Factory {
|
||||
public:
|
||||
virtual ~Factory();
|
||||
/** \brief Allocate a CharReader via operator new().
|
||||
* \throw std::exception if something goes wrong (e.g. invalid settings)
|
||||
*/
|
||||
virtual StreamWriter* newStreamWriter() const = 0;
|
||||
}; // Factory
|
||||
}; // StreamWriter
|
||||
|
||||
/** \brief Write into stringstream, then return string, for convenience.
|
||||
* A StreamWriter will be created from the factory, used, and then deleted.
|
||||
*/
|
||||
class JSON_API Writer {
|
||||
JSONCPP_STRING JSON_API writeString(StreamWriter::Factory const& factory, Value const& root);
|
||||
|
||||
|
||||
/** \brief Build a StreamWriter implementation.
|
||||
|
||||
Usage:
|
||||
\code
|
||||
using namespace Json;
|
||||
Value value = ...;
|
||||
StreamWriterBuilder builder;
|
||||
builder["commentStyle"] = "None";
|
||||
builder["indentation"] = " "; // or whatever you like
|
||||
std::unique_ptr<Json::StreamWriter> writer(
|
||||
builder.newStreamWriter());
|
||||
writer->write(value, &std::cout);
|
||||
std::cout << std::endl; // add lf and flush
|
||||
\endcode
|
||||
*/
|
||||
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
|
||||
public:
|
||||
// Note: We use a Json::Value so that we can add data-members to this class
|
||||
// without a major version bump.
|
||||
/** Configuration of this builder.
|
||||
Available settings (case-sensitive):
|
||||
- "commentStyle": "None" or "All"
|
||||
- "indentation": "<anything>"
|
||||
- "enableYAMLCompatibility": false or true
|
||||
- slightly change the whitespace around colons
|
||||
- "dropNullPlaceholders": false or true
|
||||
- Drop the "null" string from the writer's output for nullValues.
|
||||
Strictly speaking, this is not valid JSON. But when the output is being
|
||||
fed to a browser's Javascript, it makes for smaller output and the
|
||||
browser can handle the output just fine.
|
||||
- "useSpecialFloats": false or true
|
||||
- If true, outputs non-finite floating point values in the following way:
|
||||
NaN values as "NaN", positive infinity as "Infinity", and negative infinity
|
||||
as "-Infinity".
|
||||
|
||||
You can examine 'settings_` yourself
|
||||
to see the defaults. You can also write and read them just like any
|
||||
JSON Value.
|
||||
\sa setDefaults()
|
||||
*/
|
||||
Json::Value settings_;
|
||||
|
||||
StreamWriterBuilder();
|
||||
~StreamWriterBuilder() JSONCPP_OVERRIDE;
|
||||
|
||||
/**
|
||||
* \throw std::exception if something goes wrong (e.g. invalid settings)
|
||||
*/
|
||||
StreamWriter* newStreamWriter() const JSONCPP_OVERRIDE;
|
||||
|
||||
/** \return true if 'settings' are legal and consistent;
|
||||
* otherwise, indicate bad settings via 'invalid'.
|
||||
*/
|
||||
bool validate(Json::Value* invalid) const;
|
||||
/** A simple way to update a specific setting.
|
||||
*/
|
||||
Value& operator[](JSONCPP_STRING key);
|
||||
|
||||
/** Called by ctor, but you can use this to reset settings_.
|
||||
* \pre 'settings' != NULL (but Json::null is fine)
|
||||
* \remark Defaults:
|
||||
* \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
|
||||
*/
|
||||
static void setDefaults(Json::Value* settings);
|
||||
};
|
||||
|
||||
/** \brief Abstract class for writers.
|
||||
* \deprecated Use StreamWriter. (And really, this is an implementation detail.)
|
||||
*/
|
||||
class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer {
|
||||
public:
|
||||
virtual ~Writer();
|
||||
|
||||
virtual std::string write(const Value& root) = 0;
|
||||
virtual JSONCPP_STRING write(const Value& root) = 0;
|
||||
};
|
||||
|
||||
/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
|
||||
@@ -40,11 +155,13 @@ public:
|
||||
*consumption,
|
||||
* but may be usefull to support feature such as RPC where bandwith is limited.
|
||||
* \sa Reader, Value
|
||||
* \deprecated Use StreamWriterBuilder.
|
||||
*/
|
||||
class JSON_API FastWriter : public Writer {
|
||||
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter : public Writer {
|
||||
|
||||
public:
|
||||
FastWriter();
|
||||
virtual ~FastWriter() {}
|
||||
~FastWriter() JSONCPP_OVERRIDE {}
|
||||
|
||||
void enableYAMLCompatibility();
|
||||
|
||||
@@ -58,12 +175,12 @@ public:
|
||||
void omitEndingLineFeed();
|
||||
|
||||
public: // overridden from Writer
|
||||
virtual std::string write(const Value& root);
|
||||
JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE;
|
||||
|
||||
private:
|
||||
void writeValue(const Value& value);
|
||||
|
||||
std::string document_;
|
||||
JSONCPP_STRING document_;
|
||||
bool yamlCompatiblityEnabled_;
|
||||
bool dropNullPlaceholders_;
|
||||
bool omitEndingLineFeed_;
|
||||
@@ -91,40 +208,41 @@ private:
|
||||
*#CommentPlacement.
|
||||
*
|
||||
* \sa Reader, Value, Value::setComment()
|
||||
* \deprecated Use StreamWriterBuilder.
|
||||
*/
|
||||
class JSON_API StyledWriter : public Writer {
|
||||
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API StyledWriter : public Writer {
|
||||
public:
|
||||
StyledWriter();
|
||||
virtual ~StyledWriter() {}
|
||||
~StyledWriter() JSONCPP_OVERRIDE {}
|
||||
|
||||
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);
|
||||
JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE;
|
||||
|
||||
private:
|
||||
void writeValue(const Value& value);
|
||||
void writeArrayValue(const Value& value);
|
||||
bool isMultineArray(const Value& value);
|
||||
void pushValue(const std::string& value);
|
||||
void pushValue(const JSONCPP_STRING& value);
|
||||
void writeIndent();
|
||||
void writeWithIndent(const std::string& value);
|
||||
void writeWithIndent(const JSONCPP_STRING& value);
|
||||
void indent();
|
||||
void unindent();
|
||||
void writeCommentBeforeValue(const Value& root);
|
||||
void writeCommentAfterValueOnSameLine(const Value& root);
|
||||
bool hasCommentForValue(const Value& value);
|
||||
static std::string normalizeEOL(const std::string& text);
|
||||
static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text);
|
||||
|
||||
typedef std::vector<std::string> ChildValues;
|
||||
typedef std::vector<JSONCPP_STRING> ChildValues;
|
||||
|
||||
ChildValues childValues_;
|
||||
std::string document_;
|
||||
std::string indentString_;
|
||||
int rightMargin_;
|
||||
int indentSize_;
|
||||
JSONCPP_STRING document_;
|
||||
JSONCPP_STRING indentString_;
|
||||
unsigned int rightMargin_;
|
||||
unsigned int indentSize_;
|
||||
bool addChildValues_;
|
||||
};
|
||||
|
||||
@@ -151,10 +269,14 @@ private:
|
||||
#CommentPlacement.
|
||||
*
|
||||
* \sa Reader, Value, Value::setComment()
|
||||
* \deprecated Use StreamWriterBuilder.
|
||||
*/
|
||||
class JSON_API StyledStreamWriter {
|
||||
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API StyledStreamWriter {
|
||||
public:
|
||||
StyledStreamWriter(std::string indentation = "\t");
|
||||
/**
|
||||
* \param indentation Each level will be indented by this amount extra.
|
||||
*/
|
||||
StyledStreamWriter(JSONCPP_STRING indentation = "\t");
|
||||
~StyledStreamWriter() {}
|
||||
|
||||
public:
|
||||
@@ -164,48 +286,51 @@ public:
|
||||
* \note There is no point in deriving from Writer, since write() should not
|
||||
* return a value.
|
||||
*/
|
||||
void write(std::ostream& out, const Value& root);
|
||||
void write(JSONCPP_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 pushValue(const JSONCPP_STRING& value);
|
||||
void writeIndent();
|
||||
void writeWithIndent(const std::string& value);
|
||||
void writeWithIndent(const JSONCPP_STRING& value);
|
||||
void indent();
|
||||
void unindent();
|
||||
void writeCommentBeforeValue(const Value& root);
|
||||
void writeCommentAfterValueOnSameLine(const Value& root);
|
||||
bool hasCommentForValue(const Value& value);
|
||||
static std::string normalizeEOL(const std::string& text);
|
||||
static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text);
|
||||
|
||||
typedef std::vector<std::string> ChildValues;
|
||||
typedef std::vector<JSONCPP_STRING> ChildValues;
|
||||
|
||||
ChildValues childValues_;
|
||||
std::ostream* document_;
|
||||
std::string indentString_;
|
||||
int rightMargin_;
|
||||
std::string indentation_;
|
||||
bool addChildValues_;
|
||||
JSONCPP_OSTREAM* document_;
|
||||
JSONCPP_STRING indentString_;
|
||||
unsigned int rightMargin_;
|
||||
JSONCPP_STRING indentation_;
|
||||
bool addChildValues_ : 1;
|
||||
bool indented_ : 1;
|
||||
};
|
||||
|
||||
#if defined(JSON_HAS_INT64)
|
||||
std::string JSON_API valueToString(Int value);
|
||||
std::string JSON_API valueToString(UInt value);
|
||||
JSONCPP_STRING JSON_API valueToString(Int value);
|
||||
JSONCPP_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);
|
||||
JSONCPP_STRING JSON_API valueToString(LargestInt value);
|
||||
JSONCPP_STRING JSON_API valueToString(LargestUInt value);
|
||||
JSONCPP_STRING JSON_API valueToString(double value);
|
||||
JSONCPP_STRING JSON_API valueToString(bool value);
|
||||
JSONCPP_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);
|
||||
JSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root);
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
#pragma warning(pop)
|
||||
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
// 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
|
||||
@@ -1,360 +0,0 @@
|
||||
// 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
|
||||
@@ -1,473 +0,0 @@
|
||||
// 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
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2007-2011 Baptiste Lepilleur
|
||||
// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.
|
||||
// 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
|
||||
@@ -14,18 +15,51 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <istream>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
|
||||
#if defined(_MSC_VER)
|
||||
#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
|
||||
#define snprintf sprintf_s
|
||||
#elif _MSC_VER >= 1900 // VC++ 14.0 and above
|
||||
#define snprintf std::snprintf
|
||||
#else
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#elif defined(__ANDROID__) || defined(__QNXNTO__)
|
||||
#define snprintf snprintf
|
||||
#elif __cplusplus >= 201103L
|
||||
#if !defined(__MINGW32__) && !defined(__CYGWIN__)
|
||||
#define snprintf std::snprintf
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__QNXNTO__)
|
||||
#define sscanf std::sscanf
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
|
||||
// Disable warning about strdup being deprecated.
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile time to change the stack limit
|
||||
#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
|
||||
#define JSONCPP_DEPRECATED_STACK_LIMIT 1000
|
||||
#endif
|
||||
|
||||
static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
|
||||
|
||||
namespace Json {
|
||||
|
||||
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
|
||||
typedef std::unique_ptr<CharReader> CharReaderPtr;
|
||||
#else
|
||||
typedef std::auto_ptr<CharReader> CharReaderPtr;
|
||||
#endif
|
||||
|
||||
// Implementation of class Features
|
||||
// ////////////////////////////////
|
||||
|
||||
@@ -47,24 +81,7 @@ Features Features::strictMode() {
|
||||
// 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) {
|
||||
bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
|
||||
for (; begin < end; ++begin)
|
||||
if (*begin == '\n' || *begin == '\r')
|
||||
return true;
|
||||
@@ -86,7 +103,7 @@ Reader::Reader(const Features& features)
|
||||
|
||||
bool
|
||||
Reader::parse(const std::string& document, Value& root, bool collectComments) {
|
||||
document_ = document;
|
||||
document_.assign(document.begin(), document.end());
|
||||
const char* begin = document_.c_str();
|
||||
const char* end = begin + document_.length();
|
||||
return parse(begin, end, root, collectComments);
|
||||
@@ -98,11 +115,11 @@ bool Reader::parse(std::istream& sin, Value& root, bool collectComments) {
|
||||
// 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
|
||||
// Since JSONCPP_STRING is reference-counted, this at least does not
|
||||
// create an extra copy.
|
||||
std::string doc;
|
||||
JSONCPP_STRING doc;
|
||||
std::getline(sin, doc, (char)EOF);
|
||||
return parse(doc, root, collectComments);
|
||||
return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
|
||||
}
|
||||
|
||||
bool Reader::parse(const char* beginDoc,
|
||||
@@ -119,7 +136,7 @@ bool Reader::parse(const char* beginDoc,
|
||||
current_ = begin_;
|
||||
lastValueEnd_ = 0;
|
||||
lastValue_ = 0;
|
||||
commentsBefore_ = "";
|
||||
commentsBefore_.clear();
|
||||
errors_.clear();
|
||||
while (!nodes_.empty())
|
||||
nodes_.pop();
|
||||
@@ -147,21 +164,18 @@ bool Reader::parse(const char* beginDoc,
|
||||
}
|
||||
|
||||
bool Reader::readValue() {
|
||||
// readValue() may call itself only if it calls readObject() or ReadArray().
|
||||
// These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue().
|
||||
// parse() executes one nodes_.push(), so > instead of >=.
|
||||
if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
|
||||
|
||||
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_ = "";
|
||||
commentsBefore_.clear();
|
||||
}
|
||||
|
||||
switch (token.type_) {
|
||||
@@ -180,31 +194,42 @@ bool Reader::readValue() {
|
||||
successful = decodeString(token);
|
||||
break;
|
||||
case tokenTrue:
|
||||
currentValue() = true;
|
||||
{
|
||||
Value v(true);
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
}
|
||||
break;
|
||||
case tokenFalse:
|
||||
currentValue() = false;
|
||||
{
|
||||
Value v(false);
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
}
|
||||
break;
|
||||
case tokenNull:
|
||||
currentValue() = Value();
|
||||
{
|
||||
Value v;
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
}
|
||||
break;
|
||||
case tokenArraySeparator:
|
||||
case tokenObjectEnd:
|
||||
case tokenArrayEnd:
|
||||
if (features_.allowDroppedNullPlaceholders_) {
|
||||
// "Un-read" the current token and mark the current value as a null
|
||||
// token.
|
||||
current_--;
|
||||
currentValue() = Value();
|
||||
Value v;
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(current_ - begin_ - 1);
|
||||
currentValue().setOffsetLimit(current_ - begin_);
|
||||
break;
|
||||
}
|
||||
// Else, fall through...
|
||||
} // Else, fall through...
|
||||
default:
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
@@ -229,13 +254,6 @@ void Reader::skipCommentTokens(Token& 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_;
|
||||
@@ -351,19 +369,39 @@ bool Reader::readComment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSONCPP_STRING Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
|
||||
JSONCPP_STRING normalized;
|
||||
normalized.reserve(static_cast<size_t>(end - begin));
|
||||
Reader::Location current = begin;
|
||||
while (current != end) {
|
||||
char c = *current++;
|
||||
if (c == '\r') {
|
||||
if (current != end && *current == '\n')
|
||||
// convert dos EOL
|
||||
++current;
|
||||
// convert Mac EOL
|
||||
normalized += '\n';
|
||||
} else {
|
||||
normalized += c;
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
void
|
||||
Reader::addComment(Location begin, Location end, CommentPlacement placement) {
|
||||
assert(collectComments_);
|
||||
const JSONCPP_STRING& normalized = normalizeEOL(begin, end);
|
||||
if (placement == commentAfterOnSameLine) {
|
||||
assert(lastValue_ != 0);
|
||||
lastValue_->setComment(std::string(begin, end), placement);
|
||||
lastValue_->setComment(normalized, placement);
|
||||
} else {
|
||||
commentsBefore_ += std::string(begin, end);
|
||||
commentsBefore_ += normalized;
|
||||
}
|
||||
}
|
||||
|
||||
bool Reader::readCStyleComment() {
|
||||
while (current_ != end_) {
|
||||
while ((current_ + 1) < end_) {
|
||||
Char c = getNextChar();
|
||||
if (c == '*' && *current_ == '/')
|
||||
break;
|
||||
@@ -374,23 +412,43 @@ bool Reader::readCStyleComment() {
|
||||
bool Reader::readCppStyleComment() {
|
||||
while (current_ != end_) {
|
||||
Char c = getNextChar();
|
||||
if (c == '\r' || c == '\n')
|
||||
if (c == '\n')
|
||||
break;
|
||||
if (c == '\r') {
|
||||
// Consume DOS EOL. It will be normalized in addComment.
|
||||
if (current_ != end_ && *current_ == '\n')
|
||||
getNextChar();
|
||||
// Break on Moc OS 9 EOL.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Reader::readNumber() {
|
||||
while (current_ != end_) {
|
||||
if (!(*current_ >= '0' && *current_ <= '9') &&
|
||||
!in(*current_, '.', 'e', 'E', '+', '-'))
|
||||
break;
|
||||
++current_;
|
||||
const char *p = current_;
|
||||
char c = '0'; // stopgap for already consumed character
|
||||
// integral part
|
||||
while (c >= '0' && c <= '9')
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
// fractional part
|
||||
if (c == '.') {
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
while (c >= '0' && c <= '9')
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
}
|
||||
// exponential part
|
||||
if (c == 'e' || c == 'E') {
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
if (c == '+' || c == '-')
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
while (c >= '0' && c <= '9')
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool Reader::readString() {
|
||||
Char c = 0;
|
||||
Char c = '\0';
|
||||
while (current_ != end_) {
|
||||
c = getNextChar();
|
||||
if (c == '\\')
|
||||
@@ -403,8 +461,9 @@ bool Reader::readString() {
|
||||
|
||||
bool Reader::readObject(Token& tokenStart) {
|
||||
Token tokenName;
|
||||
std::string name;
|
||||
currentValue() = Value(objectValue);
|
||||
JSONCPP_STRING name;
|
||||
Value init(objectValue);
|
||||
currentValue().swapPayload(init);
|
||||
currentValue().setOffsetStart(tokenStart.start_ - begin_);
|
||||
while (readToken(tokenName)) {
|
||||
bool initialTokenOk = true;
|
||||
@@ -414,7 +473,7 @@ bool Reader::readObject(Token& tokenStart) {
|
||||
break;
|
||||
if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
|
||||
return true;
|
||||
name = "";
|
||||
name.clear();
|
||||
if (tokenName.type_ == tokenString) {
|
||||
if (!decodeString(tokenName, name))
|
||||
return recoverFromError(tokenObjectEnd);
|
||||
@@ -422,7 +481,7 @@ bool Reader::readObject(Token& tokenStart) {
|
||||
Value numberName;
|
||||
if (!decodeNumber(tokenName, numberName))
|
||||
return recoverFromError(tokenObjectEnd);
|
||||
name = numberName.asString();
|
||||
name = JSONCPP_STRING(numberName.asCString());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -457,10 +516,11 @@ bool Reader::readObject(Token& tokenStart) {
|
||||
}
|
||||
|
||||
bool Reader::readArray(Token& tokenStart) {
|
||||
currentValue() = Value(arrayValue);
|
||||
Value init(arrayValue);
|
||||
currentValue().swapPayload(init);
|
||||
currentValue().setOffsetStart(tokenStart.start_ - begin_);
|
||||
skipSpaces();
|
||||
if (*current_ == ']') // empty array
|
||||
if (current_ != end_ && *current_ == ']') // empty array
|
||||
{
|
||||
Token endArray;
|
||||
readToken(endArray);
|
||||
@@ -497,20 +557,13 @@ bool Reader::decodeNumber(Token& token) {
|
||||
Value decoded;
|
||||
if (!decodeNumber(token, decoded))
|
||||
return false;
|
||||
currentValue() = decoded;
|
||||
currentValue().swapPayload(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.
|
||||
@@ -518,17 +571,16 @@ bool Reader::decodeNumber(Token& token, Value& decoded) {
|
||||
bool isNegative = *current == '-';
|
||||
if (isNegative)
|
||||
++current;
|
||||
// TODO: Help the compiler do the div and mod at compile time or get rid of them.
|
||||
Value::LargestUInt maxIntegerValue =
|
||||
isNegative ? Value::LargestUInt(-Value::minLargestInt)
|
||||
isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
|
||||
: Value::maxLargestUInt;
|
||||
Value::LargestUInt threshold = maxIntegerValue / 10;
|
||||
Value::LargestUInt value = 0;
|
||||
while (current < token.end_) {
|
||||
Char c = *current++;
|
||||
if (c < '0' || c > '9')
|
||||
return addError("'" + std::string(token.start_, token.end_) +
|
||||
"' is not a number.",
|
||||
token);
|
||||
return decodeDouble(token, decoded);
|
||||
Value::UInt digit(static_cast<Value::UInt>(c - '0'));
|
||||
if (value >= threshold) {
|
||||
// We've hit or exceeded the max value divided by 10 (rounded down). If
|
||||
@@ -542,7 +594,9 @@ bool Reader::decodeNumber(Token& token, Value& decoded) {
|
||||
}
|
||||
value = value * 10 + digit;
|
||||
}
|
||||
if (isNegative)
|
||||
if (isNegative && value == maxIntegerValue)
|
||||
decoded = Value::minLargestInt;
|
||||
else if (isNegative)
|
||||
decoded = -Value::LargestInt(value);
|
||||
else if (value <= Value::LargestUInt(Value::maxInt))
|
||||
decoded = Value::LargestInt(value);
|
||||
@@ -555,7 +609,7 @@ bool Reader::decodeDouble(Token& token) {
|
||||
Value decoded;
|
||||
if (!decodeDouble(token, decoded))
|
||||
return false;
|
||||
currentValue() = decoded;
|
||||
currentValue().swapPayload(decoded);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
return true;
|
||||
@@ -563,34 +617,10 @@ bool Reader::decodeDouble(Token& token) {
|
||||
|
||||
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_) +
|
||||
JSONCPP_STRING buffer(token.start_, token.end_);
|
||||
JSONCPP_ISTRINGSTREAM is(buffer);
|
||||
if (!(is >> value))
|
||||
return addError("'" + JSONCPP_STRING(token.start_, token.end_) +
|
||||
"' is not a number.",
|
||||
token);
|
||||
decoded = value;
|
||||
@@ -598,17 +628,18 @@ bool Reader::decodeDouble(Token& token, Value& decoded) {
|
||||
}
|
||||
|
||||
bool Reader::decodeString(Token& token) {
|
||||
std::string decoded;
|
||||
if (!decodeString(token, decoded))
|
||||
JSONCPP_STRING decoded_string;
|
||||
if (!decodeString(token, decoded_string))
|
||||
return false;
|
||||
currentValue() = decoded;
|
||||
Value decoded(decoded_string);
|
||||
currentValue().swapPayload(decoded);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Reader::decodeString(Token& token, std::string& decoded) {
|
||||
decoded.reserve(token.end_ - token.start_ - 2);
|
||||
bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) {
|
||||
decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
|
||||
Location current = token.start_ + 1; // skip '"'
|
||||
Location end = token.end_ - 1; // do not include '"'
|
||||
while (current != end) {
|
||||
@@ -692,13 +723,13 @@ bool Reader::decodeUnicodeCodePoint(Token& token,
|
||||
bool Reader::decodeUnicodeEscapeSequence(Token& token,
|
||||
Location& current,
|
||||
Location end,
|
||||
unsigned int& unicode) {
|
||||
unsigned int& ret_unicode) {
|
||||
if (end - current < 4)
|
||||
return addError(
|
||||
"Bad unicode escape sequence in string: four digits expected.",
|
||||
token,
|
||||
current);
|
||||
unicode = 0;
|
||||
int unicode = 0;
|
||||
for (int index = 0; index < 4; ++index) {
|
||||
Char c = *current++;
|
||||
unicode *= 16;
|
||||
@@ -714,11 +745,12 @@ bool Reader::decodeUnicodeEscapeSequence(Token& token,
|
||||
token,
|
||||
current);
|
||||
}
|
||||
ret_unicode = static_cast<unsigned int>(unicode);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Reader::addError(const std::string& message, Token& token, Location extra) {
|
||||
Reader::addError(const JSONCPP_STRING& message, Token& token, Location extra) {
|
||||
ErrorInfo info;
|
||||
info.token_ = token;
|
||||
info.message_ = message;
|
||||
@@ -728,7 +760,7 @@ Reader::addError(const std::string& message, Token& token, Location extra) {
|
||||
}
|
||||
|
||||
bool Reader::recoverFromError(TokenType skipUntilToken) {
|
||||
int errorCount = int(errors_.size());
|
||||
size_t const errorCount = errors_.size();
|
||||
Token skip;
|
||||
for (;;) {
|
||||
if (!readToken(skip))
|
||||
@@ -740,7 +772,7 @@ bool Reader::recoverFromError(TokenType skipUntilToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Reader::addErrorAndRecover(const std::string& message,
|
||||
bool Reader::addErrorAndRecover(const JSONCPP_STRING& message,
|
||||
Token& token,
|
||||
TokenType skipUntilToken) {
|
||||
addError(message, token);
|
||||
@@ -778,29 +810,21 @@ void Reader::getLocationLineAndColumn(Location location,
|
||||
++line;
|
||||
}
|
||||
|
||||
std::string Reader::getLocationLineAndColumn(Location location) const {
|
||||
JSONCPP_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 {
|
||||
JSONCPP_STRING Reader::getFormatedErrorMessages() const {
|
||||
return getFormattedErrorMessages();
|
||||
}
|
||||
|
||||
std::string Reader::getFormattedErrorMessages() const {
|
||||
std::string formattedMessage;
|
||||
JSONCPP_STRING Reader::getFormattedErrorMessages() const {
|
||||
JSONCPP_STRING formattedMessage;
|
||||
for (Errors::const_iterator itError = errors_.begin();
|
||||
itError != errors_.end();
|
||||
++itError) {
|
||||
@@ -830,8 +854,8 @@ std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
|
||||
return allErrors;
|
||||
}
|
||||
|
||||
bool Reader::pushError(const Value& value, const std::string& message) {
|
||||
size_t length = end_ - begin_;
|
||||
bool Reader::pushError(const Value& value, const JSONCPP_STRING& message) {
|
||||
ptrdiff_t const length = end_ - begin_;
|
||||
if(value.getOffsetStart() > length
|
||||
|| value.getOffsetLimit() > length)
|
||||
return false;
|
||||
@@ -847,8 +871,8 @@ bool Reader::pushError(const Value& value, const std::string& message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) {
|
||||
size_t length = end_ - begin_;
|
||||
bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) {
|
||||
ptrdiff_t const length = end_ - begin_;
|
||||
if(value.getOffsetStart() > length
|
||||
|| value.getOffsetLimit() > length
|
||||
|| extra.getOffsetLimit() > length)
|
||||
@@ -869,15 +893,1170 @@ 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);
|
||||
// exact copy of Features
|
||||
class OurFeatures {
|
||||
public:
|
||||
static OurFeatures all();
|
||||
bool allowComments_;
|
||||
bool strictRoot_;
|
||||
bool allowDroppedNullPlaceholders_;
|
||||
bool allowNumericKeys_;
|
||||
bool allowSingleQuotes_;
|
||||
bool failIfExtra_;
|
||||
bool rejectDupKeys_;
|
||||
bool allowSpecialFloats_;
|
||||
int stackLimit_;
|
||||
}; // OurFeatures
|
||||
|
||||
// exact copy of Implementation of class Features
|
||||
// ////////////////////////////////
|
||||
|
||||
OurFeatures OurFeatures::all() { return OurFeatures(); }
|
||||
|
||||
// Implementation of class Reader
|
||||
// ////////////////////////////////
|
||||
|
||||
// exact copy of Reader, renamed to OurReader
|
||||
class OurReader {
|
||||
public:
|
||||
typedef char Char;
|
||||
typedef const Char* Location;
|
||||
struct StructuredError {
|
||||
ptrdiff_t offset_start;
|
||||
ptrdiff_t offset_limit;
|
||||
JSONCPP_STRING message;
|
||||
};
|
||||
|
||||
OurReader(OurFeatures const& features);
|
||||
bool parse(const char* beginDoc,
|
||||
const char* endDoc,
|
||||
Value& root,
|
||||
bool collectComments = true);
|
||||
JSONCPP_STRING getFormattedErrorMessages() const;
|
||||
std::vector<StructuredError> getStructuredErrors() const;
|
||||
bool pushError(const Value& value, const JSONCPP_STRING& message);
|
||||
bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra);
|
||||
bool good() const;
|
||||
|
||||
private:
|
||||
OurReader(OurReader const&); // no impl
|
||||
void operator=(OurReader const&); // no impl
|
||||
|
||||
enum TokenType {
|
||||
tokenEndOfStream = 0,
|
||||
tokenObjectBegin,
|
||||
tokenObjectEnd,
|
||||
tokenArrayBegin,
|
||||
tokenArrayEnd,
|
||||
tokenString,
|
||||
tokenNumber,
|
||||
tokenTrue,
|
||||
tokenFalse,
|
||||
tokenNull,
|
||||
tokenNaN,
|
||||
tokenPosInf,
|
||||
tokenNegInf,
|
||||
tokenArraySeparator,
|
||||
tokenMemberSeparator,
|
||||
tokenComment,
|
||||
tokenError
|
||||
};
|
||||
|
||||
class Token {
|
||||
public:
|
||||
TokenType type_;
|
||||
Location start_;
|
||||
Location end_;
|
||||
};
|
||||
|
||||
class ErrorInfo {
|
||||
public:
|
||||
Token token_;
|
||||
JSONCPP_STRING message_;
|
||||
Location extra_;
|
||||
};
|
||||
|
||||
typedef std::deque<ErrorInfo> Errors;
|
||||
|
||||
bool readToken(Token& token);
|
||||
void skipSpaces();
|
||||
bool match(Location pattern, int patternLength);
|
||||
bool readComment();
|
||||
bool readCStyleComment();
|
||||
bool readCppStyleComment();
|
||||
bool readString();
|
||||
bool readStringSingleQuote();
|
||||
bool readNumber(bool checkInf);
|
||||
bool readValue();
|
||||
bool readObject(Token& token);
|
||||
bool readArray(Token& token);
|
||||
bool decodeNumber(Token& token);
|
||||
bool decodeNumber(Token& token, Value& decoded);
|
||||
bool decodeString(Token& token);
|
||||
bool decodeString(Token& token, JSONCPP_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 JSONCPP_STRING& message, Token& token, Location extra = 0);
|
||||
bool recoverFromError(TokenType skipUntilToken);
|
||||
bool addErrorAndRecover(const JSONCPP_STRING& message,
|
||||
Token& token,
|
||||
TokenType skipUntilToken);
|
||||
void skipUntilSpace();
|
||||
Value& currentValue();
|
||||
Char getNextChar();
|
||||
void
|
||||
getLocationLineAndColumn(Location location, int& line, int& column) const;
|
||||
JSONCPP_STRING getLocationLineAndColumn(Location location) const;
|
||||
void addComment(Location begin, Location end, CommentPlacement placement);
|
||||
void skipCommentTokens(Token& token);
|
||||
|
||||
static JSONCPP_STRING normalizeEOL(Location begin, Location end);
|
||||
static bool containsNewLine(Location begin, Location end);
|
||||
|
||||
typedef std::stack<Value*> Nodes;
|
||||
Nodes nodes_;
|
||||
Errors errors_;
|
||||
JSONCPP_STRING document_;
|
||||
Location begin_;
|
||||
Location end_;
|
||||
Location current_;
|
||||
Location lastValueEnd_;
|
||||
Value* lastValue_;
|
||||
JSONCPP_STRING commentsBefore_;
|
||||
|
||||
OurFeatures const features_;
|
||||
bool collectComments_;
|
||||
}; // OurReader
|
||||
|
||||
// complete copy of Read impl, for OurReader
|
||||
|
||||
bool OurReader::containsNewLine(OurReader::Location begin, OurReader::Location end) {
|
||||
for (; begin < end; ++begin)
|
||||
if (*begin == '\n' || *begin == '\r')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
OurReader::OurReader(OurFeatures const& features)
|
||||
: errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
|
||||
lastValue_(), commentsBefore_(),
|
||||
features_(features), collectComments_() {
|
||||
}
|
||||
|
||||
bool OurReader::parse(const char* beginDoc,
|
||||
const char* endDoc,
|
||||
Value& root,
|
||||
bool collectComments) {
|
||||
if (!features_.allowComments_) {
|
||||
collectComments = false;
|
||||
}
|
||||
|
||||
begin_ = beginDoc;
|
||||
end_ = endDoc;
|
||||
collectComments_ = collectComments;
|
||||
current_ = begin_;
|
||||
lastValueEnd_ = 0;
|
||||
lastValue_ = 0;
|
||||
commentsBefore_.clear();
|
||||
errors_.clear();
|
||||
while (!nodes_.empty())
|
||||
nodes_.pop();
|
||||
nodes_.push(&root);
|
||||
|
||||
bool successful = readValue();
|
||||
Token token;
|
||||
skipCommentTokens(token);
|
||||
if (features_.failIfExtra_) {
|
||||
if ((features_.strictRoot_ || token.type_ != tokenError) && token.type_ != tokenEndOfStream) {
|
||||
addError("Extra non-whitespace after JSON value.", token);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (collectComments_ && !commentsBefore_.empty())
|
||||
root.setComment(commentsBefore_, commentAfter);
|
||||
if (features_.strictRoot_) {
|
||||
if (!root.isArray() && !root.isObject()) {
|
||||
// Set error location to start of doc, ideally should be first token found
|
||||
// in doc
|
||||
token.type_ = tokenError;
|
||||
token.start_ = beginDoc;
|
||||
token.end_ = endDoc;
|
||||
addError(
|
||||
"A valid JSON document must be either an array or an object value.",
|
||||
token);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return successful;
|
||||
}
|
||||
|
||||
bool OurReader::readValue() {
|
||||
// To preserve the old behaviour we cast size_t to int.
|
||||
if (static_cast<int>(nodes_.size()) > features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue().");
|
||||
Token token;
|
||||
skipCommentTokens(token);
|
||||
bool successful = true;
|
||||
|
||||
if (collectComments_ && !commentsBefore_.empty()) {
|
||||
currentValue().setComment(commentsBefore_, commentBefore);
|
||||
commentsBefore_.clear();
|
||||
}
|
||||
|
||||
switch (token.type_) {
|
||||
case tokenObjectBegin:
|
||||
successful = readObject(token);
|
||||
currentValue().setOffsetLimit(current_ - begin_);
|
||||
break;
|
||||
case tokenArrayBegin:
|
||||
successful = readArray(token);
|
||||
currentValue().setOffsetLimit(current_ - begin_);
|
||||
break;
|
||||
case tokenNumber:
|
||||
successful = decodeNumber(token);
|
||||
break;
|
||||
case tokenString:
|
||||
successful = decodeString(token);
|
||||
break;
|
||||
case tokenTrue:
|
||||
{
|
||||
Value v(true);
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
}
|
||||
break;
|
||||
case tokenFalse:
|
||||
{
|
||||
Value v(false);
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
}
|
||||
break;
|
||||
case tokenNull:
|
||||
{
|
||||
Value v;
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
}
|
||||
break;
|
||||
case tokenNaN:
|
||||
{
|
||||
Value v(std::numeric_limits<double>::quiet_NaN());
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
}
|
||||
break;
|
||||
case tokenPosInf:
|
||||
{
|
||||
Value v(std::numeric_limits<double>::infinity());
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
}
|
||||
break;
|
||||
case tokenNegInf:
|
||||
{
|
||||
Value v(-std::numeric_limits<double>::infinity());
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
}
|
||||
break;
|
||||
case tokenArraySeparator:
|
||||
case tokenObjectEnd:
|
||||
case tokenArrayEnd:
|
||||
if (features_.allowDroppedNullPlaceholders_) {
|
||||
// "Un-read" the current token and mark the current value as a null
|
||||
// token.
|
||||
current_--;
|
||||
Value v;
|
||||
currentValue().swapPayload(v);
|
||||
currentValue().setOffsetStart(current_ - begin_ - 1);
|
||||
currentValue().setOffsetLimit(current_ - begin_);
|
||||
break;
|
||||
} // else, fall through ...
|
||||
default:
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
return addError("Syntax error: value, object or array expected.", token);
|
||||
}
|
||||
|
||||
if (collectComments_) {
|
||||
lastValueEnd_ = current_;
|
||||
lastValue_ = ¤tValue();
|
||||
}
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
void OurReader::skipCommentTokens(Token& token) {
|
||||
if (features_.allowComments_) {
|
||||
do {
|
||||
readToken(token);
|
||||
} while (token.type_ == tokenComment);
|
||||
} else {
|
||||
readToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
bool OurReader::readToken(Token& token) {
|
||||
skipSpaces();
|
||||
token.start_ = current_;
|
||||
Char c = getNextChar();
|
||||
bool ok = true;
|
||||
switch (c) {
|
||||
case '{':
|
||||
token.type_ = tokenObjectBegin;
|
||||
break;
|
||||
case '}':
|
||||
token.type_ = tokenObjectEnd;
|
||||
break;
|
||||
case '[':
|
||||
token.type_ = tokenArrayBegin;
|
||||
break;
|
||||
case ']':
|
||||
token.type_ = tokenArrayEnd;
|
||||
break;
|
||||
case '"':
|
||||
token.type_ = tokenString;
|
||||
ok = readString();
|
||||
break;
|
||||
case '\'':
|
||||
if (features_.allowSingleQuotes_) {
|
||||
token.type_ = tokenString;
|
||||
ok = readStringSingleQuote();
|
||||
break;
|
||||
} // else continue
|
||||
case '/':
|
||||
token.type_ = tokenComment;
|
||||
ok = readComment();
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
token.type_ = tokenNumber;
|
||||
readNumber(false);
|
||||
break;
|
||||
case '-':
|
||||
if (readNumber(true)) {
|
||||
token.type_ = tokenNumber;
|
||||
} else {
|
||||
token.type_ = tokenNegInf;
|
||||
ok = features_.allowSpecialFloats_ && match("nfinity", 7);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
token.type_ = tokenTrue;
|
||||
ok = match("rue", 3);
|
||||
break;
|
||||
case 'f':
|
||||
token.type_ = tokenFalse;
|
||||
ok = match("alse", 4);
|
||||
break;
|
||||
case 'n':
|
||||
token.type_ = tokenNull;
|
||||
ok = match("ull", 3);
|
||||
break;
|
||||
case 'N':
|
||||
if (features_.allowSpecialFloats_) {
|
||||
token.type_ = tokenNaN;
|
||||
ok = match("aN", 2);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
if (features_.allowSpecialFloats_) {
|
||||
token.type_ = tokenPosInf;
|
||||
ok = match("nfinity", 7);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
token.type_ = tokenArraySeparator;
|
||||
break;
|
||||
case ':':
|
||||
token.type_ = tokenMemberSeparator;
|
||||
break;
|
||||
case 0:
|
||||
token.type_ = tokenEndOfStream;
|
||||
break;
|
||||
default:
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (!ok)
|
||||
token.type_ = tokenError;
|
||||
token.end_ = current_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OurReader::skipSpaces() {
|
||||
while (current_ != end_) {
|
||||
Char c = *current_;
|
||||
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
|
||||
++current_;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool OurReader::match(Location pattern, int patternLength) {
|
||||
if (end_ - current_ < patternLength)
|
||||
return false;
|
||||
int index = patternLength;
|
||||
while (index--)
|
||||
if (current_[index] != pattern[index])
|
||||
return false;
|
||||
current_ += patternLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::readComment() {
|
||||
Location commentBegin = current_ - 1;
|
||||
Char c = getNextChar();
|
||||
bool successful = false;
|
||||
if (c == '*')
|
||||
successful = readCStyleComment();
|
||||
else if (c == '/')
|
||||
successful = readCppStyleComment();
|
||||
if (!successful)
|
||||
return false;
|
||||
|
||||
if (collectComments_) {
|
||||
CommentPlacement placement = commentBefore;
|
||||
if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
|
||||
if (c != '*' || !containsNewLine(commentBegin, current_))
|
||||
placement = commentAfterOnSameLine;
|
||||
}
|
||||
|
||||
addComment(commentBegin, current_, placement);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSONCPP_STRING OurReader::normalizeEOL(OurReader::Location begin, OurReader::Location end) {
|
||||
JSONCPP_STRING normalized;
|
||||
normalized.reserve(static_cast<size_t>(end - begin));
|
||||
OurReader::Location current = begin;
|
||||
while (current != end) {
|
||||
char c = *current++;
|
||||
if (c == '\r') {
|
||||
if (current != end && *current == '\n')
|
||||
// convert dos EOL
|
||||
++current;
|
||||
// convert Mac EOL
|
||||
normalized += '\n';
|
||||
} else {
|
||||
normalized += c;
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
void
|
||||
OurReader::addComment(Location begin, Location end, CommentPlacement placement) {
|
||||
assert(collectComments_);
|
||||
const JSONCPP_STRING& normalized = normalizeEOL(begin, end);
|
||||
if (placement == commentAfterOnSameLine) {
|
||||
assert(lastValue_ != 0);
|
||||
lastValue_->setComment(normalized, placement);
|
||||
} else {
|
||||
commentsBefore_ += normalized;
|
||||
}
|
||||
}
|
||||
|
||||
bool OurReader::readCStyleComment() {
|
||||
while ((current_ + 1) < end_) {
|
||||
Char c = getNextChar();
|
||||
if (c == '*' && *current_ == '/')
|
||||
break;
|
||||
}
|
||||
return getNextChar() == '/';
|
||||
}
|
||||
|
||||
bool OurReader::readCppStyleComment() {
|
||||
while (current_ != end_) {
|
||||
Char c = getNextChar();
|
||||
if (c == '\n')
|
||||
break;
|
||||
if (c == '\r') {
|
||||
// Consume DOS EOL. It will be normalized in addComment.
|
||||
if (current_ != end_ && *current_ == '\n')
|
||||
getNextChar();
|
||||
// Break on Moc OS 9 EOL.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::readNumber(bool checkInf) {
|
||||
const char *p = current_;
|
||||
if (checkInf && p != end_ && *p == 'I') {
|
||||
current_ = ++p;
|
||||
return false;
|
||||
}
|
||||
char c = '0'; // stopgap for already consumed character
|
||||
// integral part
|
||||
while (c >= '0' && c <= '9')
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
// fractional part
|
||||
if (c == '.') {
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
while (c >= '0' && c <= '9')
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
}
|
||||
// exponential part
|
||||
if (c == 'e' || c == 'E') {
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
if (c == '+' || c == '-')
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
while (c >= '0' && c <= '9')
|
||||
c = (current_ = p) < end_ ? *p++ : '\0';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool OurReader::readString() {
|
||||
Char c = 0;
|
||||
while (current_ != end_) {
|
||||
c = getNextChar();
|
||||
if (c == '\\')
|
||||
getNextChar();
|
||||
else if (c == '"')
|
||||
break;
|
||||
}
|
||||
return c == '"';
|
||||
}
|
||||
|
||||
|
||||
bool OurReader::readStringSingleQuote() {
|
||||
Char c = 0;
|
||||
while (current_ != end_) {
|
||||
c = getNextChar();
|
||||
if (c == '\\')
|
||||
getNextChar();
|
||||
else if (c == '\'')
|
||||
break;
|
||||
}
|
||||
return c == '\'';
|
||||
}
|
||||
|
||||
bool OurReader::readObject(Token& tokenStart) {
|
||||
Token tokenName;
|
||||
JSONCPP_STRING name;
|
||||
Value init(objectValue);
|
||||
currentValue().swapPayload(init);
|
||||
currentValue().setOffsetStart(tokenStart.start_ - begin_);
|
||||
while (readToken(tokenName)) {
|
||||
bool initialTokenOk = true;
|
||||
while (tokenName.type_ == tokenComment && initialTokenOk)
|
||||
initialTokenOk = readToken(tokenName);
|
||||
if (!initialTokenOk)
|
||||
break;
|
||||
if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
|
||||
return true;
|
||||
name.clear();
|
||||
if (tokenName.type_ == tokenString) {
|
||||
if (!decodeString(tokenName, name))
|
||||
return recoverFromError(tokenObjectEnd);
|
||||
} else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
|
||||
Value numberName;
|
||||
if (!decodeNumber(tokenName, numberName))
|
||||
return recoverFromError(tokenObjectEnd);
|
||||
name = numberName.asString();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
Token colon;
|
||||
if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
|
||||
return addErrorAndRecover(
|
||||
"Missing ':' after object member name", colon, tokenObjectEnd);
|
||||
}
|
||||
if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30");
|
||||
if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
|
||||
JSONCPP_STRING msg = "Duplicate key: '" + name + "'";
|
||||
return addErrorAndRecover(
|
||||
msg, tokenName, tokenObjectEnd);
|
||||
}
|
||||
Value& value = currentValue()[name];
|
||||
nodes_.push(&value);
|
||||
bool ok = readValue();
|
||||
nodes_.pop();
|
||||
if (!ok) // error already set
|
||||
return recoverFromError(tokenObjectEnd);
|
||||
|
||||
Token comma;
|
||||
if (!readToken(comma) ||
|
||||
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
|
||||
comma.type_ != tokenComment)) {
|
||||
return addErrorAndRecover(
|
||||
"Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
|
||||
}
|
||||
bool finalizeTokenOk = true;
|
||||
while (comma.type_ == tokenComment && finalizeTokenOk)
|
||||
finalizeTokenOk = readToken(comma);
|
||||
if (comma.type_ == tokenObjectEnd)
|
||||
return true;
|
||||
}
|
||||
return addErrorAndRecover(
|
||||
"Missing '}' or object member name", tokenName, tokenObjectEnd);
|
||||
}
|
||||
|
||||
bool OurReader::readArray(Token& tokenStart) {
|
||||
Value init(arrayValue);
|
||||
currentValue().swapPayload(init);
|
||||
currentValue().setOffsetStart(tokenStart.start_ - begin_);
|
||||
skipSpaces();
|
||||
if (current_ != end_ && *current_ == ']') // empty array
|
||||
{
|
||||
Token endArray;
|
||||
readToken(endArray);
|
||||
return true;
|
||||
}
|
||||
int index = 0;
|
||||
for (;;) {
|
||||
Value& value = currentValue()[index++];
|
||||
nodes_.push(&value);
|
||||
bool ok = readValue();
|
||||
nodes_.pop();
|
||||
if (!ok) // error already set
|
||||
return recoverFromError(tokenArrayEnd);
|
||||
|
||||
Token token;
|
||||
// Accept Comment after last item in the array.
|
||||
ok = readToken(token);
|
||||
while (token.type_ == tokenComment && ok) {
|
||||
ok = readToken(token);
|
||||
}
|
||||
bool badTokenType =
|
||||
(token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
|
||||
if (!ok || badTokenType) {
|
||||
return addErrorAndRecover(
|
||||
"Missing ',' or ']' in array declaration", token, tokenArrayEnd);
|
||||
}
|
||||
if (token.type_ == tokenArrayEnd)
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::decodeNumber(Token& token) {
|
||||
Value decoded;
|
||||
if (!decodeNumber(token, decoded))
|
||||
return false;
|
||||
currentValue().swapPayload(decoded);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::decodeNumber(Token& token, Value& decoded) {
|
||||
// Attempts to parse the number as an integer. If the number is
|
||||
// larger than the maximum supported value of an integer then
|
||||
// we decode the number as a double.
|
||||
Location current = token.start_;
|
||||
bool isNegative = *current == '-';
|
||||
if (isNegative)
|
||||
++current;
|
||||
// TODO: Help the compiler do the div and mod at compile time or get rid of them.
|
||||
Value::LargestUInt maxIntegerValue =
|
||||
isNegative ? Value::LargestUInt(-Value::minLargestInt)
|
||||
: Value::maxLargestUInt;
|
||||
Value::LargestUInt threshold = maxIntegerValue / 10;
|
||||
Value::LargestUInt value = 0;
|
||||
while (current < token.end_) {
|
||||
Char c = *current++;
|
||||
if (c < '0' || c > '9')
|
||||
return decodeDouble(token, decoded);
|
||||
Value::UInt digit(static_cast<Value::UInt>(c - '0'));
|
||||
if (value >= threshold) {
|
||||
// We've hit or exceeded the max value divided by 10 (rounded down). If
|
||||
// a) we've only just touched the limit, b) this is the last digit, and
|
||||
// c) it's small enough to fit in that rounding delta, we're okay.
|
||||
// Otherwise treat this number as a double to avoid overflow.
|
||||
if (value > threshold || current != token.end_ ||
|
||||
digit > maxIntegerValue % 10) {
|
||||
return decodeDouble(token, decoded);
|
||||
}
|
||||
}
|
||||
value = value * 10 + digit;
|
||||
}
|
||||
if (isNegative)
|
||||
decoded = -Value::LargestInt(value);
|
||||
else if (value <= Value::LargestUInt(Value::maxInt))
|
||||
decoded = Value::LargestInt(value);
|
||||
else
|
||||
decoded = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::decodeDouble(Token& token) {
|
||||
Value decoded;
|
||||
if (!decodeDouble(token, decoded))
|
||||
return false;
|
||||
currentValue().swapPayload(decoded);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::decodeDouble(Token& token, Value& decoded) {
|
||||
double value = 0;
|
||||
const int bufferSize = 32;
|
||||
int count;
|
||||
ptrdiff_t const length = token.end_ - token.start_;
|
||||
|
||||
// Sanity check to avoid buffer overflow exploits.
|
||||
if (length < 0) {
|
||||
return addError("Unable to parse token length", token);
|
||||
}
|
||||
size_t const ulength = static_cast<size_t>(length);
|
||||
|
||||
// Avoid using a string constant for the format control string given to
|
||||
// sscanf, as this can cause hard to debug crashes on OS X. See here for more
|
||||
// info:
|
||||
//
|
||||
// http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
|
||||
char format[] = "%lf";
|
||||
|
||||
if (length <= bufferSize) {
|
||||
Char buffer[bufferSize + 1];
|
||||
memcpy(buffer, token.start_, ulength);
|
||||
buffer[length] = 0;
|
||||
fixNumericLocaleInput(buffer, buffer + length);
|
||||
count = sscanf(buffer, format, &value);
|
||||
} else {
|
||||
JSONCPP_STRING buffer(token.start_, token.end_);
|
||||
count = sscanf(buffer.c_str(), format, &value);
|
||||
}
|
||||
|
||||
if (count != 1)
|
||||
return addError("'" + JSONCPP_STRING(token.start_, token.end_) +
|
||||
"' is not a number.",
|
||||
token);
|
||||
decoded = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::decodeString(Token& token) {
|
||||
JSONCPP_STRING decoded_string;
|
||||
if (!decodeString(token, decoded_string))
|
||||
return false;
|
||||
Value decoded(decoded_string);
|
||||
currentValue().swapPayload(decoded);
|
||||
currentValue().setOffsetStart(token.start_ - begin_);
|
||||
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) {
|
||||
decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
|
||||
Location current = token.start_ + 1; // skip '"'
|
||||
Location end = token.end_ - 1; // do not include '"'
|
||||
while (current != end) {
|
||||
Char c = *current++;
|
||||
if (c == '"')
|
||||
break;
|
||||
else if (c == '\\') {
|
||||
if (current == end)
|
||||
return addError("Empty escape sequence in string", token, current);
|
||||
Char escape = *current++;
|
||||
switch (escape) {
|
||||
case '"':
|
||||
decoded += '"';
|
||||
break;
|
||||
case '/':
|
||||
decoded += '/';
|
||||
break;
|
||||
case '\\':
|
||||
decoded += '\\';
|
||||
break;
|
||||
case 'b':
|
||||
decoded += '\b';
|
||||
break;
|
||||
case 'f':
|
||||
decoded += '\f';
|
||||
break;
|
||||
case 'n':
|
||||
decoded += '\n';
|
||||
break;
|
||||
case 'r':
|
||||
decoded += '\r';
|
||||
break;
|
||||
case 't':
|
||||
decoded += '\t';
|
||||
break;
|
||||
case 'u': {
|
||||
unsigned int unicode;
|
||||
if (!decodeUnicodeCodePoint(token, current, end, unicode))
|
||||
return false;
|
||||
decoded += codePointToUTF8(unicode);
|
||||
} break;
|
||||
default:
|
||||
return addError("Bad escape sequence in string", token, current);
|
||||
}
|
||||
} else {
|
||||
decoded += c;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::decodeUnicodeCodePoint(Token& token,
|
||||
Location& current,
|
||||
Location end,
|
||||
unsigned int& unicode) {
|
||||
|
||||
if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
|
||||
return false;
|
||||
if (unicode >= 0xD800 && unicode <= 0xDBFF) {
|
||||
// surrogate pairs
|
||||
if (end - current < 6)
|
||||
return addError(
|
||||
"additional six characters expected to parse unicode surrogate pair.",
|
||||
token,
|
||||
current);
|
||||
unsigned int surrogatePair;
|
||||
if (*(current++) == '\\' && *(current++) == 'u') {
|
||||
if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
|
||||
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
|
||||
} else
|
||||
return false;
|
||||
} else
|
||||
return addError("expecting another \\u token to begin the second half of "
|
||||
"a unicode surrogate pair",
|
||||
token,
|
||||
current);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::decodeUnicodeEscapeSequence(Token& token,
|
||||
Location& current,
|
||||
Location end,
|
||||
unsigned int& ret_unicode) {
|
||||
if (end - current < 4)
|
||||
return addError(
|
||||
"Bad unicode escape sequence in string: four digits expected.",
|
||||
token,
|
||||
current);
|
||||
int 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);
|
||||
}
|
||||
ret_unicode = static_cast<unsigned int>(unicode);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
OurReader::addError(const JSONCPP_STRING& message, Token& token, Location extra) {
|
||||
ErrorInfo info;
|
||||
info.token_ = token;
|
||||
info.message_ = message;
|
||||
info.extra_ = extra;
|
||||
errors_.push_back(info);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OurReader::recoverFromError(TokenType skipUntilToken) {
|
||||
size_t errorCount = errors_.size();
|
||||
Token skip;
|
||||
for (;;) {
|
||||
if (!readToken(skip))
|
||||
errors_.resize(errorCount); // discard errors caused by recovery
|
||||
if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
|
||||
break;
|
||||
}
|
||||
errors_.resize(errorCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OurReader::addErrorAndRecover(const JSONCPP_STRING& message,
|
||||
Token& token,
|
||||
TokenType skipUntilToken) {
|
||||
addError(message, token);
|
||||
return recoverFromError(skipUntilToken);
|
||||
}
|
||||
|
||||
Value& OurReader::currentValue() { return *(nodes_.top()); }
|
||||
|
||||
OurReader::Char OurReader::getNextChar() {
|
||||
if (current_ == end_)
|
||||
return 0;
|
||||
return *current_++;
|
||||
}
|
||||
|
||||
void OurReader::getLocationLineAndColumn(Location location,
|
||||
int& line,
|
||||
int& column) const {
|
||||
Location current = begin_;
|
||||
Location lastLineStart = current;
|
||||
line = 0;
|
||||
while (current < location && current != end_) {
|
||||
Char c = *current++;
|
||||
if (c == '\r') {
|
||||
if (*current == '\n')
|
||||
++current;
|
||||
lastLineStart = current;
|
||||
++line;
|
||||
} else if (c == '\n') {
|
||||
lastLineStart = current;
|
||||
++line;
|
||||
}
|
||||
}
|
||||
// column & line start at 1
|
||||
column = int(location - lastLineStart) + 1;
|
||||
++line;
|
||||
}
|
||||
|
||||
JSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const {
|
||||
int line, column;
|
||||
getLocationLineAndColumn(location, line, column);
|
||||
char buffer[18 + 16 + 16 + 1];
|
||||
snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
JSONCPP_STRING OurReader::getFormattedErrorMessages() const {
|
||||
JSONCPP_STRING formattedMessage;
|
||||
for (Errors::const_iterator itError = errors_.begin();
|
||||
itError != errors_.end();
|
||||
++itError) {
|
||||
const ErrorInfo& error = *itError;
|
||||
formattedMessage +=
|
||||
"* " + getLocationLineAndColumn(error.token_.start_) + "\n";
|
||||
formattedMessage += " " + error.message_ + "\n";
|
||||
if (error.extra_)
|
||||
formattedMessage +=
|
||||
"See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
|
||||
}
|
||||
return formattedMessage;
|
||||
}
|
||||
|
||||
std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
|
||||
std::vector<OurReader::StructuredError> allErrors;
|
||||
for (Errors::const_iterator itError = errors_.begin();
|
||||
itError != errors_.end();
|
||||
++itError) {
|
||||
const ErrorInfo& error = *itError;
|
||||
OurReader::StructuredError structured;
|
||||
structured.offset_start = error.token_.start_ - begin_;
|
||||
structured.offset_limit = error.token_.end_ - begin_;
|
||||
structured.message = error.message_;
|
||||
allErrors.push_back(structured);
|
||||
}
|
||||
return allErrors;
|
||||
}
|
||||
|
||||
bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message) {
|
||||
ptrdiff_t length = end_ - begin_;
|
||||
if(value.getOffsetStart() > length
|
||||
|| value.getOffsetLimit() > length)
|
||||
return false;
|
||||
Token token;
|
||||
token.type_ = tokenError;
|
||||
token.start_ = begin_ + value.getOffsetStart();
|
||||
token.end_ = end_ + value.getOffsetLimit();
|
||||
ErrorInfo info;
|
||||
info.token_ = token;
|
||||
info.message_ = message;
|
||||
info.extra_ = 0;
|
||||
errors_.push_back(info);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) {
|
||||
ptrdiff_t length = end_ - begin_;
|
||||
if(value.getOffsetStart() > length
|
||||
|| value.getOffsetLimit() > length
|
||||
|| extra.getOffsetLimit() > length)
|
||||
return false;
|
||||
Token token;
|
||||
token.type_ = tokenError;
|
||||
token.start_ = begin_ + value.getOffsetStart();
|
||||
token.end_ = begin_ + value.getOffsetLimit();
|
||||
ErrorInfo info;
|
||||
info.token_ = token;
|
||||
info.message_ = message;
|
||||
info.extra_ = begin_ + extra.getOffsetStart();
|
||||
errors_.push_back(info);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OurReader::good() const {
|
||||
return !errors_.size();
|
||||
}
|
||||
|
||||
|
||||
class OurCharReader : public CharReader {
|
||||
bool const collectComments_;
|
||||
OurReader reader_;
|
||||
public:
|
||||
OurCharReader(
|
||||
bool collectComments,
|
||||
OurFeatures const& features)
|
||||
: collectComments_(collectComments)
|
||||
, reader_(features)
|
||||
{}
|
||||
bool parse(
|
||||
char const* beginDoc, char const* endDoc,
|
||||
Value* root, JSONCPP_STRING* errs) JSONCPP_OVERRIDE {
|
||||
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
|
||||
if (errs) {
|
||||
*errs = reader_.getFormattedErrorMessages();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
};
|
||||
|
||||
CharReaderBuilder::CharReaderBuilder()
|
||||
{
|
||||
setDefaults(&settings_);
|
||||
}
|
||||
CharReaderBuilder::~CharReaderBuilder()
|
||||
{}
|
||||
CharReader* CharReaderBuilder::newCharReader() const
|
||||
{
|
||||
bool collectComments = settings_["collectComments"].asBool();
|
||||
OurFeatures features = OurFeatures::all();
|
||||
features.allowComments_ = settings_["allowComments"].asBool();
|
||||
features.strictRoot_ = settings_["strictRoot"].asBool();
|
||||
features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool();
|
||||
features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
|
||||
features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
|
||||
features.stackLimit_ = settings_["stackLimit"].asInt();
|
||||
features.failIfExtra_ = settings_["failIfExtra"].asBool();
|
||||
features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
|
||||
features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
|
||||
return new OurCharReader(collectComments, features);
|
||||
}
|
||||
static void getValidReaderKeys(std::set<JSONCPP_STRING>* valid_keys)
|
||||
{
|
||||
valid_keys->clear();
|
||||
valid_keys->insert("collectComments");
|
||||
valid_keys->insert("allowComments");
|
||||
valid_keys->insert("strictRoot");
|
||||
valid_keys->insert("allowDroppedNullPlaceholders");
|
||||
valid_keys->insert("allowNumericKeys");
|
||||
valid_keys->insert("allowSingleQuotes");
|
||||
valid_keys->insert("stackLimit");
|
||||
valid_keys->insert("failIfExtra");
|
||||
valid_keys->insert("rejectDupKeys");
|
||||
valid_keys->insert("allowSpecialFloats");
|
||||
}
|
||||
bool CharReaderBuilder::validate(Json::Value* invalid) const
|
||||
{
|
||||
Json::Value my_invalid;
|
||||
if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL
|
||||
Json::Value& inv = *invalid;
|
||||
std::set<JSONCPP_STRING> valid_keys;
|
||||
getValidReaderKeys(&valid_keys);
|
||||
Value::Members keys = settings_.getMemberNames();
|
||||
size_t n = keys.size();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
JSONCPP_STRING const& key = keys[i];
|
||||
if (valid_keys.find(key) == valid_keys.end()) {
|
||||
inv[key] = settings_[key];
|
||||
}
|
||||
}
|
||||
return 0u == inv.size();
|
||||
}
|
||||
Value& CharReaderBuilder::operator[](JSONCPP_STRING key)
|
||||
{
|
||||
return settings_[key];
|
||||
}
|
||||
// static
|
||||
void CharReaderBuilder::strictMode(Json::Value* settings)
|
||||
{
|
||||
//! [CharReaderBuilderStrictMode]
|
||||
(*settings)["allowComments"] = false;
|
||||
(*settings)["strictRoot"] = true;
|
||||
(*settings)["allowDroppedNullPlaceholders"] = false;
|
||||
(*settings)["allowNumericKeys"] = false;
|
||||
(*settings)["allowSingleQuotes"] = false;
|
||||
(*settings)["stackLimit"] = 1000;
|
||||
(*settings)["failIfExtra"] = true;
|
||||
(*settings)["rejectDupKeys"] = true;
|
||||
(*settings)["allowSpecialFloats"] = false;
|
||||
//! [CharReaderBuilderStrictMode]
|
||||
}
|
||||
// static
|
||||
void CharReaderBuilder::setDefaults(Json::Value* settings)
|
||||
{
|
||||
//! [CharReaderBuilderDefaults]
|
||||
(*settings)["collectComments"] = true;
|
||||
(*settings)["allowComments"] = true;
|
||||
(*settings)["strictRoot"] = false;
|
||||
(*settings)["allowDroppedNullPlaceholders"] = false;
|
||||
(*settings)["allowNumericKeys"] = false;
|
||||
(*settings)["allowSingleQuotes"] = false;
|
||||
(*settings)["stackLimit"] = 1000;
|
||||
(*settings)["failIfExtra"] = false;
|
||||
(*settings)["rejectDupKeys"] = false;
|
||||
(*settings)["allowSpecialFloats"] = false;
|
||||
//! [CharReaderBuilderDefaults]
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// global functions
|
||||
|
||||
bool parseFromStream(
|
||||
CharReader::Factory const& fact, JSONCPP_ISTREAM& sin,
|
||||
Value* root, JSONCPP_STRING* errs)
|
||||
{
|
||||
JSONCPP_OSTRINGSTREAM ssin;
|
||||
ssin << sin.rdbuf();
|
||||
JSONCPP_STRING doc = ssin.str();
|
||||
char const* begin = doc.data();
|
||||
char const* end = begin + doc.size();
|
||||
// Note that we do not actually need a null-terminator.
|
||||
CharReaderPtr const reader(fact.newCharReader());
|
||||
return reader->parse(begin, end, root, errs);
|
||||
}
|
||||
|
||||
JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root) {
|
||||
CharReaderBuilder b;
|
||||
JSONCPP_STRING errs;
|
||||
bool ok = parseFromStream(b, sin, &root, &errs);
|
||||
if (!ok) {
|
||||
fprintf(stderr,
|
||||
"Error from reader: %s",
|
||||
reader.getFormattedErrorMessages().c_str());
|
||||
errs.c_str());
|
||||
|
||||
JSON_FAIL_MESSAGE("reader error");
|
||||
throwRuntimeError(errs);
|
||||
}
|
||||
return sin;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -6,6 +6,16 @@
|
||||
#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
|
||||
#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
|
||||
|
||||
|
||||
// Also support old flag NO_LOCALE_SUPPORT
|
||||
#ifdef NO_LOCALE_SUPPORT
|
||||
#define JSONCPP_NO_LOCALE_SUPPORT
|
||||
#endif
|
||||
|
||||
#ifndef JSONCPP_NO_LOCALE_SUPPORT
|
||||
#include <clocale>
|
||||
#endif
|
||||
|
||||
/* This header provides common string manipulation support, such as UTF-8,
|
||||
* portable conversion from/to string...
|
||||
*
|
||||
@@ -13,10 +23,18 @@
|
||||
*/
|
||||
|
||||
namespace Json {
|
||||
static char getDecimalPoint() {
|
||||
#ifdef JSONCPP_NO_LOCALE_SUPPORT
|
||||
return '\0';
|
||||
#else
|
||||
struct lconv* lc = localeconv();
|
||||
return lc ? *(lc->decimal_point) : '\0';
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Converts a unicode code-point to UTF-8.
|
||||
static inline std::string codePointToUTF8(unsigned int cp) {
|
||||
std::string result;
|
||||
static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) {
|
||||
JSONCPP_STRING result;
|
||||
|
||||
// based on description from http://en.wikipedia.org/wiki/UTF-8
|
||||
|
||||
@@ -30,8 +48,8 @@ static inline std::string codePointToUTF8(unsigned int cp) {
|
||||
} 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)));
|
||||
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
|
||||
result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
|
||||
} else if (cp <= 0x10FFFF) {
|
||||
result.resize(4);
|
||||
result[3] = static_cast<char>(0x80 | (0x3f & cp));
|
||||
@@ -43,7 +61,7 @@ static inline std::string codePointToUTF8(unsigned int cp) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns true if ch is a control character (in range [0,32[).
|
||||
/// Returns true if ch is a control character (in range [1,31]).
|
||||
static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }
|
||||
|
||||
enum {
|
||||
@@ -63,7 +81,7 @@ typedef char UIntToStringBuffer[uintToStringBufferSize];
|
||||
static inline void uintToString(LargestUInt value, char*& current) {
|
||||
*--current = 0;
|
||||
do {
|
||||
*--current = char(value % 10) + '0';
|
||||
*--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));
|
||||
value /= 10;
|
||||
} while (value != 0);
|
||||
}
|
||||
@@ -82,6 +100,18 @@ static inline void fixNumericLocale(char* begin, char* end) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void fixNumericLocaleInput(char* begin, char* end) {
|
||||
char decimalPoint = getDecimalPoint();
|
||||
if (decimalPoint != '\0' && decimalPoint != '.') {
|
||||
while (begin < end) {
|
||||
if (*begin == '.') {
|
||||
*begin = decimalPoint;
|
||||
}
|
||||
++begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Json {
|
||||
|
||||
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2011 Baptiste Lepilleur
|
||||
// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -7,9 +7,6 @@
|
||||
#include <json/assertions.h>
|
||||
#include <json/value.h>
|
||||
#include <json/writer.h>
|
||||
#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
|
||||
#include "json_batchallocator.h"
|
||||
#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
#include <math.h>
|
||||
#include <sstream>
|
||||
@@ -20,6 +17,7 @@
|
||||
#include <cpptl/conststring.h>
|
||||
#endif
|
||||
#include <cstddef> // size_t
|
||||
#include <algorithm> // min()
|
||||
|
||||
#define JSON_ASSERT_UNREACHABLE assert(false)
|
||||
|
||||
@@ -33,9 +31,22 @@ namespace Json {
|
||||
#else
|
||||
#define ALIGNAS(byte_alignment)
|
||||
#endif
|
||||
static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
|
||||
const unsigned char& kNullRef = kNull[0];
|
||||
const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
|
||||
//static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
|
||||
//const unsigned char& kNullRef = kNull[0];
|
||||
//const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
|
||||
//const Value& Value::nullRef = null;
|
||||
|
||||
// static
|
||||
Value const& Value::nullSingleton()
|
||||
{
|
||||
static Value const nullStatic;
|
||||
return nullStatic;
|
||||
}
|
||||
|
||||
// for backwards compatibility, we'll leave these global references around, but DO NOT
|
||||
// use them in JSONCPP library code any more!
|
||||
Value const& Value::null = Value::nullSingleton();
|
||||
Value const& Value::nullRef = Value::nullSingleton();
|
||||
|
||||
const Int Value::minInt = Int(~(UInt(-1) / 2));
|
||||
const Int Value::maxInt = Int(UInt(-1) / 2);
|
||||
@@ -53,17 +64,17 @@ const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
|
||||
const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
|
||||
const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
|
||||
|
||||
/// Unknown size marker
|
||||
static const unsigned int unknown = (unsigned)-1;
|
||||
|
||||
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
|
||||
template <typename T, typename U>
|
||||
static inline bool InRange(double d, T min, U max) {
|
||||
// The casts can lose precision, but we are looking only for
|
||||
// an approximate range. Might fail on edge cases though. ~cdunn
|
||||
//return d >= static_cast<double>(min) && d <= static_cast<double>(max);
|
||||
return d >= min && d <= max;
|
||||
}
|
||||
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
|
||||
static inline double integerToDouble(Json::UInt64 value) {
|
||||
return static_cast<double>(Int64(value / 2)) * 2.0 + Int64(value & 1);
|
||||
return static_cast<double>(Int64(value / 2)) * 2.0 + static_cast<double>(Int64(value & 1));
|
||||
}
|
||||
|
||||
template <typename T> static inline double integerToDouble(T value) {
|
||||
@@ -84,27 +95,84 @@ static inline bool InRange(double d, T min, U max) {
|
||||
* @return Pointer on the duplicate instance of string.
|
||||
*/
|
||||
static inline char* duplicateStringValue(const char* value,
|
||||
unsigned int length = unknown) {
|
||||
if (length == unknown)
|
||||
length = (unsigned int)strlen(value);
|
||||
|
||||
size_t length)
|
||||
{
|
||||
// Avoid an integer overflow in the call to malloc below by limiting length
|
||||
// to a sane value.
|
||||
if (length >= (unsigned)Value::maxInt)
|
||||
if (length >= static_cast<size_t>(Value::maxInt))
|
||||
length = Value::maxInt - 1;
|
||||
|
||||
char* newString = static_cast<char*>(malloc(length + 1));
|
||||
JSON_ASSERT_MESSAGE(newString != 0,
|
||||
"in Json::Value::duplicateStringValue(): "
|
||||
"Failed to allocate string value buffer");
|
||||
if (newString == NULL) {
|
||||
throwRuntimeError(
|
||||
"in Json::Value::duplicateStringValue(): "
|
||||
"Failed to allocate string value buffer");
|
||||
}
|
||||
memcpy(newString, value, length);
|
||||
newString[length] = 0;
|
||||
return newString;
|
||||
}
|
||||
|
||||
/** Free the string duplicated by duplicateStringValue().
|
||||
/* Record the length as a prefix.
|
||||
*/
|
||||
static inline void releaseStringValue(char* value) { free(value); }
|
||||
static inline char* duplicateAndPrefixStringValue(
|
||||
const char* value,
|
||||
unsigned int length)
|
||||
{
|
||||
// Avoid an integer overflow in the call to malloc below by limiting length
|
||||
// to a sane value.
|
||||
JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) - sizeof(unsigned) - 1U,
|
||||
"in Json::Value::duplicateAndPrefixStringValue(): "
|
||||
"length too big for prefixing");
|
||||
unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U;
|
||||
char* newString = static_cast<char*>(malloc(actualLength));
|
||||
if (newString == 0) {
|
||||
throwRuntimeError(
|
||||
"in Json::Value::duplicateAndPrefixStringValue(): "
|
||||
"Failed to allocate string value buffer");
|
||||
}
|
||||
*reinterpret_cast<unsigned*>(newString) = length;
|
||||
memcpy(newString + sizeof(unsigned), value, length);
|
||||
newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later
|
||||
return newString;
|
||||
}
|
||||
inline static void decodePrefixedString(
|
||||
bool isPrefixed, char const* prefixed,
|
||||
unsigned* length, char const** value)
|
||||
{
|
||||
if (!isPrefixed) {
|
||||
*length = static_cast<unsigned>(strlen(prefixed));
|
||||
*value = prefixed;
|
||||
} else {
|
||||
*length = *reinterpret_cast<unsigned const*>(prefixed);
|
||||
*value = prefixed + sizeof(unsigned);
|
||||
}
|
||||
}
|
||||
/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue().
|
||||
*/
|
||||
#if JSONCPP_USING_SECURE_MEMORY
|
||||
static inline void releasePrefixedStringValue(char* value) {
|
||||
unsigned length = 0;
|
||||
char const* valueDecoded;
|
||||
decodePrefixedString(true, value, &length, &valueDecoded);
|
||||
size_t const size = sizeof(unsigned) + length + 1U;
|
||||
memset(value, 0, size);
|
||||
free(value);
|
||||
}
|
||||
static inline void releaseStringValue(char* value, unsigned length) {
|
||||
// length==0 => we allocated the strings memory
|
||||
size_t size = (length==0) ? strlen(value) : length;
|
||||
memset(value, 0, size);
|
||||
free(value);
|
||||
}
|
||||
#else // !JSONCPP_USING_SECURE_MEMORY
|
||||
static inline void releasePrefixedStringValue(char* value) {
|
||||
free(value);
|
||||
}
|
||||
static inline void releaseStringValue(char* value, unsigned) {
|
||||
free(value);
|
||||
}
|
||||
#endif // JSONCPP_USING_SECURE_MEMORY
|
||||
|
||||
} // namespace Json
|
||||
|
||||
@@ -116,16 +184,36 @@ static inline void releaseStringValue(char* value) { free(value); }
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
#if !defined(JSON_IS_AMALGAMATION)
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
#include "json_internalarray.inl"
|
||||
#include "json_internalmap.inl"
|
||||
#endif // JSON_VALUE_USE_INTERNAL_MAP
|
||||
|
||||
#include "json_valueiterator.inl"
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
namespace Json {
|
||||
|
||||
Exception::Exception(JSONCPP_STRING const& msg)
|
||||
: msg_(msg)
|
||||
{}
|
||||
Exception::~Exception() JSONCPP_NOEXCEPT
|
||||
{}
|
||||
char const* Exception::what() const JSONCPP_NOEXCEPT
|
||||
{
|
||||
return msg_.c_str();
|
||||
}
|
||||
RuntimeError::RuntimeError(JSONCPP_STRING const& msg)
|
||||
: Exception(msg)
|
||||
{}
|
||||
LogicError::LogicError(JSONCPP_STRING const& msg)
|
||||
: Exception(msg)
|
||||
{}
|
||||
JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg)
|
||||
{
|
||||
throw RuntimeError(msg);
|
||||
}
|
||||
JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg)
|
||||
{
|
||||
throw LogicError(msg);
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
@@ -134,22 +222,25 @@ namespace Json {
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
Value::CommentInfo::CommentInfo() : comment_(0) {}
|
||||
Value::CommentInfo::CommentInfo() : comment_(0)
|
||||
{}
|
||||
|
||||
Value::CommentInfo::~CommentInfo() {
|
||||
if (comment_)
|
||||
releaseStringValue(comment_);
|
||||
releaseStringValue(comment_, 0u);
|
||||
}
|
||||
|
||||
void Value::CommentInfo::setComment(const char* text) {
|
||||
if (comment_)
|
||||
releaseStringValue(comment_);
|
||||
void Value::CommentInfo::setComment(const char* text, size_t len) {
|
||||
if (comment_) {
|
||||
releaseStringValue(comment_, 0u);
|
||||
comment_ = 0;
|
||||
}
|
||||
JSON_ASSERT(text != 0);
|
||||
JSON_ASSERT_MESSAGE(
|
||||
text[0] == '\0' || text[0] == '/',
|
||||
"in Json::Value::setComment(): Comments must start with /");
|
||||
// It seems that /**/ style comments are acceptable as well.
|
||||
comment_ = duplicateStringValue(text);
|
||||
comment_ = duplicateStringValue(text, len);
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
@@ -159,29 +250,41 @@ void Value::CommentInfo::setComment(const char* text) {
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
|
||||
// Notes: index_ indicates if the string was allocated when
|
||||
// Notes: policy_ indicates if the string was allocated when
|
||||
// a string is stored.
|
||||
|
||||
Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {}
|
||||
Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {}
|
||||
|
||||
Value::CZString::CZString(const char* cstr, DuplicationPolicy allocate)
|
||||
: cstr_(allocate == duplicate ? duplicateStringValue(cstr) : cstr),
|
||||
index_(allocate) {}
|
||||
Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate)
|
||||
: cstr_(str) {
|
||||
// allocate != duplicate
|
||||
storage_.policy_ = allocate & 0x3;
|
||||
storage_.length_ = ulength & 0x3FFFFFFF;
|
||||
}
|
||||
|
||||
Value::CZString::CZString(const CZString& other)
|
||||
: cstr_(other.index_ != noDuplication && other.cstr_ != 0
|
||||
? duplicateStringValue(other.cstr_)
|
||||
: other.cstr_),
|
||||
index_(other.cstr_
|
||||
? static_cast<ArrayIndex>(other.index_ == noDuplication
|
||||
Value::CZString::CZString(const CZString& other) {
|
||||
cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0
|
||||
? duplicateStringValue(other.cstr_, other.storage_.length_)
|
||||
: other.cstr_);
|
||||
storage_.policy_ = static_cast<unsigned>(other.cstr_
|
||||
? (static_cast<DuplicationPolicy>(other.storage_.policy_) == noDuplication
|
||||
? noDuplication : duplicate)
|
||||
: other.index_) {}
|
||||
: static_cast<DuplicationPolicy>(other.storage_.policy_)) & 3U;
|
||||
storage_.length_ = other.storage_.length_;
|
||||
}
|
||||
|
||||
#if JSON_HAS_RVALUE_REFERENCES
|
||||
Value::CZString::CZString(CZString&& other)
|
||||
: cstr_(other.cstr_), index_(other.index_) {
|
||||
other.cstr_ = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
Value::CZString::~CZString() {
|
||||
if (cstr_ && index_ == duplicate)
|
||||
releaseStringValue(const_cast<char*>(cstr_));
|
||||
if (cstr_ && storage_.policy_ == duplicate) {
|
||||
releaseStringValue(const_cast<char*>(cstr_), storage_.length_ + 1u); //+1 for null terminating character for sake of completeness but not actually necessary
|
||||
}
|
||||
}
|
||||
|
||||
void Value::CZString::swap(CZString& other) {
|
||||
@@ -189,34 +292,53 @@ void Value::CZString::swap(CZString& other) {
|
||||
std::swap(index_, other.index_);
|
||||
}
|
||||
|
||||
Value::CZString& Value::CZString::operator=(CZString other) {
|
||||
swap(other);
|
||||
Value::CZString& Value::CZString::operator=(const CZString& other) {
|
||||
cstr_ = other.cstr_;
|
||||
index_ = other.index_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if JSON_HAS_RVALUE_REFERENCES
|
||||
Value::CZString& Value::CZString::operator=(CZString&& other) {
|
||||
cstr_ = other.cstr_;
|
||||
index_ = other.index_;
|
||||
other.cstr_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Value::CZString::operator<(const CZString& other) const {
|
||||
if (cstr_) {
|
||||
assert(other.cstr_);
|
||||
return strcmp(cstr_, other.cstr_) < 0;
|
||||
}
|
||||
return index_ < other.index_;
|
||||
if (!cstr_) return index_ < other.index_;
|
||||
//return strcmp(cstr_, other.cstr_) < 0;
|
||||
// Assume both are strings.
|
||||
unsigned this_len = this->storage_.length_;
|
||||
unsigned other_len = other.storage_.length_;
|
||||
unsigned min_len = std::min<unsigned>(this_len, other_len);
|
||||
JSON_ASSERT(this->cstr_ && other.cstr_);
|
||||
int comp = memcmp(this->cstr_, other.cstr_, min_len);
|
||||
if (comp < 0) return true;
|
||||
if (comp > 0) return false;
|
||||
return (this_len < other_len);
|
||||
}
|
||||
|
||||
bool Value::CZString::operator==(const CZString& other) const {
|
||||
if (cstr_) {
|
||||
assert(other.cstr_);
|
||||
return strcmp(cstr_, other.cstr_) == 0;
|
||||
}
|
||||
return index_ == other.index_;
|
||||
if (!cstr_) return index_ == other.index_;
|
||||
//return strcmp(cstr_, other.cstr_) == 0;
|
||||
// Assume both are strings.
|
||||
unsigned this_len = this->storage_.length_;
|
||||
unsigned other_len = other.storage_.length_;
|
||||
if (this_len != other_len) return false;
|
||||
JSON_ASSERT(this->cstr_ && other.cstr_);
|
||||
int comp = memcmp(this->cstr_, other.cstr_, this_len);
|
||||
return comp == 0;
|
||||
}
|
||||
|
||||
ArrayIndex Value::CZString::index() const { return index_; }
|
||||
|
||||
const char* Value::CZString::c_str() const { return cstr_; }
|
||||
|
||||
bool Value::CZString::isStaticString() const { return index_ == noDuplication; }
|
||||
|
||||
#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
//const char* Value::CZString::c_str() const { return cstr_; }
|
||||
const char* Value::CZString::data() const { return cstr_; }
|
||||
unsigned Value::CZString::length() const { return storage_.length_; }
|
||||
bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; }
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
@@ -230,9 +352,10 @@ bool Value::CZString::isStaticString() const { return index_ == noDuplication; }
|
||||
* memset( this, 0, sizeof(Value) )
|
||||
* This optimization is used in ValueInternalMap fast allocator.
|
||||
*/
|
||||
Value::Value(ValueType type) {
|
||||
initBasic(type);
|
||||
switch (type) {
|
||||
Value::Value(ValueType vtype) {
|
||||
static char const emptyString[] = "";
|
||||
initBasic(vtype);
|
||||
switch (vtype) {
|
||||
case nullValue:
|
||||
break;
|
||||
case intValue:
|
||||
@@ -243,21 +366,13 @@ Value::Value(ValueType type) {
|
||||
value_.real_ = 0.0;
|
||||
break;
|
||||
case stringValue:
|
||||
value_.string_ = 0;
|
||||
// allocated_ == false, so this is safe.
|
||||
value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString));
|
||||
break;
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
value_.map_ = new ObjectValues();
|
||||
break;
|
||||
#else
|
||||
case arrayValue:
|
||||
value_.array_ = arrayAllocator()->newArray();
|
||||
break;
|
||||
case objectValue:
|
||||
value_.map_ = mapAllocator()->newMap();
|
||||
break;
|
||||
#endif
|
||||
case booleanValue:
|
||||
value_.bool_ = false;
|
||||
break;
|
||||
@@ -293,19 +408,20 @@ Value::Value(double value) {
|
||||
|
||||
Value::Value(const char* value) {
|
||||
initBasic(stringValue, true);
|
||||
value_.string_ = duplicateStringValue(value);
|
||||
JSON_ASSERT_MESSAGE(value != NULL, "Null Value Passed to Value Constructor");
|
||||
value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value)));
|
||||
}
|
||||
|
||||
Value::Value(const char* beginValue, const char* endValue) {
|
||||
initBasic(stringValue, true);
|
||||
value_.string_ =
|
||||
duplicateStringValue(beginValue, (unsigned int)(endValue - beginValue));
|
||||
duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue));
|
||||
}
|
||||
|
||||
Value::Value(const std::string& value) {
|
||||
Value::Value(const JSONCPP_STRING& value) {
|
||||
initBasic(stringValue, true);
|
||||
value_.string_ =
|
||||
duplicateStringValue(value.c_str(), (unsigned int)value.length());
|
||||
duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length()));
|
||||
}
|
||||
|
||||
Value::Value(const StaticString& value) {
|
||||
@@ -316,7 +432,7 @@ Value::Value(const StaticString& value) {
|
||||
#ifdef JSON_USE_CPPTL
|
||||
Value::Value(const CppTL::ConstString& value) {
|
||||
initBasic(stringValue, true);
|
||||
value_.string_ = duplicateStringValue(value, value.length());
|
||||
value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length()));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -325,14 +441,11 @@ Value::Value(bool value) {
|
||||
value_.bool_ = value;
|
||||
}
|
||||
|
||||
Value::Value(const Value& other)
|
||||
Value::Value(Value const& other)
|
||||
: type_(other.type_), allocated_(false)
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
,
|
||||
itemIsUsed_(0)
|
||||
#endif
|
||||
,
|
||||
comments_(0), start_(other.start_), limit_(other.limit_) {
|
||||
comments_(0), start_(other.start_), limit_(other.limit_)
|
||||
{
|
||||
switch (type_) {
|
||||
case nullValue:
|
||||
case intValue:
|
||||
@@ -342,27 +455,22 @@ Value::Value(const Value& other)
|
||||
value_ = other.value_;
|
||||
break;
|
||||
case stringValue:
|
||||
if (other.value_.string_) {
|
||||
value_.string_ = duplicateStringValue(other.value_.string_);
|
||||
if (other.value_.string_ && other.allocated_) {
|
||||
unsigned len;
|
||||
char const* str;
|
||||
decodePrefixedString(other.allocated_, other.value_.string_,
|
||||
&len, &str);
|
||||
value_.string_ = duplicateAndPrefixStringValue(str, len);
|
||||
allocated_ = true;
|
||||
} else {
|
||||
value_.string_ = 0;
|
||||
value_.string_ = other.value_.string_;
|
||||
allocated_ = false;
|
||||
}
|
||||
break;
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
value_.map_ = new ObjectValues(*other.value_.map_);
|
||||
break;
|
||||
#else
|
||||
case arrayValue:
|
||||
value_.array_ = arrayAllocator()->newArrayCopy(*other.value_.array_);
|
||||
break;
|
||||
case objectValue:
|
||||
value_.map_ = mapAllocator()->newMapCopy(*other.value_.map_);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
JSON_ASSERT_UNREACHABLE;
|
||||
}
|
||||
@@ -371,11 +479,20 @@ Value::Value(const Value& other)
|
||||
for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
|
||||
const CommentInfo& otherComment = other.comments_[comment];
|
||||
if (otherComment.comment_)
|
||||
comments_[comment].setComment(otherComment.comment_);
|
||||
comments_[comment].setComment(
|
||||
otherComment.comment_, strlen(otherComment.comment_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if JSON_HAS_RVALUE_REFERENCES
|
||||
// Move constructor
|
||||
Value::Value(Value&& other) {
|
||||
initBasic(nullValue);
|
||||
swap(other);
|
||||
}
|
||||
#endif
|
||||
|
||||
Value::~Value() {
|
||||
switch (type_) {
|
||||
case nullValue:
|
||||
@@ -386,27 +503,19 @@ Value::~Value() {
|
||||
break;
|
||||
case stringValue:
|
||||
if (allocated_)
|
||||
releaseStringValue(value_.string_);
|
||||
releasePrefixedStringValue(value_.string_);
|
||||
break;
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
delete value_.map_;
|
||||
break;
|
||||
#else
|
||||
case arrayValue:
|
||||
arrayAllocator()->destructArray(value_.array_);
|
||||
break;
|
||||
case objectValue:
|
||||
mapAllocator()->destructMap(value_.map_);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
JSON_ASSERT_UNREACHABLE;
|
||||
}
|
||||
|
||||
if (comments_)
|
||||
delete[] comments_;
|
||||
delete[] comments_;
|
||||
|
||||
value_.uint_ = 0;
|
||||
}
|
||||
|
||||
Value& Value::operator=(Value other) {
|
||||
@@ -414,18 +523,36 @@ Value& Value::operator=(Value other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Value::swap(Value& other) {
|
||||
void Value::swapPayload(Value& other) {
|
||||
ValueType temp = type_;
|
||||
type_ = other.type_;
|
||||
other.type_ = temp;
|
||||
std::swap(value_, other.value_);
|
||||
int temp2 = allocated_;
|
||||
allocated_ = other.allocated_;
|
||||
other.allocated_ = temp2;
|
||||
other.allocated_ = temp2 & 0x1;
|
||||
}
|
||||
|
||||
void Value::copyPayload(const Value& other) {
|
||||
type_ = other.type_;
|
||||
value_ = other.value_;
|
||||
allocated_ = other.allocated_;
|
||||
}
|
||||
|
||||
void Value::swap(Value& other) {
|
||||
swapPayload(other);
|
||||
std::swap(comments_, other.comments_);
|
||||
std::swap(start_, other.start_);
|
||||
std::swap(limit_, other.limit_);
|
||||
}
|
||||
|
||||
void Value::copy(const Value& other) {
|
||||
copyPayload(other);
|
||||
comments_ = other.comments_;
|
||||
start_ = other.start_;
|
||||
limit_ = other.limit_;
|
||||
}
|
||||
|
||||
ValueType Value::type() const { return type_; }
|
||||
|
||||
int Value::compare(const Value& other) const {
|
||||
@@ -452,10 +579,24 @@ bool Value::operator<(const Value& other) const {
|
||||
case booleanValue:
|
||||
return value_.bool_ < other.value_.bool_;
|
||||
case stringValue:
|
||||
return (value_.string_ == 0 && other.value_.string_) ||
|
||||
(other.value_.string_ && value_.string_ &&
|
||||
strcmp(value_.string_, other.value_.string_) < 0);
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
{
|
||||
if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
|
||||
if (other.value_.string_) return true;
|
||||
else return false;
|
||||
}
|
||||
unsigned this_len;
|
||||
unsigned other_len;
|
||||
char const* this_str;
|
||||
char const* other_str;
|
||||
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
|
||||
decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
|
||||
unsigned min_len = std::min<unsigned>(this_len, other_len);
|
||||
JSON_ASSERT(this_str && other_str);
|
||||
int comp = memcmp(this_str, other_str, min_len);
|
||||
if (comp < 0) return true;
|
||||
if (comp > 0) return false;
|
||||
return (this_len < other_len);
|
||||
}
|
||||
case arrayValue:
|
||||
case objectValue: {
|
||||
int delta = int(value_.map_->size() - other.value_.map_->size());
|
||||
@@ -463,12 +604,6 @@ bool Value::operator<(const Value& other) const {
|
||||
return delta < 0;
|
||||
return (*value_.map_) < (*other.value_.map_);
|
||||
}
|
||||
#else
|
||||
case arrayValue:
|
||||
return value_.array_->compare(*(other.value_.array_)) < 0;
|
||||
case objectValue:
|
||||
return value_.map_->compare(*(other.value_.map_)) < 0;
|
||||
#endif
|
||||
default:
|
||||
JSON_ASSERT_UNREACHABLE;
|
||||
}
|
||||
@@ -501,20 +636,25 @@ bool Value::operator==(const Value& other) const {
|
||||
case booleanValue:
|
||||
return value_.bool_ == other.value_.bool_;
|
||||
case stringValue:
|
||||
return (value_.string_ == other.value_.string_) ||
|
||||
(other.value_.string_ && value_.string_ &&
|
||||
strcmp(value_.string_, other.value_.string_) == 0);
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
{
|
||||
if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
|
||||
return (value_.string_ == other.value_.string_);
|
||||
}
|
||||
unsigned this_len;
|
||||
unsigned other_len;
|
||||
char const* this_str;
|
||||
char const* other_str;
|
||||
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
|
||||
decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
|
||||
if (this_len != other_len) return false;
|
||||
JSON_ASSERT(this_str && other_str);
|
||||
int comp = memcmp(this_str, other_str, this_len);
|
||||
return comp == 0;
|
||||
}
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
return value_.map_->size() == other.value_.map_->size() &&
|
||||
(*value_.map_) == (*other.value_.map_);
|
||||
#else
|
||||
case arrayValue:
|
||||
return value_.array_->compare(*(other.value_.array_)) == 0;
|
||||
case objectValue:
|
||||
return value_.map_->compare(*(other.value_.map_)) == 0;
|
||||
#endif
|
||||
default:
|
||||
JSON_ASSERT_UNREACHABLE;
|
||||
}
|
||||
@@ -526,15 +666,46 @@ bool Value::operator!=(const Value& other) const { return !(*this == other); }
|
||||
const char* Value::asCString() const {
|
||||
JSON_ASSERT_MESSAGE(type_ == stringValue,
|
||||
"in Json::Value::asCString(): requires stringValue");
|
||||
return value_.string_;
|
||||
if (value_.string_ == 0) return 0;
|
||||
unsigned this_len;
|
||||
char const* this_str;
|
||||
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
|
||||
return this_str;
|
||||
}
|
||||
|
||||
std::string Value::asString() const {
|
||||
#if JSONCPP_USING_SECURE_MEMORY
|
||||
unsigned Value::getCStringLength() const {
|
||||
JSON_ASSERT_MESSAGE(type_ == stringValue,
|
||||
"in Json::Value::asCString(): requires stringValue");
|
||||
if (value_.string_ == 0) return 0;
|
||||
unsigned this_len;
|
||||
char const* this_str;
|
||||
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
|
||||
return this_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Value::getString(char const** str, char const** cend) const {
|
||||
if (type_ != stringValue) return false;
|
||||
if (value_.string_ == 0) return false;
|
||||
unsigned length;
|
||||
decodePrefixedString(this->allocated_, this->value_.string_, &length, str);
|
||||
*cend = *str + length;
|
||||
return true;
|
||||
}
|
||||
|
||||
JSONCPP_STRING Value::asString() const {
|
||||
switch (type_) {
|
||||
case nullValue:
|
||||
return "";
|
||||
case stringValue:
|
||||
return value_.string_ ? value_.string_ : "";
|
||||
{
|
||||
if (value_.string_ == 0) return "";
|
||||
unsigned this_len;
|
||||
char const* this_str;
|
||||
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
|
||||
return JSONCPP_STRING(this_str, this_len);
|
||||
}
|
||||
case booleanValue:
|
||||
return value_.bool_ ? "true" : "false";
|
||||
case intValue:
|
||||
@@ -550,7 +721,11 @@ std::string Value::asString() const {
|
||||
|
||||
#ifdef JSON_USE_CPPTL
|
||||
CppTL::ConstString Value::asConstString() const {
|
||||
return CppTL::ConstString(asString().c_str());
|
||||
unsigned len;
|
||||
char const* str;
|
||||
decodePrefixedString(allocated_, value_.string_,
|
||||
&len, &str);
|
||||
return CppTL::ConstString(str, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -689,7 +864,8 @@ float Value::asFloat() const {
|
||||
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
|
||||
return static_cast<float>(value_.uint_);
|
||||
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
|
||||
return integerToDouble(value_.uint_);
|
||||
// This can fail (silently?) if the value is bigger than MAX_FLOAT.
|
||||
return static_cast<float>(integerToDouble(value_.uint_));
|
||||
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
|
||||
case realValue:
|
||||
return static_cast<float>(value_.real_);
|
||||
@@ -714,7 +890,8 @@ bool Value::asBool() const {
|
||||
case uintValue:
|
||||
return value_.uint_ ? true : false;
|
||||
case realValue:
|
||||
return value_.real_ ? true : false;
|
||||
// This is kind of strange. Not recommended.
|
||||
return (value_.real_ != 0.0) ? true : false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -726,7 +903,7 @@ bool Value::isConvertibleTo(ValueType other) const {
|
||||
case nullValue:
|
||||
return (isNumeric() && asDouble() == 0.0) ||
|
||||
(type_ == booleanValue && value_.bool_ == false) ||
|
||||
(type_ == stringValue && asString() == "") ||
|
||||
(type_ == stringValue && asString().empty()) ||
|
||||
(type_ == arrayValue && value_.map_->size() == 0) ||
|
||||
(type_ == objectValue && value_.map_->size() == 0) ||
|
||||
type_ == nullValue;
|
||||
@@ -764,7 +941,6 @@ ArrayIndex Value::size() const {
|
||||
case booleanValue:
|
||||
case stringValue:
|
||||
return 0;
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
case arrayValue: // size of the array is highest index + 1
|
||||
if (!value_.map_->empty()) {
|
||||
ObjectValues::const_iterator itLast = value_.map_->end();
|
||||
@@ -774,12 +950,6 @@ ArrayIndex Value::size() const {
|
||||
return 0;
|
||||
case objectValue:
|
||||
return ArrayIndex(value_.map_->size());
|
||||
#else
|
||||
case arrayValue:
|
||||
return Int(value_.array_->size());
|
||||
case objectValue:
|
||||
return Int(value_.map_->size());
|
||||
#endif
|
||||
}
|
||||
JSON_ASSERT_UNREACHABLE;
|
||||
return 0; // unreachable;
|
||||
@@ -801,19 +971,10 @@ void Value::clear() {
|
||||
start_ = 0;
|
||||
limit_ = 0;
|
||||
switch (type_) {
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
value_.map_->clear();
|
||||
break;
|
||||
#else
|
||||
case arrayValue:
|
||||
value_.array_->clear();
|
||||
break;
|
||||
case objectValue:
|
||||
value_.map_->clear();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -824,7 +985,6 @@ void Value::resize(ArrayIndex newSize) {
|
||||
"in Json::Value::resize(): requires arrayValue");
|
||||
if (type_ == nullValue)
|
||||
*this = Value(arrayValue);
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
ArrayIndex oldSize = size();
|
||||
if (newSize == 0)
|
||||
clear();
|
||||
@@ -834,11 +994,8 @@ void Value::resize(ArrayIndex newSize) {
|
||||
for (ArrayIndex index = newSize; index < oldSize; ++index) {
|
||||
value_.map_->erase(index);
|
||||
}
|
||||
assert(size() == newSize);
|
||||
JSON_ASSERT(size() == newSize);
|
||||
}
|
||||
#else
|
||||
value_.array_->resize(newSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
Value& Value::operator[](ArrayIndex index) {
|
||||
@@ -847,18 +1004,14 @@ Value& Value::operator[](ArrayIndex index) {
|
||||
"in Json::Value::operator[](ArrayIndex): requires arrayValue");
|
||||
if (type_ == nullValue)
|
||||
*this = Value(arrayValue);
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
CZString key(index);
|
||||
ObjectValues::iterator it = value_.map_->lower_bound(key);
|
||||
if (it != value_.map_->end() && (*it).first == key)
|
||||
return (*it).second;
|
||||
|
||||
ObjectValues::value_type defaultValue(key, null);
|
||||
ObjectValues::value_type defaultValue(key, nullSingleton());
|
||||
it = value_.map_->insert(it, defaultValue);
|
||||
return (*it).second;
|
||||
#else
|
||||
return value_.array_->resolveReference(index);
|
||||
#endif
|
||||
}
|
||||
|
||||
Value& Value::operator[](int index) {
|
||||
@@ -873,17 +1026,12 @@ const Value& Value::operator[](ArrayIndex index) const {
|
||||
type_ == nullValue || type_ == arrayValue,
|
||||
"in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
|
||||
if (type_ == nullValue)
|
||||
return null;
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
return nullSingleton();
|
||||
CZString key(index);
|
||||
ObjectValues::const_iterator it = value_.map_->find(key);
|
||||
if (it == value_.map_->end())
|
||||
return null;
|
||||
return nullSingleton();
|
||||
return (*it).second;
|
||||
#else
|
||||
Value* value = value_.array_->find(index);
|
||||
return value ? *value : null;
|
||||
#endif
|
||||
}
|
||||
|
||||
const Value& Value::operator[](int index) const {
|
||||
@@ -893,149 +1041,215 @@ const Value& Value::operator[](int index) const {
|
||||
return (*this)[ArrayIndex(index)];
|
||||
}
|
||||
|
||||
Value& Value::operator[](const char* key) {
|
||||
return resolveReference(key, false);
|
||||
}
|
||||
|
||||
void Value::initBasic(ValueType type, bool allocated) {
|
||||
type_ = type;
|
||||
void Value::initBasic(ValueType vtype, bool allocated) {
|
||||
type_ = vtype;
|
||||
allocated_ = allocated;
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
itemIsUsed_ = 0;
|
||||
#endif
|
||||
comments_ = 0;
|
||||
start_ = 0;
|
||||
limit_ = 0;
|
||||
}
|
||||
|
||||
Value& Value::resolveReference(const char* key, bool isStatic) {
|
||||
// Access an object value by name, create a null member if it does not exist.
|
||||
// @pre Type of '*this' is object or null.
|
||||
// @param key is null-terminated.
|
||||
Value& Value::resolveReference(const char* key) {
|
||||
JSON_ASSERT_MESSAGE(
|
||||
type_ == nullValue || type_ == objectValue,
|
||||
"in Json::Value::resolveReference(): requires objectValue");
|
||||
if (type_ == nullValue)
|
||||
*this = Value(objectValue);
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
CZString actualKey(
|
||||
key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy);
|
||||
key, static_cast<unsigned>(strlen(key)), CZString::noDuplication); // NOTE!
|
||||
ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
|
||||
if (it != value_.map_->end() && (*it).first == actualKey)
|
||||
return (*it).second;
|
||||
|
||||
ObjectValues::value_type defaultValue(actualKey, null);
|
||||
ObjectValues::value_type defaultValue(actualKey, nullSingleton());
|
||||
it = value_.map_->insert(it, defaultValue);
|
||||
Value& value = (*it).second;
|
||||
return value;
|
||||
}
|
||||
|
||||
// @param key is not null-terminated.
|
||||
Value& Value::resolveReference(char const* key, char const* cend)
|
||||
{
|
||||
JSON_ASSERT_MESSAGE(
|
||||
type_ == nullValue || type_ == objectValue,
|
||||
"in Json::Value::resolveReference(key, end): requires objectValue");
|
||||
if (type_ == nullValue)
|
||||
*this = Value(objectValue);
|
||||
CZString actualKey(
|
||||
key, static_cast<unsigned>(cend-key), CZString::duplicateOnCopy);
|
||||
ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
|
||||
if (it != value_.map_->end() && (*it).first == actualKey)
|
||||
return (*it).second;
|
||||
|
||||
ObjectValues::value_type defaultValue(actualKey, nullSingleton());
|
||||
it = value_.map_->insert(it, defaultValue);
|
||||
Value& value = (*it).second;
|
||||
return value;
|
||||
#else
|
||||
return value_.map_->resolveReference(key, isStatic);
|
||||
#endif
|
||||
}
|
||||
|
||||
Value Value::get(ArrayIndex index, const Value& defaultValue) const {
|
||||
const Value* value = &((*this)[index]);
|
||||
return value == &null ? defaultValue : *value;
|
||||
return value == &nullSingleton() ? defaultValue : *value;
|
||||
}
|
||||
|
||||
bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
|
||||
|
||||
const Value& Value::operator[](const char* key) const {
|
||||
Value const* Value::find(char const* key, char const* cend) const
|
||||
{
|
||||
JSON_ASSERT_MESSAGE(
|
||||
type_ == nullValue || type_ == objectValue,
|
||||
"in Json::Value::operator[](char const*)const: requires objectValue");
|
||||
if (type_ == nullValue)
|
||||
return null;
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
CZString actualKey(key, CZString::noDuplication);
|
||||
"in Json::Value::find(key, end, found): requires objectValue or nullValue");
|
||||
if (type_ == nullValue) return NULL;
|
||||
CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication);
|
||||
ObjectValues::const_iterator it = value_.map_->find(actualKey);
|
||||
if (it == value_.map_->end())
|
||||
return null;
|
||||
return (*it).second;
|
||||
#else
|
||||
const Value* value = value_.map_->find(key);
|
||||
return value ? *value : null;
|
||||
#endif
|
||||
if (it == value_.map_->end()) return NULL;
|
||||
return &(*it).second;
|
||||
}
|
||||
const Value& Value::operator[](const char* key) const
|
||||
{
|
||||
Value const* found = find(key, key + strlen(key));
|
||||
if (!found) return nullSingleton();
|
||||
return *found;
|
||||
}
|
||||
Value const& Value::operator[](JSONCPP_STRING const& key) const
|
||||
{
|
||||
Value const* found = find(key.data(), key.data() + key.length());
|
||||
if (!found) return nullSingleton();
|
||||
return *found;
|
||||
}
|
||||
|
||||
Value& Value::operator[](const std::string& key) {
|
||||
return (*this)[key.c_str()];
|
||||
Value& Value::operator[](const char* key) {
|
||||
return resolveReference(key, key + strlen(key));
|
||||
}
|
||||
|
||||
const Value& Value::operator[](const std::string& key) const {
|
||||
return (*this)[key.c_str()];
|
||||
Value& Value::operator[](const JSONCPP_STRING& key) {
|
||||
return resolveReference(key.data(), key.data() + key.length());
|
||||
}
|
||||
|
||||
Value& Value::operator[](const StaticString& key) {
|
||||
return resolveReference(key, true);
|
||||
return resolveReference(key.c_str());
|
||||
}
|
||||
|
||||
#ifdef JSON_USE_CPPTL
|
||||
Value& Value::operator[](const CppTL::ConstString& key) {
|
||||
return (*this)[key.c_str()];
|
||||
return resolveReference(key.c_str(), key.end_c_str());
|
||||
}
|
||||
|
||||
const Value& Value::operator[](const CppTL::ConstString& key) const {
|
||||
return (*this)[key.c_str()];
|
||||
Value const& Value::operator[](CppTL::ConstString const& key) const
|
||||
{
|
||||
Value const* found = find(key.c_str(), key.end_c_str());
|
||||
if (!found) return nullSingleton();
|
||||
return *found;
|
||||
}
|
||||
#endif
|
||||
|
||||
Value& Value::append(const Value& value) { return (*this)[size()] = value; }
|
||||
|
||||
Value Value::get(const char* key, const Value& defaultValue) const {
|
||||
const Value* value = &((*this)[key]);
|
||||
return value == &null ? defaultValue : *value;
|
||||
#if JSON_HAS_RVALUE_REFERENCES
|
||||
Value& Value::append(Value&& value) { return (*this)[size()] = value; }
|
||||
#endif
|
||||
|
||||
Value Value::get(char const* key, char const* cend, Value const& defaultValue) const
|
||||
{
|
||||
Value const* found = find(key, cend);
|
||||
return !found ? defaultValue : *found;
|
||||
}
|
||||
Value Value::get(char const* key, Value const& defaultValue) const
|
||||
{
|
||||
return get(key, key + strlen(key), defaultValue);
|
||||
}
|
||||
Value Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const
|
||||
{
|
||||
return get(key.data(), key.data() + key.length(), defaultValue);
|
||||
}
|
||||
|
||||
Value Value::get(const std::string& key, const Value& defaultValue) const {
|
||||
return get(key.c_str(), defaultValue);
|
||||
}
|
||||
|
||||
Value Value::removeMember(const char* key) {
|
||||
bool Value::removeMember(const char* key, const char* cend, Value* removed)
|
||||
{
|
||||
if (type_ != objectValue) {
|
||||
return false;
|
||||
}
|
||||
CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication);
|
||||
ObjectValues::iterator it = value_.map_->find(actualKey);
|
||||
if (it == value_.map_->end())
|
||||
return false;
|
||||
*removed = it->second;
|
||||
value_.map_->erase(it);
|
||||
return true;
|
||||
}
|
||||
bool Value::removeMember(const char* key, Value* removed)
|
||||
{
|
||||
return removeMember(key, key + strlen(key), removed);
|
||||
}
|
||||
bool Value::removeMember(JSONCPP_STRING const& key, Value* removed)
|
||||
{
|
||||
return removeMember(key.data(), key.data() + key.length(), removed);
|
||||
}
|
||||
Value Value::removeMember(const char* key)
|
||||
{
|
||||
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
|
||||
"in Json::Value::removeMember(): requires objectValue");
|
||||
if (type_ == nullValue)
|
||||
return null;
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
CZString actualKey(key, CZString::noDuplication);
|
||||
ObjectValues::iterator it = value_.map_->find(actualKey);
|
||||
if (it == value_.map_->end())
|
||||
return null;
|
||||
Value old(it->second);
|
||||
value_.map_->erase(it);
|
||||
return old;
|
||||
#else
|
||||
Value* value = value_.map_->find(key);
|
||||
if (value) {
|
||||
Value old(*value);
|
||||
value_.map_.remove(key);
|
||||
return old;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
return nullSingleton();
|
||||
|
||||
Value removed; // null
|
||||
removeMember(key, key + strlen(key), &removed);
|
||||
return removed; // still null if removeMember() did nothing
|
||||
}
|
||||
Value Value::removeMember(const JSONCPP_STRING& key)
|
||||
{
|
||||
return removeMember(key.c_str());
|
||||
}
|
||||
|
||||
Value Value::removeMember(const std::string& key) {
|
||||
return removeMember(key.c_str());
|
||||
bool Value::removeIndex(ArrayIndex index, Value* removed) {
|
||||
if (type_ != arrayValue) {
|
||||
return false;
|
||||
}
|
||||
CZString key(index);
|
||||
ObjectValues::iterator it = value_.map_->find(key);
|
||||
if (it == value_.map_->end()) {
|
||||
return false;
|
||||
}
|
||||
*removed = it->second;
|
||||
ArrayIndex oldSize = size();
|
||||
// shift left all items left, into the place of the "removed"
|
||||
for (ArrayIndex i = index; i < (oldSize - 1); ++i){
|
||||
CZString keey(i);
|
||||
(*value_.map_)[keey] = (*this)[i + 1];
|
||||
}
|
||||
// erase the last one ("leftover")
|
||||
CZString keyLast(oldSize - 1);
|
||||
ObjectValues::iterator itLast = value_.map_->find(keyLast);
|
||||
value_.map_->erase(itLast);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef JSON_USE_CPPTL
|
||||
Value Value::get(const CppTL::ConstString& key,
|
||||
const Value& defaultValue) const {
|
||||
return get(key.c_str(), defaultValue);
|
||||
return get(key.c_str(), key.end_c_str(), defaultValue);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Value::isMember(const char* key) const {
|
||||
const Value* value = &((*this)[key]);
|
||||
return value != &null;
|
||||
bool Value::isMember(char const* key, char const* cend) const
|
||||
{
|
||||
Value const* value = find(key, cend);
|
||||
return NULL != value;
|
||||
}
|
||||
|
||||
bool Value::isMember(const std::string& key) const {
|
||||
return isMember(key.c_str());
|
||||
bool Value::isMember(char const* key) const
|
||||
{
|
||||
return isMember(key, key + strlen(key));
|
||||
}
|
||||
bool Value::isMember(JSONCPP_STRING const& key) const
|
||||
{
|
||||
return isMember(key.data(), key.data() + key.length());
|
||||
}
|
||||
|
||||
#ifdef JSON_USE_CPPTL
|
||||
bool Value::isMember(const CppTL::ConstString& key) const {
|
||||
return isMember(key.c_str());
|
||||
return isMember(key.c_str(), key.end_c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1047,19 +1261,12 @@ Value::Members Value::getMemberNames() const {
|
||||
return Value::Members();
|
||||
Members members;
|
||||
members.reserve(value_.map_->size());
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
ObjectValues::const_iterator it = value_.map_->begin();
|
||||
ObjectValues::const_iterator itEnd = value_.map_->end();
|
||||
for (; it != itEnd; ++it)
|
||||
members.push_back(std::string((*it).first.c_str()));
|
||||
#else
|
||||
ValueInternalMap::IteratorState it;
|
||||
ValueInternalMap::IteratorState itEnd;
|
||||
value_.map_->makeBeginIterator(it);
|
||||
value_.map_->makeEndIterator(itEnd);
|
||||
for (; !ValueInternalMap::equals(it, itEnd); ValueInternalMap::increment(it))
|
||||
members.push_back(std::string(ValueInternalMap::key(it)));
|
||||
#endif
|
||||
for (; it != itEnd; ++it) {
|
||||
members.push_back(JSONCPP_STRING((*it).first.data(),
|
||||
(*it).first.length()));
|
||||
}
|
||||
return members;
|
||||
}
|
||||
//
|
||||
@@ -1100,7 +1307,11 @@ bool Value::isBool() const { return type_ == booleanValue; }
|
||||
bool Value::isInt() const {
|
||||
switch (type_) {
|
||||
case intValue:
|
||||
#if defined(JSON_HAS_INT64)
|
||||
return value_.int_ >= minInt && value_.int_ <= maxInt;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
case uintValue:
|
||||
return value_.uint_ <= UInt(maxInt);
|
||||
case realValue:
|
||||
@@ -1115,9 +1326,17 @@ bool Value::isInt() const {
|
||||
bool Value::isUInt() const {
|
||||
switch (type_) {
|
||||
case intValue:
|
||||
#if defined(JSON_HAS_INT64)
|
||||
return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
|
||||
#else
|
||||
return value_.int_ >= 0;
|
||||
#endif
|
||||
case uintValue:
|
||||
#if defined(JSON_HAS_INT64)
|
||||
return value_.uint_ <= maxUInt;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
case realValue:
|
||||
return value_.real_ >= 0 && value_.real_ <= maxUInt &&
|
||||
IsIntegral(value_.real_);
|
||||
@@ -1168,16 +1387,28 @@ bool Value::isUInt64() const {
|
||||
}
|
||||
|
||||
bool Value::isIntegral() const {
|
||||
switch (type_) {
|
||||
case intValue:
|
||||
case uintValue:
|
||||
return true;
|
||||
case realValue:
|
||||
#if defined(JSON_HAS_INT64)
|
||||
return isInt64() || isUInt64();
|
||||
// Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
|
||||
// double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
|
||||
// require the value to be strictly less than the limit.
|
||||
return value_.real_ >= double(minInt64) && value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);
|
||||
#else
|
||||
return isInt() || isUInt();
|
||||
#endif
|
||||
return value_.real_ >= minInt && value_.real_ <= maxUInt && IsIntegral(value_.real_);
|
||||
#endif // JSON_HAS_INT64
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Value::isDouble() const { return type_ == realValue || isIntegral(); }
|
||||
bool Value::isDouble() const { return type_ == intValue || type_ == uintValue || type_ == realValue; }
|
||||
|
||||
bool Value::isNumeric() const { return isIntegral() || isDouble(); }
|
||||
bool Value::isNumeric() const { return isDouble(); }
|
||||
|
||||
bool Value::isString() const { return type_ == stringValue; }
|
||||
|
||||
@@ -1185,63 +1416,59 @@ bool Value::isArray() const { return type_ == arrayValue; }
|
||||
|
||||
bool Value::isObject() const { return type_ == objectValue; }
|
||||
|
||||
void Value::setComment(const char* comment, CommentPlacement placement) {
|
||||
void Value::setComment(const char* comment, size_t len, CommentPlacement placement) {
|
||||
if (!comments_)
|
||||
comments_ = new CommentInfo[numberOfCommentPlacement];
|
||||
comments_[placement].setComment(comment);
|
||||
if ((len > 0) && (comment[len-1] == '\n')) {
|
||||
// Always discard trailing newline, to aid indentation.
|
||||
len -= 1;
|
||||
}
|
||||
comments_[placement].setComment(comment, len);
|
||||
}
|
||||
|
||||
void Value::setComment(const std::string& comment, CommentPlacement placement) {
|
||||
setComment(comment.c_str(), placement);
|
||||
void Value::setComment(const char* comment, CommentPlacement placement) {
|
||||
setComment(comment, strlen(comment), placement);
|
||||
}
|
||||
|
||||
void Value::setComment(const JSONCPP_STRING& comment, CommentPlacement placement) {
|
||||
setComment(comment.c_str(), comment.length(), placement);
|
||||
}
|
||||
|
||||
bool Value::hasComment(CommentPlacement placement) const {
|
||||
return comments_ != 0 && comments_[placement].comment_ != 0;
|
||||
}
|
||||
|
||||
std::string Value::getComment(CommentPlacement placement) const {
|
||||
JSONCPP_STRING Value::getComment(CommentPlacement placement) const {
|
||||
if (hasComment(placement))
|
||||
return comments_[placement].comment_;
|
||||
return "";
|
||||
}
|
||||
|
||||
void Value::setOffsetStart(size_t start) { start_ = start; }
|
||||
void Value::setOffsetStart(ptrdiff_t start) { start_ = start; }
|
||||
|
||||
void Value::setOffsetLimit(size_t limit) { limit_ = limit; }
|
||||
void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; }
|
||||
|
||||
size_t Value::getOffsetStart() const { return start_; }
|
||||
ptrdiff_t Value::getOffsetStart() const { return start_; }
|
||||
|
||||
size_t Value::getOffsetLimit() const { return limit_; }
|
||||
ptrdiff_t Value::getOffsetLimit() const { return limit_; }
|
||||
|
||||
std::string Value::toStyledString() const {
|
||||
StyledWriter writer;
|
||||
return writer.write(*this);
|
||||
JSONCPP_STRING Value::toStyledString() const {
|
||||
StreamWriterBuilder builder;
|
||||
|
||||
JSONCPP_STRING out = this->hasComment(commentBefore) ? "\n" : "";
|
||||
out += Json::writeString(builder, *this);
|
||||
out += "\n";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Value::const_iterator Value::begin() const {
|
||||
switch (type_) {
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
case arrayValue:
|
||||
if (value_.array_) {
|
||||
ValueInternalArray::IteratorState it;
|
||||
value_.array_->makeBeginIterator(it);
|
||||
return const_iterator(it);
|
||||
}
|
||||
break;
|
||||
case objectValue:
|
||||
if (value_.map_) {
|
||||
ValueInternalMap::IteratorState it;
|
||||
value_.map_->makeBeginIterator(it);
|
||||
return const_iterator(it);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
if (value_.map_)
|
||||
return const_iterator(value_.map_->begin());
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1250,28 +1477,11 @@ Value::const_iterator Value::begin() const {
|
||||
|
||||
Value::const_iterator Value::end() const {
|
||||
switch (type_) {
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
case arrayValue:
|
||||
if (value_.array_) {
|
||||
ValueInternalArray::IteratorState it;
|
||||
value_.array_->makeEndIterator(it);
|
||||
return const_iterator(it);
|
||||
}
|
||||
break;
|
||||
case objectValue:
|
||||
if (value_.map_) {
|
||||
ValueInternalMap::IteratorState it;
|
||||
value_.map_->makeEndIterator(it);
|
||||
return const_iterator(it);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
if (value_.map_)
|
||||
return const_iterator(value_.map_->end());
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1280,28 +1490,11 @@ Value::const_iterator Value::end() const {
|
||||
|
||||
Value::iterator Value::begin() {
|
||||
switch (type_) {
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
case arrayValue:
|
||||
if (value_.array_) {
|
||||
ValueInternalArray::IteratorState it;
|
||||
value_.array_->makeBeginIterator(it);
|
||||
return iterator(it);
|
||||
}
|
||||
break;
|
||||
case objectValue:
|
||||
if (value_.map_) {
|
||||
ValueInternalMap::IteratorState it;
|
||||
value_.map_->makeBeginIterator(it);
|
||||
return iterator(it);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
if (value_.map_)
|
||||
return iterator(value_.map_->begin());
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1310,28 +1503,11 @@ Value::iterator Value::begin() {
|
||||
|
||||
Value::iterator Value::end() {
|
||||
switch (type_) {
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
case arrayValue:
|
||||
if (value_.array_) {
|
||||
ValueInternalArray::IteratorState it;
|
||||
value_.array_->makeEndIterator(it);
|
||||
return iterator(it);
|
||||
}
|
||||
break;
|
||||
case objectValue:
|
||||
if (value_.map_) {
|
||||
ValueInternalMap::IteratorState it;
|
||||
value_.map_->makeEndIterator(it);
|
||||
return iterator(it);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
if (value_.map_)
|
||||
return iterator(value_.map_->end());
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1349,19 +1525,20 @@ PathArgument::PathArgument(ArrayIndex index)
|
||||
PathArgument::PathArgument(const char* key)
|
||||
: key_(key), index_(), kind_(kindKey) {}
|
||||
|
||||
PathArgument::PathArgument(const std::string& key)
|
||||
PathArgument::PathArgument(const JSONCPP_STRING& key)
|
||||
: key_(key.c_str()), index_(), kind_(kindKey) {}
|
||||
|
||||
// class Path
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
Path::Path(const std::string& path,
|
||||
Path::Path(const JSONCPP_STRING& path,
|
||||
const PathArgument& a1,
|
||||
const PathArgument& a2,
|
||||
const PathArgument& a3,
|
||||
const PathArgument& a4,
|
||||
const PathArgument& a5) {
|
||||
InArgs in;
|
||||
in.reserve(5);
|
||||
in.push_back(&a1);
|
||||
in.push_back(&a2);
|
||||
in.push_back(&a3);
|
||||
@@ -1370,7 +1547,7 @@ Path::Path(const std::string& path,
|
||||
makePath(path, in);
|
||||
}
|
||||
|
||||
void Path::makePath(const std::string& path, const InArgs& in) {
|
||||
void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) {
|
||||
const char* current = path.c_str();
|
||||
const char* end = current + path.length();
|
||||
InArgs::const_iterator itInArg = in.begin();
|
||||
@@ -1385,23 +1562,23 @@ void Path::makePath(const std::string& path, const InArgs& in) {
|
||||
index = index * 10 + ArrayIndex(*current - '0');
|
||||
args_.push_back(index);
|
||||
}
|
||||
if (current == end || *current++ != ']')
|
||||
if (current == end || *++current != ']')
|
||||
invalidPath(path, int(current - path.c_str()));
|
||||
} else if (*current == '%') {
|
||||
addPathInArg(path, in, itInArg, PathArgument::kindKey);
|
||||
++current;
|
||||
} else if (*current == '.') {
|
||||
} else if (*current == '.' || *current == ']') {
|
||||
++current;
|
||||
} else {
|
||||
const char* beginName = current;
|
||||
while (current != end && !strchr("[.", *current))
|
||||
++current;
|
||||
args_.push_back(std::string(beginName, current));
|
||||
args_.push_back(JSONCPP_STRING(beginName, current));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Path::addPathInArg(const std::string& /*path*/,
|
||||
void Path::addPathInArg(const JSONCPP_STRING& /*path*/,
|
||||
const InArgs& in,
|
||||
InArgs::const_iterator& itInArg,
|
||||
PathArgument::Kind kind) {
|
||||
@@ -1410,11 +1587,11 @@ void Path::addPathInArg(const std::string& /*path*/,
|
||||
} else if ((*itInArg)->kind_ != kind) {
|
||||
// Error: bad argument type
|
||||
} else {
|
||||
args_.push_back(**itInArg);
|
||||
args_.push_back(**itInArg++);
|
||||
}
|
||||
}
|
||||
|
||||
void Path::invalidPath(const std::string& /*path*/, int /*location*/) {
|
||||
void Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/) {
|
||||
// Error: invalid path.
|
||||
}
|
||||
|
||||
@@ -1425,16 +1602,19 @@ const Value& Path::resolve(const Value& root) const {
|
||||
if (arg.kind_ == PathArgument::kindIndex) {
|
||||
if (!node->isArray() || !node->isValidIndex(arg.index_)) {
|
||||
// Error: unable to resolve path (array value expected at position...
|
||||
return Value::null;
|
||||
}
|
||||
node = &((*node)[arg.index_]);
|
||||
} else if (arg.kind_ == PathArgument::kindKey) {
|
||||
if (!node->isObject()) {
|
||||
// Error: unable to resolve path (object value expected at position...)
|
||||
return Value::null;
|
||||
}
|
||||
node = &((*node)[arg.key_]);
|
||||
if (node == &Value::null) {
|
||||
if (node == &Value::nullSingleton()) {
|
||||
// Error: unable to resolve path (object has no member named '' at
|
||||
// position...)
|
||||
return Value::null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1453,7 +1633,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const {
|
||||
if (!node->isObject())
|
||||
return defaultValue;
|
||||
node = &((*node)[arg.key_]);
|
||||
if (node == &Value::null)
|
||||
if (node == &Value::nullSingleton())
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -16,68 +16,29 @@ namespace Json {
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
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_;
|
||||
return other.current_ - current_;
|
||||
#else
|
||||
// Iterator for null value are initialized using the default
|
||||
// constructor, which initialize current_ to the default
|
||||
@@ -100,80 +61,58 @@ ValueIteratorBase::computeDistance(const SelfType& other) const {
|
||||
}
|
||||
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.data()) {
|
||||
if (czstring.isStaticString())
|
||||
return Value(StaticString(czstring.c_str()));
|
||||
return Value(czstring.c_str());
|
||||
return Value(StaticString(czstring.data()));
|
||||
return Value(czstring.data(), czstring.data() + czstring.length());
|
||||
}
|
||||
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())
|
||||
if (!czstring.data())
|
||||
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
|
||||
JSONCPP_STRING ValueIteratorBase::name() const {
|
||||
char const* keey;
|
||||
char const* end;
|
||||
keey = memberName(&end);
|
||||
if (!keey) return JSONCPP_STRING();
|
||||
return JSONCPP_STRING(keey, end);
|
||||
}
|
||||
|
||||
char const* ValueIteratorBase::memberName() const {
|
||||
const char* cname = (*current_).first.data();
|
||||
return cname ? cname : "";
|
||||
}
|
||||
|
||||
char const* ValueIteratorBase::memberName(char const** end) const {
|
||||
const char* cname = (*current_).first.data();
|
||||
if (!cname) {
|
||||
*end = NULL;
|
||||
return NULL;
|
||||
}
|
||||
*end = cname + (*current_).first.length();
|
||||
return cname;
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
@@ -186,19 +125,12 @@ const char* ValueIteratorBase::memberName() const {
|
||||
|
||||
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(ValueIterator const& other)
|
||||
: ValueIteratorBase(other) {}
|
||||
|
||||
ValueConstIterator& ValueConstIterator::
|
||||
operator=(const ValueIteratorBase& other) {
|
||||
@@ -216,19 +148,13 @@ operator=(const ValueIteratorBase& other) {
|
||||
|
||||
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) {}
|
||||
: ValueIteratorBase(other) {
|
||||
throwRuntimeError("ConstIterator to Iterator should never be allowed.");
|
||||
}
|
||||
|
||||
ValueIterator::ValueIterator(const ValueIterator& other)
|
||||
: ValueIteratorBase(other) {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2011 Baptiste Lepilleur
|
||||
// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// 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
|
||||
@@ -7,15 +7,62 @@
|
||||
#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>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <set>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
|
||||
#include <float.h>
|
||||
#define isfinite _finite
|
||||
#elif defined(__sun) && defined(__SVR4) //Solaris
|
||||
#if !defined(isfinite)
|
||||
#include <ieeefp.h>
|
||||
#define isfinite finite
|
||||
#endif
|
||||
#elif defined(_AIX)
|
||||
#if !defined(isfinite)
|
||||
#include <math.h>
|
||||
#define isfinite finite
|
||||
#endif
|
||||
#elif defined(__hpux)
|
||||
#if !defined(isfinite)
|
||||
#if defined(__ia64) && !defined(finite)
|
||||
#define isfinite(x) ((sizeof(x) == sizeof(float) ? \
|
||||
_Isfinitef(x) : _IsFinite(x)))
|
||||
#else
|
||||
#include <math.h>
|
||||
#define isfinite finite
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#include <cmath>
|
||||
#if !(defined(__QNXNTO__)) // QNX already defines isfinite
|
||||
#define isfinite std::isfinite
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
|
||||
#define snprintf sprintf_s
|
||||
#elif _MSC_VER >= 1900 // VC++ 14.0 and above
|
||||
#define snprintf std::snprintf
|
||||
#else
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#elif defined(__ANDROID__) || defined(__QNXNTO__)
|
||||
#define snprintf snprintf
|
||||
#elif __cplusplus >= 201103L
|
||||
#if !defined(__MINGW32__) && !defined(__CYGWIN__)
|
||||
#define snprintf std::snprintf
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__BORLANDC__)
|
||||
#include <float.h>
|
||||
#define isfinite _finite
|
||||
#define snprintf _snprintf
|
||||
@@ -62,6 +109,12 @@
|
||||
|
||||
namespace Json {
|
||||
|
||||
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
|
||||
typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
|
||||
#else
|
||||
typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
|
||||
#endif
|
||||
|
||||
static bool containsControlCharacter(const char* str) {
|
||||
while (*str) {
|
||||
if (isControlCharacter(*(str++)))
|
||||
@@ -70,20 +123,33 @@ static bool containsControlCharacter(const char* str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string valueToString(LargestInt value) {
|
||||
static bool containsControlCharacter0(const char* str, unsigned len) {
|
||||
char const* end = str + len;
|
||||
while (end != str) {
|
||||
if (isControlCharacter(*str) || 0==*str)
|
||||
return true;
|
||||
++str;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
JSONCPP_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)
|
||||
if (value == Value::minLargestInt) {
|
||||
uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
|
||||
*--current = '-';
|
||||
} else if (value < 0) {
|
||||
uintToString(LargestUInt(-value), current);
|
||||
*--current = '-';
|
||||
} else {
|
||||
uintToString(LargestUInt(value), current);
|
||||
}
|
||||
assert(current >= buffer);
|
||||
return current;
|
||||
}
|
||||
|
||||
std::string valueToString(LargestUInt value) {
|
||||
JSONCPP_STRING valueToString(LargestUInt value) {
|
||||
UIntToStringBuffer buffer;
|
||||
char* current = buffer + sizeof(buffer);
|
||||
uintToString(value, current);
|
||||
@@ -93,68 +159,70 @@ std::string valueToString(LargestUInt value) {
|
||||
|
||||
#if defined(JSON_HAS_INT64)
|
||||
|
||||
std::string valueToString(Int value) {
|
||||
JSONCPP_STRING valueToString(Int value) {
|
||||
return valueToString(LargestInt(value));
|
||||
}
|
||||
|
||||
std::string valueToString(UInt value) {
|
||||
JSONCPP_STRING valueToString(UInt value) {
|
||||
return valueToString(LargestUInt(value));
|
||||
}
|
||||
|
||||
#endif // # if defined(JSON_HAS_INT64)
|
||||
|
||||
std::string valueToString(double value) {
|
||||
namespace {
|
||||
JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) {
|
||||
// Allocate a buffer that is more than large enough to store the 16 digits of
|
||||
// precision requested below.
|
||||
char buffer[32];
|
||||
char buffer[36];
|
||||
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
|
||||
char formatString[15];
|
||||
snprintf(formatString, sizeof(formatString), "%%.%dg", precision);
|
||||
|
||||
// Print into the buffer. We need not request the alternative representation
|
||||
// that always has a decimal point because JSON doesn't distingish the
|
||||
// concepts of reals and integers.
|
||||
if (isfinite(value)) {
|
||||
len = snprintf(buffer, sizeof(buffer), "%.16g", value);
|
||||
len = snprintf(buffer, sizeof(buffer), formatString, value);
|
||||
fixNumericLocale(buffer, buffer + len);
|
||||
|
||||
// try to ensure we preserve the fact that this was given to us as a double on input
|
||||
if (!strchr(buffer, '.') && !strchr(buffer, 'e')) {
|
||||
strcat(buffer, ".0");
|
||||
}
|
||||
|
||||
} else {
|
||||
// IEEE standard states that NaN values will not compare to themselves
|
||||
if (value != value) {
|
||||
len = snprintf(buffer, sizeof(buffer), "null");
|
||||
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
|
||||
} else if (value < 0) {
|
||||
len = snprintf(buffer, sizeof(buffer), "-1e+9999");
|
||||
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
|
||||
} else {
|
||||
len = snprintf(buffer, sizeof(buffer), "1e+9999");
|
||||
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "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"; }
|
||||
JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); }
|
||||
|
||||
std::string valueToQuotedString(const char* value) {
|
||||
JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; }
|
||||
|
||||
JSONCPP_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 + "\"";
|
||||
return JSONCPP_STRING("\"") + value + "\"";
|
||||
// We have to walk value and escape any special characters.
|
||||
// Appending to std::string is not efficient, but this should be rare.
|
||||
// Appending to JSONCPP_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 =
|
||||
JSONCPP_STRING::size_type maxsize =
|
||||
strlen(value) * 2 + 3; // allescaped+quotes+NULL
|
||||
std::string result;
|
||||
JSONCPP_STRING result;
|
||||
result.reserve(maxsize); // to avoid lots of mallocs
|
||||
result += "\"";
|
||||
for (const char* c = value; *c != 0; ++c) {
|
||||
@@ -190,7 +258,85 @@ std::string valueToQuotedString(const char* value) {
|
||||
// sequence from occurring.
|
||||
default:
|
||||
if (isControlCharacter(*c)) {
|
||||
std::ostringstream oss;
|
||||
JSONCPP_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;
|
||||
}
|
||||
|
||||
// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
|
||||
static char const* strnpbrk(char const* s, char const* accept, size_t n) {
|
||||
assert((s || !n) && accept);
|
||||
|
||||
char const* const end = s + n;
|
||||
for (char const* cur = s; cur < end; ++cur) {
|
||||
int const c = *cur;
|
||||
for (char const* a = accept; *a; ++a) {
|
||||
if (*a == c) {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) {
|
||||
if (value == NULL)
|
||||
return "";
|
||||
// Not sure how to handle unicode...
|
||||
if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
|
||||
!containsControlCharacter0(value, length))
|
||||
return JSONCPP_STRING("\"") + value + "\"";
|
||||
// We have to walk value and escape any special characters.
|
||||
// Appending to JSONCPP_STRING is not efficient, but this should be rare.
|
||||
// (Note: forward slashes are *not* rare, but I am not escaping them.)
|
||||
JSONCPP_STRING::size_type maxsize =
|
||||
length * 2 + 3; // allescaped+quotes+NULL
|
||||
JSONCPP_STRING result;
|
||||
result.reserve(maxsize); // to avoid lots of mallocs
|
||||
result += "\"";
|
||||
char const* end = value + length;
|
||||
for (const char* c = value; c != end; ++c) {
|
||||
switch (*c) {
|
||||
case '\"':
|
||||
result += "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
result += "\\\\";
|
||||
break;
|
||||
case '\b':
|
||||
result += "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
result += "\\f";
|
||||
break;
|
||||
case '\n':
|
||||
result += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
result += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
result += "\\t";
|
||||
break;
|
||||
// case '/':
|
||||
// Even though \/ is considered a legal escape in JSON, a bare
|
||||
// slash is also legal, so I see no reason to escape it.
|
||||
// (I hope I am not misunderstanding something.)
|
||||
// blep notes: actually escaping \/ may be useful in javascript to avoid </
|
||||
// sequence.
|
||||
// Should add a flag to allow this compatibility mode and prevent this
|
||||
// sequence from occurring.
|
||||
default:
|
||||
if ((isControlCharacter(*c)) || (*c == 0)) {
|
||||
JSONCPP_OSTRINGSTREAM oss;
|
||||
oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
|
||||
<< std::setw(4) << static_cast<int>(*c);
|
||||
result += oss.str();
|
||||
@@ -221,8 +367,8 @@ void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
|
||||
|
||||
void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
|
||||
|
||||
std::string FastWriter::write(const Value& root) {
|
||||
document_ = "";
|
||||
JSONCPP_STRING FastWriter::write(const Value& root) {
|
||||
document_.clear();
|
||||
writeValue(root);
|
||||
if (!omitEndingLineFeed_)
|
||||
document_ += "\n";
|
||||
@@ -245,15 +391,21 @@ void FastWriter::writeValue(const Value& value) {
|
||||
document_ += valueToString(value.asDouble());
|
||||
break;
|
||||
case stringValue:
|
||||
document_ += valueToQuotedString(value.asCString());
|
||||
{
|
||||
// Is NULL possible for value.string_? No.
|
||||
char const* str;
|
||||
char const* end;
|
||||
bool ok = value.getString(&str, &end);
|
||||
if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
|
||||
break;
|
||||
}
|
||||
case booleanValue:
|
||||
document_ += valueToString(value.asBool());
|
||||
break;
|
||||
case arrayValue: {
|
||||
document_ += '[';
|
||||
int size = value.size();
|
||||
for (int index = 0; index < size; ++index) {
|
||||
ArrayIndex size = value.size();
|
||||
for (ArrayIndex index = 0; index < size; ++index) {
|
||||
if (index > 0)
|
||||
document_ += ',';
|
||||
writeValue(value[index]);
|
||||
@@ -265,10 +417,10 @@ void FastWriter::writeValue(const Value& value) {
|
||||
document_ += '{';
|
||||
for (Value::Members::iterator it = members.begin(); it != members.end();
|
||||
++it) {
|
||||
const std::string& name = *it;
|
||||
const JSONCPP_STRING& name = *it;
|
||||
if (it != members.begin())
|
||||
document_ += ',';
|
||||
document_ += valueToQuotedString(name.c_str());
|
||||
document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
|
||||
document_ += yamlCompatiblityEnabled_ ? ": " : ":";
|
||||
writeValue(value[name]);
|
||||
}
|
||||
@@ -283,10 +435,10 @@ void FastWriter::writeValue(const Value& value) {
|
||||
StyledWriter::StyledWriter()
|
||||
: rightMargin_(74), indentSize_(3), addChildValues_() {}
|
||||
|
||||
std::string StyledWriter::write(const Value& root) {
|
||||
document_ = "";
|
||||
JSONCPP_STRING StyledWriter::write(const Value& root) {
|
||||
document_.clear();
|
||||
addChildValues_ = false;
|
||||
indentString_ = "";
|
||||
indentString_.clear();
|
||||
writeCommentBeforeValue(root);
|
||||
writeValue(root);
|
||||
writeCommentAfterValueOnSameLine(root);
|
||||
@@ -309,8 +461,15 @@ void StyledWriter::writeValue(const Value& value) {
|
||||
pushValue(valueToString(value.asDouble()));
|
||||
break;
|
||||
case stringValue:
|
||||
pushValue(valueToQuotedString(value.asCString()));
|
||||
{
|
||||
// Is NULL possible for value.string_? No.
|
||||
char const* str;
|
||||
char const* end;
|
||||
bool ok = value.getString(&str, &end);
|
||||
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
|
||||
else pushValue("");
|
||||
break;
|
||||
}
|
||||
case booleanValue:
|
||||
pushValue(valueToString(value.asBool()));
|
||||
break;
|
||||
@@ -326,7 +485,7 @@ void StyledWriter::writeValue(const Value& value) {
|
||||
indent();
|
||||
Value::Members::iterator it = members.begin();
|
||||
for (;;) {
|
||||
const std::string& name = *it;
|
||||
const JSONCPP_STRING& name = *it;
|
||||
const Value& childValue = value[name];
|
||||
writeCommentBeforeValue(childValue);
|
||||
writeWithIndent(valueToQuotedString(name.c_str()));
|
||||
@@ -390,23 +549,25 @@ void StyledWriter::writeArrayValue(const Value& value) {
|
||||
}
|
||||
|
||||
bool StyledWriter::isMultineArray(const Value& value) {
|
||||
int size = value.size();
|
||||
ArrayIndex const size = value.size();
|
||||
bool isMultiLine = size * 3 >= rightMargin_;
|
||||
childValues_.clear();
|
||||
for (int index = 0; index < size && !isMultiLine; ++index) {
|
||||
for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
|
||||
const Value& childValue = value[index];
|
||||
isMultiLine =
|
||||
isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
|
||||
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) {
|
||||
ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
||||
for (ArrayIndex index = 0; index < size; ++index) {
|
||||
if (hasCommentForValue(value[index])) {
|
||||
isMultiLine = true;
|
||||
}
|
||||
writeValue(value[index]);
|
||||
lineLength += int(childValues_[index].length());
|
||||
lineLength += static_cast<ArrayIndex>(childValues_[index].length());
|
||||
}
|
||||
addChildValues_ = false;
|
||||
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
||||
@@ -414,7 +575,7 @@ bool StyledWriter::isMultineArray(const Value& value) {
|
||||
return isMultiLine;
|
||||
}
|
||||
|
||||
void StyledWriter::pushValue(const std::string& value) {
|
||||
void StyledWriter::pushValue(const JSONCPP_STRING& value) {
|
||||
if (addChildValues_)
|
||||
childValues_.push_back(value);
|
||||
else
|
||||
@@ -432,15 +593,15 @@ void StyledWriter::writeIndent() {
|
||||
document_ += indentString_;
|
||||
}
|
||||
|
||||
void StyledWriter::writeWithIndent(const std::string& value) {
|
||||
void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) {
|
||||
writeIndent();
|
||||
document_ += value;
|
||||
}
|
||||
|
||||
void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
|
||||
void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); }
|
||||
|
||||
void StyledWriter::unindent() {
|
||||
assert(int(indentString_.size()) >= indentSize_);
|
||||
assert(indentString_.size() >= indentSize_);
|
||||
indentString_.resize(indentString_.size() - indentSize_);
|
||||
}
|
||||
|
||||
@@ -450,26 +611,27 @@ void StyledWriter::writeCommentBeforeValue(const Value& root) {
|
||||
|
||||
document_ += "\n";
|
||||
writeIndent();
|
||||
std::string normalizedComment = normalizeEOL(root.getComment(commentBefore));
|
||||
std::string::const_iterator iter = normalizedComment.begin();
|
||||
while (iter != normalizedComment.end()) {
|
||||
const JSONCPP_STRING& comment = root.getComment(commentBefore);
|
||||
JSONCPP_STRING::const_iterator iter = comment.begin();
|
||||
while (iter != comment.end()) {
|
||||
document_ += *iter;
|
||||
if (*iter == '\n' && *(iter + 1) == '/')
|
||||
if (*iter == '\n' &&
|
||||
(iter != comment.end() && *(iter + 1) == '/'))
|
||||
writeIndent();
|
||||
++iter;
|
||||
}
|
||||
|
||||
// Comments are stripped of newlines, so add one here
|
||||
// Comments are stripped of trailing newlines, so add one here
|
||||
document_ += "\n";
|
||||
}
|
||||
|
||||
void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
|
||||
if (root.hasComment(commentAfterOnSameLine))
|
||||
document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
|
||||
document_ += " " + root.getComment(commentAfterOnSameLine);
|
||||
|
||||
if (root.hasComment(commentAfter)) {
|
||||
document_ += "\n";
|
||||
document_ += normalizeEOL(root.getComment(commentAfter));
|
||||
document_ += root.getComment(commentAfter);
|
||||
document_ += "\n";
|
||||
}
|
||||
}
|
||||
@@ -480,37 +642,21 @@ bool StyledWriter::hasCommentForValue(const Value& value) {
|
||||
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)
|
||||
StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation)
|
||||
: document_(NULL), rightMargin_(74), indentation_(indentation),
|
||||
addChildValues_() {}
|
||||
|
||||
void StyledStreamWriter::write(std::ostream& out, const Value& root) {
|
||||
void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) {
|
||||
document_ = &out;
|
||||
addChildValues_ = false;
|
||||
indentString_ = "";
|
||||
indentString_.clear();
|
||||
indented_ = true;
|
||||
writeCommentBeforeValue(root);
|
||||
if (!indented_) writeIndent();
|
||||
indented_ = true;
|
||||
writeValue(root);
|
||||
writeCommentAfterValueOnSameLine(root);
|
||||
*document_ << "\n";
|
||||
@@ -532,8 +678,15 @@ void StyledStreamWriter::writeValue(const Value& value) {
|
||||
pushValue(valueToString(value.asDouble()));
|
||||
break;
|
||||
case stringValue:
|
||||
pushValue(valueToQuotedString(value.asCString()));
|
||||
{
|
||||
// Is NULL possible for value.string_? No.
|
||||
char const* str;
|
||||
char const* end;
|
||||
bool ok = value.getString(&str, &end);
|
||||
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
|
||||
else pushValue("");
|
||||
break;
|
||||
}
|
||||
case booleanValue:
|
||||
pushValue(valueToString(value.asBool()));
|
||||
break;
|
||||
@@ -549,7 +702,7 @@ void StyledStreamWriter::writeValue(const Value& value) {
|
||||
indent();
|
||||
Value::Members::iterator it = members.begin();
|
||||
for (;;) {
|
||||
const std::string& name = *it;
|
||||
const JSONCPP_STRING& name = *it;
|
||||
const Value& childValue = value[name];
|
||||
writeCommentBeforeValue(childValue);
|
||||
writeWithIndent(valueToQuotedString(name.c_str()));
|
||||
@@ -586,8 +739,10 @@ void StyledStreamWriter::writeArrayValue(const Value& value) {
|
||||
if (hasChildValue)
|
||||
writeWithIndent(childValues_[index]);
|
||||
else {
|
||||
writeIndent();
|
||||
if (!indented_) writeIndent();
|
||||
indented_ = true;
|
||||
writeValue(childValue);
|
||||
indented_ = false;
|
||||
}
|
||||
if (++index == size) {
|
||||
writeCommentAfterValueOnSameLine(childValue);
|
||||
@@ -613,23 +768,25 @@ void StyledStreamWriter::writeArrayValue(const Value& value) {
|
||||
}
|
||||
|
||||
bool StyledStreamWriter::isMultineArray(const Value& value) {
|
||||
int size = value.size();
|
||||
ArrayIndex const size = value.size();
|
||||
bool isMultiLine = size * 3 >= rightMargin_;
|
||||
childValues_.clear();
|
||||
for (int index = 0; index < size && !isMultiLine; ++index) {
|
||||
for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
|
||||
const Value& childValue = value[index];
|
||||
isMultiLine =
|
||||
isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
|
||||
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) {
|
||||
ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
||||
for (ArrayIndex index = 0; index < size; ++index) {
|
||||
if (hasCommentForValue(value[index])) {
|
||||
isMultiLine = true;
|
||||
}
|
||||
writeValue(value[index]);
|
||||
lineLength += int(childValues_[index].length());
|
||||
lineLength += static_cast<ArrayIndex>(childValues_[index].length());
|
||||
}
|
||||
addChildValues_ = false;
|
||||
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
||||
@@ -637,7 +794,7 @@ bool StyledStreamWriter::isMultineArray(const Value& value) {
|
||||
return isMultiLine;
|
||||
}
|
||||
|
||||
void StyledStreamWriter::pushValue(const std::string& value) {
|
||||
void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) {
|
||||
if (addChildValues_)
|
||||
childValues_.push_back(value);
|
||||
else
|
||||
@@ -645,24 +802,17 @@ void StyledStreamWriter::pushValue(const std::string& 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';
|
||||
}
|
||||
*/
|
||||
// blep intended this to look at the so-far-written string
|
||||
// to determine whether we are already indented, but
|
||||
// with a stream we cannot do that. So we rely on some saved state.
|
||||
// The caller checks indented_.
|
||||
*document_ << '\n' << indentString_;
|
||||
}
|
||||
|
||||
void StyledStreamWriter::writeWithIndent(const std::string& value) {
|
||||
writeIndent();
|
||||
void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) {
|
||||
if (!indented_) writeIndent();
|
||||
*document_ << value;
|
||||
indented_ = false;
|
||||
}
|
||||
|
||||
void StyledStreamWriter::indent() { indentString_ += indentation_; }
|
||||
@@ -675,19 +825,30 @@ void StyledStreamWriter::unindent() {
|
||||
void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
|
||||
if (!root.hasComment(commentBefore))
|
||||
return;
|
||||
*document_ << normalizeEOL(root.getComment(commentBefore));
|
||||
*document_ << "\n";
|
||||
|
||||
if (!indented_) writeIndent();
|
||||
const JSONCPP_STRING& comment = root.getComment(commentBefore);
|
||||
JSONCPP_STRING::const_iterator iter = comment.begin();
|
||||
while (iter != comment.end()) {
|
||||
*document_ << *iter;
|
||||
if (*iter == '\n' &&
|
||||
(iter != comment.end() && *(iter + 1) == '/'))
|
||||
// writeIndent(); // would include newline
|
||||
*document_ << indentString_;
|
||||
++iter;
|
||||
}
|
||||
indented_ = false;
|
||||
}
|
||||
|
||||
void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
|
||||
if (root.hasComment(commentAfterOnSameLine))
|
||||
*document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
|
||||
*document_ << ' ' << root.getComment(commentAfterOnSameLine);
|
||||
|
||||
if (root.hasComment(commentAfter)) {
|
||||
*document_ << "\n";
|
||||
*document_ << normalizeEOL(root.getComment(commentAfter));
|
||||
*document_ << "\n";
|
||||
writeIndent();
|
||||
*document_ << root.getComment(commentAfter);
|
||||
}
|
||||
indented_ = false;
|
||||
}
|
||||
|
||||
bool StyledStreamWriter::hasCommentForValue(const Value& value) {
|
||||
@@ -696,28 +857,400 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) {
|
||||
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;
|
||||
//////////////////////////
|
||||
// BuiltStyledStreamWriter
|
||||
|
||||
/// Scoped enums are not available until C++11.
|
||||
struct CommentStyle {
|
||||
/// Decide whether to write comments.
|
||||
enum Enum {
|
||||
None, ///< Drop all comments.
|
||||
Most, ///< Recover odd behavior of previous versions (not implemented yet).
|
||||
All ///< Keep all comments.
|
||||
};
|
||||
};
|
||||
|
||||
struct BuiltStyledStreamWriter : public StreamWriter
|
||||
{
|
||||
BuiltStyledStreamWriter(
|
||||
JSONCPP_STRING const& indentation,
|
||||
CommentStyle::Enum cs,
|
||||
JSONCPP_STRING const& colonSymbol,
|
||||
JSONCPP_STRING const& nullSymbol,
|
||||
JSONCPP_STRING const& endingLineFeedSymbol,
|
||||
bool useSpecialFloats,
|
||||
unsigned int precision);
|
||||
int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;
|
||||
private:
|
||||
void writeValue(Value const& value);
|
||||
void writeArrayValue(Value const& value);
|
||||
bool isMultineArray(Value const& value);
|
||||
void pushValue(JSONCPP_STRING const& value);
|
||||
void writeIndent();
|
||||
void writeWithIndent(JSONCPP_STRING const& value);
|
||||
void indent();
|
||||
void unindent();
|
||||
void writeCommentBeforeValue(Value const& root);
|
||||
void writeCommentAfterValueOnSameLine(Value const& root);
|
||||
static bool hasCommentForValue(const Value& value);
|
||||
|
||||
typedef std::vector<JSONCPP_STRING> ChildValues;
|
||||
|
||||
ChildValues childValues_;
|
||||
JSONCPP_STRING indentString_;
|
||||
unsigned int rightMargin_;
|
||||
JSONCPP_STRING indentation_;
|
||||
CommentStyle::Enum cs_;
|
||||
JSONCPP_STRING colonSymbol_;
|
||||
JSONCPP_STRING nullSymbol_;
|
||||
JSONCPP_STRING endingLineFeedSymbol_;
|
||||
bool addChildValues_ : 1;
|
||||
bool indented_ : 1;
|
||||
bool useSpecialFloats_ : 1;
|
||||
unsigned int precision_;
|
||||
};
|
||||
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
|
||||
JSONCPP_STRING const& indentation,
|
||||
CommentStyle::Enum cs,
|
||||
JSONCPP_STRING const& colonSymbol,
|
||||
JSONCPP_STRING const& nullSymbol,
|
||||
JSONCPP_STRING const& endingLineFeedSymbol,
|
||||
bool useSpecialFloats,
|
||||
unsigned int precision)
|
||||
: rightMargin_(74)
|
||||
, indentation_(indentation)
|
||||
, cs_(cs)
|
||||
, colonSymbol_(colonSymbol)
|
||||
, nullSymbol_(nullSymbol)
|
||||
, endingLineFeedSymbol_(endingLineFeedSymbol)
|
||||
, addChildValues_(false)
|
||||
, indented_(false)
|
||||
, useSpecialFloats_(useSpecialFloats)
|
||||
, precision_(precision)
|
||||
{
|
||||
}
|
||||
int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout)
|
||||
{
|
||||
sout_ = sout;
|
||||
addChildValues_ = false;
|
||||
indented_ = true;
|
||||
indentString_.clear();
|
||||
writeCommentBeforeValue(root);
|
||||
if (!indented_) writeIndent();
|
||||
indented_ = true;
|
||||
writeValue(root);
|
||||
writeCommentAfterValueOnSameLine(root);
|
||||
*sout_ << endingLineFeedSymbol_;
|
||||
sout_ = NULL;
|
||||
return 0;
|
||||
}
|
||||
void BuiltStyledStreamWriter::writeValue(Value const& value) {
|
||||
switch (value.type()) {
|
||||
case nullValue:
|
||||
pushValue(nullSymbol_);
|
||||
break;
|
||||
case intValue:
|
||||
pushValue(valueToString(value.asLargestInt()));
|
||||
break;
|
||||
case uintValue:
|
||||
pushValue(valueToString(value.asLargestUInt()));
|
||||
break;
|
||||
case realValue:
|
||||
pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));
|
||||
break;
|
||||
case stringValue:
|
||||
{
|
||||
// Is NULL is possible for value.string_? No.
|
||||
char const* str;
|
||||
char const* end;
|
||||
bool ok = value.getString(&str, &end);
|
||||
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
|
||||
else pushValue("");
|
||||
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 (;;) {
|
||||
JSONCPP_STRING const& name = *it;
|
||||
Value const& childValue = value[name];
|
||||
writeCommentBeforeValue(childValue);
|
||||
writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())));
|
||||
*sout_ << colonSymbol_;
|
||||
writeValue(childValue);
|
||||
if (++it == members.end()) {
|
||||
writeCommentAfterValueOnSameLine(childValue);
|
||||
break;
|
||||
}
|
||||
*sout_ << ",";
|
||||
writeCommentAfterValueOnSameLine(childValue);
|
||||
}
|
||||
unindent();
|
||||
writeWithIndent("}");
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& sout, const Value& root) {
|
||||
Json::StyledStreamWriter writer;
|
||||
writer.write(sout, root);
|
||||
void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
|
||||
unsigned size = value.size();
|
||||
if (size == 0)
|
||||
pushValue("[]");
|
||||
else {
|
||||
bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
|
||||
if (isMultiLine) {
|
||||
writeWithIndent("[");
|
||||
indent();
|
||||
bool hasChildValue = !childValues_.empty();
|
||||
unsigned index = 0;
|
||||
for (;;) {
|
||||
Value const& childValue = value[index];
|
||||
writeCommentBeforeValue(childValue);
|
||||
if (hasChildValue)
|
||||
writeWithIndent(childValues_[index]);
|
||||
else {
|
||||
if (!indented_) writeIndent();
|
||||
indented_ = true;
|
||||
writeValue(childValue);
|
||||
indented_ = false;
|
||||
}
|
||||
if (++index == size) {
|
||||
writeCommentAfterValueOnSameLine(childValue);
|
||||
break;
|
||||
}
|
||||
*sout_ << ",";
|
||||
writeCommentAfterValueOnSameLine(childValue);
|
||||
}
|
||||
unindent();
|
||||
writeWithIndent("]");
|
||||
} else // output on a single line
|
||||
{
|
||||
assert(childValues_.size() == size);
|
||||
*sout_ << "[";
|
||||
if (!indentation_.empty()) *sout_ << " ";
|
||||
for (unsigned index = 0; index < size; ++index) {
|
||||
if (index > 0)
|
||||
*sout_ << ((!indentation_.empty()) ? ", " : ",");
|
||||
*sout_ << childValues_[index];
|
||||
}
|
||||
if (!indentation_.empty()) *sout_ << " ";
|
||||
*sout_ << "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BuiltStyledStreamWriter::isMultineArray(Value const& value) {
|
||||
ArrayIndex const size = value.size();
|
||||
bool isMultiLine = size * 3 >= rightMargin_;
|
||||
childValues_.clear();
|
||||
for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
|
||||
Value const& childValue = value[index];
|
||||
isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
|
||||
childValue.size() > 0);
|
||||
}
|
||||
if (!isMultiLine) // check if line length > max line length
|
||||
{
|
||||
childValues_.reserve(size);
|
||||
addChildValues_ = true;
|
||||
ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
||||
for (ArrayIndex index = 0; index < size; ++index) {
|
||||
if (hasCommentForValue(value[index])) {
|
||||
isMultiLine = true;
|
||||
}
|
||||
writeValue(value[index]);
|
||||
lineLength += static_cast<ArrayIndex>(childValues_[index].length());
|
||||
}
|
||||
addChildValues_ = false;
|
||||
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
||||
}
|
||||
return isMultiLine;
|
||||
}
|
||||
|
||||
void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) {
|
||||
if (addChildValues_)
|
||||
childValues_.push_back(value);
|
||||
else
|
||||
*sout_ << value;
|
||||
}
|
||||
|
||||
void BuiltStyledStreamWriter::writeIndent() {
|
||||
// blep intended this to look at the so-far-written string
|
||||
// to determine whether we are already indented, but
|
||||
// with a stream we cannot do that. So we rely on some saved state.
|
||||
// The caller checks indented_.
|
||||
|
||||
if (!indentation_.empty()) {
|
||||
// In this case, drop newlines too.
|
||||
*sout_ << '\n' << indentString_;
|
||||
}
|
||||
}
|
||||
|
||||
void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) {
|
||||
if (!indented_) writeIndent();
|
||||
*sout_ << value;
|
||||
indented_ = false;
|
||||
}
|
||||
|
||||
void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
|
||||
|
||||
void BuiltStyledStreamWriter::unindent() {
|
||||
assert(indentString_.size() >= indentation_.size());
|
||||
indentString_.resize(indentString_.size() - indentation_.size());
|
||||
}
|
||||
|
||||
void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
|
||||
if (cs_ == CommentStyle::None) return;
|
||||
if (!root.hasComment(commentBefore))
|
||||
return;
|
||||
|
||||
if (!indented_) writeIndent();
|
||||
const JSONCPP_STRING& comment = root.getComment(commentBefore);
|
||||
JSONCPP_STRING::const_iterator iter = comment.begin();
|
||||
while (iter != comment.end()) {
|
||||
*sout_ << *iter;
|
||||
if (*iter == '\n' &&
|
||||
(iter != comment.end() && *(iter + 1) == '/'))
|
||||
// writeIndent(); // would write extra newline
|
||||
*sout_ << indentString_;
|
||||
++iter;
|
||||
}
|
||||
indented_ = false;
|
||||
}
|
||||
|
||||
void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
|
||||
if (cs_ == CommentStyle::None) return;
|
||||
if (root.hasComment(commentAfterOnSameLine))
|
||||
*sout_ << " " + root.getComment(commentAfterOnSameLine);
|
||||
|
||||
if (root.hasComment(commentAfter)) {
|
||||
writeIndent();
|
||||
*sout_ << root.getComment(commentAfter);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
|
||||
return value.hasComment(commentBefore) ||
|
||||
value.hasComment(commentAfterOnSameLine) ||
|
||||
value.hasComment(commentAfter);
|
||||
}
|
||||
|
||||
///////////////
|
||||
// StreamWriter
|
||||
|
||||
StreamWriter::StreamWriter()
|
||||
: sout_(NULL)
|
||||
{
|
||||
}
|
||||
StreamWriter::~StreamWriter()
|
||||
{
|
||||
}
|
||||
StreamWriter::Factory::~Factory()
|
||||
{}
|
||||
StreamWriterBuilder::StreamWriterBuilder()
|
||||
{
|
||||
setDefaults(&settings_);
|
||||
}
|
||||
StreamWriterBuilder::~StreamWriterBuilder()
|
||||
{}
|
||||
StreamWriter* StreamWriterBuilder::newStreamWriter() const
|
||||
{
|
||||
JSONCPP_STRING indentation = settings_["indentation"].asString();
|
||||
JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
|
||||
bool eyc = settings_["enableYAMLCompatibility"].asBool();
|
||||
bool dnp = settings_["dropNullPlaceholders"].asBool();
|
||||
bool usf = settings_["useSpecialFloats"].asBool();
|
||||
unsigned int pre = settings_["precision"].asUInt();
|
||||
CommentStyle::Enum cs = CommentStyle::All;
|
||||
if (cs_str == "All") {
|
||||
cs = CommentStyle::All;
|
||||
} else if (cs_str == "None") {
|
||||
cs = CommentStyle::None;
|
||||
} else {
|
||||
throwRuntimeError("commentStyle must be 'All' or 'None'");
|
||||
}
|
||||
JSONCPP_STRING colonSymbol = " : ";
|
||||
if (eyc) {
|
||||
colonSymbol = ": ";
|
||||
} else if (indentation.empty()) {
|
||||
colonSymbol = ":";
|
||||
}
|
||||
JSONCPP_STRING nullSymbol = "null";
|
||||
if (dnp) {
|
||||
nullSymbol.clear();
|
||||
}
|
||||
if (pre > 17) pre = 17;
|
||||
JSONCPP_STRING endingLineFeedSymbol;
|
||||
return new BuiltStyledStreamWriter(
|
||||
indentation, cs,
|
||||
colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);
|
||||
}
|
||||
static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys)
|
||||
{
|
||||
valid_keys->clear();
|
||||
valid_keys->insert("indentation");
|
||||
valid_keys->insert("commentStyle");
|
||||
valid_keys->insert("enableYAMLCompatibility");
|
||||
valid_keys->insert("dropNullPlaceholders");
|
||||
valid_keys->insert("useSpecialFloats");
|
||||
valid_keys->insert("precision");
|
||||
}
|
||||
bool StreamWriterBuilder::validate(Json::Value* invalid) const
|
||||
{
|
||||
Json::Value my_invalid;
|
||||
if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL
|
||||
Json::Value& inv = *invalid;
|
||||
std::set<JSONCPP_STRING> valid_keys;
|
||||
getValidWriterKeys(&valid_keys);
|
||||
Value::Members keys = settings_.getMemberNames();
|
||||
size_t n = keys.size();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
JSONCPP_STRING const& key = keys[i];
|
||||
if (valid_keys.find(key) == valid_keys.end()) {
|
||||
inv[key] = settings_[key];
|
||||
}
|
||||
}
|
||||
return 0u == inv.size();
|
||||
}
|
||||
Value& StreamWriterBuilder::operator[](JSONCPP_STRING key)
|
||||
{
|
||||
return settings_[key];
|
||||
}
|
||||
// static
|
||||
void StreamWriterBuilder::setDefaults(Json::Value* settings)
|
||||
{
|
||||
//! [StreamWriterBuilderDefaults]
|
||||
(*settings)["commentStyle"] = "All";
|
||||
(*settings)["indentation"] = "\t";
|
||||
(*settings)["enableYAMLCompatibility"] = false;
|
||||
(*settings)["dropNullPlaceholders"] = false;
|
||||
(*settings)["useSpecialFloats"] = false;
|
||||
(*settings)["precision"] = 17;
|
||||
//! [StreamWriterBuilderDefaults]
|
||||
}
|
||||
|
||||
JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) {
|
||||
JSONCPP_OSTRINGSTREAM sout;
|
||||
StreamWriterPtr const writer(builder.newStreamWriter());
|
||||
writer->write(root, &sout);
|
||||
return sout.str();
|
||||
}
|
||||
|
||||
JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) {
|
||||
StreamWriterBuilder builder;
|
||||
StreamWriterPtr const writer(builder.newStreamWriter());
|
||||
writer->write(root, &sout);
|
||||
return sout;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user