Compare commits

..

77 Commits
1.4.3 ... 1.5.4

Author SHA1 Message Date
Christopher Dunn
ee4ea0ec3f delete debug code from test 2015-03-07 15:47:39 -06:00
Christopher Dunn
ce19001238 require length
Ugh! I meant to do this long ago. It would have caught my blunder.
2015-03-07 15:12:52 -06:00
Christopher Dunn
078f991c57 1.5.4 <- 1.5.3
important bug-fix (thx to datadiode@)
2015-03-07 14:52:01 -06:00
Christopher Dunn
72b5293695 Merge pull request #207 from cdunn2001/fix_CZString_copy_constructor
Fix czstring copy constructor
2015-03-07 14:49:54 -06:00
Christopher Dunn
a63d82d78a drop unused CString ctor case
`Value::CZString::CZString(char const* str, unsigned length, DuplicationPolicy allocate)` with `allocate == duplicate` does not happen.
2015-03-07 14:43:37 -06:00
datadiode
ee83f8891c Trivial fixes in CZString constructors. 2015-03-07 14:43:07 -06:00
Christopher Dunn
5c448687e1 fix ValueTest/zeroes* 2015-03-07 14:41:15 -06:00
Christopher Dunn
401e98269e old-style enum namespacing 2015-03-06 16:11:55 -06:00
Christopher Dunn
b2a7438d08 Merge pull request #205 from open-source-parsers/reject-dup-keys
[Shekhar (shakers007) wrote](https://sourceforge.net/p/jsoncpp/bugs/22/):

> As per RFC4627 (section 2.2), names within an object should be unique. When using JSONCPP's strict mode, parsing such an object should fail.
2015-03-06 12:58:55 -06:00
Christopher Dunn
62ad140d18 rejectDupKeys 2015-03-06 12:39:05 -06:00
Christopher Dunn
527332d5d5 add rejectDupKeys feature - not yet impld 2015-03-06 12:38:58 -06:00
Christopher Dunn
cada3b951f test for repeated key in strictMode
https://sourceforge.net/p/jsoncpp/bugs/22/
2015-03-06 12:38:00 -06:00
Christopher Dunn
ff61752444 change str_ for cross-compilation
https://sourceforge.net/p/jsoncpp/bugs/59/
2015-03-06 10:31:46 -06:00
Christopher Dunn
7f439f4276 clarify operator= 2015-03-06 09:22:57 -06:00
Christopher Dunn
3976f17ffd test assignment over-writes comments, but swapPayload() does not 2015-03-06 09:16:19 -06:00
Christopher Dunn
80ca11bb41 test commentBefore
for issue #203
2015-03-06 05:55:19 -06:00
Christopher Dunn
2fc08b4ebd clarify which versions work with old compilers 2015-03-05 21:45:42 -06:00
Christopher Dunn
239c733ab5 1.5.3 <- 1.5.2 2015-03-05 18:27:52 -06:00
Christopher Dunn
295e73ff3c generate both version.h and version from CMakelists.txt
This forces consistency, since they will be re-generated whenever
a git operation alters CMakelists.txt. They are still in the repo
because users might not actually run cmake.
2015-03-05 18:27:39 -06:00
Christopher Dunn
2a840c105c had trouble finding Python on Windows
With this change, `make jsoncpp_check` will still fail if Python
is missing, so our CI tests are unaffected.
2015-03-05 17:42:12 -06:00
Christopher Dunn
7ec98dc9fe Merge pull request #202 from open-source-parsers/get-with-zero
`Value::get(key, default)` with zero
2015-03-05 16:56:56 -06:00
Christopher Dunn
0fd2875a44 fix get() for embedded zeroes in key
This method had been overlooked.
2015-03-05 16:47:29 -06:00
Christopher Dunn
d31151d150 test get(key, default) 2015-03-05 16:44:50 -06:00
Christopher Dunn
f50145fbda Merge pull request #201 from open-source-parsers/vs
* Copy .dll for running unit-tests in VisualStudio.
* Stop using `do{}while(0)` idiom b/c VisualStudio warns.
* Fix a warning in a test.
2015-03-05 16:37:42 -06:00
Christopher Dunn
b3e6f3d70f drop do{}while(0) idiom
Rationale:
  * http://stackoverflow.com/questions/154136/do-while-and-if-else-statements-in-c-c-macros/154138#154138

But Visual Studio issues a warning: `warning C4127: conditional expression is constant`
  * http://stackoverflow.com/questions/1946445/c-c-how-to-use-the-do-while0-construct-without-compiler-warnings-like-c412
2015-03-05 15:26:29 -06:00
Christopher Dunn
13e063c336 copy .dll for unit-test
Fix 2nd problem in issue #200.
* http://stackoverflow.com/questions/10671916/how-to-copy-dll-files-into-the-same-folder-as-the-executable-using-cmake

Q: What about the Python tests?
A: They are not normally run in Visual Studio. If desired, one can set PATH.
2015-03-05 15:23:20 -06:00
Christopher Dunn
f57da48f48 maybe address warning
cmake -DCMAKE_BUILD_TYPE=debug -DJSONCPP_LIB_BUILD_STATIC=OFF
-DJSONCPP_LIB_BUILD_SHARED=ON -G "Visual Studio 10" ../..

`potentially uninitialized local variable 'dist' (line 2212 of
test_lib_json/main.cpp)`
2015-03-05 14:41:42 -06:00
Christopher Dunn
eaa3fd8eca maybe fix DLL symbols for VS 2015-03-05 10:11:43 -06:00
Christopher Dunn
ff63d034e5 1.5.2 <- 1.5.1
* Fixed compile error for VS2013.
* Added `operator[]` to Builders. Recalling previous minor versions for
  bug-fixes.
2015-03-05 09:18:44 -06:00
Christopher Dunn
2aa4557e79 Merge pull request #199 from open-source-parsers/set-builders
Setting builders
2015-03-05 09:18:06 -06:00
Christopher Dunn
37dde9d29d fix example in docs 2015-03-05 09:18:06 -06:00
Christopher Dunn
c312dd5ef7 Builder::operator[] plus tests 2015-03-05 09:18:01 -06:00
Christopher Dunn
42d7e59fe0 fix compiler-error and warnings for VS2013
fix issue #200
2015-03-05 09:15:10 -06:00
Christopher Dunn
a8104a8035 1.5.1 <- 1.5.0
Fixed Builders ::validate(). Added tests.
2015-03-04 21:18:05 -06:00
Christopher Dunn
7b22768c33 test Builders::validate() 2015-03-04 21:17:03 -06:00
Christopher Dunn
19c49a459d fix Builders::validate()
(cherry picked from commit 626cfcdbb8)
2015-03-04 21:14:53 -06:00
Christopher Dunn
99822b27cd clarify python version 2015-03-03 16:17:11 -06:00
Christopher Dunn
8a70297869 fix inline doxygen comments 2015-03-03 16:17:08 -06:00
Christopher Dunn
24f544996f no struct init
The C struct initializer is not standard C++.
GCC and Clang handle this (at least in some versions) but some
compilers might not.
2015-03-03 10:15:09 -06:00
Christopher Dunn
0c91927da2 assertions should be logic_error 2015-03-03 09:45:33 -06:00
Christopher Dunn
493f6dcebe keep StaticString (!allocated_) for copy ctor 2015-03-03 09:36:22 -06:00
Christopher Dunn
eaa06355e1 test Json::Value::null 2015-03-03 08:45:52 -06:00
Christopher Dunn
effd732aa1 null -> nullRef 2015-03-03 01:25:33 -06:00
Christopher Dunn
70cd04d49a 1.5.0 <- 1.4.4 2015-03-03 00:45:57 -06:00
Christopher Dunn
9e49e3d84a Merge pull request #197 from cdunn2001/allow-zero
allow zeroes in strings
2015-03-03 00:44:19 -06:00
Christopher Dunn
2d653bd15d fix security hole for string-key-lengths > 2^30 2015-03-03 00:14:54 -06:00
Christopher Dunn
585b267595 tests for zeroes
* ValueTest/zeroes
* ValueTest/zeroesInKeys
2015-03-03 00:14:54 -06:00
Christopher Dunn
c28610fb5d fix StaticString test
* support zeroes in string_
* support zeroes in writer; provide getString(char**, unsigned*)
* valueToQuotedStringN(), isCC0(), etc
* allow zeroes for cpptl ConstString
* allocated => non-static
2015-03-03 00:14:54 -06:00
Christopher Dunn
a53283568f cp duplicateStringValue() 2015-03-03 00:14:53 -06:00
Christopher Dunn
ef21fbc785 doc new behavior 2015-03-03 00:14:53 -06:00
Christopher Dunn
25342bac13 support UTF-8 for const methods 2015-03-03 00:14:53 -06:00
Christopher Dunn
2b9abc3ebf Merge pull request #195 from cdunn2001/len-in-czstring
Store length in CZString
2015-03-03 00:07:03 -06:00
Christopher Dunn
e6b46e4503 stop computing strlen() in CZString 2015-03-02 23:50:59 -06:00
Christopher Dunn
8a77037320 actually store length in CZString 2015-03-02 23:50:59 -06:00
Christopher Dunn
57ad051f67 allow length in CZString 2015-03-02 23:50:59 -06:00
Christopher Dunn
b383fdc61e use memcmp in CZString
This is a loss of efficiency, but it prepares for an increase when we
have stored lengths.
2015-03-02 23:50:59 -06:00
Mattes D
5d79275a5b Added MSVC+CMake generated files to .gitignore.
pull #193
2015-03-02 23:50:14 -06:00
Christopher Dunn
c1e834a110 Merge pull request #194 from cdunn2001/master
test StaticString
2015-03-02 17:14:36 -06:00
Christopher Dunn
0570f9eefb test StaticString 2015-03-02 17:06:09 -06:00
Christopher Dunn
b947b0b3df Merge pull request #192 from marklakata/size_t
changed length argument of duplicateStringValue from unsigned int to siz...
2015-03-02 17:03:24 -06:00
Mark Lakata
8051cf6ba7 changed length argument of duplicateStringValue from unsigned int to size_t, to avoid warnings in Visual Studio. 2015-03-02 11:57:17 -08:00
Christopher Dunn
c8bf184ea9 Merge pull request #189 from open-source-parsers/skip-py-tests
skip python jsontestrunner by default
2015-02-26 09:55:21 -06:00
Christopher Dunn
9998094eee skip python jsontestrunner by default
To run these tests, in cmake build-dir:
    make jsoncpp_check

TravisCI is now set to run these always.

For now, the test_json_lib unit-tests will still run.

issue #187
2015-02-26 09:41:45 -06:00
Christopher Dunn
4ac4cac2be Merge pull request #185 from open-source-parsers/drop-internal-map
`JSON_VALUE_USE_INTERNAL_MAP` and `JSON_USE_SIMPLE_INTERNAL_ALLOCATOR` did not actually compile with current compilers. In fact, as far as I can tell, they haven't worked since just after the 0.6.0-rc2 release. And according to blep, there are bugs, e.g. [this one](https://sourceforge.net/p/jsoncpp/bugs/27). It's probably better to hide the implementation of Value (maybe in the next major release) and to put experiments like this into completely separate classes.
2015-02-25 10:12:20 -06:00
Christopher Dunn
4788764844 drop JSON_VALUE_USE_INTERNAL_MAP, JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
And remove some old headers.

These were not actually compiling anymore, and there were outstanding,
known bugs, e.g. https://sourceforge.net/p/jsoncpp/bugs/27
2015-02-25 10:04:13 -06:00
Christopher Dunn
6c898441e3 test-amalgamate
Not tested via Travis (since this is outside cmake), but at least
we can more easily validate changes to `amalgamate.py`.
2015-02-25 10:04:12 -06:00
Christopher Dunn
7d1f656859 Merge pull request #183 from open-source-parsers/allow-single-quote
Allow single quote
2015-02-24 18:24:42 -06:00
Christopher Dunn
0c66e698fb allowSingleQuotes
issue #182
2015-02-24 15:49:45 -06:00
Christopher Dunn
b9229b7400 failing test for allowSingleQuotes 2015-02-23 16:40:06 -06:00
Christopher Dunn
f9db82af17 1.4.4 < 1.4.3
* fixed Readers when `allowDropNullPlaceholders`
2015-02-19 15:37:57 -06:00
Christopher Dunn
ae71879549 Merge pull request #179 from cdunn2001/drop-null
Fix failures for `allowDroppedNullPlaceholders`.
2015-02-19 12:38:16 -06:00
Christopher Dunn
7b3683ccd1 apply fix to old Reader 2015-02-19 11:37:17 -06:00
Christopher Dunn
58499031a4 fix all cases from issue -- all pass! 2015-02-19 11:35:28 -06:00
Christopher Dunn
2c8c1ac0ec all 8 failing cases from issue 2015-02-19 11:35:20 -06:00
Christopher Dunn
c58e93b014 fix failing object case 2015-02-19 11:34:35 -06:00
Christopher Dunn
eed193e151 object cases; 1st passes, 2nd fails 2015-02-19 11:34:35 -06:00
Christopher Dunn
4382a7b585 cases 0,1,5,9,12 from issue -- passing 2015-02-19 11:32:42 -06:00
30 changed files with 1051 additions and 1860 deletions

23
.gitignore vendored
View File

@@ -10,4 +10,27 @@
/libs/
/doc/doxyfile
/dist/
#/version
#/include/json/version.h
# MSVC project files:
*.sln
*.vcxproj
*.filters
*.user
*.sdf
*.opensdf
*.suo
# MSVC build files:
*.lib
*.obj
*.tlog/
*.pdb
# CMake-generated files:
CMakeFiles/
CTestTestFile.cmake
cmake_install.cmake
pkg-config/jsoncpp.pc
jsoncpp_lib_static.dir/

View File

@@ -7,7 +7,7 @@ language: cpp
compiler:
- gcc
- clang
script: cmake -DJSONCPP_WITH_CMAKE_PACKAGE=$CMAKE_PKG -DJSONCPP_LIB_BUILD_SHARED=$SHARED_LIB -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_VERBOSE_MAKEFILE=$VERBOSE_MAKE . && make
script: cmake -DJSONCPP_WITH_CMAKE_PACKAGE=$CMAKE_PKG -DJSONCPP_LIB_BUILD_SHARED=$SHARED_LIB -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_VERBOSE_MAKEFILE=$VERBOSE_MAKE . && make && make jsoncpp_check
env:
matrix:
- SHARED_LIB=ON STATIC_LIB=ON CMAKE_PKG=ON BUILD_TYPE=release VERBOSE_MAKE=false

View File

@@ -1,8 +1,10 @@
# vim: et ts=4 sts=4 sw=4 tw=0
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.5)
PROJECT(jsoncpp)
ENABLE_TESTING()
OPTION(JSONCPP_WITH_TESTS "Compile and run JsonCpp test executables" ON)
OPTION(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" ON)
OPTION(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" ON)
OPTION(JSONCPP_WITH_WARNING_AS_ERROR "Force compilation to fail if a warning occurs" OFF)
OPTION(JSONCPP_WITH_PKGCONFIG_SUPPORT "Generate and install .pc files" ON)
@@ -54,17 +56,24 @@ MACRO(jsoncpp_parse_version VERSION_TEXT OUPUT_PREFIX)
ENDMACRO(jsoncpp_parse_version)
# Read out version from "version" file
FILE(STRINGS "version" JSONCPP_VERSION)
#FILE(STRINGS "version" JSONCPP_VERSION)
#SET( JSONCPP_VERSION_MAJOR X )
#SET( JSONCPP_VERSION_MINOR Y )
#SET( JSONCPP_VERSION_PATCH Z )
SET( JSONCPP_VERSION 1.5.4 )
jsoncpp_parse_version( ${JSONCPP_VERSION} JSONCPP_VERSION )
IF(NOT JSONCPP_VERSION_FOUND)
MESSAGE(FATAL_ERROR "Failed to parse version string properly. Expect X.Y.Z")
ENDIF(NOT JSONCPP_VERSION_FOUND)
#IF(NOT JSONCPP_VERSION_FOUND)
# MESSAGE(FATAL_ERROR "Failed to parse version string properly. Expect X.Y.Z")
#ENDIF(NOT JSONCPP_VERSION_FOUND)
MESSAGE(STATUS "JsonCpp Version: ${JSONCPP_VERSION_MAJOR}.${JSONCPP_VERSION_MINOR}.${JSONCPP_VERSION_PATCH}")
# File version.h is only regenerated on CMake configure step
CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/src/lib_json/version.h.in"
"${PROJECT_SOURCE_DIR}/include/json/version.h" )
"${PROJECT_SOURCE_DIR}/include/json/version.h"
NEWLINE_STYLE UNIX )
CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/version.in"
"${PROJECT_SOURCE_DIR}/version"
NEWLINE_STYLE UNIX )
macro(UseCompilationWarningAsError)
if ( MSVC )

