diff options
Diffstat (limited to '3rdparty/jsoncpp/src')
-rw-r--r-- | 3rdparty/jsoncpp/src/jsontestrunner/CMakeLists.txt | 17 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/jsontestrunner/main.cpp | 186 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/lib_json/CMakeLists.txt | 80 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/lib_json/json_batchallocator.h | 121 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/lib_json/json_internalarray.inl | 360 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/lib_json/json_internalmap.inl | 473 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/lib_json/json_reader.cpp | 1272 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/lib_json/json_value.cpp | 662 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/lib_json/json_valueiterator.inl | 129 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/lib_json/json_writer.cpp | 642 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/test_lib_json/CMakeLists.txt | 32 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/test_lib_json/jsontest.cpp | 2 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/test_lib_json/jsontest.h | 6 | ||||
-rw-r--r-- | 3rdparty/jsoncpp/src/test_lib_json/main.cpp | 777 |
14 files changed, 3107 insertions, 1652 deletions
diff --git a/3rdparty/jsoncpp/src/jsontestrunner/CMakeLists.txt b/3rdparty/jsoncpp/src/jsontestrunner/CMakeLists.txt index dd8e2175f0f..820a7cd3524 100644 --- a/3rdparty/jsoncpp/src/jsontestrunner/CMakeLists.txt +++ b/3rdparty/jsoncpp/src/jsontestrunner/CMakeLists.txt @@ -1,20 +1,23 @@ -FIND_PACKAGE(PythonInterp 2.6 REQUIRED) - -IF(JSONCPP_LIB_BUILD_SHARED) - ADD_DEFINITIONS( -DJSON_DLL ) -ENDIF(JSONCPP_LIB_BUILD_SHARED) +FIND_PACKAGE(PythonInterp 2.6) ADD_EXECUTABLE(jsontestrunner_exe main.cpp ) -TARGET_LINK_LIBRARIES(jsontestrunner_exe jsoncpp_lib) + +IF(BUILD_SHARED_LIBS) + ADD_DEFINITIONS( -DJSON_DLL ) + TARGET_LINK_LIBRARIES(jsontestrunner_exe jsoncpp_lib) +ELSE(BUILD_SHARED_LIBS) + TARGET_LINK_LIBRARIES(jsontestrunner_exe jsoncpp_lib_static) +ENDIF(BUILD_SHARED_LIBS) + SET_TARGET_PROPERTIES(jsontestrunner_exe PROPERTIES OUTPUT_NAME jsontestrunner_exe) 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 ) diff --git a/3rdparty/jsoncpp/src/jsontestrunner/main.cpp b/3rdparty/jsoncpp/src/jsontestrunner/main.cpp index ba985877db4..1ec1fb6fbcd 100644 --- a/3rdparty/jsoncpp/src/jsontestrunner/main.cpp +++ b/3rdparty/jsoncpp/src/jsontestrunner/main.cpp @@ -8,12 +8,22 @@ #include <json/json.h> #include <algorithm> // sort +#include <sstream> #include <stdio.h> #if defined(_MSC_VER) && _MSC_VER >= 1310 #pragma warning(disable : 4996) // disable fopen deprecation warning #endif +struct Options +{ + std::string path; + Json::Features features; + bool parseOnly; + typedef std::string (*writeFuncType)(Json::Value const&); + writeFuncType write; +}; + static std::string normalizeFloatingPointStr(double value) { char buffer[32]; #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) @@ -129,43 +139,67 @@ printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { static int parseAndSaveValueTree(const std::string& input, const std::string& actual, const std::string& kind, - Json::Value& root, const Json::Features& features, - bool parseOnly) { + bool parseOnly, + Json::Value* root) +{ Json::Reader reader(features); - bool parsingSuccessful = reader.parse(input, root); + bool parsingSuccessful = reader.parse(input, *root); if (!parsingSuccessful) { printf("Failed to parse %s file: \n%s\n", kind.c_str(), reader.getFormattedErrorMessages().c_str()); return 1; } - if (!parseOnly) { FILE* factual = fopen(actual.c_str(), "wt"); if (!factual) { printf("Failed to create %s actual file.\n", kind.c_str()); return 2; } - printValueTree(factual, root); + printValueTree(factual, *root); fclose(factual); } return 0; } - -static int rewriteValueTree(const std::string& rewritePath, - const Json::Value& root, - std::string& rewrite) { - // Json::FastWriter writer; - // writer.enableYAMLCompatibility(); +// static std::string useFastWriter(Json::Value const& root) { +// Json::FastWriter writer; +// writer.enableYAMLCompatibility(); +// return writer.write(root); +// } +static std::string useStyledWriter( + Json::Value const& root) +{ Json::StyledWriter writer; - rewrite = writer.write(root); + return writer.write(root); +} +static std::string useStyledStreamWriter( + Json::Value const& root) +{ + Json::StyledStreamWriter writer; + std::ostringstream sout; + writer.write(sout, root); + return sout.str(); +} +static std::string useBuiltStyledStreamWriter( + Json::Value const& root) +{ + Json::StreamWriterBuilder builder; + return Json::writeString(builder, root); +} +static int rewriteValueTree( + const std::string& rewritePath, + const Json::Value& root, + Options::writeFuncType write, + std::string* rewrite) +{ + *rewrite = write(root); FILE* fout = fopen(rewritePath.c_str(), "wt"); if (!fout) { printf("Failed to create rewrite file: %s\n", rewritePath.c_str()); return 2; } - fprintf(fout, "%s\n", rewrite.c_str()); + fprintf(fout, "%s\n", rewrite->c_str()); fclose(fout); return 0; } @@ -194,84 +228,98 @@ static int printUsage(const char* argv[]) { return 3; } -int parseCommandLine(int argc, - const char* argv[], - Json::Features& features, - std::string& path, - bool& parseOnly) { - parseOnly = false; +static int parseCommandLine( + int argc, const char* argv[], Options* opts) +{ + opts->parseOnly = false; + opts->write = &useStyledWriter; if (argc < 2) { return printUsage(argv); } - int index = 1; - if (std::string(argv[1]) == "--json-checker") { - features = Json::Features::strictMode(); - parseOnly = true; + if (std::string(argv[index]) == "--json-checker") { + opts->features = Json::Features::strictMode(); + opts->parseOnly = true; ++index; } - - if (std::string(argv[1]) == "--json-config") { + if (std::string(argv[index]) == "--json-config") { printConfig(); return 3; } - + if (std::string(argv[index]) == "--json-writer") { + ++index; + std::string const writerName(argv[index++]); + if (writerName == "StyledWriter") { + opts->write = &useStyledWriter; + } else if (writerName == "StyledStreamWriter") { + opts->write = &useStyledStreamWriter; + } else if (writerName == "BuiltStyledStreamWriter") { + opts->write = &useBuiltStyledStreamWriter; + } else { + printf("Unknown '--json-writer %s'\n", writerName.c_str()); + return 4; + } + } if (index == argc || index + 1 < argc) { return printUsage(argv); } - - path = argv[index]; + opts->path = argv[index]; return 0; } +static int runTest(Options const& opts) +{ + int exitCode = 0; + + std::string input = readInputTestFile(opts.path.c_str()); + if (input.empty()) { + printf("Failed to read input or empty input: %s\n", opts.path.c_str()); + return 3; + } + + std::string basePath = removeSuffix(opts.path, ".json"); + if (!opts.parseOnly && basePath.empty()) { + printf("Bad input path. Path does not end with '.expected':\n%s\n", + opts.path.c_str()); + return 3; + } + std::string const actualPath = basePath + ".actual"; + std::string const rewritePath = basePath + ".rewrite"; + std::string const rewriteActualPath = basePath + ".actual-rewrite"; + + Json::Value root; + exitCode = parseAndSaveValueTree( + input, actualPath, "input", + opts.features, opts.parseOnly, &root); + if (exitCode || opts.parseOnly) { + return exitCode; + } + std::string rewrite; + exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite); + if (exitCode) { + return exitCode; + } + Json::Value rewriteRoot; + exitCode = parseAndSaveValueTree( + rewrite, rewriteActualPath, "rewrite", + opts.features, opts.parseOnly, &rewriteRoot); + if (exitCode) { + return exitCode; + } + return 0; +} int main(int argc, const char* argv[]) { - std::string path; - Json::Features features; - bool parseOnly; - int exitCode = parseCommandLine(argc, argv, features, path, parseOnly); + Options opts; + int exitCode = parseCommandLine(argc, argv, &opts); if (exitCode != 0) { + printf("Failed to parse command-line."); return exitCode; } - try { - std::string input = readInputTestFile(path.c_str()); - if (input.empty()) { - printf("Failed to read input or empty input: %s\n", path.c_str()); - return 3; - } - - std::string basePath = removeSuffix(argv[1], ".json"); - if (!parseOnly && basePath.empty()) { - printf("Bad input path. Path does not end with '.expected':\n%s\n", - path.c_str()); - return 3; - } - - std::string actualPath = basePath + ".actual"; - std::string rewritePath = basePath + ".rewrite"; - std::string rewriteActualPath = basePath + ".actual-rewrite"; - - Json::Value root; - exitCode = parseAndSaveValueTree( - input, actualPath, "input", root, features, parseOnly); - if (exitCode == 0 && !parseOnly) { - std::string rewrite; - exitCode = rewriteValueTree(rewritePath, root, rewrite); - if (exitCode == 0) { - Json::Value rewriteRoot; - exitCode = parseAndSaveValueTree(rewrite, - rewriteActualPath, - "rewrite", - rewriteRoot, - features, - parseOnly); - } - } + return runTest(opts); } catch (const std::exception& e) { printf("Unhandled exception:\n%s\n", e.what()); - exitCode = 1; + return 1; } - - return exitCode; } diff --git a/3rdparty/jsoncpp/src/lib_json/CMakeLists.txt b/3rdparty/jsoncpp/src/lib_json/CMakeLists.txt index d0f6a5ea5ab..79ffa2ec5fe 100644 --- a/3rdparty/jsoncpp/src/lib_json/CMakeLists.txt +++ b/3rdparty/jsoncpp/src/lib_json/CMakeLists.txt @@ -1,20 +1,8 @@ -OPTION(JSONCPP_LIB_BUILD_SHARED "Build jsoncpp_lib as a shared library." OFF) -IF(BUILD_SHARED_LIBS) - SET(JSONCPP_LIB_BUILD_SHARED ON) -ENDIF(BUILD_SHARED_LIBS) - -IF(JSONCPP_LIB_BUILD_SHARED) - SET(JSONCPP_LIB_TYPE SHARED) - ADD_DEFINITIONS( -DJSON_DLL_BUILD ) -ELSE(JSONCPP_LIB_BUILD_SHARED) - SET(JSONCPP_LIB_TYPE STATIC) -ENDIF(JSONCPP_LIB_BUILD_SHARED) - if( CMAKE_COMPILER_IS_GNUCXX ) #Get compiler version. execute_process( COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GNUCXX_VERSION ) - + #-Werror=* was introduced -after- GCC 4.1.2 if( GNUCXX_VERSION VERSION_GREATER 4.1.2 ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=strict-aliasing") @@ -36,25 +24,13 @@ SET( PUBLIC_HEADERS SOURCE_GROUP( "Public API" FILES ${PUBLIC_HEADERS} ) -ADD_LIBRARY( jsoncpp_lib ${JSONCPP_LIB_TYPE} - ${PUBLIC_HEADERS} - json_tool.h - json_reader.cpp - json_batchallocator.h - json_valueiterator.inl - json_value.cpp - json_writer.cpp - version.h.in - ) -SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES OUTPUT_NAME jsoncpp ) -SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES VERSION ${JSONCPP_VERSION} SOVERSION ${JSONCPP_VERSION_MAJOR} ) - -IF(NOT CMAKE_VERSION VERSION_LESS 2.8.11) - TARGET_INCLUDE_DIRECTORIES( jsoncpp_lib PUBLIC - $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}> - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}> - ) -ENDIF(NOT CMAKE_VERSION VERSION_LESS 2.8.11) +SET(jsoncpp_sources + json_tool.h + json_reader.cpp + json_valueiterator.inl + json_value.cpp + json_writer.cpp + version.h.in) # Install instructions for this target IF(JSONCPP_WITH_CMAKE_PACKAGE) @@ -63,8 +39,42 @@ ELSE(JSONCPP_WITH_CMAKE_PACKAGE) SET(INSTALL_EXPORT) ENDIF(JSONCPP_WITH_CMAKE_PACKAGE) -INSTALL( TARGETS jsoncpp_lib ${INSTALL_EXPORT} +IF(BUILD_SHARED_LIBS) + ADD_DEFINITIONS( -DJSON_DLL_BUILD ) + ADD_LIBRARY(jsoncpp_lib SHARED ${PUBLIC_HEADERS} ${jsoncpp_sources}) + SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES VERSION ${JSONCPP_VERSION} SOVERSION ${JSONCPP_VERSION_MAJOR}) + SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES OUTPUT_NAME jsoncpp + DEBUG_OUTPUT_NAME jsoncpp${DEBUG_LIBNAME_SUFFIX} ) + + INSTALL( TARGETS jsoncpp_lib ${INSTALL_EXPORT} RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR} LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR} - ARCHIVE DESTINATION ${ARCHIVE_INSTALL_DIR} -) + ARCHIVE DESTINATION ${ARCHIVE_INSTALL_DIR}) + + IF(NOT CMAKE_VERSION VERSION_LESS 2.8.11) + TARGET_INCLUDE_DIRECTORIES( jsoncpp_lib PUBLIC + $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}> + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}>) + ENDIF(NOT CMAKE_VERSION VERSION_LESS 2.8.11) + +ENDIF() + +IF(BUILD_STATIC_LIBS) + ADD_LIBRARY(jsoncpp_lib_static STATIC ${PUBLIC_HEADERS} ${jsoncpp_sources}) + SET_TARGET_PROPERTIES( jsoncpp_lib_static PROPERTIES VERSION ${JSONCPP_VERSION} SOVERSION ${JSONCPP_VERSION_MAJOR}) + SET_TARGET_PROPERTIES( jsoncpp_lib_static PROPERTIES OUTPUT_NAME jsoncpp + DEBUG_OUTPUT_NAME jsoncpp${DEBUG_LIBNAME_SUFFIX} ) + + INSTALL( TARGETS jsoncpp_lib_static ${INSTALL_EXPORT} + RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR} + LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${ARCHIVE_INSTALL_DIR}) + + IF(NOT CMAKE_VERSION VERSION_LESS 2.8.11) + TARGET_INCLUDE_DIRECTORIES( jsoncpp_lib_static PUBLIC + $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}> + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}> + ) + ENDIF(NOT CMAKE_VERSION VERSION_LESS 2.8.11) + +ENDIF() diff --git a/3rdparty/jsoncpp/src/lib_json/json_batchallocator.h b/3rdparty/jsoncpp/src/lib_json/json_batchallocator.h deleted file mode 100644 index 2fbef7a8601..00000000000 --- a/3rdparty/jsoncpp/src/lib_json/json_batchallocator.h +++ /dev/null @@ -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 diff --git a/3rdparty/jsoncpp/src/lib_json/json_internalarray.inl b/3rdparty/jsoncpp/src/lib_json/json_internalarray.inl deleted file mode 100644 index 9ee15e9dbf2..00000000000 --- a/3rdparty/jsoncpp/src/lib_json/json_internalarray.inl +++ /dev/null @@ -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 diff --git a/3rdparty/jsoncpp/src/lib_json/json_internalmap.inl b/3rdparty/jsoncpp/src/lib_json/json_internalmap.inl deleted file mode 100644 index ef3f3302dc9..00000000000 --- a/3rdparty/jsoncpp/src/lib_json/json_internalmap.inl +++ /dev/null @@ -1,473 +0,0 @@ -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -// included by json_value.cpp - -namespace Json { - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueInternalMap -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -/** \internal MUST be safely initialized using memset( this, 0, - * sizeof(ValueInternalLink) ); - * This optimization is used by the fast allocator. - */ -ValueInternalLink::ValueInternalLink() : previous_(0), next_(0) {} - -ValueInternalLink::~ValueInternalLink() { - for (int index = 0; index < itemPerLink; ++index) { - if (!items_[index].isItemAvailable()) { - if (!items_[index].isMemberNameStatic()) - free(keys_[index]); - } else - break; - } -} - -ValueMapAllocator::~ValueMapAllocator() {} - -#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -class DefaultValueMapAllocator : public ValueMapAllocator { -public: // overridden from ValueMapAllocator - virtual ValueInternalMap* newMap() { return new ValueInternalMap(); } - - virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) { - return new ValueInternalMap(other); - } - - virtual void destructMap(ValueInternalMap* map) { delete map; } - - virtual ValueInternalLink* allocateMapBuckets(unsigned int size) { - return new ValueInternalLink[size]; - } - - virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; } - - virtual ValueInternalLink* allocateMapLink() { - return new ValueInternalLink(); - } - - virtual void releaseMapLink(ValueInternalLink* link) { delete link; } -}; -#else -/// @todo make this thread-safe (lock when accessign batch allocator) -class DefaultValueMapAllocator : public ValueMapAllocator { -public: // overridden from ValueMapAllocator - virtual ValueInternalMap* newMap() { - ValueInternalMap* map = mapsAllocator_.allocate(); - new (map) ValueInternalMap(); // placement new - return map; - } - - virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) { - ValueInternalMap* map = mapsAllocator_.allocate(); - new (map) ValueInternalMap(other); // placement new - return map; - } - - virtual void destructMap(ValueInternalMap* map) { - if (map) { - map->~ValueInternalMap(); - mapsAllocator_.release(map); - } - } - - virtual ValueInternalLink* allocateMapBuckets(unsigned int size) { - return new ValueInternalLink[size]; - } - - virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; } - - virtual ValueInternalLink* allocateMapLink() { - ValueInternalLink* link = linksAllocator_.allocate(); - memset(link, 0, sizeof(ValueInternalLink)); - return link; - } - - virtual void releaseMapLink(ValueInternalLink* link) { - link->~ValueInternalLink(); - linksAllocator_.release(link); - } - -private: - BatchAllocator<ValueInternalMap, 1> mapsAllocator_; - BatchAllocator<ValueInternalLink, 1> linksAllocator_; -}; -#endif - -static ValueMapAllocator*& mapAllocator() { - static DefaultValueMapAllocator defaultAllocator; - static ValueMapAllocator* mapAllocator = &defaultAllocator; - return mapAllocator; -} - -static struct DummyMapAllocatorInitializer { - DummyMapAllocatorInitializer() { - mapAllocator(); // ensure mapAllocator() statics are initialized before - // main(). - } -} dummyMapAllocatorInitializer; - -// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. - -/* -use linked list hash map. -buckets array is a container. -linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) -value have extra state: valid, available, deleted -*/ - -ValueInternalMap::ValueInternalMap() - : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) {} - -ValueInternalMap::ValueInternalMap(const ValueInternalMap& other) - : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) { - reserve(other.itemCount_); - IteratorState it; - IteratorState itEnd; - other.makeBeginIterator(it); - other.makeEndIterator(itEnd); - for (; !equals(it, itEnd); increment(it)) { - bool isStatic; - const char* memberName = key(it, isStatic); - const Value& aValue = value(it); - resolveReference(memberName, isStatic) = aValue; - } -} - -ValueInternalMap& ValueInternalMap::operator=(ValueInternalMap other) { - swap(other); - return *this; -} - -ValueInternalMap::~ValueInternalMap() { - if (buckets_) { - for (BucketIndex bucketIndex = 0; bucketIndex < bucketsSize_; - ++bucketIndex) { - ValueInternalLink* link = buckets_[bucketIndex].next_; - while (link) { - ValueInternalLink* linkToRelease = link; - link = link->next_; - mapAllocator()->releaseMapLink(linkToRelease); - } - } - mapAllocator()->releaseMapBuckets(buckets_); - } -} - -void ValueInternalMap::swap(ValueInternalMap& other) { - ValueInternalLink* tempBuckets = buckets_; - buckets_ = other.buckets_; - other.buckets_ = tempBuckets; - ValueInternalLink* tempTailLink = tailLink_; - tailLink_ = other.tailLink_; - other.tailLink_ = tempTailLink; - BucketIndex tempBucketsSize = bucketsSize_; - bucketsSize_ = other.bucketsSize_; - other.bucketsSize_ = tempBucketsSize; - BucketIndex tempItemCount = itemCount_; - itemCount_ = other.itemCount_; - other.itemCount_ = tempItemCount; -} - -void ValueInternalMap::clear() { - ValueInternalMap dummy; - swap(dummy); -} - -ValueInternalMap::BucketIndex ValueInternalMap::size() const { - return itemCount_; -} - -bool ValueInternalMap::reserveDelta(BucketIndex growth) { - return reserve(itemCount_ + growth); -} - -bool ValueInternalMap::reserve(BucketIndex newItemCount) { - if (!buckets_ && newItemCount > 0) { - buckets_ = mapAllocator()->allocateMapBuckets(1); - bucketsSize_ = 1; - tailLink_ = &buckets_[0]; - } - // BucketIndex idealBucketCount = (newItemCount + - // ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; - return true; -} - -const Value* ValueInternalMap::find(const char* key) const { - if (!bucketsSize_) - return 0; - HashKey hashedKey = hash(key); - BucketIndex bucketIndex = hashedKey % bucketsSize_; - for (const ValueInternalLink* current = &buckets_[bucketIndex]; current != 0; - current = current->next_) { - for (BucketIndex index = 0; index < ValueInternalLink::itemPerLink; - ++index) { - if (current->items_[index].isItemAvailable()) - return 0; - if (strcmp(key, current->keys_[index]) == 0) - return ¤t->items_[index]; - } - } - return 0; -} - -Value* ValueInternalMap::find(const char* key) { - const ValueInternalMap* constThis = this; - return const_cast<Value*>(constThis->find(key)); -} - -Value& ValueInternalMap::resolveReference(const char* key, bool isStatic) { - HashKey hashedKey = hash(key); - if (bucketsSize_) { - BucketIndex bucketIndex = hashedKey % bucketsSize_; - ValueInternalLink** previous = 0; - BucketIndex index; - for (ValueInternalLink* current = &buckets_[bucketIndex]; current != 0; - previous = ¤t->next_, current = current->next_) { - for (index = 0; index < ValueInternalLink::itemPerLink; ++index) { - if (current->items_[index].isItemAvailable()) - return setNewItem(key, isStatic, current, index); - if (strcmp(key, current->keys_[index]) == 0) - return current->items_[index]; - } - } - } - - reserveDelta(1); - return unsafeAdd(key, isStatic, hashedKey); -} - -void ValueInternalMap::remove(const char* key) { - HashKey hashedKey = hash(key); - if (!bucketsSize_) - return; - BucketIndex bucketIndex = hashedKey % bucketsSize_; - for (ValueInternalLink* link = &buckets_[bucketIndex]; link != 0; - link = link->next_) { - BucketIndex index; - for (index = 0; index < ValueInternalLink::itemPerLink; ++index) { - if (link->items_[index].isItemAvailable()) - return; - if (strcmp(key, link->keys_[index]) == 0) { - doActualRemove(link, index, bucketIndex); - return; - } - } - } -} - -void ValueInternalMap::doActualRemove(ValueInternalLink* link, - BucketIndex index, - BucketIndex bucketIndex) { - // find last item of the bucket and swap it with the 'removed' one. - // set removed items flags to 'available'. - // if last page only contains 'available' items, then desallocate it (it's - // empty) - ValueInternalLink*& lastLink = getLastLinkInBucket(index); - BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 - for (; lastItemIndex < ValueInternalLink::itemPerLink; - ++lastItemIndex) // may be optimized with dicotomic search - { - if (lastLink->items_[lastItemIndex].isItemAvailable()) - break; - } - - BucketIndex lastUsedIndex = lastItemIndex - 1; - Value* valueToDelete = &link->items_[index]; - Value* valueToPreserve = &lastLink->items_[lastUsedIndex]; - if (valueToDelete != valueToPreserve) - valueToDelete->swap(*valueToPreserve); - if (lastUsedIndex == 0) // page is now empty - { // remove it from bucket linked list and delete it. - ValueInternalLink* linkPreviousToLast = lastLink->previous_; - if (linkPreviousToLast != 0) // can not deleted bucket link. - { - mapAllocator()->releaseMapLink(lastLink); - linkPreviousToLast->next_ = 0; - lastLink = linkPreviousToLast; - } - } else { - Value dummy; - valueToPreserve->swap(dummy); // restore deleted to default Value. - valueToPreserve->setItemUsed(false); - } - --itemCount_; -} - -ValueInternalLink*& -ValueInternalMap::getLastLinkInBucket(BucketIndex bucketIndex) { - if (bucketIndex == bucketsSize_ - 1) - return tailLink_; - ValueInternalLink*& previous = buckets_[bucketIndex + 1].previous_; - if (!previous) - previous = &buckets_[bucketIndex]; - return previous; -} - -Value& ValueInternalMap::setNewItem(const char* key, - bool isStatic, - ValueInternalLink* link, - BucketIndex index) { - char* duplicatedKey = makeMemberName(key); - ++itemCount_; - link->keys_[index] = duplicatedKey; - link->items_[index].setItemUsed(); - link->items_[index].setMemberNameIsStatic(isStatic); - return link->items_[index]; // items already default constructed. -} - -Value& -ValueInternalMap::unsafeAdd(const char* key, bool isStatic, HashKey hashedKey) { - JSON_ASSERT_MESSAGE(bucketsSize_ > 0, - "ValueInternalMap::unsafeAdd(): internal logic error."); - BucketIndex bucketIndex = hashedKey % bucketsSize_; - ValueInternalLink*& previousLink = getLastLinkInBucket(bucketIndex); - ValueInternalLink* link = previousLink; - BucketIndex index; - for (index = 0; index < ValueInternalLink::itemPerLink; ++index) { - if (link->items_[index].isItemAvailable()) - break; - } - if (index == ValueInternalLink::itemPerLink) // need to add a new page - { - ValueInternalLink* newLink = mapAllocator()->allocateMapLink(); - index = 0; - link->next_ = newLink; - previousLink = newLink; - link = newLink; - } - return setNewItem(key, isStatic, link, index); -} - -ValueInternalMap::HashKey ValueInternalMap::hash(const char* key) const { - HashKey hash = 0; - while (*key) - hash += *key++ * 37; - return hash; -} - -int ValueInternalMap::compare(const ValueInternalMap& other) const { - int sizeDiff(itemCount_ - other.itemCount_); - if (sizeDiff != 0) - return sizeDiff; - // Strict order guaranty is required. Compare all keys FIRST, then compare - // values. - IteratorState it; - IteratorState itEnd; - makeBeginIterator(it); - makeEndIterator(itEnd); - for (; !equals(it, itEnd); increment(it)) { - if (!other.find(key(it))) - return 1; - } - - // All keys are equals, let's compare values - makeBeginIterator(it); - for (; !equals(it, itEnd); increment(it)) { - const Value* otherValue = other.find(key(it)); - int valueDiff = value(it).compare(*otherValue); - if (valueDiff != 0) - return valueDiff; - } - return 0; -} - -void ValueInternalMap::makeBeginIterator(IteratorState& it) const { - it.map_ = const_cast<ValueInternalMap*>(this); - it.bucketIndex_ = 0; - it.itemIndex_ = 0; - it.link_ = buckets_; -} - -void ValueInternalMap::makeEndIterator(IteratorState& it) const { - it.map_ = const_cast<ValueInternalMap*>(this); - it.bucketIndex_ = bucketsSize_; - it.itemIndex_ = 0; - it.link_ = 0; -} - -bool ValueInternalMap::equals(const IteratorState& x, - const IteratorState& other) { - return x.map_ == other.map_ && x.bucketIndex_ == other.bucketIndex_ && - x.link_ == other.link_ && x.itemIndex_ == other.itemIndex_; -} - -void ValueInternalMap::incrementBucket(IteratorState& iterator) { - ++iterator.bucketIndex_; - JSON_ASSERT_MESSAGE( - iterator.bucketIndex_ <= iterator.map_->bucketsSize_, - "ValueInternalMap::increment(): attempting to iterate beyond end."); - if (iterator.bucketIndex_ == iterator.map_->bucketsSize_) - iterator.link_ = 0; - else - iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); - iterator.itemIndex_ = 0; -} - -void ValueInternalMap::increment(IteratorState& iterator) { - JSON_ASSERT_MESSAGE(iterator.map_, - "Attempting to iterator using invalid iterator."); - ++iterator.itemIndex_; - if (iterator.itemIndex_ == ValueInternalLink::itemPerLink) { - JSON_ASSERT_MESSAGE( - iterator.link_ != 0, - "ValueInternalMap::increment(): attempting to iterate beyond end."); - iterator.link_ = iterator.link_->next_; - if (iterator.link_ == 0) - incrementBucket(iterator); - } else if (iterator.link_->items_[iterator.itemIndex_].isItemAvailable()) { - incrementBucket(iterator); - } -} - -void ValueInternalMap::decrement(IteratorState& iterator) { - if (iterator.itemIndex_ == 0) { - JSON_ASSERT_MESSAGE(iterator.map_, - "Attempting to iterate using invalid iterator."); - if (iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_]) { - JSON_ASSERT_MESSAGE(iterator.bucketIndex_ > 0, - "Attempting to iterate beyond beginning."); - --(iterator.bucketIndex_); - } - iterator.link_ = iterator.link_->previous_; - iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; - } -} - -const char* ValueInternalMap::key(const IteratorState& iterator) { - JSON_ASSERT_MESSAGE(iterator.link_, - "Attempting to iterate using invalid iterator."); - return iterator.link_->keys_[iterator.itemIndex_]; -} - -const char* ValueInternalMap::key(const IteratorState& iterator, - bool& isStatic) { - JSON_ASSERT_MESSAGE(iterator.link_, - "Attempting to iterate using invalid iterator."); - isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); - return iterator.link_->keys_[iterator.itemIndex_]; -} - -Value& ValueInternalMap::value(const IteratorState& iterator) { - JSON_ASSERT_MESSAGE(iterator.link_, - "Attempting to iterate using invalid iterator."); - return iterator.link_->items_[iterator.itemIndex_]; -} - -int ValueInternalMap::distance(const IteratorState& x, const IteratorState& y) { - int offset = 0; - IteratorState it = x; - while (!equals(it, y)) - increment(it); - return offset; -} - -} // namespace Json diff --git a/3rdparty/jsoncpp/src/lib_json/json_reader.cpp b/3rdparty/jsoncpp/src/lib_json/json_reader.cpp index c5111f8d708..ceff66c3134 100644 --- a/3rdparty/jsoncpp/src/lib_json/json_reader.cpp +++ b/3rdparty/jsoncpp/src/lib_json/json_reader.cpp @@ -14,6 +14,9 @@ #include <cassert> #include <cstring> #include <istream> +#include <sstream> +#include <memory> +#include <set> #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below #define snprintf _snprintf @@ -24,8 +27,17 @@ #pragma warning(disable : 4996) #endif +static int const stackLimit_g = 1000; +static int stackDepth_g = 0; // see readValue() + namespace Json { +#if __cplusplus >= 201103L +typedef std::unique_ptr<CharReader> CharReaderPtr; +#else +typedef std::auto_ptr<CharReader> CharReaderPtr; +#endif + // Implementation of class Features // //////////////////////////////// @@ -47,23 +59,6 @@ Features Features::strictMode() { // Implementation of class Reader // //////////////////////////////// -static inline bool in(Reader::Char c, - Reader::Char c1, - Reader::Char c2, - Reader::Char c3, - Reader::Char c4) { - return c == c1 || c == c2 || c == c3 || c == c4; -} - -static inline bool in(Reader::Char c, - Reader::Char c1, - Reader::Char c2, - Reader::Char c3, - Reader::Char c4, - Reader::Char c5) { - return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; -} - static bool containsNewLine(Reader::Location begin, Reader::Location end) { for (; begin < end; ++begin) if (*begin == '\n' || *begin == '\r') @@ -125,6 +120,7 @@ bool Reader::parse(const char* beginDoc, nodes_.pop(); nodes_.push(&root); + stackDepth_g = 0; // Yes, this is bad coding, but options are limited. bool successful = readValue(); Token token; skipCommentTokens(token); @@ -147,19 +143,18 @@ bool Reader::parse(const char* beginDoc, } bool Reader::readValue() { + // This is a non-reentrant way to support a stackLimit. Terrible! + // But this deprecated class has a security problem: Bad input can + // cause a seg-fault. This seems like a fair, binary-compatible way + // to prevent the problem. + if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_g; + Token token; skipCommentTokens(token); bool successful = true; if (collectComments_ && !commentsBefore_.empty()) { - // Remove newline characters at the end of the comments - size_t lastNonNewline = commentsBefore_.find_last_not_of("\r\n"); - if (lastNonNewline != std::string::npos) { - commentsBefore_.erase(lastNonNewline + 1); - } else { - commentsBefore_.clear(); - } - currentValue().setComment(commentsBefore_, commentBefore); commentsBefore_ = ""; } @@ -180,31 +175,42 @@ bool Reader::readValue() { successful = decodeString(token); break; case tokenTrue: - currentValue() = true; + { + Value v(true); + currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); + } break; case tokenFalse: - currentValue() = false; + { + Value v(false); + currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); + } break; case tokenNull: - currentValue() = Value(); + { + Value v; + currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); + } break; case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: if (features_.allowDroppedNullPlaceholders_) { // "Un-read" the current token and mark the current value as a null // token. current_--; - currentValue() = Value(); + Value v; + currentValue().swapPayload(v); currentValue().setOffsetStart(current_ - begin_ - 1); currentValue().setOffsetLimit(current_ - begin_); break; - } - // Else, fall through... + } // Else, fall through... default: currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -216,6 +222,7 @@ bool Reader::readValue() { lastValue_ = ¤tValue(); } + --stackDepth_g; return successful; } @@ -229,13 +236,6 @@ void Reader::skipCommentTokens(Token& token) { } } -bool Reader::expectToken(TokenType type, Token& token, const char* message) { - readToken(token); - if (token.type_ != type) - return addError(message, token); - return true; -} - bool Reader::readToken(Token& token) { skipSpaces(); token.start_ = current_; @@ -351,14 +351,34 @@ bool Reader::readComment() { return true; } +static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { + std::string normalized; + normalized.reserve(end - begin); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + void Reader::addComment(Location begin, Location end, CommentPlacement placement) { assert(collectComments_); + const std::string& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { assert(lastValue_ != 0); - lastValue_->setComment(std::string(begin, end), placement); + lastValue_->setComment(normalized, placement); } else { - commentsBefore_ += std::string(begin, end); + commentsBefore_ += normalized; } } @@ -374,18 +394,38 @@ bool Reader::readCStyleComment() { bool Reader::readCppStyleComment() { while (current_ != end_) { Char c = getNextChar(); - if (c == '\r' || c == '\n') + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. break; + } } return true; } void Reader::readNumber() { - while (current_ != end_) { - if (!(*current_ >= '0' && *current_ <= '9') && - !in(*current_, '.', 'e', 'E', '+', '-')) - break; - ++current_; + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : 0; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; } } @@ -404,7 +444,8 @@ bool Reader::readString() { bool Reader::readObject(Token& tokenStart) { Token tokenName; std::string name; - currentValue() = Value(objectValue); + Value init(objectValue); + currentValue().swapPayload(init); currentValue().setOffsetStart(tokenStart.start_ - begin_); while (readToken(tokenName)) { bool initialTokenOk = true; @@ -457,7 +498,8 @@ bool Reader::readObject(Token& tokenStart) { } bool Reader::readArray(Token& tokenStart) { - currentValue() = Value(arrayValue); + Value init(arrayValue); + currentValue().swapPayload(init); currentValue().setOffsetStart(tokenStart.start_ - begin_); skipSpaces(); if (*current_ == ']') // empty array @@ -497,20 +539,13 @@ bool Reader::decodeNumber(Token& token) { Value decoded; if (!decodeNumber(token, decoded)) return false; - currentValue() = decoded; + currentValue().swapPayload(decoded); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); return true; } bool Reader::decodeNumber(Token& token, Value& decoded) { - bool isDouble = false; - for (Location inspect = token.start_; inspect != token.end_; ++inspect) { - isDouble = isDouble || in(*inspect, '.', 'e', 'E', '+') || - (*inspect == '-' && inspect != token.start_); - } - if (isDouble) - return decodeDouble(token, decoded); // Attempts to parse the number as an integer. If the number is // larger than the maximum supported value of an integer then // we decode the number as a double. @@ -518,6 +553,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { bool isNegative = *current == '-'; if (isNegative) ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) : Value::maxLargestUInt; @@ -526,9 +562,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { while (current < token.end_) { Char c = *current++; if (c < '0' || c > '9') - return addError("'" + std::string(token.start_, token.end_) + - "' is not a number.", - token); + return decodeDouble(token, decoded); Value::UInt digit(c - '0'); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If @@ -555,7 +589,7 @@ bool Reader::decodeDouble(Token& token) { Value decoded; if (!decodeDouble(token, decoded)) return false; - currentValue() = decoded; + currentValue().swapPayload(decoded); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); return true; @@ -563,6 +597,945 @@ bool Reader::decodeDouble(Token& token) { bool Reader::decodeDouble(Token& token, Value& decoded) { double value = 0; + std::string buffer(token.start_, token.end_); + std::istringstream is(buffer); + if (!(is >> value)) + return addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + std::string decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, std::string& decoded) { + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + return true; +} + +bool +Reader::addError(const std::string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + int errorCount = int(errors_.size()); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::string Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) +#if defined(WINCE) + _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#else + sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif +#else + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif + return buffer; +} + +// Deprecated. Preserved for backward compatibility +std::string Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +std::string Reader::getFormattedErrorMessages() const { + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { + std::vector<Reader::StructuredError> allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const std::string& message) { + size_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) { + size_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { + return !errors_.size(); +} + +// exact copy of Features +class OurFeatures { +public: + static OurFeatures all(); + OurFeatures(); + bool allowComments_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + int stackLimit_; +}; // OurFeatures + +// exact copy of Implementation of class Features +// //////////////////////////////// + +OurFeatures::OurFeatures() + : allowComments_(true), strictRoot_(false) + , allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) + , allowSingleQuotes_(false) + , failIfExtra_(false) +{ +} + +OurFeatures OurFeatures::all() { return OurFeatures(); } + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + size_t offset_start; + size_t offset_limit; + std::string message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + std::string getFormattedErrorMessages() const; + std::vector<StructuredError> getStructuredErrors() const; + bool pushError(const Value& value, const std::string& message); + bool pushError(const Value& value, const std::string& message, const Value& extra); + bool good() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque<ErrorInfo> Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack<Value*> Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + int stackDepth_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +// complete copy of Read impl, for OurReader + +OurReader::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool OurReader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + stackDepth_ = 0; + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_) { + if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_; + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + --stackDepth_; + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + break; + } // else continue + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool OurReader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool OurReader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +void +OurReader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const std::string& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void OurReader::readNumber() { + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : 0; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& tokenStart) { + Token tokenName; + std::string name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + std::string msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover( + msg, tokenName, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool OurReader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(c - '0'); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; const int bufferSize = 32; int count; int length = int(token.end_ - token.start_); @@ -597,17 +1570,18 @@ bool Reader::decodeDouble(Token& token, Value& decoded) { return true; } -bool Reader::decodeString(Token& token) { - std::string decoded; - if (!decodeString(token, decoded)) +bool OurReader::decodeString(Token& token) { + std::string decoded_string; + if (!decodeString(token, decoded_string)) return false; - currentValue() = decoded; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); return true; } -bool Reader::decodeString(Token& token, std::string& decoded) { +bool OurReader::decodeString(Token& token, std::string& decoded) { decoded.reserve(token.end_ - token.start_ - 2); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' @@ -660,7 +1634,7 @@ bool Reader::decodeString(Token& token, std::string& decoded) { return true; } -bool Reader::decodeUnicodeCodePoint(Token& token, +bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode) { @@ -689,7 +1663,7 @@ bool Reader::decodeUnicodeCodePoint(Token& token, return true; } -bool Reader::decodeUnicodeEscapeSequence(Token& token, +bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode) { @@ -718,7 +1692,7 @@ bool Reader::decodeUnicodeEscapeSequence(Token& token, } bool -Reader::addError(const std::string& message, Token& token, Location extra) { +OurReader::addError(const std::string& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -727,7 +1701,7 @@ Reader::addError(const std::string& message, Token& token, Location extra) { return false; } -bool Reader::recoverFromError(TokenType skipUntilToken) { +bool OurReader::recoverFromError(TokenType skipUntilToken) { int errorCount = int(errors_.size()); Token skip; for (;;) { @@ -740,22 +1714,22 @@ bool Reader::recoverFromError(TokenType skipUntilToken) { return false; } -bool Reader::addErrorAndRecover(const std::string& message, +bool OurReader::addErrorAndRecover(const std::string& message, Token& token, TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); } -Value& Reader::currentValue() { return *(nodes_.top()); } +Value& OurReader::currentValue() { return *(nodes_.top()); } -Reader::Char Reader::getNextChar() { +OurReader::Char OurReader::getNextChar() { if (current_ == end_) return 0; return *current_++; } -void Reader::getLocationLineAndColumn(Location location, +void OurReader::getLocationLineAndColumn(Location location, int& line, int& column) const { Location current = begin_; @@ -778,7 +1752,7 @@ void Reader::getLocationLineAndColumn(Location location, ++line; } -std::string Reader::getLocationLineAndColumn(Location location) const { +std::string OurReader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; @@ -794,12 +1768,7 @@ std::string Reader::getLocationLineAndColumn(Location location) const { return buffer; } -// Deprecated. Preserved for backward compatibility -std::string Reader::getFormatedErrorMessages() const { - return getFormattedErrorMessages(); -} - -std::string Reader::getFormattedErrorMessages() const { +std::string OurReader::getFormattedErrorMessages() const { std::string formattedMessage; for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); @@ -815,13 +1784,13 @@ std::string Reader::getFormattedErrorMessages() const { return formattedMessage; } -std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { - std::vector<Reader::StructuredError> allErrors; +std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const { + std::vector<OurReader::StructuredError> allErrors; for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) { const ErrorInfo& error = *itError; - Reader::StructuredError structured; + OurReader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; structured.message = error.message_; @@ -830,7 +1799,7 @@ std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { return allErrors; } -bool Reader::pushError(const Value& value, const std::string& message) { +bool OurReader::pushError(const Value& value, const std::string& message) { size_t length = end_ - begin_; if(value.getOffsetStart() > length || value.getOffsetLimit() > length) @@ -847,7 +1816,7 @@ bool Reader::pushError(const Value& value, const std::string& message) { return true; } -bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) { +bool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) { size_t length = end_ - begin_; if(value.getOffsetStart() > length || value.getOffsetLimit() > length @@ -865,19 +1834,142 @@ bool Reader::pushError(const Value& value, const std::string& message, const Val return true; } -bool Reader::good() const { +bool OurReader::good() const { return !errors_.size(); } + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; +public: + OurCharReader( + bool collectComments, + OurFeatures const& features) + : collectComments_(collectComments) + , reader_(features) + {} + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, std::string* errs) { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() +{ + setDefaults(&settings_); +} +CharReaderBuilder::~CharReaderBuilder() +{} +CharReader* CharReaderBuilder::newCharReader() const +{ + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + features.stackLimit_ = settings_["stackLimit"].asInt(); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + return new OurCharReader(collectComments, features); +} +static void getValidReaderKeys(std::set<std::string>* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("collectComments"); + valid_keys->insert("allowComments"); + valid_keys->insert("strictRoot"); + valid_keys->insert("allowDroppedNullPlaceholders"); + valid_keys->insert("allowNumericKeys"); + valid_keys->insert("allowSingleQuotes"); + valid_keys->insert("stackLimit"); + valid_keys->insert("failIfExtra"); + valid_keys->insert("rejectDupKeys"); +} +bool CharReaderBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set<std::string> valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& CharReaderBuilder::operator[](std::string key) +{ + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) +{ +//! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; +//! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) +{ +//! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; +//! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream( + CharReader::Factory const& fact, std::istream& sin, + Value* root, std::string* errs) +{ + std::ostringstream ssin; + ssin << sin.rdbuf(); + std::string doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + std::istream& operator>>(std::istream& sin, Value& root) { - Json::Reader reader; - bool ok = reader.parse(sin, root, true); + CharReaderBuilder b; + std::string errs; + bool ok = parseFromStream(b, sin, &root, &errs); if (!ok) { fprintf(stderr, "Error from reader: %s", - reader.getFormattedErrorMessages().c_str()); + errs.c_str()); - JSON_FAIL_MESSAGE("reader error"); + throwRuntimeError("reader error"); } return sin; } diff --git a/3rdparty/jsoncpp/src/lib_json/json_value.cpp b/3rdparty/jsoncpp/src/lib_json/json_value.cpp index b73deac1725..cc7d4d3d8ba 100644 --- a/3rdparty/jsoncpp/src/lib_json/json_value.cpp +++ b/3rdparty/jsoncpp/src/lib_json/json_value.cpp @@ -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) @@ -36,6 +34,7 @@ namespace Json { static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; const unsigned char& kNullRef = kNull[0]; const Value& Value::null = reinterpret_cast<const Value&>(kNullRef); +const Value& Value::nullRef = null; const Int Value::minInt = Int(~(UInt(-1) / 2)); const Int Value::maxInt = Int(UInt(-1) / 2); @@ -53,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) { @@ -84,25 +80,59 @@ 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)); - JSON_ASSERT_MESSAGE(newString != 0, - "in Json::Value::duplicateStringValue(): " - "Failed to allocate string value buffer"); + if (newString == NULL) { + throwRuntimeError( + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } memcpy(newString, value, length); newString[length] = 0; return newString; } -/** Free the string duplicated by duplicateStringValue(). +/* Record the length as a prefix. + */ +static inline 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)); + if (newString == 0) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast<unsigned*>(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString( + bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) +{ + if (!isPrefixed) { + *length = static_cast<unsigned>(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast<unsigned const*>(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). */ static inline void releaseStringValue(char* value) { free(value); } @@ -116,16 +146,53 @@ static inline void releaseStringValue(char* value) { free(value); } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// #if !defined(JSON_IS_AMALGAMATION) -#ifdef JSON_VALUE_USE_INTERNAL_MAP -#include "json_internalarray.inl" -#include "json_internalmap.inl" -#endif // JSON_VALUE_USE_INTERNAL_MAP #include "json_valueiterator.inl" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { +class JSON_API Exception : public std::exception { +public: + Exception(std::string const& msg); + virtual ~Exception() throw(); + virtual char const* what() const throw(); +protected: + std::string const msg_; +}; +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(std::string const& msg); +}; +class JSON_API LogicError : public Exception { +public: + LogicError(std::string const& msg); +}; + +Exception::Exception(std::string const& msg) + : msg_(msg) +{} +Exception::~Exception() throw() +{} +char const* Exception::what() const throw() +{ + return msg_.c_str(); +} +RuntimeError::RuntimeError(std::string const& msg) + : Exception(msg) +{} +LogicError::LogicError(std::string const& msg) + : Exception(msg) +{} +void throwRuntimeError(std::string const& msg) +{ + throw RuntimeError(msg); +} +void throwLogicError(std::string const& msg) +{ + throw LogicError(msg); +} + // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -141,15 +208,17 @@ Value::CommentInfo::~CommentInfo() { releaseStringValue(comment_); } -void Value::CommentInfo::setComment(const char* text) { - if (comment_) +void Value::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { releaseStringValue(comment_); + comment_ = 0; + } JSON_ASSERT(text != 0); JSON_ASSERT_MESSAGE( text[0] == '\0' || text[0] == '/', "in Json::Value::setComment(): Comments must start with /"); // It seems that /**/ style comments are acceptable as well. - comment_ = duplicateStringValue(text); + comment_ = duplicateStringValue(text, len); } // ////////////////////////////////////////////////////////////////// @@ -159,28 +228,34 @@ void Value::CommentInfo::setComment(const char* text) { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -#ifndef JSON_VALUE_USE_INTERNAL_MAP -// Notes: index_ indicates if the string was allocated when +// Notes: policy_ indicates if the string was allocated when // a string is stored. Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {} -Value::CZString::CZString(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_)); } @@ -195,24 +270,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; } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -241,19 +327,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; @@ -289,19 +366,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) { @@ -312,7 +389,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 @@ -321,14 +398,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: @@ -338,27 +412,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; } @@ -367,7 +436,8 @@ Value::Value(const Value& other) for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { const CommentInfo& otherComment = other.comments_[comment]; if (otherComment.comment_) - comments_[comment].setComment(otherComment.comment_); + comments_[comment].setComment( + otherComment.comment_, strlen(otherComment.comment_)); } } } @@ -384,19 +454,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; } @@ -410,7 +471,7 @@ Value& Value::operator=(Value other) { return *this; } -void Value::swap(Value& other) { +void Value::swapPayload(Value& other) { ValueType temp = type_; type_ = other.type_; other.type_ = temp; @@ -418,6 +479,11 @@ void Value::swap(Value& other) { int temp2 = allocated_; allocated_ = other.allocated_; other.allocated_ = temp2; +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); std::swap(start_, other.start_); std::swap(limit_, other.limit_); } @@ -448,10 +514,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()); @@ -459,12 +538,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; } @@ -497,20 +570,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; } @@ -522,7 +599,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 { @@ -530,7 +620,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: @@ -546,7 +642,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 @@ -760,7 +860,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(); @@ -770,12 +869,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; @@ -797,19 +890,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; } @@ -820,7 +904,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(); @@ -832,9 +915,6 @@ void Value::resize(ArrayIndex newSize) { } assert(size() == newSize); } -#else - value_.array_->resize(newSize); -#endif } Value& Value::operator[](ArrayIndex index) { @@ -843,18 +923,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) { @@ -869,17 +945,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 { @@ -889,149 +960,211 @@ 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 std::string& key) { - return (*this)[key.c_str()]; +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); } -const Value& Value::operator[](const std::string& key) const { - return (*this)[key.c_str()]; +Value& Value::operator[](const std::string& key) { + return resolveReference(key.data(), key.data() + key.length()); } Value& Value::operator[](const StaticString& key) { - return resolveReference(key, true); + return resolveReference(key.c_str()); } #ifdef JSON_USE_CPPTL Value& Value::operator[](const CppTL::ConstString& key) { - return (*this)[key.c_str()]; + return resolveReference(key.c_str(), key.end_c_str()); } - -const Value& Value::operator[](const CppTL::ConstString& key) const { - return (*this)[key.c_str()]; +Value const& Value::operator[](CppTL::ConstString const& key) const +{ + Value const* found = find(key.c_str(), key.end_c_str()); + if (!found) return 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); } -Value Value::removeMember(const char* key) { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, - "in Json::Value::removeMember(): requires objectValue"); - if (type_ == nullValue) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey(key, CZString::noDuplication); + +bool Value::removeMember(const char* key, const char* end, Value* removed) +{ + if (type_ != objectValue) { + return false; + } + CZString actualKey(key, static_cast<unsigned>(end-key), CZString::noDuplication); ObjectValues::iterator it = value_.map_->find(actualKey); if (it == value_.map_->end()) - return null; - Value old(it->second); + return false; + *removed = it->second; value_.map_->erase(it); - return old; -#else - Value* value = value_.map_->find(key); - if (value) { - Value old(*value); - value_.map_.remove(key); - return old; - } else { - return null; - } -#endif + return true; +} +bool Value::removeMember(const char* key, Value* removed) +{ + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(std::string const& key, Value* removed) +{ + return removeMember(key.data(), key.data() + key.length(), removed); } +Value Value::removeMember(const char* key) +{ + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return nullRef; -Value Value::removeMember(const std::string& key) { + Value removed; // null + removeMember(key, key + strlen(key), &removed); + return removed; // still null if removeMember() did nothing +} +Value Value::removeMember(const std::string& key) +{ return removeMember(key.c_str()); } +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type_ != arrayValue) { + return false; + } + CZString key(index); + ObjectValues::iterator it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + CZString key(i); + (*value_.map_)[key] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + ObjectValues::iterator itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + #ifdef JSON_USE_CPPTL Value Value::get(const CppTL::ConstString& key, const Value& defaultValue) const { - return get(key.c_str(), defaultValue); + return get(key.c_str(), key.end_c_str(), defaultValue); } #endif -bool Value::isMember(const char* key) const { - const Value* value = &((*this)[key]); - return value != &null; +bool Value::isMember(char const* key, char const* 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 @@ -1043,19 +1176,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; } // @@ -1181,14 +1307,22 @@ bool Value::isArray() const { return type_ == arrayValue; } bool Value::isObject() const { return type_ == objectValue; } -void Value::setComment(const char* comment, CommentPlacement placement) { +void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { if (!comments_) comments_ = new CommentInfo[numberOfCommentPlacement]; - comments_[placement].setComment(comment); + if ((len > 0) && (comment[len-1] == '\n')) { + // Always discard trailing newline, to aid indentation. + len -= 1; + } + comments_[placement].setComment(comment, len); +} + +void Value::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); } void Value::setComment(const std::string& comment, CommentPlacement placement) { - setComment(comment.c_str(), placement); + setComment(comment.c_str(), comment.length(), placement); } bool Value::hasComment(CommentPlacement placement) const { @@ -1216,28 +1350,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; } @@ -1246,28 +1363,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; } @@ -1276,28 +1376,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; } @@ -1306,28 +1389,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; } @@ -1428,7 +1494,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...) } @@ -1449,7 +1515,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; } } diff --git a/3rdparty/jsoncpp/src/lib_json/json_valueiterator.inl b/3rdparty/jsoncpp/src/lib_json/json_valueiterator.inl index a9f7df63a7c..d01d3c018c9 100644 --- a/3rdparty/jsoncpp/src/lib_json/json_valueiterator.inl +++ b/3rdparty/jsoncpp/src/lib_json/json_valueiterator.inl @@ -16,68 +16,29 @@ namespace Json { // ////////////////////////////////////////////////////////////////// ValueIteratorBase::ValueIteratorBase() -#ifndef JSON_VALUE_USE_INTERNAL_MAP : current_(), isNull_(true) { } -#else - : isArray_(true), isNull_(true) { - iterator_.array_ = ValueInternalArray::IteratorState(); -} -#endif -#ifndef JSON_VALUE_USE_INTERNAL_MAP ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator& current) : current_(current), isNull_(false) {} -#else -ValueIteratorBase::ValueIteratorBase( - const ValueInternalArray::IteratorState& state) - : isArray_(true) { - iterator_.array_ = state; -} - -ValueIteratorBase::ValueIteratorBase( - const ValueInternalMap::IteratorState& state) - : isArray_(false) { - iterator_.map_ = state; -} -#endif Value& ValueIteratorBase::deref() const { -#ifndef JSON_VALUE_USE_INTERNAL_MAP return current_->second; -#else - if (isArray_) - return ValueInternalArray::dereference(iterator_.array_); - return ValueInternalMap::value(iterator_.map_); -#endif } void ValueIteratorBase::increment() { -#ifndef JSON_VALUE_USE_INTERNAL_MAP ++current_; -#else - if (isArray_) - ValueInternalArray::increment(iterator_.array_); - ValueInternalMap::increment(iterator_.map_); -#endif } void ValueIteratorBase::decrement() { -#ifndef JSON_VALUE_USE_INTERNAL_MAP --current_; -#else - if (isArray_) - ValueInternalArray::decrement(iterator_.array_); - ValueInternalMap::decrement(iterator_.map_); -#endif } ValueIteratorBase::difference_type ValueIteratorBase::computeDistance(const SelfType& other) const { -#ifndef JSON_VALUE_USE_INTERNAL_MAP #ifdef JSON_USE_CPPTL_SMALLMAP - return current_ - other.current_; + return other.current_ - current_; #else // Iterator for null value are initialized using the default // constructor, which initialize current_ to the default @@ -100,80 +61,58 @@ ValueIteratorBase::computeDistance(const SelfType& other) const { } return myDistance; #endif -#else - if (isArray_) - return ValueInternalArray::distance(iterator_.array_, - other.iterator_.array_); - return ValueInternalMap::distance(iterator_.map_, other.iterator_.map_); -#endif } bool ValueIteratorBase::isEqual(const SelfType& other) const { -#ifndef JSON_VALUE_USE_INTERNAL_MAP if (isNull_) { return other.isNull_; } return current_ == other.current_; -#else - if (isArray_) - return ValueInternalArray::equals(iterator_.array_, other.iterator_.array_); - return ValueInternalMap::equals(iterator_.map_, other.iterator_.map_); -#endif } void ValueIteratorBase::copy(const SelfType& other) { -#ifndef JSON_VALUE_USE_INTERNAL_MAP current_ = other.current_; isNull_ = other.isNull_; -#else - if (isArray_) - iterator_.array_ = other.iterator_.array_; - iterator_.map_ = other.iterator_.map_; -#endif } Value ValueIteratorBase::key() const { -#ifndef JSON_VALUE_USE_INTERNAL_MAP const Value::CZString czstring = (*current_).first; - if (czstring.c_str()) { + if (czstring.data()) { if (czstring.isStaticString()) - return Value(StaticString(czstring.c_str())); - return Value(czstring.c_str()); + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); } return Value(czstring.index()); -#else - if (isArray_) - return Value(ValueInternalArray::indexOf(iterator_.array_)); - bool isStatic; - const char* memberName = ValueInternalMap::key(iterator_.map_, isStatic); - if (isStatic) - return Value(StaticString(memberName)); - return Value(memberName); -#endif } UInt ValueIteratorBase::index() const { -#ifndef JSON_VALUE_USE_INTERNAL_MAP const Value::CZString czstring = (*current_).first; - if (!czstring.c_str()) + if (!czstring.data()) return czstring.index(); return Value::UInt(-1); -#else - if (isArray_) - return Value::UInt(ValueInternalArray::indexOf(iterator_.array_)); - return Value::UInt(-1); -#endif } -const char* ValueIteratorBase::memberName() const { -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const char* name = (*current_).first.c_str(); +std::string ValueIteratorBase::name() const { + char const* key; + char const* end; + key = memberName(&end); + if (!key) return std::string(); + return std::string(key, end); +} + +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 +125,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 +145,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) {} diff --git a/3rdparty/jsoncpp/src/lib_json/json_writer.cpp b/3rdparty/jsoncpp/src/lib_json/json_writer.cpp index 5113c38be1d..2f940c8a43f 100644 --- a/3rdparty/jsoncpp/src/lib_json/json_writer.cpp +++ b/3rdparty/jsoncpp/src/lib_json/json_writer.cpp @@ -7,15 +7,35 @@ #include <json/writer.h> #include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include <utility> -#include <assert.h> -#include <stdio.h> -#include <string.h> -#include <sstream> #include <iomanip> -#include <math.h> +#include <memory> +#include <sstream> +#include <utility> +#include <set> +#include <cassert> +#include <cstring> +#include <cstdio> + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#include <float.h> +#define isfinite _finite +#elif defined(__sun) && defined(__SVR4) //Solaris +#include <ieeefp.h> +#define isfinite finite +#else +#include <cmath> +#define isfinite std::isfinite +#endif #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#define snprintf _snprintf +#elif defined(__ANDROID__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#define snprintf std::snprintf +#endif + +#if defined(__BORLANDC__) #include <float.h> #define isfinite _finite #define snprintf _snprintf @@ -26,13 +46,14 @@ #pragma warning(disable : 4996) #endif -#if defined(__sun) && defined(__SVR4) //Solaris -#include <ieeefp.h> -#define isfinite finite -#endif - namespace Json { +#if __cplusplus >= 201103L +typedef std::unique_ptr<StreamWriter> StreamWriterPtr; +#else +typedef std::auto_ptr<StreamWriter> StreamWriterPtr; +#endif + static bool containsControlCharacter(const char* str) { while (*str) { if (isControlCharacter(*(str++))) @@ -41,6 +62,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); @@ -175,6 +206,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() {} @@ -216,8 +325,14 @@ void FastWriter::writeValue(const Value& value) { document_ += valueToString(value.asDouble()); break; case stringValue: - document_ += valueToQuotedString(value.asCString()); + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str)); break; + } case booleanValue: document_ += valueToString(value.asBool()); break; @@ -239,7 +354,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(), static_cast<unsigned>(name.length())); document_ += yamlCompatiblityEnabled_ ? ": " : ":"; writeValue(value[name]); } @@ -280,8 +395,15 @@ void StyledWriter::writeValue(const Value& value) { pushValue(valueToString(value.asDouble())); break; case stringValue: - pushValue(valueToQuotedString(value.asCString())); + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); + else pushValue(""); break; + } case booleanValue: pushValue(valueToString(value.asBool())); break; @@ -376,6 +498,9 @@ bool StyledWriter::isMultineArray(const Value& value) { addChildValues_ = true; int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } writeValue(value[index]); lineLength += int(childValues_[index].length()); } @@ -421,26 +546,27 @@ void StyledWriter::writeCommentBeforeValue(const Value& root) { document_ += "\n"; writeIndent(); - std::string normalizedComment = normalizeEOL(root.getComment(commentBefore)); - std::string::const_iterator iter = normalizedComment.begin(); - while (iter != normalizedComment.end()) { + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { document_ += *iter; - if (*iter == '\n' && *(iter + 1) == '/') + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) writeIndent(); ++iter; } - // Comments are stripped of newlines, so add one here + // Comments are stripped of trailing newlines, so add one here document_ += "\n"; } void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) - document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + document_ += " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { document_ += "\n"; - document_ += normalizeEOL(root.getComment(commentAfter)); + document_ += root.getComment(commentAfter); document_ += "\n"; } } @@ -451,25 +577,6 @@ bool StyledWriter::hasCommentForValue(const Value& value) { value.hasComment(commentAfter); } -std::string StyledWriter::normalizeEOL(const std::string& text) { - std::string normalized; - normalized.reserve(text.length()); - const char* begin = text.c_str(); - const char* end = begin + text.length(); - const char* current = begin; - while (current != end) { - char c = *current++; - if (c == '\r') // mac or dos EOL - { - if (*current == '\n') // convert dos EOL - ++current; - normalized += '\n'; - } else // handle unix EOL & other char - normalized += c; - } - return normalized; -} - // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// @@ -481,7 +588,10 @@ void StyledStreamWriter::write(std::ostream& out, const Value& root) { document_ = &out; addChildValues_ = false; indentString_ = ""; + indented_ = true; writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *document_ << "\n"; @@ -503,8 +613,15 @@ void StyledStreamWriter::writeValue(const Value& value) { pushValue(valueToString(value.asDouble())); break; case stringValue: - pushValue(valueToQuotedString(value.asCString())); + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); + else pushValue(""); break; + } case booleanValue: pushValue(valueToString(value.asBool())); break; @@ -557,8 +674,10 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { if (hasChildValue) writeWithIndent(childValues_[index]); else { - writeIndent(); + if (!indented_) writeIndent(); + indented_ = true; writeValue(childValue); + indented_ = false; } if (++index == size) { writeCommentAfterValueOnSameLine(childValue); @@ -599,6 +718,9 @@ bool StyledStreamWriter::isMultineArray(const Value& value) { addChildValues_ = true; int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } writeValue(value[index]); lineLength += int(childValues_[index].length()); } @@ -616,24 +738,17 @@ void StyledStreamWriter::pushValue(const std::string& value) { } void StyledStreamWriter::writeIndent() { - /* - Some comments in this method would have been nice. ;-) - - if ( !document_.empty() ) - { - char last = document_[document_.length()-1]; - if ( last == ' ' ) // already indented - return; - if ( last != '\n' ) // Comments may add new-line - *document_ << '\n'; - } - */ + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. *document_ << '\n' << indentString_; } void StyledStreamWriter::writeWithIndent(const std::string& value) { - writeIndent(); + if (!indented_) writeIndent(); *document_ << value; + indented_ = false; } void StyledStreamWriter::indent() { indentString_ += indentation_; } @@ -646,19 +761,30 @@ void StyledStreamWriter::unindent() { void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; - *document_ << normalizeEOL(root.getComment(commentBefore)); - *document_ << "\n"; + + if (!indented_) writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; } void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) - *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + *document_ << ' ' << root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { - *document_ << "\n"; - *document_ << normalizeEOL(root.getComment(commentAfter)); - *document_ << "\n"; + writeIndent(); + *document_ << root.getComment(commentAfter); } + indented_ = false; } bool StyledStreamWriter::hasCommentForValue(const Value& value) { @@ -667,28 +793,386 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { value.hasComment(commentAfter); } -std::string StyledStreamWriter::normalizeEOL(const std::string& text) { - std::string normalized; - normalized.reserve(text.length()); - const char* begin = text.c_str(); - const char* end = begin + text.length(); - const char* current = begin; - while (current != end) { - char c = *current++; - if (c == '\r') // mac or dos EOL +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter +{ + BuiltStyledStreamWriter( + std::string const& indentation, + CommentStyle::Enum cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::string const& endingLineFeedSymbol); + virtual int write(Value const& root, std::ostream* sout); +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultineArray(Value const& value); + void pushValue(std::string const& value); + void writeIndent(); + void writeWithIndent(std::string const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + typedef std::vector<std::string> ChildValues; + + ChildValues childValues_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + CommentStyle::Enum cs_; + std::string colonSymbol_; + std::string nullSymbol_; + std::string endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + std::string const& indentation, + CommentStyle::Enum cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::string const& endingLineFeedSymbol) + : rightMargin_(74) + , indentation_(indentation) + , cs_(cs) + , colonSymbol_(colonSymbol) + , nullSymbol_(nullSymbol) + , endingLineFeedSymbol_(endingLineFeedSymbol) + , addChildValues_(false) + , indented_(false) +{ +} +int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) +{ + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_ = ""; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = NULL; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL is possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + std::string const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()))); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line { - if (*current == '\n') // convert dos EOL - ++current; - normalized += '\n'; - } else // handle unix EOL & other char - normalized += c; + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ", "; + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); } - return normalized; + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(std::string const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { + if (!indented_) writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() + : sout_(NULL) +{ +} +StreamWriter::~StreamWriter() +{ +} +StreamWriter::Factory::~Factory() +{} +StreamWriterBuilder::StreamWriterBuilder() +{ + setDefaults(&settings_); +} +StreamWriterBuilder::~StreamWriterBuilder() +{} +StreamWriter* StreamWriterBuilder::newStreamWriter() const +{ + std::string indentation = settings_["indentation"].asString(); + std::string cs_str = settings_["commentStyle"].asString(); + bool eyc = settings_["enableYAMLCompatibility"].asBool(); + bool dnp = settings_["dropNullPlaceholders"].asBool(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + std::string colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + std::string nullSymbol = "null"; + if (dnp) { + nullSymbol = ""; + } + std::string endingLineFeedSymbol = ""; + return new BuiltStyledStreamWriter( + indentation, cs, + colonSymbol, nullSymbol, endingLineFeedSymbol); +} +static void getValidWriterKeys(std::set<std::string>* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("indentation"); + valid_keys->insert("commentStyle"); + valid_keys->insert("enableYAMLCompatibility"); + valid_keys->insert("dropNullPlaceholders"); +} +bool StreamWriterBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set<std::string> valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& StreamWriterBuilder::operator[](std::string key) +{ + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) +{ + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + //! [StreamWriterBuilderDefaults] +} + +std::string writeString(StreamWriter::Factory const& builder, Value const& root) { + std::ostringstream sout; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); } -std::ostream& operator<<(std::ostream& sout, const Value& root) { - Json::StyledStreamWriter writer; - writer.write(sout, root); +std::ostream& operator<<(std::ostream& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); return sout; } diff --git a/3rdparty/jsoncpp/src/test_lib_json/CMakeLists.txt b/3rdparty/jsoncpp/src/test_lib_json/CMakeLists.txt index 420d659968c..f54c85f6817 100644 --- a/3rdparty/jsoncpp/src/test_lib_json/CMakeLists.txt +++ b/3rdparty/jsoncpp/src/test_lib_json/CMakeLists.txt @@ -1,7 +1,4 @@ - -IF(JSONCPP_LIB_BUILD_SHARED) - ADD_DEFINITIONS( -DJSON_DLL ) -ENDIF(JSONCPP_LIB_BUILD_SHARED) +# vim: et ts=4 sts=4 sw=4 tw=0 ADD_EXECUTABLE( jsoncpp_test jsontest.cpp @@ -9,14 +6,33 @@ ADD_EXECUTABLE( jsoncpp_test main.cpp ) -TARGET_LINK_LIBRARIES(jsoncpp_test jsoncpp_lib) + +IF(BUILD_SHARED_LIBS) + ADD_DEFINITIONS( -DJSON_DLL ) + TARGET_LINK_LIBRARIES(jsoncpp_test jsoncpp_lib) +ELSE(BUILD_SHARED_LIBS) + TARGET_LINK_LIBRARIES(jsoncpp_test jsoncpp_lib_static) +ENDIF(BUILD_SHARED_LIBS) + +# another way to solve issue #90 +#set_target_properties(jsoncpp_test PROPERTIES COMPILE_FLAGS -ffloat-store) # 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(BUILD_SHARED_LIBS) + # 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(BUILD_SHARED_LIBS) + # Just run the test executable. + ADD_CUSTOM_COMMAND( TARGET jsoncpp_test + POST_BUILD + COMMAND $<TARGET_FILE:jsoncpp_test>) + ENDIF(BUILD_SHARED_LIBS) ENDIF(JSONCPP_WITH_POST_BUILD_UNITTEST) SET_TARGET_PROPERTIES(jsoncpp_test PROPERTIES OUTPUT_NAME jsoncpp_test) diff --git a/3rdparty/jsoncpp/src/test_lib_json/jsontest.cpp b/3rdparty/jsoncpp/src/test_lib_json/jsontest.cpp index ef9c543fa47..bd9463fa589 100644 --- a/3rdparty/jsoncpp/src/test_lib_json/jsontest.cpp +++ b/3rdparty/jsoncpp/src/test_lib_json/jsontest.cpp @@ -323,7 +323,7 @@ void Runner::listTests() const { } int Runner::runCommandLine(int argc, const char* argv[]) const { - typedef std::deque<std::string> TestNames; + // typedef std::deque<std::string> TestNames; Runner subrunner; for (int index = 1; index < argc; ++index) { std::string opt = argv[index]; diff --git a/3rdparty/jsoncpp/src/test_lib_json/jsontest.h b/3rdparty/jsoncpp/src/test_lib_json/jsontest.h index 5c56a40b0ff..d6b7cf386a2 100644 --- a/3rdparty/jsoncpp/src/test_lib_json/jsontest.h +++ b/3rdparty/jsoncpp/src/test_lib_json/jsontest.h @@ -178,8 +178,8 @@ private: template <typename T, typename U> TestResult& checkEqual(TestResult& result, - const T& expected, - const U& actual, + T expected, + U actual, const char* file, unsigned int line, const char* expr) { @@ -214,7 +214,7 @@ TestResult& checkStringEqual(TestResult& result, #define JSONTEST_ASSERT_PRED(expr) \ { \ JsonTest::PredicateContext _minitest_Context = { \ - result_->predicateId_, __FILE__, __LINE__, #expr \ + result_->predicateId_, __FILE__, __LINE__, #expr, NULL, NULL \ }; \ result_->predicateStackTail_->next_ = &_minitest_Context; \ result_->predicateId_ += 1; \ diff --git a/3rdparty/jsoncpp/src/test_lib_json/main.cpp b/3rdparty/jsoncpp/src/test_lib_json/main.cpp index 51c5e7442b1..0d4284e5373 100644 --- a/3rdparty/jsoncpp/src/test_lib_json/main.cpp +++ b/3rdparty/jsoncpp/src/test_lib_json/main.cpp @@ -6,7 +6,7 @@ #include "jsontest.h" #include <json/config.h> #include <json/json.h> -#include <stdexcept> +#include <cstring> // Make numeric limits more convenient to talk about. // Assumes int type in 32 bits. @@ -17,8 +17,8 @@ #define kint64min Json::Value::minInt64 #define kuint64max Json::Value::maxUInt64 -static const double kdint64max = double(kint64max); -static const float kfint64max = float(kint64max); +//static const double kdint64max = double(kint64max); +//static const float kfint64max = float(kint64max); static const float kfint32max = float(kint32max); static const float kfuint32max = float(kuint32max); @@ -198,6 +198,18 @@ JSONTEST_FIXTURE(ValueTest, objects) { object1_["some other id"] = "foo"; JSONTEST_ASSERT_EQUAL(Json::Value("foo"), object1_["some other id"]); + JSONTEST_ASSERT_EQUAL(Json::Value("foo"), object1_["some other id"]); + + // Remove. + Json::Value got; + bool did; + did = object1_.removeMember("some other id", &got); + JSONTEST_ASSERT_EQUAL(Json::Value("foo"), got); + JSONTEST_ASSERT_EQUAL(true, did); + got = Json::Value("bar"); + did = object1_.removeMember("some other id", &got); + JSONTEST_ASSERT_EQUAL(Json::Value("bar"), got); + JSONTEST_ASSERT_EQUAL(false, did); } JSONTEST_FIXTURE(ValueTest, arrays) { @@ -240,6 +252,24 @@ JSONTEST_FIXTURE(ValueTest, arrays) { array1_[2] = Json::Value(17); JSONTEST_ASSERT_EQUAL(Json::Value(), array1_[1]); JSONTEST_ASSERT_EQUAL(Json::Value(17), array1_[2]); + Json::Value got; + JSONTEST_ASSERT_EQUAL(true, array1_.removeIndex(2, &got)); + JSONTEST_ASSERT_EQUAL(Json::Value(17), got); + JSONTEST_ASSERT_EQUAL(false, array1_.removeIndex(2, &got)); // gone now +} +JSONTEST_FIXTURE(ValueTest, arrayIssue252) +{ + int count = 5; + Json::Value root; + Json::Value item; + root["array"] = Json::Value::nullRef; + for (int i = 0; i < count; i++) + { + item["a"] = i; + item["b"] = i; + root["array"][i] = item; + } + //JSONTEST_ASSERT_EQUAL(5, root["array"].size()); } JSONTEST_FIXTURE(ValueTest, null) { @@ -265,6 +295,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) { @@ -1499,6 +1531,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(std::string("// 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) { @@ -1510,6 +1662,39 @@ JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) { JSONTEST_ASSERT(writer.write(nullValue) == "\n"); } +struct StreamWriterTest : JsonTest::TestCase {}; + +JSONTEST_FIXTURE(StreamWriterTest, dropNullPlaceholders) { + Json::StreamWriterBuilder b; + Json::Value nullValue; + b.settings_["dropNullPlaceholders"] = false; + JSONTEST_ASSERT(Json::writeString(b, nullValue) == "null"); + b.settings_["dropNullPlaceholders"] = true; + 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) { @@ -1601,12 +1786,561 @@ JSONTEST_FIXTURE(ReaderTest, parseWithDetailError) { JSONTEST_ASSERT(errors.at(0).message == "Bad escape sequence in string"); } +struct CharReaderTest : JsonTest::TestCase {}; + +JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrors) { + Json::CharReaderBuilder b; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + Json::Value root; + char const doc[] = "{ \"property\" : \"value\" }"; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(ok); + JSONTEST_ASSERT(errs.size() == 0); + delete reader; +} + +JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrorsTestingOffsets) { + Json::CharReaderBuilder b; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + Json::Value root; + char const doc[] = + "{ \"property\" : [\"value\", \"value2\"], \"obj\" : " + "{ \"nested\" : 123, \"bool\" : true}, \"null\" : " + "null, \"false\" : false }"; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(ok); + JSONTEST_ASSERT(errs.size() == 0); + delete reader; +} + +JSONTEST_FIXTURE(CharReaderTest, parseWithOneError) { + Json::CharReaderBuilder b; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + Json::Value root; + char const doc[] = + "{ \"property\" :: \"value\" }"; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(!ok); + JSONTEST_ASSERT(errs == + "* Line 1, Column 15\n Syntax error: value, object or array " + "expected.\n"); + delete reader; +} + +JSONTEST_FIXTURE(CharReaderTest, parseChineseWithOneError) { + Json::CharReaderBuilder b; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + Json::Value root; + char const doc[] = + "{ \"pr佐藤erty\" :: \"value\" }"; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(!ok); + JSONTEST_ASSERT(errs == + "* Line 1, Column 19\n Syntax error: value, object or array " + "expected.\n"); + delete reader; +} + +JSONTEST_FIXTURE(CharReaderTest, parseWithDetailError) { + Json::CharReaderBuilder b; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + Json::Value root; + char const doc[] = + "{ \"property\" : \"v\\alue\" }"; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(!ok); + JSONTEST_ASSERT(errs == + "* Line 1, Column 16\n Bad escape sequence in string\nSee " + "Line 1, Column 20 for detail.\n"); + delete reader; +} + +JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) { + Json::CharReaderBuilder b; + Json::Value root; + char const doc[] = + "{ \"property\" : \"value\" }"; + { + b.settings_["stackLimit"] = 2; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(ok); + JSONTEST_ASSERT(errs == ""); + JSONTEST_ASSERT_EQUAL("value", root["property"]); + delete reader; + } + { + b.settings_["stackLimit"] = 1; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + JSONTEST_ASSERT_THROWS(reader->parse( + doc, doc + std::strlen(doc), + &root, &errs)); + delete reader; + } +} + +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) { + // This is interpretted as a string value followed by a colon. + Json::CharReaderBuilder b; + Json::Value root; + char const doc[] = + " \"property\" : \"value\" }"; + { + b.settings_["failIfExtra"] = false; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(ok); + JSONTEST_ASSERT(errs == ""); + JSONTEST_ASSERT_EQUAL("property", root); + delete reader; + } + { + b.settings_["failIfExtra"] = true; + 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(errs, + "* Line 1, Column 13\n" + " Extra non-whitespace after JSON value.\n"); + JSONTEST_ASSERT_EQUAL("property", root); + delete reader; + } + { + b.settings_["failIfExtra"] = false; + 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(errs, + "* Line 1, Column 13\n" + " Extra non-whitespace after JSON value.\n"); + JSONTEST_ASSERT_EQUAL("property", root); + delete reader; + } +} +JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue107) { + // This is interpretted as an int value followed by a colon. + Json::CharReaderBuilder b; + Json::Value root; + char const doc[] = + "1:2:3"; + b.settings_["failIfExtra"] = true; + 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 2\n" + " Extra non-whitespace after JSON value.\n", + errs); + JSONTEST_ASSERT_EQUAL(1, root.asInt()); + delete reader; +} +JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterObject) { + Json::CharReaderBuilder b; + Json::Value root; + { + char const doc[] = + "{ \"property\" : \"value\" } //trailing\n//comment\n"; + b.settings_["failIfExtra"] = true; + 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("", errs); + JSONTEST_ASSERT_EQUAL("value", root["property"]); + delete reader; + } +} +JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterArray) { + Json::CharReaderBuilder b; + Json::Value root; + char const doc[] = + "[ \"property\" , \"value\" ] //trailing\n//comment\n"; + b.settings_["failIfExtra"] = true; + 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("", errs); + JSONTEST_ASSERT_EQUAL("value", root[1u]); + delete reader; +} +JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) { + Json::CharReaderBuilder b; + Json::Value root; + char const doc[] = + " true /*trailing\ncomment*/"; + b.settings_["failIfExtra"] = true; + 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("", errs); + 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()); + } + delete reader; +} + +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()); + } + delete reader; +} + +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 {}; + +JSONTEST_FIXTURE(IteratorTest, distance) { + Json::Value json; + json["k1"] = "a"; + json["k2"] = "b"; + int dist = 0; + std::string str; + for (Json::ValueIterator it = json.begin(); it != json.end(); ++it) { + dist = it - json.begin(); + str = it->asString().c_str(); + } + JSONTEST_ASSERT_EQUAL(1, dist); + JSONTEST_ASSERT_STRING_EQUAL("b", str); +} + +JSONTEST_FIXTURE(IteratorTest, names) { + Json::Value json; + json["k1"] = "a"; + json["k2"] = "b"; + Json::ValueIterator it = json.begin(); + JSONTEST_ASSERT(it != json.end()); + JSONTEST_ASSERT_EQUAL(Json::Value("k1"), it.key()); + JSONTEST_ASSERT_STRING_EQUAL("k1", it.name()); + JSONTEST_ASSERT_EQUAL(-1, it.index()); + ++it; + JSONTEST_ASSERT(it != json.end()); + JSONTEST_ASSERT_EQUAL(Json::Value("k2"), it.key()); + JSONTEST_ASSERT_STRING_EQUAL("k2", it.name()); + JSONTEST_ASSERT_EQUAL(-1, it.index()); + ++it; + JSONTEST_ASSERT(it == json.end()); +} + +JSONTEST_FIXTURE(IteratorTest, indexes) { + Json::Value json; + json[0] = "a"; + json[1] = "b"; + Json::ValueIterator it = json.begin(); + JSONTEST_ASSERT(it != json.end()); + JSONTEST_ASSERT_EQUAL(Json::Value(Json::ArrayIndex(0)), it.key()); + JSONTEST_ASSERT_STRING_EQUAL("", it.name()); + JSONTEST_ASSERT_EQUAL(0, it.index()); + ++it; + JSONTEST_ASSERT(it != json.end()); + JSONTEST_ASSERT_EQUAL(Json::Value(Json::ArrayIndex(1)), it.key()); + JSONTEST_ASSERT_STRING_EQUAL("", it.name()); + JSONTEST_ASSERT_EQUAL(1, it.index()); + ++it; + JSONTEST_ASSERT(it == json.end()); +} + int main(int argc, const char* argv[]) { JsonTest::Runner runner; JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, memberCount); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, objects); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, arrays); + JSONTEST_REGISTER_FIXTURE(runner, ValueTest, arrayIssue252); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, null); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, strings); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, bools); @@ -1623,6 +2357,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( @@ -1631,7 +2374,33 @@ int main(int argc, const char* argv[]) { JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseChineseWithOneError); JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithDetailError); - JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders); + JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithNoErrors); + JSONTEST_REGISTER_FIXTURE( + runner, CharReaderTest, parseWithNoErrorsTestingOffsets); + JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithOneError); + JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseChineseWithOneError); + 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); + JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, names); + JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, indexes); return runner.runCommandLine(argc, argv); } |