View File

@@ -16,7 +16,7 @@ format to store user input files.
## A note on backward-compatibility
* `1.y.z` is built with C++11.
* `0.8.z` can be used with older compilers.
* `0.y.z` can be used with older compilers.
* Major versions maintain binary-compatibility.
Using JsonCpp in your project

View File

@@ -1,6 +1,6 @@
"""Amalgate json-cpp library sources into a single source and header file.
Requires Python 2.6
Works with python2.6+ and python3.4+.
Example of invocation (must be invoked from json-cpp top directory):
python amalgate.py
@@ -118,7 +118,6 @@ def amalgamate_source(source_top_dir=None,
lib_json = "src/lib_json"
source.add_file(os.path.join(lib_json, "json_tool.h"))
source.add_file(os.path.join(lib_json, "json_reader.cpp"))
source.add_file(os.path.join(lib_json, "json_batchallocator.h"))
source.add_file(os.path.join(lib_json, "json_valueiterator.inl"))
source.add_file(os.path.join(lib_json, "json_value.cpp"))
source.add_file(os.path.join(lib_json, "json_writer.cpp"))

View File

@@ -24,6 +24,7 @@ build:
test-amalgamate:
python2.7 amalgamate.py
python3.4 amalgamate.py
cd dist; gcc -I. -c jsoncpp.cpp
clean:
\rm -rf *.gz *.asc dist/

View File

@@ -1946,8 +1946,7 @@ INCLUDE_FILE_PATTERNS = *.h
PREDEFINED = "_MSC_VER=1400" \
_CPPRTTI \
_WIN32 \
JSONCPP_DOC_EXCLUDE_IMPLEMENTATION \
JSON_VALUE_USE_INTERNAL_MAP
JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The

View File

@@ -96,13 +96,13 @@ features without losing binary-compatibility.
\code
// For convenience, use `writeString()` with a specialized builder.
Json::StreamWriterBuilder wbuilder;
wbuilder.settings_["indentation"] = "\t"; // simple Json::Value
wbuilder["indentation"] = "\t";
std::string document = Json::writeString(wbuilder, root);
// Here, using a specialized Builder, we discard comments and
// record errors as we parse.
Json::CharReaderBuilder rbuilder;
rbuilder.settings_["collectComments"] = false; // simple Json::Value
rbuilder["collectComments"] = false;
std::string errs;
bool ok = Json::parseFromStream(rbuilder, std::cin, &root, &errs);
\endcode

View File

@@ -1946,8 +1946,7 @@ INCLUDE_FILE_PATTERNS = *.h
PREDEFINED = "_MSC_VER=1400" \
_CPPRTTI \
_WIN32 \
JSONCPP_DOC_EXCLUDE_IMPLEMENTATION \
JSON_VALUE_USE_INTERNAL_MAP
JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The

View File

@@ -16,11 +16,15 @@
#if JSON_USE_EXCEPTION
#include <stdexcept>
#define JSON_ASSERT(condition) \
if (!(condition)) {throw std::runtime_error( "assert json failed" );} // @todo <= add detail about condition in exception
#define JSON_FAIL_MESSAGE(message) do{std::ostringstream oss; oss << message; throw std::runtime_error(oss.str());}while(0)
//#define JSON_FAIL_MESSAGE(message) throw std::runtime_error(message)
{if (!(condition)) {throw std::logic_error( "assert json failed" );}} // @todo <= add detail about condition in exception
#define JSON_FAIL_MESSAGE(message) \
{ \
std::ostringstream oss; oss << message; \
throw std::logic_error(oss.str()); \
}
//#define JSON_FAIL_MESSAGE(message) throw std::logic_error(message)
#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 abort, for a core-dump or debugger.

View File

@@ -15,17 +15,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.

View File

@@ -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

View File

@@ -285,7 +285,7 @@ Usage:
\code
using namespace Json;
CharReaderBuilder builder;
builder.settings_["collectComments"] = false;
builder["collectComments"] = false;
Value value;
std::string errs;
bool ok = parseFromStream(builder, std::cin, &value, &errs);
@@ -310,6 +310,8 @@ public:
- 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.
@@ -318,6 +320,8 @@ public:
- `"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.
You can examine 'settings_` yourself
to see the defaults. You can also write and read them just like any
@@ -335,6 +339,11 @@ public:
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](std::string key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
@@ -353,7 +362,7 @@ public:
* Someday we might have a real StreamReader, but for now this
* is convenient.
*/
bool parseFromStream(
bool JSON_API parseFromStream(
CharReader::Factory const&,
std::istream&,
Value* root, std::string* errs);

View File

@@ -74,14 +74,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,26 +99,27 @@ 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 ValueIterator iterator;
@@ -133,8 +134,8 @@ public:
typedef Json::LargestUInt LargestUInt;
typedef Json::ArrayIndex ArrayIndex;
static const Value& null; ///! We regret this reference to a global instance; prefer the simpler Value().
static const Value& nullRef; ///! just a kludge for binary-compatibility; same as null
static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value().
static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null
/// Minimum signed integer value that can be stored in a Json::Value.
static const LargestInt minLargestInt;
/// Maximum signed integer value that can be stored in a Json::Value.
@@ -160,7 +161,6 @@ public:
private:
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
#ifndef JSON_VALUE_USE_INTERNAL_MAP
class CZString {
public:
enum DuplicationPolicy {
@@ -169,20 +169,31 @@ 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);
~CZString();
CZString& operator=(CZString other);
bool operator<(const CZString& other) const;
bool operator==(const CZString& other) const;
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 {
DuplicationPolicy policy_: 2;
unsigned length_: 30; // 1GB max
};
char const* cstr_; // actually, a prefixed string, unless policy is noDup
union {
ArrayIndex index_;
StringStorage storage_;
};
};
public:
@@ -191,7 +202,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:
@@ -218,20 +228,25 @@ 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* beginValue, const char* endValue); ///< 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 std::string& value); ///< Copy data() til size(). Embedded zeroes too.
#ifdef JSON_USE_CPPTL
Value(const CppTL::ConstString& value);
#endif
@@ -240,7 +255,8 @@ Json::Value obj_value(Json::objectValue); // {}
Value(const Value& other);
~Value();
// Deep copy, then swap(other).
/// Deep copy, then swap(other).
/// \note Over-write existing comments. To preserve comments, use #swapPayload().
Value& operator=(Value other);
/// Swap everything.
void swap(Value& other);
@@ -258,8 +274,13 @@ Json::Value obj_value(Json::objectValue); // {}
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!
std::string asString() const; ///< Embedded zeroes are possible.
/** Get raw char* of string-value.
* \return false if !string. (Seg-fault if str or end are NULL.)
*/
bool getString(
char const** str, char const** end) const;
#ifdef JSON_USE_CPPTL
CppTL::ConstString asConstString() const;
#endif
@@ -350,19 +371,23 @@ Json::Value obj_value(Json::objectValue); // {}
Value& append(const Value& value);
/// Access an object value by name, create a null member if it does not exist.
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
/// Exceeding that will cause an exception.
Value& operator[](const char* key);
/// Access an object value by name, returns null if there is no member with
/// that name.
const Value& operator[](const char* key) const;
/// Access an object value by name, create a null member if it does not exist.
/// \param key may contain embedded nulls.
Value& operator[](const std::string& key);
/// Access an object value by name, returns null if there is no member with
/// that name.
/// \param key may contain embedded nulls.
const Value& operator[](const std::string& key) const;
/** \brief Access an object value by name, create a null member if it does not
exist.
* If the object 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
@@ -380,13 +405,29 @@ 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.
/// \note deep copy
/// \param key may contain embedded nulls.
Value get(const char* key, const char* end, const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \param key may contain embedded nulls.
Value get(const std::string& key, const Value& defaultValue) const;
#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-key) >= 2^30
Value const* find(char const* key, char const* end) const;
/// Most general and efficient version of object-mutators.
/// \note As stated elsewhere, behavior is undefined if (end-key) >= 2^30
/// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
Value const* demand(char const* key, char const* end);
/// \brief Remove and return the named member.
///
/// Do nothing if it did not exist.
@@ -396,14 +437,21 @@ Json::Value obj_value(Json::objectValue); // {}
/// \deprecated
Value removeMember(const char* key);
/// Same as removeMember(const char*)
/// \param key may contain embedded nulls.
/// \deprecated
Value removeMember(const std::string& key);
/// Same as removeMember(const char* key, 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(const char* key, Value* removed);
bool removeMember(std::string const& key, Value* removed);
/// Same as removeMember(std::string const& key, Value* removed)
bool removeMember(const char* key, const char* end, Value* removed);
/** \brief Remove the indexed array element.
O(n) expensive operations.
@@ -413,9 +461,13 @@ Json::Value obj_value(Json::objectValue); // {}
bool removeIndex(ArrayIndex i, Value* removed);
/// Return true if the object has a member named key.
/// \note 'key' must be null-terminated.
bool isMember(const char* key) const;
/// Return true if the object has a member named key.
/// \param key may contain embedded nulls.
bool isMember(const std::string& key) const;
/// Same as isMember(std::string const& key)const
bool isMember(const char* key, 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;
@@ -461,21 +513,9 @@ Json::Value obj_value(Json::objectValue); // {}
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();
@@ -499,20 +539,12 @@ 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;
unsigned 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.
unsigned int memberNameIsStatic_ : 1; // used by the ValueInternalMap container.
#endif
// If not allocated_, string_ must be null-terminated.
CommentInfo* comments_;
// [start, limit) byte offsets in the source JSON text from which this Value
@@ -584,345 +616,6 @@ private:
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.
*
*/
@@ -934,12 +627,7 @@ public:
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); }
@@ -958,7 +646,12 @@ public:
/// 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.
char const* memberName() const;
/// Return the member name of the referenced Value, or NULL if it is not an
/// objectValue.
/// Better version than memberName(). Allows embedded nulls.
char const* memberName(char const** end) const;
protected:
Value& deref() const;
@@ -974,17 +667,9 @@ 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
};
/** \brief const iterator for object and array value.
@@ -995,8 +680,8 @@ 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;
@@ -1006,12 +691,7 @@ public:
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);
@@ -1062,12 +742,7 @@ public:
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);

View File

@@ -4,10 +4,10 @@
#ifndef JSON_VERSION_H_INCLUDED
# define JSON_VERSION_H_INCLUDED
# define JSONCPP_VERSION_STRING "1.4.3"
# define JSONCPP_VERSION_STRING "1.5.4"
# define JSONCPP_VERSION_MAJOR 1
# define JSONCPP_VERSION_MINOR 4
# define JSONCPP_VERSION_PATCH 3
# define JSONCPP_VERSION_MINOR 5
# define JSONCPP_VERSION_PATCH 4
# define JSONCPP_VERSION_QUALIFIER
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))

View File

@@ -66,7 +66,7 @@ public:
/** \brief Write into stringstream, then return string, for convenience.
* A StreamWriter will be created from the factory, used, and then deleted.
*/
std::string writeString(StreamWriter::Factory const& factory, Value const& root);
std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root);
/** \brief Build a StreamWriter implementation.
@@ -76,8 +76,8 @@ Usage:
using namespace Json;
Value value = ...;
StreamWriterBuilder builder;
builder.settings_["commentStyle"] = "None";
builder.settings_["indentation"] = " "; // or whatever you like
builder["commentStyle"] = "None";
builder["indentation"] = " "; // or whatever you like
std::unique_ptr<Json::StreamWriter> writer(
builder.newStreamWriter());
writer->write(value, &std::cout);
@@ -119,6 +119,10 @@ public:
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](std::string key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:

View File

@@ -178,15 +178,6 @@
<File
RelativePath="..\..\include\json\json.h">
</File>
<File
RelativePath="..\..\src\lib_json\json_batchallocator.h">
</File>
<File
RelativePath="..\..\src\lib_json\json_internalarray.inl">
</File>
<File
RelativePath="..\..\src\lib_json\json_internalmap.inl">
</File>
<File
RelativePath="..\..\src\lib_json\json_reader.cpp">
</File>

View File

@@ -1,4 +1,4 @@
FIND_PACKAGE(PythonInterp 2.6 REQUIRED)
FIND_PACKAGE(PythonInterp 2.6)
IF(JSONCPP_LIB_BUILD_SHARED)
ADD_DEFINITIONS( -DJSON_DLL )
@@ -20,7 +20,7 @@ IF(PYTHONINTERP_FOUND)
# Run end to end parser/writer tests
SET(TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../test)
SET(RUNJSONTESTS_PATH ${TEST_DIR}/runjsontests.py)
ADD_CUSTOM_TARGET(jsoncpp_readerwriter_tests ALL
ADD_CUSTOM_TARGET(jsoncpp_readerwriter_tests
"${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
DEPENDS jsontestrunner_exe jsoncpp_test
)

View File

@@ -34,7 +34,6 @@ SOURCE_GROUP( "Public API" FILES ${PUBLIC_HEADERS} )
SET(jsoncpp_sources
json_tool.h
json_reader.cpp
json_batchallocator.h
json_valueiterator.inl
json_value.cpp
json_writer.cpp

View File

@@ -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

View File

@@ -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

View File

@@ -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 &current->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 = &current->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

View File

@@ -200,6 +200,8 @@ bool Reader::readValue() {
}
break;
case tokenArraySeparator:
case tokenObjectEnd:
case tokenArrayEnd:
if (features_.allowDroppedNullPlaceholders_) {
// "Un-read" the current token and mark the current value as a null
// token.
@@ -209,8 +211,7 @@ bool Reader::readValue() {
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_);
@@ -908,13 +909,14 @@ bool Reader::good() const {
class OurFeatures {
public:
static OurFeatures all();
static OurFeatures strictMode();
OurFeatures();
bool allowComments_;
bool strictRoot_;
bool allowDroppedNullPlaceholders_;
bool allowNumericKeys_;
bool allowSingleQuotes_;
bool failIfExtra_;
bool rejectDupKeys_;
int stackLimit_;
}; // OurFeatures
@@ -922,20 +924,15 @@ public:
// ////////////////////////////////
OurFeatures::OurFeatures()
: allowComments_(true), strictRoot_(false),
allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}
: allowComments_(true), strictRoot_(false)
, allowDroppedNullPlaceholders_(false), allowNumericKeys_(false)
, allowSingleQuotes_(false)
, failIfExtra_(false)
{
}
OurFeatures OurFeatures::all() { return OurFeatures(); }
OurFeatures OurFeatures::strictMode() {
OurFeatures features;
features.allowComments_ = false;
features.strictRoot_ = true;
features.allowDroppedNullPlaceholders_ = false;
features.allowNumericKeys_ = false;
return features;
}
// Implementation of class Reader
// ////////////////////////////////
@@ -1005,6 +1002,7 @@ private:
bool readCStyleComment();
bool readCppStyleComment();
bool readString();
bool readStringSingleQuote();
void readNumber();
bool readValue();
bool readObject(Token& token);
@@ -1160,6 +1158,8 @@ bool OurReader::readValue() {
}
break;
case tokenArraySeparator:
case tokenObjectEnd:
case tokenArrayEnd:
if (features_.allowDroppedNullPlaceholders_) {
// "Un-read" the current token and mark the current value as a null
// token.
@@ -1169,8 +1169,7 @@ bool OurReader::readValue() {
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_);
@@ -1218,6 +1217,12 @@ bool OurReader::readToken(Token& token) {
token.type_ = tokenString;
ok = readString();
break;
case '\'':
if (features_.allowSingleQuotes_) {
token.type_ = tokenString;
ok = readStringSingleQuote();
break;
} // else continue
case '/':
token.type_ = tokenComment;
ok = readComment();
@@ -1369,7 +1374,6 @@ void OurReader::readNumber() {
c = (current_ = p) < end_ ? *p++ : 0;
}
}
bool OurReader::readString() {
Char c = 0;
while (current_ != end_) {
@@ -1382,6 +1386,19 @@ bool OurReader::readString() {
return c == '"';
}
bool OurReader::readStringSingleQuote() {
Char c = 0;
while (current_ != end_) {
c = getNextChar();
if (c == '\\')
getNextChar();
else if (c == '\'')
break;
}
return c == '\'';
}
bool OurReader::readObject(Token& tokenStart) {
Token tokenName;
std::string name;
@@ -1414,6 +1431,12 @@ bool OurReader::readObject(Token& tokenStart) {
return addErrorAndRecover(
"Missing ':' after object member name", colon, tokenObjectEnd);
}
if (name.length() >= (1U<<30)) throw std::runtime_error("keylength >= 2^30");
if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
std::string msg = "Duplicate key: '" + name + "'";
return addErrorAndRecover(
msg, tokenName, tokenObjectEnd);
}
Value& value = currentValue()[name];
nodes_.push(&value);
bool ok = readValue();
@@ -1876,8 +1899,10 @@ CharReader* CharReaderBuilder::newCharReader() const
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();
return new OurCharReader(collectComments, features);
}
static void getValidReaderKeys(std::set<std::string>* valid_keys)
@@ -1888,15 +1913,16 @@ static void getValidReaderKeys(std::set<std::string>* valid_keys)
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");
}
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;
bool valid = true;
std::set<std::string> valid_keys;
getValidReaderKeys(&valid_keys);
Value::Members keys = settings_.getMemberNames();
@@ -1907,7 +1933,11 @@ bool CharReaderBuilder::validate(Json::Value* invalid) const
inv[key] = settings_[key];
}
}
return valid;
return 0u == inv.size();
}
Value& CharReaderBuilder::operator[](std::string key)
{
return settings_[key];
}
// static
void CharReaderBuilder::strictMode(Json::Value* settings)
@@ -1917,7 +1947,9 @@ void CharReaderBuilder::strictMode(Json::Value* settings)
(*settings)["strictRoot"] = true;
(*settings)["allowDroppedNullPlaceholders"] = false;
(*settings)["allowNumericKeys"] = false;
(*settings)["allowSingleQuotes"] = false;
(*settings)["failIfExtra"] = true;
(*settings)["rejectDupKeys"] = true;
//! [CharReaderBuilderStrictMode]
}
// static
@@ -1929,8 +1961,10 @@ void CharReaderBuilder::setDefaults(Json::Value* settings)
(*settings)["strictRoot"] = false;
(*settings)["allowDroppedNullPlaceholders"] = false;
(*settings)["allowNumericKeys"] = false;
(*settings)["allowSingleQuotes"] = false;
(*settings)["stackLimit"] = 1000;
(*settings)["failIfExtra"] = false;
(*settings)["rejectDupKeys"] = false;
//! [CharReaderBuilderDefaults]
}

View File

@@ -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)
@@ -54,9 +52,6 @@ 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) {
@@ -85,13 +80,10 @@ 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 >= (size_t)Value::maxInt)
length = Value::maxInt - 1;
char* newString = static_cast<char*>(malloc(length + 1));
@@ -103,7 +95,40 @@ static inline char* duplicateStringValue(const char* value,
return newString;
}
/** Free the string duplicated by duplicateStringValue().
/* Record the length as a prefix.
*/
static inline char* duplicateAndPrefixStringValue(
const char* value,
unsigned int length)
{
// Avoid an integer overflow in the call to malloc below by limiting length
// to a sane value.
JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U,
"in Json::Value::duplicateAndPrefixStringValue(): "
"length too big for prefixing");
unsigned actualLength = length + sizeof(unsigned) + 1U;
char* newString = static_cast<char*>(malloc(actualLength));
JSON_ASSERT_MESSAGE(newString != 0,
"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 = strlen(prefixed);
*value = prefixed;
} else {
*length = *reinterpret_cast<unsigned const*>(prefixed);
*value = prefixed + sizeof(unsigned);
}
}
/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue().
*/
static inline void releaseStringValue(char* value) { free(value); }
@@ -117,10 +142,6 @@ 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)
@@ -162,28 +183,34 @@ void Value::CommentInfo::setComment(const char* text, size_t len) {
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
#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(const char* cstr, DuplicationPolicy allocate)
: cstr_(allocate == duplicate ? duplicateStringValue(cstr) : cstr),
index_(allocate) {}
Value::CZString::CZString(char const* str, unsigned length, DuplicationPolicy allocate)
: cstr_(str)
{
// allocate != duplicate
storage_.policy_ = allocate;
storage_.length_ = length;
}
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
: cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0
? duplicateStringValue(other.cstr_, other.storage_.length_)
: other.cstr_)
{
storage_.policy_ = (other.cstr_
? (other.storage_.policy_ == noDuplication
? noDuplication : duplicate)
: other.index_) {}
: other.storage_.policy_);
storage_.length_ = other.storage_.length_;
}
Value::CZString::~CZString() {
if (cstr_ && index_ == duplicate)
if (cstr_ && storage_.policy_ == duplicate)
releaseStringValue(const_cast<char*>(cstr_));
}
@@ -198,24 +225,35 @@ Value::CZString& Value::CZString::operator=(CZString other) {
}
bool Value::CZString::operator<(const CZString& other) const {
if (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(this_len, other_len);
int comp = memcmp(this->cstr_, other.cstr_, min_len);
if (comp < 0) return true;
if (comp > 0) return false;
return (this_len < other_len);
}
bool Value::CZString::operator==(const CZString& other) const {
if (cstr_)
return 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;
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; }
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
@@ -244,19 +282,10 @@ Value::Value(ValueType type) {
case stringValue:
value_.string_ = 0;
break;
#ifndef JSON_VALUE_USE_INTERNAL_MAP
case arrayValue:
case objectValue:
value_.map_ = new ObjectValues();
break;
#else
case arrayValue:
value_.array_ = arrayAllocator()->newArray();
break;
case objectValue:
value_.map_ = mapAllocator()->newMap();
break;
#endif
case booleanValue:
value_.bool_ = false;
break;
@@ -292,19 +321,19 @@ Value::Value(double value) {
Value::Value(const char* value) {
initBasic(stringValue, true);
value_.string_ = duplicateStringValue(value);
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) {
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) {
@@ -315,7 +344,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
@@ -324,14 +353,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:
@@ -341,27 +367,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;
}
@@ -388,19 +409,10 @@ Value::~Value() {
if (allocated_)
releaseStringValue(value_.string_);
break;
#ifndef JSON_VALUE_USE_INTERNAL_MAP
case arrayValue:
case objectValue:
delete value_.map_;
break;
#else
case arrayValue:
arrayAllocator()->destructArray(value_.array_);
break;
case objectValue:
mapAllocator()->destructMap(value_.map_);
break;
#endif
default:
JSON_ASSERT_UNREACHABLE;
}
@@ -457,10 +469,23 @@ 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(this_len, other_len);
int comp = memcmp(this_str, other_str, min_len);
if (comp < 0) return true;
if (comp > 0) return false;
return (this_len < other_len);
}
case arrayValue:
case objectValue: {
int delta = int(value_.map_->size() - other.value_.map_->size());
@@ -468,12 +493,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;
}
@@ -506,20 +525,24 @@ 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;
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;
}
@@ -531,7 +554,20 @@ 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;
}
bool Value::getString(char const** str, char const** end) const {
if (type_ != stringValue) return false;
if (value_.string_ == 0) return false;
unsigned length;
decodePrefixedString(this->allocated_, this->value_.string_, &length, str);
*end = *str + length;
return true;
}
std::string Value::asString() const {
@@ -539,7 +575,13 @@ std::string Value::asString() const {
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 std::string(this_str, this_len);
}
case booleanValue:
return value_.bool_ ? "true" : "false";
case intValue:
@@ -555,7 +597,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
@@ -769,7 +815,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();
@@ -779,12 +824,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;
@@ -806,19 +845,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;
}
@@ -829,7 +859,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();
@@ -841,9 +870,6 @@ void Value::resize(ArrayIndex newSize) {
}
assert(size() == newSize);
}
#else
value_.array_->resize(newSize);
#endif
}
Value& Value::operator[](ArrayIndex index) {
@@ -852,18 +878,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, nullRef);
it = value_.map_->insert(it, defaultValue);
return (*it).second;
#else
return value_.array_->resolveReference(index);
#endif
}
Value& Value::operator[](int index) {
@@ -878,17 +900,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 nullRef;
CZString key(index);
ObjectValues::const_iterator it = value_.map_->find(key);
if (it == value_.map_->end())
return null;
return nullRef;
return (*it).second;
#else
Value* value = value_.array_->find(index);
return value ? *value : null;
#endif
}
const Value& Value::operator[](int index) const {
@@ -898,138 +915,161 @@ 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;
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, nullRef);
it = value_.map_->insert(it, defaultValue);
Value& value = (*it).second;
return value;
}
// @param key is not null-terminated.
Value& Value::resolveReference(char const* key, char const* end)
{
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>(end-key), CZString::duplicateOnCopy);
ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
if (it != value_.map_->end() && (*it).first == actualKey)
return (*it).second;
ObjectValues::value_type defaultValue(actualKey, nullRef);
it = value_.map_->insert(it, defaultValue);
Value& value = (*it).second;
return value;
#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 == &nullRef ? 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* end) 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>(end-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 nullRef;
return *found;
}
Value const& Value::operator[](std::string const& key) const
{
Value const* found = find(key.data(), key.data() + key.length());
if (!found) return nullRef;
return *found;
}
Value& Value::operator[](const char* key) {
return resolveReference(key, key + strlen(key));
}
Value& Value::operator[](const std::string& key) {
return (*this)[key.c_str()];
}
const Value& Value::operator[](const std::string& key) const {
return (*this)[key.c_str()];
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 nullRef;
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;
Value Value::get(char const* key, char const* end, Value const& defaultValue) const
{
Value const* found = find(key, end);
return !found ? defaultValue : *found;
}
Value Value::get(const std::string& key, const Value& defaultValue) const {
return get(key.c_str(), defaultValue);
Value Value::get(char const* key, Value const& defaultValue) const
{
return get(key, key + strlen(key), defaultValue);
}
Value Value::get(std::string const& key, Value const& defaultValue) const
{
return get(key.data(), key.data() + key.length(), defaultValue);
}
bool Value::removeMember(const char* key, Value* removed) {
bool Value::removeMember(const char* key, const char* end, Value* removed)
{
if (type_ != objectValue) {
return false;
}
#ifndef JSON_VALUE_USE_INTERNAL_MAP
CZString actualKey(key, CZString::noDuplication);
CZString actualKey(key, static_cast<unsigned>(end-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;
#else
Value* value = value_.map_->find(key);
if (value) {
*removed = *value;
value_.map_.remove(key);
return true;
} else {
return false;
}
#endif
}
Value Value::removeMember(const char* key) {
bool Value::removeMember(const char* key, Value* removed)
{
return removeMember(key, key + strlen(key), removed);
}
bool Value::removeMember(std::string const& key, Value* removed)
{
return removeMember(key.data(), key.data() + key.length(), removed);
}
Value Value::removeMember(const char* key)
{
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
"in Json::Value::removeMember(): requires objectValue");
if (type_ == nullValue)
return null;
return nullRef;
Value removed; // null
removeMember(key, &removed);
removeMember(key, key + strlen(key), &removed);
return removed; // still null if removeMember() did nothing
}
Value Value::removeMember(const std::string& key) {
Value Value::removeMember(const std::string& key)
{
return removeMember(key.c_str());
}
@@ -1037,10 +1077,6 @@ bool Value::removeIndex(ArrayIndex index, Value* removed) {
if (type_ != arrayValue) {
return false;
}
#ifdef JSON_VALUE_USE_INTERNAL_MAP
JSON_FAIL_MESSAGE("removeIndex is not implemented for ValueInternalArray.");
return false;
#else
CZString key(index);
ObjectValues::iterator it = value_.map_->find(key);
if (it == value_.map_->end()) {
@@ -1058,28 +1094,32 @@ bool Value::removeIndex(ArrayIndex index, Value* removed) {
ObjectValues::iterator itLast = value_.map_->find(keyLast);
value_.map_->erase(itLast);
return true;
#endif
}
#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* end) const
{
Value const* value = find(key, end);
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(std::string const& key) const
{
return isMember(key.data(), key.data() + key.length());
}
#ifdef JSON_USE_CPPTL
bool Value::isMember(const CppTL::ConstString& key) const {
return isMember(key.c_str());
return isMember(key.c_str(), key.end_c_str());
}
#endif
@@ -1091,19 +1131,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(std::string((*it).first.data(),
(*it).first.length()));
}
return members;
}
//
@@ -1272,28 +1305,11 @@ std::string Value::toStyledString() const {
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;
}
@@ -1302,28 +1318,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;
}
@@ -1332,28 +1331,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;
}
@@ -1362,28 +1344,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;
}
@@ -1484,7 +1449,7 @@ const Value& Path::resolve(const Value& root) const {
// Error: unable to resolve path (object value expected at position...)
}
node = &((*node)[arg.key_]);
if (node == &Value::null) {
if (node == &Value::nullRef) {
// Error: unable to resolve path (object has no member named '' at
// position...)
}
@@ -1505,7 +1470,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::nullRef)
return defaultValue;
}
}

View File

@@ -16,66 +16,27 @@ 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 other.current_ - current_;
#else
@@ -100,80 +61,50 @@ 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();
char const* ValueIteratorBase::memberName() const {
const char* name = (*current_).first.data();
return name ? name : "";
#else
if (!isArray_)
return ValueInternalMap::key(iterator_.map_);
return "";
#endif
}
char const* ValueIteratorBase::memberName(char const** end) const {
const char* name = (*current_).first.data();
if (!name) {
*end = NULL;
return NULL;
}
*end = name + (*current_).first.length();
return name;
}
// //////////////////////////////////////////////////////////////////
@@ -186,19 +117,9 @@ 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::
operator=(const ValueIteratorBase& other) {
@@ -216,16 +137,8 @@ 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) {}

View File

@@ -50,6 +50,16 @@ static bool containsControlCharacter(const char* str) {
return false;
}
static bool containsControlCharacter0(const char* str, unsigned len) {
char const* end = str + len;
while (end != str) {
if (isControlCharacter(*str) || 0==*str)
return true;
++str;
}
return false;
}
std::string valueToString(LargestInt value) {
UIntToStringBuffer buffer;
char* current = buffer + sizeof(buffer);
@@ -184,6 +194,84 @@ std::string valueToQuotedString(const char* value) {
return result;
}
// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
static char const* strnpbrk(char const* s, char const* accept, size_t n) {
assert((s || !n) && accept);
char const* const end = s + n;
for (char const* cur = s; cur < end; ++cur) {
int const c = *cur;
for (char const* a = accept; *a; ++a) {
if (*a == c) {
return cur;
}
}
}
return NULL;
}
static std::string valueToQuotedStringN(const char* value, unsigned length) {
if (value == NULL)
return "";
// Not sure how to handle unicode...
if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
!containsControlCharacter0(value, length))
return std::string("\"") + value + "\"";
// We have to walk value and escape any special characters.
// Appending to std::string is not efficient, but this should be rare.
// (Note: forward slashes are *not* rare, but I am not escaping them.)
std::string::size_type maxsize =
length * 2 + 3; // allescaped+quotes+NULL
std::string result;
result.reserve(maxsize); // to avoid lots of mallocs
result += "\"";
char const* end = value + length;
for (const char* c = value; c != end; ++c) {
switch (*c) {
case '\"':
result += "\\\"";
break;
case '\\':
result += "\\\\";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
// case '/':
// Even though \/ is considered a legal escape in JSON, a bare
// slash is also legal, so I see no reason to escape it.
// (I hope I am not misunderstanding something.)
// blep notes: actually escaping \/ may be useful in javascript to avoid </
// sequence.
// Should add a flag to allow this compatibility mode and prevent this
// sequence from occurring.
default:
if ((isControlCharacter(*c)) || (*c == 0)) {
std::ostringstream oss;
oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
<< std::setw(4) << static_cast<int>(*c);
result += oss.str();
} else {
result += *c;
}
break;
}
}
result += "\"";
return result;
}
// Class Writer
// //////////////////////////////////////////////////////////////////
Writer::~Writer() {}
@@ -248,7 +336,7 @@ void FastWriter::writeValue(const Value& value) {
const std::string& name = *it;
if (it != members.begin())
document_ += ',';
document_ += valueToQuotedString(name.c_str());
document_ += valueToQuotedStringN(name.data(), name.length());
document_ += yamlCompatiblityEnabled_ ? ": " : ":";
writeValue(value[name]);
}
@@ -289,8 +377,15 @@ void StyledWriter::writeValue(const Value& value) {
pushValue(valueToString(value.asDouble()));
break;
case stringValue:
pushValue(valueToQuotedString(value.asCString()));
{
// Is NULL is possible for value.string_?
char const* str;
char const* end;
bool ok = value.getString(&str, &end);
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
else pushValue("");
break;
}
case booleanValue:
pushValue(valueToString(value.asBool()));
break;
@@ -767,8 +862,15 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
pushValue(valueToString(value.asDouble()));
break;
case stringValue:
pushValue(valueToQuotedString(value.asCString()));
{
// Is NULL is possible for value.string_?
char const* str;
char const* end;
bool ok = value.getString(&str, &end);
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
else pushValue("");
break;
}
case booleanValue:
pushValue(valueToString(value.asBool()));
break;
@@ -787,7 +889,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
std::string const& name = *it;
Value const& childValue = value[name];
writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedString(name.c_str()));
writeWithIndent(valueToQuotedStringN(name.data(), name.length()));
*sout_ << colonSymbol_;
writeValue(childValue);
if (++it == members.end()) {
@@ -1008,7 +1110,6 @@ 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;
bool valid = true;
std::set<std::string> valid_keys;
getValidWriterKeys(&valid_keys);
Value::Members keys = settings_.getMemberNames();
@@ -1019,7 +1120,11 @@ bool StreamWriterBuilder::validate(Json::Value* invalid) const
inv[key] = settings_[key];
}
}
return valid;
return 0u == inv.size();
}
Value& StreamWriterBuilder::operator[](std::string key)
{
return settings_[key];
}
// static
void StreamWriterBuilder::setDefaults(Json::Value* settings)

View File

@@ -1,3 +1,4 @@
# vim: et ts=4 sts=4 sw=4 tw=0
IF(JSONCPP_LIB_BUILD_SHARED)
ADD_DEFINITIONS( -DJSON_DLL )
@@ -22,9 +23,19 @@ ENDIF(JSONCPP_LIB_BUILD_SHARED)
# Run unit tests in post-build
# (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?)
IF(JSONCPP_WITH_POST_BUILD_UNITTEST)
ADD_CUSTOM_COMMAND( TARGET jsoncpp_test
POST_BUILD
COMMAND $<TARGET_FILE:jsoncpp_test>)
IF(JSONCPP_LIB_BUILD_SHARED)
# First, copy the shared lib, for Microsoft.
# Then, run the test executable.
ADD_CUSTOM_COMMAND( TARGET jsoncpp_test
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:jsoncpp_lib> $<TARGET_FILE_DIR:jsoncpp_test>
COMMAND $<TARGET_FILE:jsoncpp_test>)
ELSE(JSONCPP_LIB_BUILD_SHARED)
# Just run the test executable.
ADD_CUSTOM_COMMAND( TARGET jsoncpp_test
POST_BUILD
COMMAND $<TARGET_FILE:jsoncpp_test>)
ENDIF(JSONCPP_LIB_BUILD_SHARED)
ENDIF(JSONCPP_WITH_POST_BUILD_UNITTEST)
SET_TARGET_PROPERTIES(jsoncpp_test PROPERTIES OUTPUT_NAME jsoncpp_test)

View File

@@ -282,6 +282,8 @@ JSONTEST_FIXTURE(ValueTest, null) {
JSONTEST_ASSERT_EQUAL(0.0, null_.asDouble());
JSONTEST_ASSERT_EQUAL(0.0, null_.asFloat());
JSONTEST_ASSERT_STRING_EQUAL("", null_.asString());
JSONTEST_ASSERT_EQUAL(Json::Value::null, null_);
}
JSONTEST_FIXTURE(ValueTest, strings) {
@@ -1516,6 +1518,126 @@ JSONTEST_FIXTURE(ValueTest, offsetAccessors) {
JSONTEST_ASSERT(y.getOffsetLimit() == 0);
}
JSONTEST_FIXTURE(ValueTest, StaticString) {
char mutant[] = "hello";
Json::StaticString ss(mutant);
std::string regular(mutant);
mutant[1] = 'a';
JSONTEST_ASSERT_STRING_EQUAL("hallo", ss.c_str());
JSONTEST_ASSERT_STRING_EQUAL("hello", regular.c_str());
{
Json::Value root;
root["top"] = ss;
JSONTEST_ASSERT_STRING_EQUAL("hallo", root["top"].asString());
mutant[1] = 'u';
JSONTEST_ASSERT_STRING_EQUAL("hullo", root["top"].asString());
}
{
Json::Value root;
root["top"] = regular;
JSONTEST_ASSERT_STRING_EQUAL("hello", root["top"].asString());
mutant[1] = 'u';
JSONTEST_ASSERT_STRING_EQUAL("hello", root["top"].asString());
}
}
JSONTEST_FIXTURE(ValueTest, CommentBefore) {
Json::Value val; // fill val
val.setComment("// this comment should appear before", Json::commentBefore);
Json::StreamWriterBuilder wbuilder;
wbuilder.settings_["commentStyle"] = "All";
{
char const expected[] = "// this comment should appear before\nnull";
std::string result = Json::writeString(wbuilder, val);
JSONTEST_ASSERT_STRING_EQUAL(expected, result);
std::string res2 = val.toStyledString();
std::string exp2 = "\n";
exp2 += expected;
exp2 += "\n";
JSONTEST_ASSERT_STRING_EQUAL(exp2, res2);
}
Json::Value other = "hello";
val.swapPayload(other);
{
char const expected[] = "// this comment should appear before\n\"hello\"";
std::string result = Json::writeString(wbuilder, val);
JSONTEST_ASSERT_STRING_EQUAL(expected, result);
std::string res2 = val.toStyledString();
std::string exp2 = "\n";
exp2 += expected;
exp2 += "\n";
JSONTEST_ASSERT_STRING_EQUAL(exp2, res2);
JSONTEST_ASSERT_STRING_EQUAL("null\n", other.toStyledString());
}
val = "hello";
// val.setComment("// this comment should appear before", Json::CommentPlacement::commentBefore);
// Assignment over-writes comments.
{
char const expected[] = "\"hello\"";
std::string result = Json::writeString(wbuilder, val);
JSONTEST_ASSERT_STRING_EQUAL(expected, result);
std::string res2 = val.toStyledString();
std::string exp2 = "";
exp2 += expected;
exp2 += "\n";
JSONTEST_ASSERT_STRING_EQUAL(exp2, res2);
}
}
JSONTEST_FIXTURE(ValueTest, zeroes) {
char const cstr[] = "h\0i";
std::string binary(cstr, sizeof(cstr)); // include trailing 0
JSONTEST_ASSERT_EQUAL(4U, binary.length());
Json::StreamWriterBuilder b;
{
Json::Value root;
root = binary;
JSONTEST_ASSERT_STRING_EQUAL(binary, root.asString());
}
{
char const top[] = "top";
Json::Value root;
root[top] = binary;
JSONTEST_ASSERT_STRING_EQUAL(binary, root[top].asString());
Json::Value removed;
bool did;
did = root.removeMember(top, top + sizeof(top) - 1U,
&removed);
JSONTEST_ASSERT(did);
JSONTEST_ASSERT_STRING_EQUAL(binary, removed.asString());
did = root.removeMember(top, top + sizeof(top) - 1U,
&removed);
JSONTEST_ASSERT(!did);
JSONTEST_ASSERT_STRING_EQUAL(binary, removed.asString()); // still
}
}
JSONTEST_FIXTURE(ValueTest, zeroesInKeys) {
char const cstr[] = "h\0i";
std::string binary(cstr, sizeof(cstr)); // include trailing 0
JSONTEST_ASSERT_EQUAL(4U, binary.length());
{
Json::Value root;
root[binary] = "there";
JSONTEST_ASSERT_STRING_EQUAL("there", root[binary].asString());
JSONTEST_ASSERT(!root.isMember("h"));
JSONTEST_ASSERT(root.isMember(binary));
JSONTEST_ASSERT_STRING_EQUAL("there", root.get(binary, Json::Value::nullRef).asString());
Json::Value removed;
bool did;
did = root.removeMember(binary.data(), binary.data() + binary.length(),
&removed);
JSONTEST_ASSERT(did);
JSONTEST_ASSERT_STRING_EQUAL("there", removed.asString());
did = root.removeMember(binary.data(), binary.data() + binary.length(),
&removed);
JSONTEST_ASSERT(!did);
JSONTEST_ASSERT_STRING_EQUAL("there", removed.asString()); // still
JSONTEST_ASSERT(!root.isMember(binary));
JSONTEST_ASSERT_STRING_EQUAL("", root.get(binary, Json::Value::nullRef).asString());
}
}
struct WriterTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) {
@@ -1538,6 +1660,28 @@ JSONTEST_FIXTURE(StreamWriterTest, dropNullPlaceholders) {
JSONTEST_ASSERT(Json::writeString(b, nullValue) == "");
}
JSONTEST_FIXTURE(StreamWriterTest, writeZeroes) {
std::string binary("hi", 3); // include trailing 0
JSONTEST_ASSERT_EQUAL(3, binary.length());
std::string expected("\"hi\\u0000\""); // unicoded zero
Json::StreamWriterBuilder b;
{
Json::Value root;
root = binary;
JSONTEST_ASSERT_STRING_EQUAL(binary, root.asString());
std::string out = Json::writeString(b, root);
JSONTEST_ASSERT_EQUAL(expected.size(), out.size());
JSONTEST_ASSERT_STRING_EQUAL(expected, out);
}
{
Json::Value root;
root["top"] = binary;
JSONTEST_ASSERT_STRING_EQUAL(binary, root["top"].asString());
std::string out = Json::writeString(b, root["top"]);
JSONTEST_ASSERT_STRING_EQUAL(expected, out);
}
}
struct ReaderTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) {
@@ -1741,6 +1885,29 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) {
}
}
struct CharReaderStrictModeTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(CharReaderStrictModeTest, dupKeys) {
Json::CharReaderBuilder b;
Json::Value root;
char const doc[] =
"{ \"property\" : \"value\", \"key\" : \"val1\", \"key\" : \"val2\" }";
{
b.strictMode(&b.settings_);
Json::CharReader* reader(b.newCharReader());
std::string errs;
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(!ok);
JSONTEST_ASSERT_STRING_EQUAL(
"* Line 1, Column 41\n"
" Duplicate key: 'key'\n",
errs);
JSONTEST_ASSERT_EQUAL("val1", root["key"]); // so far
delete reader;
}
}
struct CharReaderFailIfExtraTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) {
@@ -1861,6 +2028,244 @@ JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) {
JSONTEST_ASSERT_EQUAL(true, root.asBool());
delete reader;
}
struct CharReaderAllowDropNullTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(CharReaderAllowDropNullTest, issue178) {
Json::CharReaderBuilder b;
b.settings_["allowDroppedNullPlaceholders"] = true;
Json::Value root;
std::string errs;
Json::CharReader* reader(b.newCharReader());
{
char const doc[] = "{\"a\":,\"b\":true}";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
JSONTEST_ASSERT_EQUAL(Json::nullValue, root.get("a", true));
}
{
char const doc[] = "{\"a\":}";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(1u, root.size());
JSONTEST_ASSERT_EQUAL(Json::nullValue, root.get("a", true));
}
{
char const doc[] = "[]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(0u, root.size());
JSONTEST_ASSERT_EQUAL(Json::arrayValue, root);
}
{
char const doc[] = "[null]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(1u, root.size());
}
{
char const doc[] = "[,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
}
{
char const doc[] = "[,,,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(4u, root.size());
}
{
char const doc[] = "[null,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
}
{
char const doc[] = "[,null]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(2u, root.size());
}
{
char const doc[] = "[,,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(3u, root.size());
}
{
char const doc[] = "[null,,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(3u, root.size());
}
{
char const doc[] = "[,null,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(3u, root.size());
}
{
char const doc[] = "[,,null]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(3u, root.size());
}
{
char const doc[] = "[[],,,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(4u, root.size());
JSONTEST_ASSERT_EQUAL(Json::arrayValue, root[0u]);
}
{
char const doc[] = "[,[],,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(4u, root.size());
JSONTEST_ASSERT_EQUAL(Json::arrayValue, root[1u]);
}
{
char const doc[] = "[,,,[]]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(4u, root.size());
JSONTEST_ASSERT_EQUAL(Json::arrayValue, root[3u]);
}
delete reader;
}
struct CharReaderAllowSingleQuotesTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(CharReaderAllowSingleQuotesTest, issue182) {
Json::CharReaderBuilder b;
b.settings_["allowSingleQuotes"] = true;
Json::Value root;
std::string errs;
Json::CharReader* reader(b.newCharReader());
{
char const doc[] = "{'a':true,\"b\":true}";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
JSONTEST_ASSERT_EQUAL(true, root.get("a", false));
JSONTEST_ASSERT_EQUAL(true, root.get("b", false));
}
{
char const doc[] = "{'a': 'x', \"b\":'y'}";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
JSONTEST_ASSERT_STRING_EQUAL("x", root["a"].asString());
JSONTEST_ASSERT_STRING_EQUAL("y", root["b"].asString());
}
}
struct CharReaderAllowZeroesTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(CharReaderAllowZeroesTest, issue176) {
Json::CharReaderBuilder b;
b.settings_["allowSingleQuotes"] = true;
Json::Value root;
std::string errs;
Json::CharReader* reader(b.newCharReader());
{
char const doc[] = "{'a':true,\"b\":true}";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
JSONTEST_ASSERT_EQUAL(true, root.get("a", false));
JSONTEST_ASSERT_EQUAL(true, root.get("b", false));
}
{
char const doc[] = "{'a': 'x', \"b\":'y'}";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
JSONTEST_ASSERT_STRING_EQUAL("x", root["a"].asString());
JSONTEST_ASSERT_STRING_EQUAL("y", root["b"].asString());
}
}
struct BuilderTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(BuilderTest, settings) {
{
Json::Value errs;
Json::CharReaderBuilder rb;
JSONTEST_ASSERT_EQUAL(false, rb.settings_.isMember("foo"));
JSONTEST_ASSERT_EQUAL(true, rb.validate(&errs));
rb["foo"] = "bar";
JSONTEST_ASSERT_EQUAL(true, rb.settings_.isMember("foo"));
JSONTEST_ASSERT_EQUAL(false, rb.validate(&errs));
}
{
Json::Value errs;
Json::StreamWriterBuilder wb;
JSONTEST_ASSERT_EQUAL(false, wb.settings_.isMember("foo"));
JSONTEST_ASSERT_EQUAL(true, wb.validate(&errs));
wb["foo"] = "bar";
JSONTEST_ASSERT_EQUAL(true, wb.settings_.isMember("foo"));
JSONTEST_ASSERT_EQUAL(false, wb.validate(&errs));
}
}
struct IteratorTest : JsonTest::TestCase {};
@@ -1868,7 +2273,7 @@ JSONTEST_FIXTURE(IteratorTest, distance) {
Json::Value json;
json["k1"] = "a";
json["k2"] = "b";
int dist;
int dist = 0;
std::string str;
for (Json::ValueIterator it = json.begin(); it != json.end(); ++it) {
dist = it - json.begin();
@@ -1900,9 +2305,15 @@ int main(int argc, const char* argv[]) {
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareType);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, offsetAccessors);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, typeChecksThrowExceptions);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, StaticString);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, CommentBefore);
//JSONTEST_REGISTER_FIXTURE(runner, ValueTest, nulls);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, zeroes);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, zeroesInKeys);
JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, writeZeroes);
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithNoErrors);
JSONTEST_REGISTER_FIXTURE(
@@ -1919,12 +2330,22 @@ int main(int argc, const char* argv[]) {
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithDetailError);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithStackLimit);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderStrictModeTest, dupKeys);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue164);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue107);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterObject);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterArray);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterBool);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowDropNullTest, issue178);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowSingleQuotesTest, issue182);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowZeroesTest, issue176);
JSONTEST_REGISTER_FIXTURE(runner, BuilderTest, settings);
JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, distance);
return runner.runCommandLine(argc, argv);

View File

@@ -1 +1 @@
1.4.3
1.5.4

1
version.in Normal file
View File

@@ -0,0 +1 @@
@JSONCPP_VERSION@