From 51a31ce006f7dfb850771688fe6a2ccdfcf765af Mon Sep 17 00:00:00 2001 From: Sfinktah Bungholio Date: Sun, 25 Sep 2016 20:36:33 +1000 Subject: [PATCH 001/117] Fix for winmindef.h defining min/max macros --- include/rapidjson/document.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 19f5a6a5f..6b9d9ef87 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -29,6 +29,14 @@ RAPIDJSON_DIAG_PUSH #ifdef _MSC_VER RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#ifdef _MINWINDEF_ // see: http://stackoverflow.com/questions/22744262/cant-call-stdmax-because-minwindef-h-defines-max +#ifndef NOMINMAX +#pragma push_macro("min") +#pragma push_macro("max") +#undef min +#undef max +#endif +#endif #endif #ifdef __clang__ @@ -2570,6 +2578,12 @@ class GenericObject { }; RAPIDJSON_NAMESPACE_END +#ifdef _MINWINDEF_ // see: http://stackoverflow.com/questions/22744262/cant-call-stdmax-because-minwindef-h-defines-max +#ifndef NOMINMAX +#pragma pop_macro("min") +#pragma pop_macro("max") +#endif +#endif RAPIDJSON_DIAG_POP #endif // RAPIDJSON_DOCUMENT_H_ From 9d8df28c1dd92be8480fae8026fed0aa2c0d8cdd Mon Sep 17 00:00:00 2001 From: Patrick Cheng Date: Fri, 30 Sep 2016 10:47:00 -0700 Subject: [PATCH 002/117] added assertion to help suppress clang warnings --- include/rapidjson/internal/stack.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h index 022c9aab4..54ac77a82 100644 --- a/include/rapidjson/internal/stack.h +++ b/include/rapidjson/internal/stack.h @@ -113,6 +113,7 @@ class Stack { // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. template RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); // Expand the stack if needed if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) Expand(count); @@ -126,6 +127,7 @@ class Stack { template RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); T* ret = reinterpret_cast(stackTop_); stackTop_ += sizeof(T) * count; From 91a803d46394a668fd5cb51bd1b4dbea9b4b2fb0 Mon Sep 17 00:00:00 2001 From: Patrick Cheng Date: Fri, 30 Sep 2016 11:12:23 -0700 Subject: [PATCH 003/117] Reserve() is sometimes called when stackTop_ is null. The assert is invalid. --- include/rapidjson/internal/stack.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h index 54ac77a82..26b716d2b 100644 --- a/include/rapidjson/internal/stack.h +++ b/include/rapidjson/internal/stack.h @@ -113,7 +113,6 @@ class Stack { // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. template RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { - RAPIDJSON_ASSERT(stackTop_); // Expand the stack if needed if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) Expand(count); From 95224aff7dff65c448dca374b4c7fccc32680e5c Mon Sep 17 00:00:00 2001 From: Patrick Cheng Date: Fri, 30 Sep 2016 13:44:15 -0700 Subject: [PATCH 004/117] When length is 0, the code does nothing, so skip it completely. Previously, os.Push(0) would do nothing as well. But with the newly added assertion, is the stack is empty, it will fail the assertion. --- include/rapidjson/reader.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 19f8849b1..e3523a0d6 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -948,11 +948,13 @@ class GenericReader { #else length = static_cast(__builtin_ffs(r) - 1); #endif - char* q = reinterpret_cast(os.Push(length)); - for (size_t i = 0; i < length; i++) - q[i] = p[i]; + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; - p += length; + p += length; + } break; } _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); From 517dd4dbb8babb5b69e3ed6eabdaedeb177bd977 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 17 Oct 2016 14:25:24 +0800 Subject: [PATCH 005/117] Fix failing to resolve $ref in allOf causes crash in SchemaValidator::StartObject() --- include/rapidjson/schema.h | 3 +++ test/unittest/schematest.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 288b93d0f..420db6285 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1366,6 +1366,9 @@ class GenericSchemaDocument { new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); } } + else if (refEntry->schema) + *refEntry->schema = SchemaType::GetTypeless(); + refEntry->~SchemaRefEntry(); } diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 6a8b685f4..478051654 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -1308,6 +1308,14 @@ TEST(SchemaValidator, Issue608) { INVALIDATE(s, "{\"a\" : null, \"a\" : null}", "", "required", ""); } +// Fail to resolve $ref in allOf causes crash in SchemaValidator::StartObject() +TEST(SchemaValidator, Issue728_AllOfRef) { + Document sd; + sd.Parse("{\"allOf\": [{\"$ref\": \"#/abc\"}]}"); + SchemaDocument s(sd); + VALIDATE(s, "{\"key1\": \"abc\", \"key2\": \"def\"}", true); +} + #ifdef __clang__ RAPIDJSON_DIAG_POP #endif From b963eb447bee24a692d4ca718db6252a028f131a Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 17 Oct 2016 18:30:18 +0800 Subject: [PATCH 006/117] Change SchemaValidator::GetNullHandler() from singleton to instance. --- include/rapidjson/schema.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 420db6285..bb5607fb9 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1368,7 +1368,7 @@ class GenericSchemaDocument { } else if (refEntry->schema) *refEntry->schema = SchemaType::GetTypeless(); - + refEntry->~SchemaRefEntry(); } @@ -1579,11 +1579,11 @@ class GenericSchemaValidator : : schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), - outputHandler_(GetNullHandler()), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), + outputHandler_(CreateNullHandler()), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) @@ -1607,11 +1607,12 @@ class GenericSchemaValidator : : schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), - outputHandler_(outputHandler), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), + outputHandler_(outputHandler), + nullHandler_(0), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) @@ -1795,11 +1796,11 @@ RAPIDJSON_MULTILINEMACRO_END : schemaDocument_(&schemaDocument), root_(root), - outputHandler_(GetNullHandler()), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), + outputHandler_(CreateNullHandler()), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(depth) @@ -1913,20 +1914,20 @@ RAPIDJSON_MULTILINEMACRO_END Context& CurrentContext() { return *schemaStack_.template Top(); } const Context& CurrentContext() const { return *schemaStack_.template Top(); } - static OutputHandler& GetNullHandler() { - static OutputHandler nullHandler; - return nullHandler; + OutputHandler& CreateNullHandler() { + return *(nullHandler_ = static_cast(stateAllocator_->Malloc(sizeof(OutputHandler)))); } static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultDocumentStackCapacity = 256; const SchemaDocumentType* schemaDocument_; const SchemaType& root_; - OutputHandler& outputHandler_; StateAllocator* stateAllocator_; StateAllocator* ownStateAllocator_; internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler& outputHandler_; + OutputHandler* nullHandler_; bool valid_; #if RAPIDJSON_SCHEMA_VERBOSE unsigned depth_; From ddbd2ef05de8600a297dbbabc6563bd0c65f649b Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 18 Oct 2016 10:14:00 +0800 Subject: [PATCH 007/117] Restore missing deallocation of GenericSchemaValidator::nullHandler_ --- include/rapidjson/schema.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index bb5607fb9..af3b6218d 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1623,6 +1623,10 @@ class GenericSchemaValidator : //! Destructor. ~GenericSchemaValidator() { Reset(); + if (nullHandler_) { + nullHandler_->~OutputHandler(); + StateAllocator::Free(nullHandler_); + } RAPIDJSON_DELETE(ownStateAllocator_); } From 7c4e511eb0f00d074237595bc0dc62a3bd266d57 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 18 Oct 2016 11:37:15 +0800 Subject: [PATCH 008/117] Change Schema::GetTypeless() from singleton to instance Now owned by SchemaDocument::typeless_, and be shared to its Schema::typeless_ --- include/rapidjson/schema.h | 44 ++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index af3b6218d..6f1611fe8 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -349,6 +349,7 @@ class Schema { Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : allocator_(allocator), + typeless_(schemaDocument->GetTypeless()), enum_(), enumCount_(), not_(), @@ -453,7 +454,7 @@ class Schema { for (SizeType i = 0; i < propertyCount_; i++) { new (&properties_[i]) Property(); properties_[i].name = allProperties[i]; - properties_[i].schema = GetTypeless(); + properties_[i].schema = typeless_; } } } @@ -575,9 +576,7 @@ class Schema { } ~Schema() { - if (allocator_) { - allocator_->Free(enum_); - } + AllocatorType::Free(enum_); if (properties_) { for (SizeType i = 0; i < propertyCount_; i++) properties_[i].~Property(); @@ -592,7 +591,7 @@ class Schema { #if RAPIDJSON_SCHEMA_HAS_REGEX if (pattern_) { pattern_->~RegexType(); - allocator_->Free(pattern_); + AllocatorType::Free(pattern_); } #endif } @@ -610,12 +609,12 @@ class Schema { else if (additionalItemsSchema_) context.valueSchema = additionalItemsSchema_; else if (additionalItems_) - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; else RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); } else - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; context.arrayElementIndex++; } @@ -792,7 +791,7 @@ class Schema { if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { if (context.patternPropertiesSchemaCount > 0) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; } else @@ -807,7 +806,7 @@ class Schema { if (additionalPropertiesSchema_) { if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; } else @@ -815,7 +814,7 @@ class Schema { return true; } else if (additionalProperties_) { - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; return true; } @@ -949,11 +948,6 @@ class Schema { SizeType count; }; - static const SchemaType* GetTypeless() { - static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); - return &typeless; - } - template void AddUniqueElement(V1& a, const V2& v) { for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) @@ -1219,6 +1213,7 @@ class Schema { }; AllocatorType* allocator_; + const SchemaType* typeless_; uint64_t* enum_; SizeType enumCount_; SchemaArray allOf_; @@ -1350,6 +1345,9 @@ class GenericSchemaDocument { if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); + // Generate root schema, it will call CreateSchema() to create sub-schemas, // And call AddRefSchema() if there are $ref. CreateSchemaRecursive(&root_, PointerType(), document, document); @@ -1367,7 +1365,7 @@ class GenericSchemaDocument { } } else if (refEntry->schema) - *refEntry->schema = SchemaType::GetTypeless(); + *refEntry->schema = typeless_; refEntry->~SchemaRefEntry(); } @@ -1384,12 +1382,14 @@ class GenericSchemaDocument { allocator_(rhs.allocator_), ownAllocator_(rhs.ownAllocator_), root_(rhs.root_), + typeless_(rhs.typeless_), schemaMap_(std::move(rhs.schemaMap_)), schemaRef_(std::move(rhs.schemaRef_)) { rhs.remoteProvider_ = 0; rhs.allocator_ = 0; rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; } #endif @@ -1398,6 +1398,9 @@ class GenericSchemaDocument { while (!schemaMap_.Empty()) schemaMap_.template Pop(1)->~SchemaEntry(); + typeless_->~SchemaType(); + Allocator::Free(typeless_); + RAPIDJSON_DELETE(ownAllocator_); } @@ -1432,7 +1435,7 @@ class GenericSchemaDocument { void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { if (schema) - *schema = SchemaType::GetTypeless(); + *schema = typeless_; if (v.GetType() == kObjectType) { const SchemaType* s = GetSchema(pointer); @@ -1519,6 +1522,8 @@ class GenericSchemaDocument { return PointerType(); } + const SchemaType* GetTypeless() const { return typeless_; } + static const size_t kInitialSchemaMapSize = 64; static const size_t kInitialSchemaRefSize = 64; @@ -1526,6 +1531,7 @@ class GenericSchemaDocument { Allocator *allocator_; Allocator *ownAllocator_; const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; internal::Stack schemaMap_; // Stores created Pointer -> Schemas internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref }; @@ -1832,8 +1838,8 @@ RAPIDJSON_MULTILINEMACRO_END const SchemaType** sa = CurrentContext().patternPropertiesSchemas; typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; bool valueUniqueness = CurrentContext().valueUniqueness; - if (CurrentContext().valueSchema) - PushSchema(*CurrentContext().valueSchema); + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); if (count > 0) { CurrentContext().objectPatternValidatorType = patternValidatorType; From bf0cc7bea8f9e8a744098d680bdf521e343dc4db Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 18 Oct 2016 13:53:00 +0800 Subject: [PATCH 009/117] Fixed a bug for SchemaDocument move constructor --- include/rapidjson/schema.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 6f1611fe8..178e91c51 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1339,6 +1339,7 @@ class GenericSchemaDocument { allocator_(allocator), ownAllocator_(), root_(), + typeless_(), schemaMap_(allocator, kInitialSchemaMapSize), schemaRef_(allocator, kInitialSchemaRefSize) { @@ -1398,8 +1399,10 @@ class GenericSchemaDocument { while (!schemaMap_.Empty()) schemaMap_.template Pop(1)->~SchemaEntry(); - typeless_->~SchemaType(); - Allocator::Free(typeless_); + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } RAPIDJSON_DELETE(ownAllocator_); } From 992b7f5f8edaa6dbcb6c298a2c4386356a3ecb4e Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 21 Oct 2016 12:25:37 +0800 Subject: [PATCH 010/117] Fix nullHandler allocation bug --- include/rapidjson/schema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 178e91c51..e7af3cf57 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1928,7 +1928,7 @@ RAPIDJSON_MULTILINEMACRO_END const Context& CurrentContext() const { return *schemaStack_.template Top(); } OutputHandler& CreateNullHandler() { - return *(nullHandler_ = static_cast(stateAllocator_->Malloc(sizeof(OutputHandler)))); + return *(nullHandler_ = static_cast(GetStateAllocator().Malloc(sizeof(OutputHandler)))); } static const size_t kDefaultSchemaStackCapacity = 1024; From d7dd4106ea62e72c75988da821d0628e84a627b0 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 25 Oct 2016 18:21:01 +0800 Subject: [PATCH 011/117] Remove empty NumberStream::~NumberStream() Fix #781 --- include/rapidjson/reader.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index a8cee364e..71916c0ae 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1061,7 +1061,6 @@ class GenericReader { typedef typename InputStream::Ch Ch; NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } - ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } @@ -1083,7 +1082,6 @@ class GenericReader { typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} - ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch TakePush() { stackStream.Put(static_cast(Base::is.Peek())); @@ -1110,7 +1108,6 @@ class GenericReader { typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} - ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } }; From c4db88a3142548ea342e1fd971bf2a08aa70c7eb Mon Sep 17 00:00:00 2001 From: Wu Zhao Date: Wed, 26 Oct 2016 17:27:54 +0800 Subject: [PATCH 012/117] support IBM PowerPC / ppc64 / ppc64le and XL compiler Avoid POWER platform compiling error and support IBM XL C++ compiler on Linux / AIX. --- CMakeLists.txt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ceda71b1b..8ccda4be6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,13 @@ if(CCACHE_FOUND) endif(CCACHE_FOUND) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror") + if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "powerpc" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64le") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native") + else() + #FIXME: x86 is -march=native, but doesn't mean every arch is this option. To keep original project's compatibility, I leave this except POWER. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror") if (RAPIDJSON_BUILD_CXX11) if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") @@ -73,7 +79,13 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") endif() endif() elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror -Wno-missing-field-initializers") + if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "powerpc" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64le") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native") + else() + #FIXME: x86 is -march=native, but doesn't mean every arch is this option. To keep original project's compatibility, I leave this except POWER. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wno-missing-field-initializers") if (RAPIDJSON_BUILD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() @@ -90,6 +102,8 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_definitions(-D_CRT_SECURE_NO_WARNINGS=1) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") +elseif (CMAKE_CXX_COMPILER_ID MATCHES "XL") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -qarch=auto") endif() #add extra search paths for libraries and includes From 95b346c3ca6009a5d779533ae2d8d763ee9322d1 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 31 Oct 2016 18:24:17 +0800 Subject: [PATCH 013/117] Refactor GenericValue deep-clone constructor --- include/rapidjson/document.h | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 703c06114..2da67a9fb 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2419,11 +2419,28 @@ inline GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) { switch (rhs.GetType()) { - case kObjectType: - case kArrayType: { // perform deep copy via SAX Handler - GenericDocument d(&allocator); - rhs.Accept(d); - RawAssign(*d.stack_.template Pop(1)); + case kObjectType: { + SizeType count = rhs.data_.o.size; + Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator); + new (&lm[i].value) GenericValue(rm[i].value, allocator); + } + data_.f.flags = kObjectFlag; + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); } break; case kStringType: From e07d0e94380b41d9cf45734a6af1c6d603b0fc71 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 31 Oct 2016 18:28:53 +0800 Subject: [PATCH 014/117] Move GenericValue deep-clone constructor into the class declaration. --- include/rapidjson/document.h | 89 +++++++++++++++++------------------- 1 file changed, 41 insertions(+), 48 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 2da67a9fb..895af88e5 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -617,8 +617,47 @@ class GenericValue { \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). \see CopyFrom() */ - template< typename SourceAllocator > - GenericValue(const GenericValue& rhs, Allocator & allocator); + template + GenericValue(const GenericValue& rhs, Allocator& allocator) { + switch (rhs.GetType()) { + case kObjectType: { + SizeType count = rhs.data_.o.size; + Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator); + new (&lm[i].value) GenericValue(rm[i].value, allocator); + } + data_.f.flags = kObjectFlag; + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } //! Constructor for boolean value. /*! \param b Boolean value @@ -2412,52 +2451,6 @@ class GenericDocument : public GenericValue { //! GenericDocument with UTF8 encoding typedef GenericDocument > Document; -// defined here due to the dependency on GenericDocument -template -template -inline -GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) -{ - switch (rhs.GetType()) { - case kObjectType: { - SizeType count = rhs.data_.o.size; - Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); - const typename GenericValue::Member* rm = rhs.GetMembersPointer(); - for (SizeType i = 0; i < count; i++) { - new (&lm[i].name) GenericValue(rm[i].name, allocator); - new (&lm[i].value) GenericValue(rm[i].value, allocator); - } - data_.f.flags = kObjectFlag; - data_.o.size = data_.o.capacity = count; - SetMembersPointer(lm); - } - break; - case kArrayType: { - SizeType count = rhs.data_.a.size; - GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); - const GenericValue* re = rhs.GetElementsPointer(); - for (SizeType i = 0; i < count; i++) - new (&le[i]) GenericValue(re[i], allocator); - data_.f.flags = kArrayFlag; - data_.a.size = data_.a.capacity = count; - SetElementsPointer(le); - } - break; - case kStringType: - if (rhs.data_.f.flags == kConstStringFlag) { - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - } else { - SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); - } - break; - default: - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - break; - } -} - //! Helper class for accessing Value of array type. /*! Instance of this helper class is obtained by \c GenericValue::GetArray(). From a077baa9c38a2d4cd95844a1f1ecd81cf1102752 Mon Sep 17 00:00:00 2001 From: SuperSodaSea Date: Sat, 5 Nov 2016 11:55:12 +0800 Subject: [PATCH 015/117] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8B=BC=E5=86=99?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/encoding.zh-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/encoding.zh-cn.md b/doc/encoding.zh-cn.md index 681692355..808ba525f 100644 --- a/doc/encoding.zh-cn.md +++ b/doc/encoding.zh-cn.md @@ -14,7 +14,7 @@ > (in §6) JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON is written in UTF-8, JSON is 8bit compatible. When JSON is written in UTF-16 or UTF-32, the binary content-transfer-encoding must be used. > -> 翻译:JSON 可使用 UTF-8、UTF-16 或 UTF-18 表示。当 JSON 以 UTF-8 写入,该 JSON 是 8 位兼容的。当 JSON 以 UTF-16 或 UTF-32 写入,就必须使用二进制的内容传送编码。 +> 翻译:JSON 可使用 UTF-8、UTF-16 或 UTF-32 表示。当 JSON 以 UTF-8 写入,该 JSON 是 8 位兼容的。当 JSON 以 UTF-16 或 UTF-32 写入,就必须使用二进制的内容传送编码。 RapidJSON 支持多种编码。它也能检查 JSON 的编码,以及在不同编码中进行转码。所有这些功能都是在内部实现,无需使用外部的程序库(如 [ICU](http://site.icu-project.org/))。 From bff326fb24fbfeba4bc8cb2f250687d4f6445604 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 21 Nov 2016 09:37:02 +0800 Subject: [PATCH 016/117] Update sax.md --- doc/sax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sax.md b/doc/sax.md index 1d4fc2ae5..ed6d46a60 100644 --- a/doc/sax.md +++ b/doc/sax.md @@ -122,7 +122,7 @@ class Handler { When the `Reader` encounters a JSON number, it chooses a suitable C++ type mapping. And then it calls *one* function out of `Int(int)`, `Uint(unsigned)`, `Int64(int64_t)`, `Uint64(uint64_t)` and `Double(double)`. If `kParseNumbersAsStrings` is enabled, `Reader` will always calls `RawNumber()` instead. -`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `'\0'` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And beware that, the character type depends on the target encoding, which will be explained later. +`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `\0` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And beware that, the character type depends on the target encoding, which will be explained later. When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `Key()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter. From 0024592c239d99b605f9e04a7516d43d3c176f79 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 21 Nov 2016 09:50:59 +0800 Subject: [PATCH 017/117] Update sax.zh-cn.md --- doc/sax.zh-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sax.zh-cn.md b/doc/sax.zh-cn.md index b20286de9..740c339fa 100644 --- a/doc/sax.zh-cn.md +++ b/doc/sax.zh-cn.md @@ -122,7 +122,7 @@ class Handler { 当 `Reader` 遇到 JSON number,它会选择一个合适的 C++ 类型映射,然后调用 `Int(int)`、`Uint(unsigned)`、`Int64(int64_t)`、`Uint64(uint64_t)` 及 `Double(double)` 的 * 其中之一个 *。 若开启了 `kParseNumbersAsStrings` 选项,`Reader` 便会改为调用 `RawNumber()`。 -当 `Reader` 遇到 JSON string,它会调用 `String(const char* str, SizeType length, bool copy)`。第一个参数是字符串的指针。第二个参数是字符串的长度(不包含空终止符号)。注意 RapidJSON 支持字串中含有空字符 `'\0'`。若出现这种情况,便会有 `strlen(str) < length`。最后的 `copy` 参数表示处理器是否需要复制该字符串。在正常解析时,`copy = true`。仅当使用原位解析时,`copy = false`。此外,还要注意字符的类型与目标编码相关,我们稍后会再谈这一点。 +当 `Reader` 遇到 JSON string,它会调用 `String(const char* str, SizeType length, bool copy)`。第一个参数是字符串的指针。第二个参数是字符串的长度(不包含空终止符号)。注意 RapidJSON 支持字串中含有空字符 `\0`。若出现这种情况,便会有 `strlen(str) < length`。最后的 `copy` 参数表示处理器是否需要复制该字符串。在正常解析时,`copy = true`。仅当使用原位解析时,`copy = false`。此外,还要注意字符的类型与目标编码相关,我们稍后会再谈这一点。 当 `Reader` 遇到 JSON object 的开始之时,它会调用 `StartObject()`。JSON 的 object 是一个键值对(成员)的集合。若 object 包含成员,它会先为成员的名字调用 `Key()`,然后再按值的类型调用函数。它不断调用这些键值对,直至最终调用 `EndObject(SizeType memberCount)`。注意 `memberCount` 参数对处理器来说只是协助性质,使用者可能不需要此参数。 From ba34c94533b67d81bef4f7eb80d941a768c2496b Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 28 Nov 2016 12:53:24 +0200 Subject: [PATCH 018/117] Update version to 1.1.0 --- library.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library.json b/library.json index 47fd352ac..21d6bcecf 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,11 @@ { "name": "RapidJSON", + "version": "1.1.0", "keywords": "json, sax, dom, parser, generator", "description": "A fast JSON parser/generator for C++ with both SAX/DOM style API", - "include": "include", + "export": { + "include": "include" + }, "examples": "example/*/*.cpp", "repository": { From 9fe93bb9847b8cfb889d49eb7e20d94d0767350d Mon Sep 17 00:00:00 2001 From: Andrey Glebov Date: Wed, 21 Dec 2016 10:17:25 +0300 Subject: [PATCH 019/117] - replaced RAPIDJSON_NEW macro with variadic varient --- include/rapidjson/allocators.h | 2 +- include/rapidjson/document.h | 4 ++-- include/rapidjson/internal/regex.h | 2 +- include/rapidjson/internal/stack.h | 2 +- include/rapidjson/pointer.h | 4 ++-- include/rapidjson/rapidjson.h | 2 +- include/rapidjson/schema.h | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h index 98affe03f..6405bc3a0 100644 --- a/include/rapidjson/allocators.h +++ b/include/rapidjson/allocators.h @@ -236,7 +236,7 @@ class MemoryPoolAllocator { */ bool AddChunk(size_t capacity) { if (!baseAllocator_) - ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator); if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { chunk->capacity = capacity; chunk->size = 0; diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 895af88e5..5822acce8 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2088,7 +2088,7 @@ class GenericDocument : public GenericValue { GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); } //! Constructor @@ -2101,7 +2101,7 @@ class GenericDocument : public GenericValue { allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS diff --git a/include/rapidjson/internal/regex.h b/include/rapidjson/internal/regex.h index 8530cd771..500136404 100644 --- a/include/rapidjson/internal/regex.h +++ b/include/rapidjson/internal/regex.h @@ -606,7 +606,7 @@ class GenericRegexSearch { { RAPIDJSON_ASSERT(regex_.IsValid()); if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); state0_.template Reserve(regex_.stateCount_); state1_.template Reserve(regex_.stateCount_); diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h index 022c9aab4..a1b456817 100644 --- a/include/rapidjson/internal/stack.h +++ b/include/rapidjson/internal/stack.h @@ -183,7 +183,7 @@ class Stack { size_t newCapacity; if (stack_ == 0) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); newCapacity = initialCapacity_; } else { newCapacity = GetCapacity(); diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index 0206ac1c8..eab66193c 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -758,7 +758,7 @@ class GenericPointer { */ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { if (!allocator_) // allocator is independently owned. - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) @@ -806,7 +806,7 @@ class GenericPointer { // Create own allocator if user did not supply. if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); // Count number of '/' as tokenCount tokenCount_ = 0; diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 053b2ce43..77611cec0 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -583,7 +583,7 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_NEW ///! customization point for global \c new -#define RAPIDJSON_NEW(x) new x +#define RAPIDJSON_NEW(type, ...) new type(__VA_ARGS__) #endif #ifndef RAPIDJSON_DELETE ///! customization point for global \c delete diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index e7af3cf57..a99f1e835 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1344,7 +1344,7 @@ class GenericSchemaDocument { schemaRef_(allocator, kInitialSchemaRefSize) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); @@ -1823,7 +1823,7 @@ RAPIDJSON_MULTILINEMACRO_END StateAllocator& GetStateAllocator() { if (!stateAllocator_) - stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator()); + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator); return *stateAllocator_; } From 3f120caeef7362ae0b4f219c5c6cafb5d074d698 Mon Sep 17 00:00:00 2001 From: Andrey Glebov Date: Wed, 21 Dec 2016 10:41:06 +0300 Subject: [PATCH 020/117] - replaced RAPIDJSON_NEW calls in fwdtest.cpp --- test/unittest/fwdtest.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/unittest/fwdtest.cpp b/test/unittest/fwdtest.cpp index 4f3268461..b9d18e8e1 100644 --- a/test/unittest/fwdtest.cpp +++ b/test/unittest/fwdtest.cpp @@ -118,23 +118,23 @@ Foo::Foo() : memorypoolallocator(RAPIDJSON_NEW(MemoryPoolAllocator<>)), // stream.h - stringstream(RAPIDJSON_NEW(StringStream(0))), - insitustringstream(RAPIDJSON_NEW(InsituStringStream(0))), + stringstream(RAPIDJSON_NEW(StringStream, NULL)), + insitustringstream(RAPIDJSON_NEW(InsituStringStream, NULL)), // stringbuffer.h stringbuffer(RAPIDJSON_NEW(StringBuffer)), // // filereadstream.h - // filereadstream(RAPIDJSON_NEW(FileReadStream(stdout, buffer, sizeof(buffer)))), + // filereadstream(RAPIDJSON_NEW(FileReadStream, stdout, buffer, sizeof(buffer))), // // filewritestream.h - // filewritestream(RAPIDJSON_NEW(FileWriteStream(stdout, buffer, sizeof(buffer)))), + // filewritestream(RAPIDJSON_NEW(FileWriteStream, stdout, buffer, sizeof(buffer))), // memorybuffer.h memorybuffer(RAPIDJSON_NEW(MemoryBuffer)), // memorystream.h - memorystream(RAPIDJSON_NEW(MemoryStream(0, 0))), + memorystream(RAPIDJSON_NEW(MemoryStream, NULL, 0)), // reader.h basereaderhandler(RAPIDJSON_NEW((BaseReaderHandler, void>))), @@ -154,8 +154,8 @@ Foo::Foo() : pointer(RAPIDJSON_NEW(Pointer)), // schema.h - schemadocument(RAPIDJSON_NEW(SchemaDocument(*document))), - schemavalidator(RAPIDJSON_NEW(SchemaValidator(*schemadocument))) + schemadocument(RAPIDJSON_NEW(SchemaDocument, *document)), + schemavalidator(RAPIDJSON_NEW(SchemaValidator, *schemadocument)) { } From 41ceb8624f2fb46fe5e62b7d2cce471c17db7a5f Mon Sep 17 00:00:00 2001 From: Andrey Glebov Date: Wed, 21 Dec 2016 14:03:53 +0300 Subject: [PATCH 021/117] - replaced RAPIDJSON_NEW with C++98 compatible version --- include/rapidjson/allocators.h | 2 +- include/rapidjson/document.h | 4 ++-- include/rapidjson/internal/regex.h | 2 +- include/rapidjson/internal/stack.h | 2 +- include/rapidjson/pointer.h | 4 ++-- include/rapidjson/rapidjson.h | 2 +- include/rapidjson/schema.h | 4 ++-- test/unittest/fwdtest.cpp | 25 ++++++++++++++----------- 8 files changed, 24 insertions(+), 21 deletions(-) diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h index 6405bc3a0..655f4a385 100644 --- a/include/rapidjson/allocators.h +++ b/include/rapidjson/allocators.h @@ -236,7 +236,7 @@ class MemoryPoolAllocator { */ bool AddChunk(size_t capacity) { if (!baseAllocator_) - ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator); + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { chunk->capacity = capacity; chunk->size = 0; diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 5822acce8..3873b99b4 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2088,7 +2088,7 @@ class GenericDocument : public GenericValue { GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } //! Constructor @@ -2101,7 +2101,7 @@ class GenericDocument : public GenericValue { allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS diff --git a/include/rapidjson/internal/regex.h b/include/rapidjson/internal/regex.h index 500136404..936b71448 100644 --- a/include/rapidjson/internal/regex.h +++ b/include/rapidjson/internal/regex.h @@ -606,7 +606,7 @@ class GenericRegexSearch { { RAPIDJSON_ASSERT(regex_.IsValid()); if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); state0_.template Reserve(regex_.stateCount_); state1_.template Reserve(regex_.stateCount_); diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h index a1b456817..299e651ed 100644 --- a/include/rapidjson/internal/stack.h +++ b/include/rapidjson/internal/stack.h @@ -183,7 +183,7 @@ class Stack { size_t newCapacity; if (stack_ == 0) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); newCapacity = initialCapacity_; } else { newCapacity = GetCapacity(); diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index eab66193c..4d6391f90 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -758,7 +758,7 @@ class GenericPointer { */ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { if (!allocator_) // allocator is independently owned. - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) @@ -806,7 +806,7 @@ class GenericPointer { // Create own allocator if user did not supply. if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); // Count number of '/' as tokenCount tokenCount_ = 0; diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 77611cec0..a005257ee 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -583,7 +583,7 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_NEW ///! customization point for global \c new -#define RAPIDJSON_NEW(type, ...) new type(__VA_ARGS__) +#define RAPIDJSON_NEW(TypeName) new TypeName #endif #ifndef RAPIDJSON_DELETE ///! customization point for global \c delete diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index a99f1e835..4760d1b43 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1344,7 +1344,7 @@ class GenericSchemaDocument { schemaRef_(allocator, kInitialSchemaRefSize) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); @@ -1823,7 +1823,7 @@ RAPIDJSON_MULTILINEMACRO_END StateAllocator& GetStateAllocator() { if (!stateAllocator_) - stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator); + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); return *stateAllocator_; } diff --git a/test/unittest/fwdtest.cpp b/test/unittest/fwdtest.cpp index b9d18e8e1..1936d9779 100644 --- a/test/unittest/fwdtest.cpp +++ b/test/unittest/fwdtest.cpp @@ -100,6 +100,9 @@ struct Foo { #include "rapidjson/prettywriter.h" #include "rapidjson/schema.h" // -> pointer.h +typedef Transcoder, UTF8<> > TranscoderUtf8ToUtf8; +typedef BaseReaderHandler, void> BaseReaderHandlerUtf8Void; + Foo::Foo() : // encodings.h utf8(RAPIDJSON_NEW(UTF8<>)), @@ -111,40 +114,40 @@ Foo::Foo() : utf32le(RAPIDJSON_NEW(UTF32LE<>)), ascii(RAPIDJSON_NEW(ASCII<>)), autoutf(RAPIDJSON_NEW(AutoUTF)), - transcoder(RAPIDJSON_NEW((Transcoder, UTF8<> >))), + transcoder(RAPIDJSON_NEW(TranscoderUtf8ToUtf8)), // allocators.h crtallocator(RAPIDJSON_NEW(CrtAllocator)), memorypoolallocator(RAPIDJSON_NEW(MemoryPoolAllocator<>)), // stream.h - stringstream(RAPIDJSON_NEW(StringStream, NULL)), - insitustringstream(RAPIDJSON_NEW(InsituStringStream, NULL)), + stringstream(RAPIDJSON_NEW(StringStream)(NULL)), + insitustringstream(RAPIDJSON_NEW(InsituStringStream)(NULL)), // stringbuffer.h stringbuffer(RAPIDJSON_NEW(StringBuffer)), // // filereadstream.h - // filereadstream(RAPIDJSON_NEW(FileReadStream, stdout, buffer, sizeof(buffer))), + // filereadstream(RAPIDJSON_NEW(FileReadStream)(stdout, buffer, sizeof(buffer))), // // filewritestream.h - // filewritestream(RAPIDJSON_NEW(FileWriteStream, stdout, buffer, sizeof(buffer))), + // filewritestream(RAPIDJSON_NEW(FileWriteStream)(stdout, buffer, sizeof(buffer))), // memorybuffer.h memorybuffer(RAPIDJSON_NEW(MemoryBuffer)), // memorystream.h - memorystream(RAPIDJSON_NEW(MemoryStream, NULL, 0)), + memorystream(RAPIDJSON_NEW(MemoryStream)(NULL, 0)), // reader.h - basereaderhandler(RAPIDJSON_NEW((BaseReaderHandler, void>))), + basereaderhandler(RAPIDJSON_NEW(BaseReaderHandlerUtf8Void)), reader(RAPIDJSON_NEW(Reader)), // writer.h - writer(RAPIDJSON_NEW((Writer))), + writer(RAPIDJSON_NEW(Writer)), // prettywriter.h - prettywriter(RAPIDJSON_NEW((PrettyWriter))), + prettywriter(RAPIDJSON_NEW(PrettyWriter)), // document.h value(RAPIDJSON_NEW(Value)), @@ -154,8 +157,8 @@ Foo::Foo() : pointer(RAPIDJSON_NEW(Pointer)), // schema.h - schemadocument(RAPIDJSON_NEW(SchemaDocument, *document)), - schemavalidator(RAPIDJSON_NEW(SchemaValidator, *schemadocument)) + schemadocument(RAPIDJSON_NEW(SchemaDocument)(*document)), + schemavalidator(RAPIDJSON_NEW(SchemaValidator)(*schemadocument)) { } From af4ec9b7e92e1378ff18aa393e6a5dc7f440420a Mon Sep 17 00:00:00 2001 From: SuperSodaSea Date: Fri, 30 Dec 2016 23:12:41 +0800 Subject: [PATCH 022/117] Translate doc/internals.md Part 1 --- doc/internals.zh-cn.md | 365 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 doc/internals.zh-cn.md diff --git a/doc/internals.zh-cn.md b/doc/internals.zh-cn.md new file mode 100644 index 000000000..f30c56956 --- /dev/null +++ b/doc/internals.zh-cn.md @@ -0,0 +1,365 @@ +# 内部架构 + +本部分记录了一些设计和实现细节。 + +[TOC] + +# 架构 {#Architecture} + +## SAX 和 DOM + +下面的 UML 图显示了 SAX 和 DOM 的基本关系。 + +![架构 UML 类图](diagram/architecture.png) + +关系的核心是 `Handler` 概念。在 SAX 一边,`Reader` 从流解析 JSON 并将事件发送到 `Handler`。`Writer` 实现了 `Handler` 概念,用于处理相同的事件。在 DOM 一边,`Document` 实现了 `Handler` 概念,用于通过这些时间来构建 DOM。`Value` 支持了 `Value::Accept(Handler&)` 函数,它可以将 DOM 转换为事件进行发送。 + +在这个设计,SAX 是不依赖于 DOM 的。甚至 `Reader` 和 `Writer` 之间也没有依赖。这提供了连接事件发送器和处理器的灵活性。除此之外,`Value` 也是不依赖于 SAX 的。所以,除了将 DOM 序列化为 JSON 之外,用户也可以将其序列化为 XML,或者做任何其他事情。 + +## 工具类 + +SAX 和 DOM API 都依赖于3个额外的概念:`Allocator`、`Encoding` 和 `Stream`。它们的继承层次结构如下图所示。 + +![工具类 UML 类图](diagram/utilityclass.png) + +# 值(Value) {#Value} + +`Value` (实际上被定义为 `GenericValue>`)是 DOM API 的核心。本部分描述了它的设计。 + +## 数据布局 {#DataLayout} + +`Value` 是[可变类型](http://en.wikipedia.org/wiki/Variant_type)。在 RapidJSON 的上下文中,一个 `Value` 的实例可以包含6种 JSON 数据类型之一。通过使用 `union` ,这是可能实现的。每一个 `Value` 包含两个成员:`union Data data_` 和 `unsigned flags_`。`flags_` 表明了 JSON 类型,以及附加的信息。 + +下表显示了所有类型的数据布局。32位/64位列表明了字段所占用的字节数。 + +| Null | | 32位 | 64位 | +|-------------------|----------------------------------|:----:|:----:| +| (未使用) | |4 |8 | +| (未使用) | |4 |4 | +| (未使用) | |4 |4 | +| `unsigned flags_` | `kNullType kNullFlag` |4 |4 | + +| Bool | | 32位 | 64位 | +|-------------------|----------------------------------------------------|:----:|:----:| +| (未使用) | |4 |8 | +| (未使用) | |4 |4 | +| (未使用) | |4 |4 | +| `unsigned flags_` | `kBoolType` (either `kTrueFlag` or `kFalseFlag`) |4 |4 | + +| String | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `Ch* str` | 指向字符串的指针(可能拥有所有权) |4 |8 | +| `SizeType length` | 字符串长度 |4 |4 | +| (未使用) | |4 |4 | +| `unsigned flags_` | `kStringType kStringFlag ...` |4 |4 | + +| Object | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `Member* members` | 指向成员数组的指针(拥有所有权) |4 |8 | +| `SizeType size` | 成员数量 |4 |4 | +| `SizeType capacity` | 成员容量 |4 |4 | +| `unsigned flags_` | `kObjectType kObjectFlag` |4 |4 | + +| Array | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `Value* values` | 指向值数组的指针(拥有所有权) |4 |8 | +| `SizeType size` | 值数量 |4 |4 | +| `SizeType capacity` | 值容量 |4 |4 | +| `unsigned flags_` | `kArrayType kArrayFlag` |4 |4 | + +| Number (Int) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `int i` | 32位有符号整数 |4 |4 | +| (零填充) | 0 |4 |4 | +| (未使用) | |4 |8 | +| `unsigned flags_` | `kNumberType kNumberFlag kIntFlag kInt64Flag ...` |4 |4 | + +| Number (UInt) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `unsigned u` | 32位无符号整数 |4 |4 | +| (零填充) | 0 |4 |4 | +| (未使用) | |4 |8 | +| `unsigned flags_` | `kNumberType kNumberFlag kUIntFlag kUInt64Flag ...` |4 |4 | + +| Number (Int64) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `int64_t i64` | 64位有符号整数 |8 |8 | +| (未使用) | |4 |8 | +| `unsigned flags_` | `kNumberType kNumberFlag kInt64Flag ...` |4 |4 | + +| Number (Uint64) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `uint64_t i64` | 64位无符号整数 |8 |8 | +| (未使用) | |4 |8 | +| `unsigned flags_` | `kNumberType kNumberFlag kInt64Flag ...` |4 |4 | + +| Number (Double) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `uint64_t i64` | 双精度浮点数 |8 |8 | +| (未使用) | |4 |8 | +| `unsigned flags_` |`kNumberType kNumberFlag kDoubleFlag`|4 |4 | + +这里有一些需要注意的地方: +* 为了减少在64位架构上的内存消耗,`SizeType` 被定义为 `unsigned` 而不是 `size_t`。 +* 32位整数的零填充可能被放在实际类型的前面或后面,这依赖于字节序。这使得它可以将32位整数不经过任何转换就可以解释为64位整数。 +* `Int` 永远是 `Int64`,反之不然。 + +## 标志 {#Flags} + +32位的 `flags_` 包含了 JSON 类型和其他信息。如前文中的表所述,每一种 JSON 类型包含了冗余的 `kXXXType` 和 `kXXXFlag`。这个设计是为了优化测试位标志(`IsNumber()`)和获取每一种类型的序列号(`GetType()`)。 + +字符串有两个可选的标志。`kCopyFlag` 表明这个字符串拥有字符串拷贝的所有权。而 `kInlineStrFlag` 意味着使用了[短字符串优化](#ShortString)。 + +数字更加复杂一些。对于普通的整数值,它可以包含 `kIntFlag`、`kUintFlag`、 `kInt64Flag` 和/或 `kUint64Flag`,这由整数的范围决定。带有小数或者超过64位所能表达的范围的整数的数字会被存储为带有 `kDoubleFlag` 的 `double`。 + +## 短字符串优化 {#ShortString} + +[Kosta](https://github.com/Kosta-Github) 提供了很棒的短字符串优化。这个优化的xxx如下所述。除去 `flags_` ,`Value` 有12或16字节(对于32位或64位)来存储实际的数据。这为在其内部直接存储短字符串而不是存储字符串的指针创造了可能。对于1字节的字符类型(例如 `char`),它可以在 `Value` 类型内部存储至多11或15个字符的字符串。 + +|ShortString (Ch=char)| | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `Ch str[MaxChars]` | 字符串缓冲区 |11 |15 | +| `Ch invLength` | MaxChars - Length |1 |1 | +| `unsigned flags_` | `kStringType kStringFlag ...` |4 |4 | + +这里使用了一项特殊的技术。它存储了 (MaxChars - length) 而不直接存储字符串的长度。这使得存储11个字符并且带有后缀 `\0` 成为可能。 + +这个优化可以减少字符串拷贝内存占用。它也改善了缓存一致性,并进一步提高了运行时性能。 + +# 分配器(Allocator) {#InternalAllocator} + +`Allocator` 是 RapidJSON 中的概念: +~~~cpp +concept Allocator { + static const bool kNeedFree; //!< 表明这个分配器是否需要调用 Free()。 + + // 申请内存块。 + // \param size 内存块的大小,以字节记。 + // \returns 指向内存块的指针。 + void* Malloc(size_t size); + + // 调整内存块的大小。 + // \param originalPtr 当前内存块的指针。空指针是被允许的。 + // \param originalSize 当前大小,以字节记。(设计问题:因为有些分配器可能不会记录它,显示的传递它可以节约内存。) + // \param newSize 新大小,以字节记。 + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // 释放内存块。 + // \param ptr 指向内存块的指针。空指针是被允许的。 + static void Free(void *ptr); +}; +~~~ + +需要注意的是 `Malloc()` 和 `Realloc()` 是成员函数而 `Free()` 是静态成员函数。 + +## MemoryPoolAllocator {#MemoryPoolAllocator} + +`MemoryPoolAllocator` 是 DOM 的默认内存分配器。它只申请内存而不释放内存。这对于构建 DOM 树非常合适。 + +在它的内部,它从基础的内存分配器申请内存块(默认为 `CrtAllocator`)并将这些内存块存储为单向链表。当用户请求申请内存,它会遵循下列步骤来申请内存: + +1. 如果可用,使用用户提供的缓冲区。(见 [User Buffer section in DOM](doc/dom.md)) +2. 如果用户提供的缓冲区已满,使用当前内存块。 +3. 如果当前内存块已满,申请新的内存块。 + +# 解析优化 {#ParsingOptimization} + +## 使用 SIMD 跳过空格 {#SkipwhitespaceWithSIMD} + +当从流中解析 JSON 时,解析器需要跳过4种空格字符: + +1. 空格 (`U+0020`) +2. 制表符 (`U+000B`) +3. 换行 (`U+000A`) +4. 回车 (`U+000D`) + +这是一份简单的实现: +~~~cpp +void SkipWhitespace(InputStream& s) { + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + s.Take(); +} +~~~ + +但是,这需要对每个字符进行4次比较以及一些分支。这被发现是一个热点。 + +为了加速这一处理,RapidJSON 使用 SIMD 来在一次迭代中比较16个字符和4个空格。目前 RapidJSON 只支持 SSE2 和 SSE4.2 指令。同时它也只会对 UTF-8 内存流启用,包括字符串流或 *原位* 解析。 + +你可以通过在包含 `rapidjson.h` 之前定义 `RAPIDJSON_SSE2` 或 `RAPIDJSON_SSE42` 来启用这个优化。一些编译器可以检测这个设置,如 `perftest.h`: + +~~~cpp +// __SSE2__ 和 __SSE4_2__ 可被 gcc、clang 和 Intel 编译器识别: +// 如果支持的话,我们在 gmake 中使用了 -march=native 来启用 -msse2 和 -msse4.2 +#if defined(__SSE4_2__) +# define RAPIDJSON_SSE42 +#elif defined(__SSE2__) +# define RAPIDJSON_SSE2 +#endif +~~~ + +需要注意的是,这是编译期的设置。在不支持这些指令的机器上运行可执行文件会使它崩溃。 + +### Page boundary issue + +In an early version of RapidJSON, [an issue](https://code.google.com/archive/p/rapidjson/issues/104) reported that the `SkipWhitespace_SIMD()` causes crash very rarely (around 1 in 500,000). After investigation, it is suspected that `_mm_loadu_si128()` accessed bytes after `'\0'`, and across a protected page boundary. + +In [Intel® 64 and IA-32 Architectures Optimization Reference Manual +](http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html), section 10.2.1: + +> To support algorithms requiring unaligned 128-bit SIMD memory accesses, memory buffer allocation by a caller function should consider adding some pad space so that a callee function can safely use the address pointer safely with unaligned 128-bit SIMD memory operations. +> The minimal padding size should be the width of the SIMD register that might be used in conjunction with unaligned SIMD memory access. + +This is not feasible as RapidJSON should not enforce such requirement. + +To fix this issue, currently the routine process bytes up to the next aligned address. After tha, use aligned read to perform SIMD processing. Also see [#85](https://github.com/miloyip/rapidjson/issues/85). + +## Local Stream Copy {#LocalStreamCopy} + +During optimization, it is found that some compilers cannot localize some member data access of streams into local variables or registers. Experimental results show that for some stream types, making a copy of the stream and used it in inner-loop can improve performance. For example, the actual (non-SIMD) implementation of `SkipWhitespace()` is implemented as: + +~~~cpp +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + s.Take(); +} +~~~ + +Depending on the traits of stream, `StreamLocalCopy` will make (or not make) a copy of the stream object, use it locally and copy the states of stream back to the original stream. + +## Parsing to Double {#ParsingDouble} + +Parsing string into `double` is difficult. The standard library function `strtod()` can do the job but it is slow. By default, the parsers use normal precision setting. This has has maximum 3 [ULP](http://en.wikipedia.org/wiki/Unit_in_the_last_place) error and implemented in `internal::StrtodNormalPrecision()`. + +When using `kParseFullPrecisionFlag`, the parsers calls `internal::StrtodFullPrecision()` instead, and this function actually implemented 3 versions of conversion methods. +1. [Fast-Path](http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/). +2. Custom DIY-FP implementation as in [double-conversion](https://github.com/floitsch/double-conversion). +3. Big Integer Method as in (Clinger, William D. How to read floating point numbers accurately. Vol. 25. No. 6. ACM, 1990). + +If the first conversion methods fail, it will try the second, and so on. + +# Generation Optimization {#GenerationOptimization} + +## Integer-to-String conversion {#itoa} + +The naive algorithm for integer-to-string conversion involves division per each decimal digit. We have implemented various implementations and evaluated them in [itoa-benchmark](https://github.com/miloyip/itoa-benchmark). + +Although SSE2 version is the fastest but the difference is minor by comparing to the first running-up `branchlut`. And `branchlut` is pure C++ implementation so we adopt `branchlut` in RapidJSON. + +## Double-to-String conversion {#dtoa} + +Originally RapidJSON uses `snprintf(..., ..., "%g")` to achieve double-to-string conversion. This is not accurate as the default precision is 6. Later we also find that this is slow and there is an alternative. + +Google's V8 [double-conversion](https://github.com/floitsch/double-conversion +) implemented a newer, fast algorithm called Grisu3 (Loitsch, Florian. "Printing floating-point numbers quickly and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243.). + +However, since it is not header-only so that we implemented a header-only version of Grisu2. This algorithm guarantees that the result is always accurate. And in most of cases it produces the shortest (optimal) string representation. + +The header-only conversion function has been evaluated in [dtoa-benchmark](https://github.com/miloyip/dtoa-benchmark). + +# Parser {#Parser} + +## Iterative Parser {#IterativeParser} + +The iterative parser is a recursive descent LL(1) parser +implemented in a non-recursive manner. + +### Grammar {#IterativeParserGrammar} + +The grammar used for this parser is based on strict JSON syntax: +~~~~~~~~~~ +S -> array | object +array -> [ values ] +object -> { members } +values -> non-empty-values | ε +non-empty-values -> value addition-values +addition-values -> ε | , non-empty-values +members -> non-empty-members | ε +non-empty-members -> member addition-members +addition-members -> ε | , non-empty-members +member -> STRING : value +value -> STRING | NUMBER | NULL | BOOLEAN | object | array +~~~~~~~~~~ + +Note that left factoring is applied to non-terminals `values` and `members` +to make the grammar be LL(1). + +### Parsing Table {#IterativeParserParsingTable} + +Based on the grammar, we can construct the FIRST and FOLLOW set. + +The FIRST set of non-terminals is listed below: + +| NON-TERMINAL | FIRST | +|:-----------------:|:--------------------------------:| +| array | [ | +| object | { | +| values | ε STRING NUMBER NULL BOOLEAN { [ | +| addition-values | ε COMMA | +| members | ε STRING | +| addition-members | ε COMMA | +| member | STRING | +| value | STRING NUMBER NULL BOOLEAN { [ | +| S | [ { | +| non-empty-members | STRING | +| non-empty-values | STRING NUMBER NULL BOOLEAN { [ | + +The FOLLOW set is listed below: + +| NON-TERMINAL | FOLLOW | +|:-----------------:|:-------:| +| S | $ | +| array | , $ } ] | +| object | , $ } ] | +| values | ] | +| non-empty-values | ] | +| addition-values | ] | +| members | } | +| non-empty-members | } | +| addition-members | } | +| member | , } | +| value | , } ] | + +Finally the parsing table can be constructed from FIRST and FOLLOW set: + +| NON-TERMINAL | [ | { | , | : | ] | } | STRING | NUMBER | NULL | BOOLEAN | +|:-----------------:|:---------------------:|:---------------------:|:-------------------:|:-:|:-:|:-:|:-----------------------:|:---------------------:|:---------------------:|:---------------------:| +| S | array | object | | | | | | | | | +| array | [ values ] | | | | | | | | | | +| object | | { members } | | | | | | | | | +| values | non-empty-values | non-empty-values | | | ε | | non-empty-values | non-empty-values | non-empty-values | non-empty-values | +| non-empty-values | value addition-values | value addition-values | | | | | value addition-values | value addition-values | value addition-values | value addition-values | +| addition-values | | | , non-empty-values | | ε | | | | | | +| members | | | | | | ε | non-empty-members | | | | +| non-empty-members | | | | | | | member addition-members | | | | +| addition-members | | | , non-empty-members | | | ε | | | | | +| member | | | | | | | STRING : value | | | | +| value | array | object | | | | | STRING | NUMBER | NULL | BOOLEAN | + +There is a great [tool](http://hackingoff.com/compilers/predict-first-follow-set) for above grammar analysis. + +### Implementation {#IterativeParserImplementation} + +Based on the parsing table, a direct(or conventional) implementation +that pushes the production body in reverse order +while generating a production could work. + +In RapidJSON, several modifications(or adaptations to current design) are made to a direct implementation. + +First, the parsing table is encoded in a state machine in RapidJSON. +States are constructed by the head and body of production. +State transitions are constructed by production rules. +Besides, extra states are added for productions involved with `array` and `object`. +In this way the generation of array values or object members would be a single state transition, +rather than several pop/push operations in the direct implementation. +This also makes the estimation of stack size more easier. + +The state diagram is shown as follows: + +![State Diagram](diagram/iterative-parser-states-diagram.png) + +Second, the iterative parser also keeps track of array's value count and object's member count +in its internal stack, which may be different from a conventional implementation. From dba9816009916e05675d9e504fd4231fbc846382 Mon Sep 17 00:00:00 2001 From: SuperSodaSea Date: Sat, 31 Dec 2016 11:23:05 +0800 Subject: [PATCH 023/117] Translate doc/internals.md Part 2 --- doc/internals.zh-cn.md | 111 ++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 58 deletions(-) diff --git a/doc/internals.zh-cn.md b/doc/internals.zh-cn.md index f30c56956..ec5795952 100644 --- a/doc/internals.zh-cn.md +++ b/doc/internals.zh-cn.md @@ -199,23 +199,23 @@ void SkipWhitespace(InputStream& s) { 需要注意的是,这是编译期的设置。在不支持这些指令的机器上运行可执行文件会使它崩溃。 -### Page boundary issue +### 页面对齐问题 -In an early version of RapidJSON, [an issue](https://code.google.com/archive/p/rapidjson/issues/104) reported that the `SkipWhitespace_SIMD()` causes crash very rarely (around 1 in 500,000). After investigation, it is suspected that `_mm_loadu_si128()` accessed bytes after `'\0'`, and across a protected page boundary. +在 RapidJSON 的早期版本中,被报告了[一个问题](https://code.google.com/archive/p/rapidjson/issues/104):`SkipWhitespace_SIMD()` 会罕见地导致崩溃(约五十万分之一的几率)。在调查之后,怀疑是 `_mm_loadu_si128()` 访问了 `'\0'` 之后的内存,并越过被保护的页面边界。 -In [Intel® 64 and IA-32 Architectures Optimization Reference Manual -](http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html), section 10.2.1: +在 [Intel® 64 and IA-32 Architectures Optimization Reference Manual +](http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html) 中,章节 10.2.1: -> To support algorithms requiring unaligned 128-bit SIMD memory accesses, memory buffer allocation by a caller function should consider adding some pad space so that a callee function can safely use the address pointer safely with unaligned 128-bit SIMD memory operations. -> The minimal padding size should be the width of the SIMD register that might be used in conjunction with unaligned SIMD memory access. +> 为了支持需要费对齐的128位 SIMD 内存访问的算法,调用者的内存缓冲区申请应当考虑添加一些填充空间,这样被调用的函数可以安全地将地址指针用于未对齐的128位 SIMD 内存操作。 +> 在结合非对齐的 SIMD 内存操作中,最小的对齐大小应该等于 SIMD 寄存器的大小。 -This is not feasible as RapidJSON should not enforce such requirement. +对于 RapidJSON 来说,这显然是不可行的,因为 RapidJSON 不应当强迫用户进行内存对齐。 -To fix this issue, currently the routine process bytes up to the next aligned address. After tha, use aligned read to perform SIMD processing. Also see [#85](https://github.com/miloyip/rapidjson/issues/85). +为了修复这个问题,当前的代码会先按字节处理直到下一个对齐的地址。在这之后,使用对齐读取来进行 SIMD 处理。见 [#85](https://github.com/miloyip/rapidjson/issues/85)。 -## Local Stream Copy {#LocalStreamCopy} +## 局部流拷贝 {#LocalStreamCopy} -During optimization, it is found that some compilers cannot localize some member data access of streams into local variables or registers. Experimental results show that for some stream types, making a copy of the stream and used it in inner-loop can improve performance. For example, the actual (non-SIMD) implementation of `SkipWhitespace()` is implemented as: +在优化的过程中,我们发现一些编译器不能将访问流的一些成员数据放入局部变量或者寄存器中。测试结果显示,对于一些流类型,创建流的拷贝并将其用于内层循环中可以改善性能。例如,实际(非 SIMD)的 `SkipWhitespace()` 被实现为: ~~~cpp template @@ -228,48 +228,47 @@ void SkipWhitespace(InputStream& is) { } ~~~ -Depending on the traits of stream, `StreamLocalCopy` will make (or not make) a copy of the stream object, use it locally and copy the states of stream back to the original stream. +基于流的特征,`StreamLocalCopy` 会创建(或不创建)流对象的拷贝,在局部使用它并将流的状态拷贝回原来的流。 -## Parsing to Double {#ParsingDouble} +## 解析为双精度浮点数 {#ParsingDouble} -Parsing string into `double` is difficult. The standard library function `strtod()` can do the job but it is slow. By default, the parsers use normal precision setting. This has has maximum 3 [ULP](http://en.wikipedia.org/wiki/Unit_in_the_last_place) error and implemented in `internal::StrtodNormalPrecision()`. +将字符串解析为 `double` 并不简单。标准库函数 `strtod()` 可以胜任这项工作,但它比较缓慢。默认情况下,解析器使用默认的精度设置。这最多有 3[ULP](http://en.wikipedia.org/wiki/Unit_in_the_last_place) 的误差,并实现在 `internal::StrtodNormalPrecision()` 中。 -When using `kParseFullPrecisionFlag`, the parsers calls `internal::StrtodFullPrecision()` instead, and this function actually implemented 3 versions of conversion methods. -1. [Fast-Path](http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/). -2. Custom DIY-FP implementation as in [double-conversion](https://github.com/floitsch/double-conversion). -3. Big Integer Method as in (Clinger, William D. How to read floating point numbers accurately. Vol. 25. No. 6. ACM, 1990). +当使用 `kParseFullPrecisionFlag` 时,编译器会改为调用 `internal::StrtodFullPrecision()` ,这个函数会自动调用三个版本的转换。 +1. [Fast-Path](http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/)。 +2. [double-conversion](https://github.com/floitsch/double-conversion) 中的自定义 DIY-FP 实现。 +3. (Clinger, William D. How to read floating point numbers accurately. Vol. 25. No. 6. ACM, 1990) 中的大整数算法。 -If the first conversion methods fail, it will try the second, and so on. +如果第一个转换方法失败,则尝试使用第二种方法,以此类推。 -# Generation Optimization {#GenerationOptimization} +# 生成优化 {#GenerationOptimization} -## Integer-to-String conversion {#itoa} +## 整数到字符串的转换 {#itoa} -The naive algorithm for integer-to-string conversion involves division per each decimal digit. We have implemented various implementations and evaluated them in [itoa-benchmark](https://github.com/miloyip/itoa-benchmark). +整数到字符串转换的朴素算法需要对每一个十进制位进行一次处罚。我们实现了若干版本并在 [itoa-benchmark](https://github.com/miloyip/itoa-benchmark) 中对它们进行了评估。 -Although SSE2 version is the fastest but the difference is minor by comparing to the first running-up `branchlut`. And `branchlut` is pure C++ implementation so we adopt `branchlut` in RapidJSON. +虽然 SSE2 版本是最快的,但它和第二快的 `branchlut` 差距不大。而且 `branchlut` 是纯C++实现,所以我们在 RapidJSON 中使用了 `branchlut`。 -## Double-to-String conversion {#dtoa} +## 双精度浮点数到字符串的转换 {#dtoa} -Originally RapidJSON uses `snprintf(..., ..., "%g")` to achieve double-to-string conversion. This is not accurate as the default precision is 6. Later we also find that this is slow and there is an alternative. +原来 RapidJSON 使用 `snprintf(..., ..., "%g")` 来进行双精度浮点数到字符串的转换。这是不准确的,因为默认的精度是6。随后我们发现它很缓慢,而且有其它的替代品。 -Google's V8 [double-conversion](https://github.com/floitsch/double-conversion -) implemented a newer, fast algorithm called Grisu3 (Loitsch, Florian. "Printing floating-point numbers quickly and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243.). +Google 的 V8 [double-conversion](https://github.com/floitsch/double-conversion +) 实现了更新的、快速的被称为 Grisu3 的算法(Loitsch, Florian. "Printing floating-point numbers quickly and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243.)。 -However, since it is not header-only so that we implemented a header-only version of Grisu2. This algorithm guarantees that the result is always accurate. And in most of cases it produces the shortest (optimal) string representation. +然而,这个实现不是仅头文件的,所以我们实现了一个仅头文件的 Grisu2 版本。这个算法保证了结果永远精确。而且在大多数情况下,它会生成最短的(可选)字符串表示。 -The header-only conversion function has been evaluated in [dtoa-benchmark](https://github.com/miloyip/dtoa-benchmark). +这个仅头文件的转换函数在 [dtoa-benchmark](https://github.com/miloyip/dtoa-benchmark) 中进行评估。 -# Parser {#Parser} +# 解析器 {#Parser} -## Iterative Parser {#IterativeParser} +## 迭代解析 {#IterativeParser} -The iterative parser is a recursive descent LL(1) parser -implemented in a non-recursive manner. +迭代解析器是一个以非递归方式实现的递归下降的 LL(1) 解析器。 -### Grammar {#IterativeParserGrammar} +### 语法 {#IterativeParserGrammar} -The grammar used for this parser is based on strict JSON syntax: +解析器使用的语法是基于严格 JSON 语法的: ~~~~~~~~~~ S -> array | object array -> [ values ] @@ -284,14 +283,13 @@ member -> STRING : value value -> STRING | NUMBER | NULL | BOOLEAN | object | array ~~~~~~~~~~ -Note that left factoring is applied to non-terminals `values` and `members` -to make the grammar be LL(1). +注意到左因子被加入了非终结符的 `values` 和 `members` 来保证语法是 LL(1) 的。 -### Parsing Table {#IterativeParserParsingTable} +### 解析表 {#IterativeParserParsingTable} -Based on the grammar, we can construct the FIRST and FOLLOW set. +基于这份语法,我们可以构造 FIRST 和 FOLLOW 集合。 -The FIRST set of non-terminals is listed below: +非终结符的 FIRST 集合如下所示: | NON-TERMINAL | FIRST | |:-----------------:|:--------------------------------:| @@ -307,7 +305,7 @@ The FIRST set of non-terminals is listed below: | non-empty-members | STRING | | non-empty-values | STRING NUMBER NULL BOOLEAN { [ | -The FOLLOW set is listed below: +FOLLOW 集合如下所示: | NON-TERMINAL | FOLLOW | |:-----------------:|:-------:| @@ -323,7 +321,7 @@ The FOLLOW set is listed below: | member | , } | | value | , } ] | -Finally the parsing table can be constructed from FIRST and FOLLOW set: +最终可以从 FIRST 和 FOLLOW 集合生成解析表: | NON-TERMINAL | [ | { | , | : | ] | } | STRING | NUMBER | NULL | BOOLEAN | |:-----------------:|:---------------------:|:---------------------:|:-------------------:|:-:|:-:|:-:|:-----------------------:|:---------------------:|:---------------------:|:---------------------:| @@ -339,27 +337,24 @@ Finally the parsing table can be constructed from FIRST and FOLLOW set: | member | | | | | | | STRING : value | | | | | value | array | object | | | | | STRING | NUMBER | NULL | BOOLEAN | -There is a great [tool](http://hackingoff.com/compilers/predict-first-follow-set) for above grammar analysis. +对于上面的语法分析,这里有一个很棒的[工具](http://hackingoff.com/compilers/predict-first-follow-set)。 -### Implementation {#IterativeParserImplementation} +### 实现 {#IterativeParserImplementation} -Based on the parsing table, a direct(or conventional) implementation -that pushes the production body in reverse order -while generating a production could work. +基于这份解析表,一个直接的(常规的)将规则反向入栈的实现可以正常工作。 -In RapidJSON, several modifications(or adaptations to current design) are made to a direct implementation. +在 RapidJSON 中,对直接的实现进行了一些修改: -First, the parsing table is encoded in a state machine in RapidJSON. -States are constructed by the head and body of production. -State transitions are constructed by production rules. -Besides, extra states are added for productions involved with `array` and `object`. -In this way the generation of array values or object members would be a single state transition, -rather than several pop/push operations in the direct implementation. -This also makes the estimation of stack size more easier. +首先,在 RapidJSON 中,这份解析表被编码为状态机。 +规则由头部和主体组成。 +状态转换由规则构造。 +除此之外,额外的状态被添加到与 `array` 和 `object` 有关的规则。 +通过这种方式,生成数组值或对象成员可以只用一次状态转移便可完成, +而不需要在直接的实现中的多次出栈/入栈操作。 +这也使得估计栈的大小更加容易。 -The state diagram is shown as follows: +状态图如如下所示: -![State Diagram](diagram/iterative-parser-states-diagram.png) +![状态图](diagram/iterative-parser-states-diagram.png) -Second, the iterative parser also keeps track of array's value count and object's member count -in its internal stack, which may be different from a conventional implementation. +第二,迭代解析器也在内部栈保存了数组的值个数和对象成员的数量,这也与传统的实现不同。 From 835f2f4a79768137ed1d3f262bc02f3aced05ff9 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 31 Dec 2016 14:51:07 +0800 Subject: [PATCH 024/117] Update Doxyfile.zh-cn.in Change internals from English to Chinese --- doc/Doxyfile.zh-cn.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Doxyfile.zh-cn.in b/doc/Doxyfile.zh-cn.in index 87dd8661b..e7fffa678 100644 --- a/doc/Doxyfile.zh-cn.in +++ b/doc/Doxyfile.zh-cn.in @@ -777,7 +777,7 @@ INPUT = readme.zh-cn.md \ doc/sax.zh-cn.md \ doc/schema.zh-cn.md \ doc/performance.zh-cn.md \ - doc/internals.md \ + doc/internals.zh-cn.md \ doc/faq.zh-cn.md # This tag can be used to specify the character encoding of the source files From 3cc77d5d635c2411f327cc4f262f37abb66ff43c Mon Sep 17 00:00:00 2001 From: Zhihao Yuan Date: Wed, 18 Jan 2017 16:16:07 -0600 Subject: [PATCH 025/117] Treat signed-unsigned conversions as errors. --- CMakeLists.txt | 2 ++ example/CMakeLists.txt | 5 ++--- include/rapidjson/encodedstream.h | 2 +- include/rapidjson/encodings.h | 6 +++--- include/rapidjson/internal/dtoa.h | 8 ++++---- include/rapidjson/internal/ieee754.h | 4 ++-- include/rapidjson/internal/regex.h | 4 ++-- include/rapidjson/internal/strtod.h | 14 +++++++------- include/rapidjson/istreamwrapper.h | 2 +- include/rapidjson/pointer.h | 6 +++--- include/rapidjson/schema.h | 2 +- include/rapidjson/writer.h | 2 +- test/perftest/CMakeLists.txt | 2 ++ test/unittest/CMakeLists.txt | 7 +++---- test/unittest/encodingstest.cpp | 2 +- test/unittest/itoatest.cpp | 4 ++-- 16 files changed, 37 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ccda4be6..9257926c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror") + set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wsign-conversion) if (RAPIDJSON_BUILD_CXX11) if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") @@ -86,6 +87,7 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wno-missing-field-initializers") + set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wimplicit-fallthrough -Weverything) if (RAPIDJSON_BUILD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 4d448ccc0..bec6a8cbb 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -24,11 +24,10 @@ set(EXAMPLES include_directories("../include/") add_definitions(-D__STDC_FORMAT_MACROS) +set_property(DIRECTORY PROPERTY COMPILE_OPTIONS ${EXTRA_CXX_FLAGS}) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -Werror -Wall -Wextra -Weffc++ -Wswitch-default") -elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal -Wimplicit-fallthrough -Weverything") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() foreach (example ${EXAMPLES}) diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h index 145068386..223601c05 100644 --- a/include/rapidjson/encodedstream.h +++ b/include/rapidjson/encodedstream.h @@ -200,7 +200,7 @@ class AutoUTFInputStream { // xx xx xx xx UTF-8 if (!hasBOM_) { - unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); switch (pattern) { case 0x08: type_ = kUTF32BE; break; case 0x0A: type_ = kUTF16BE; break; diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h index baa7c2b17..ed7d44d36 100644 --- a/include/rapidjson/encodings.h +++ b/include/rapidjson/encodings.h @@ -157,7 +157,7 @@ struct UTF8 { if (type >= 32) { *codepoint = 0; } else { - *codepoint = (0xFF >> type) & static_cast(c); + *codepoint = (0xFFu >> type) & static_cast(c); } bool result = true; switch (type) { @@ -283,7 +283,7 @@ struct UTF16 { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; os.Put(static_cast((v >> 10) | 0xD800)); - os.Put((v & 0x3FF) | 0xDC00); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); } } @@ -299,7 +299,7 @@ struct UTF16 { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; PutUnsafe(os, static_cast((v >> 10) | 0xD800)); - PutUnsafe(os, (v & 0x3FF) | 0xDC00); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); } } diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h index 8d6350e62..bf2e9b2e5 100644 --- a/include/rapidjson/internal/dtoa.h +++ b/include/rapidjson/internal/dtoa.h @@ -41,7 +41,7 @@ inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uin } } -inline unsigned CountDecimalDigit32(uint32_t n) { +inline int CountDecimalDigit32(uint32_t n) { // Simple pure C++ implementation was faster than __builtin_clz version in this situation. if (n < 10) return 1; if (n < 100) return 2; @@ -63,7 +63,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff const DiyFp wp_w = Mp - W; uint32_t p1 = static_cast(Mp.f >> -one.e); uint64_t p2 = Mp.f & (one.f - 1); - unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] *len = 0; while (kappa > 0) { @@ -102,8 +102,8 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff kappa--; if (p2 < delta) { *K += kappa; - int index = -static_cast(kappa); - GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast(kappa)] : 0)); + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); return; } } diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h index 82bb0b99e..c2684ba2a 100644 --- a/include/rapidjson/internal/ieee754.h +++ b/include/rapidjson/internal/ieee754.h @@ -48,13 +48,13 @@ class Double { int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } - static unsigned EffectiveSignificandSize(int order) { + static int EffectiveSignificandSize(int order) { if (order >= -1021) return 53; else if (order <= -1074) return 0; else - return static_cast(order) + 1074; + return order + 1074; } private: diff --git a/include/rapidjson/internal/regex.h b/include/rapidjson/internal/regex.h index 936b71448..1369ea266 100644 --- a/include/rapidjson/internal/regex.h +++ b/include/rapidjson/internal/regex.h @@ -688,8 +688,8 @@ class GenericRegexSearch { bool matched = AddState(l, s.out); return AddState(l, s.out1) || matched; } - else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) { - stateSet_[index >> 5] |= (1 << (index & 31)); + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); *l.template PushUnsafe() = index; } return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h index 289c413b0..adf49e349 100644 --- a/include/rapidjson/internal/strtod.h +++ b/include/rapidjson/internal/strtod.h @@ -140,8 +140,8 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit significand++; size_t remaining = length - i; - const unsigned kUlpShift = 3; - const unsigned kUlp = 1 << kUlpShift; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; int64_t error = (remaining == 0) ? 0 : kUlp / 2; DiyFp v(significand, 0); @@ -177,17 +177,17 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit v = v.Normalize(); error <<= oldExp - v.e; - const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); - unsigned precisionSize = 64 - effectiveSignificandSize; + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; if (precisionSize + kUlpShift >= 64) { - unsigned scaleExp = (precisionSize + kUlpShift) - 63; + int scaleExp = (precisionSize + kUlpShift) - 63; v.f >>= scaleExp; v.e += scaleExp; - error = (error >> scaleExp) + 1 + static_cast(kUlp); + error = (error >> scaleExp) + 1 + kUlp; precisionSize -= scaleExp; } - DiyFp rounded(v.f >> precisionSize, v.e + static_cast(precisionSize)); + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; if (precisionBits >= halfWay + static_cast(error)) { diff --git a/include/rapidjson/istreamwrapper.h b/include/rapidjson/istreamwrapper.h index f5fe28977..8639c8c3c 100644 --- a/include/rapidjson/istreamwrapper.h +++ b/include/rapidjson/istreamwrapper.h @@ -54,7 +54,7 @@ class BasicIStreamWrapper { Ch Peek() const { typename StreamType::int_type c = stream_.peek(); - return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : '\0'; + return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : static_cast('\0'); } Ch Take() { diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index 4d6391f90..bc7acfd07 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -274,7 +274,7 @@ class GenericPointer { else { Ch name[21]; for (size_t i = 0; i <= length; i++) - name[i] = buffer[i]; + name[i] = static_cast(buffer[i]); Token token = { name, length, index }; return Append(token, allocator); } @@ -1029,8 +1029,8 @@ class GenericPointer { unsigned char u = static_cast(c); static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; os_.Put('%'); - os_.Put(hexDigits[u >> 4]); - os_.Put(hexDigits[u & 15]); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); } private: OutputStream& os_; diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 4760d1b43..3f81d9bb4 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1263,7 +1263,7 @@ struct TokenHelper { char buffer[21]; size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); for (size_t i = 0; i < length; i++) - *documentStack.template Push() = buffer[i]; + *documentStack.template Push() = static_cast(buffer[i]); } }; diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 8f6e174f3..874c555cc 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -352,7 +352,7 @@ class Writer { char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } diff --git a/test/perftest/CMakeLists.txt b/test/perftest/CMakeLists.txt index c33aae469..035e544d9 100644 --- a/test/perftest/CMakeLists.txt +++ b/test/perftest/CMakeLists.txt @@ -19,6 +19,8 @@ if(CCACHE_FOUND) endif() endif(CCACHE_FOUND) +set_property(DIRECTORY PROPERTY COMPILE_OPTIONS ${EXTRA_CXX_FLAGS}) + IF(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug")) add_test(NAME perftest COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/perftest diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index b3204d6c8..4e2976562 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -36,10 +36,9 @@ if(CCACHE_FOUND) endif() endif(CCACHE_FOUND) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal") -elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal -Wimplicit-fallthrough -Weverything") +set_property(DIRECTORY PROPERTY COMPILE_OPTIONS ${EXTRA_CXX_FLAGS}) + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # If the user is running a newer version of Clang that includes the # -Wdouble-promotion, we will ignore that warning. if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.7) diff --git a/test/unittest/encodingstest.cpp b/test/unittest/encodingstest.cpp index 67b0391ed..82cf77761 100644 --- a/test/unittest/encodingstest.cpp +++ b/test/unittest/encodingstest.cpp @@ -267,7 +267,7 @@ static unsigned inline decode(unsigned* state, unsigned* codep, unsigned byte) { *codep = (*state != UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6) : - (0xff >> type) & (byte); + (0xffu >> type) & (byte); *state = utf8d[256 + *state + type]; return *state; diff --git a/test/unittest/itoatest.cpp b/test/unittest/itoatest.cpp index b752a6a26..2f66bedc3 100644 --- a/test/unittest/itoatest.cpp +++ b/test/unittest/itoatest.cpp @@ -74,7 +74,7 @@ static void Verify(void(*f)(T, char*), char* (*g)(T, char*)) { VerifyValue(std::numeric_limits::max(), f, g); // 2^n - 1, 2^n, 10^n - 1, 10^n until overflow - for (uint32_t power = 2; power <= 10; power += 8) { + for (int power = 2; power <= 10; power += 8) { T i = 1, last; do { VerifyValue(i - 1, f, g); @@ -86,7 +86,7 @@ static void Verify(void(*f)(T, char*), char* (*g)(T, char*)) { last = i; if (i > static_cast(std::numeric_limits::max() / static_cast(power))) break; - i *= power; + i *= static_cast(power); } while (last < i); } } From 265fb6ee8814e81eba849581f676da29b30fb6b9 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 24 Jan 2017 09:28:52 +0800 Subject: [PATCH 026/117] Fix #831 RAPIDJSON_HAS_CXX11_RANGE_FOR is error defined --- include/rapidjson/rapidjson.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index a005257ee..e667d8b69 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -568,7 +568,7 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR #if defined(__clang__) #define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ (defined(_MSC_VER) && _MSC_VER >= 1700) #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 #else From 3693e942b72831c82a100ede5f6fbe20dabcdeb7 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 24 Jan 2017 14:54:50 +0800 Subject: [PATCH 027/117] Fix output character type in writers --- include/rapidjson/prettywriter.h | 2 +- include/rapidjson/writer.h | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index abd964f6f..d663208a0 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -249,7 +249,7 @@ class PrettyWriter : public Writer(indentChar_), count); + PutN(*Base::os_, static_cast(indentChar_), count); } Ch indentChar_; diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 874c555cc..5b3004b02 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -297,7 +297,7 @@ class Writer { const char* end = internal::i32toa(i, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -306,7 +306,7 @@ class Writer { const char* end = internal::u32toa(u, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -315,7 +315,7 @@ class Writer { const char* end = internal::i64toa(i64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -324,7 +324,7 @@ class Writer { char* end = internal::u64toa(u64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -357,7 +357,7 @@ class Writer { } bool WriteString(const Ch* str, SizeType length) { - static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char escape[256] = { #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -413,7 +413,7 @@ class Writer { else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { is.Take(); PutUnsafe(*os_, '\\'); - PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); if (escape[static_cast(c)] == 'u') { PutUnsafe(*os_, '0'); PutUnsafe(*os_, '0'); From 738864c53c541944c2e152d79600d4eb0b7e677a Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 24 Jan 2017 15:08:38 +0800 Subject: [PATCH 028/117] Remove non-ASCII character Fix #824 --- test/unittest/readertest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 64a1f9c3c..ac5a0678c 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -245,13 +245,13 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, "0.017976931348623157e+310", 1.7976931348623157e+308); // Max double in another form // Since - // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... �� 10^-324 - // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... �� 10 ^ -324 + // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... x 10^-324 + // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... x 10 ^ -324 // So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e-308 TEST_DOUBLE(fullPrecision, "2.2250738585072012e-308", 2.2250738585072014e-308); // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ // More closer to normal/subnormal boundary - // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... �� 10^-308 + // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... x 10^-308 TEST_DOUBLE(fullPrecision, "2.22507385850720113605740979670913197593481954635164564e-308", 2.2250738585072009e-308); TEST_DOUBLE(fullPrecision, "2.22507385850720113605740979670913197593481954635164565e-308", 2.2250738585072014e-308); From 6769f3e33e5f64ae579f4be3addbe05964393c07 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Thu, 2 Feb 2017 23:18:07 -0800 Subject: [PATCH 029/117] Improved reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add “filename” to the error message when JsonChecker reports an error. --- test/unittest/jsoncheckertest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unittest/jsoncheckertest.cpp b/test/unittest/jsoncheckertest.cpp index bea788d26..e8f85267f 100644 --- a/test/unittest/jsoncheckertest.cpp +++ b/test/unittest/jsoncheckertest.cpp @@ -69,10 +69,10 @@ TEST(JsonChecker, Reader) { GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) document.Parse(json); - EXPECT_TRUE(document.HasParseError()); + EXPECT_TRUE(document.HasParseError()) << filename; document.Parse(json); - EXPECT_TRUE(document.HasParseError()); + EXPECT_TRUE(document.HasParseError()) << filename; free(json); } @@ -89,10 +89,10 @@ TEST(JsonChecker, Reader) { GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) document.Parse(json); - EXPECT_FALSE(document.HasParseError()); + EXPECT_FALSE(document.HasParseError()) << filename; document.Parse(json); - EXPECT_FALSE(document.HasParseError()); + EXPECT_FALSE(document.HasParseError()) << filename; free(json); } From 20f5caa8f64f3efaf6effb35f17d14dc9fcb3aab Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Thu, 2 Feb 2017 23:25:29 -0800 Subject: [PATCH 030/117] Token-by-token pull parsing Refactored the iterative parser so that users can parse a single JSON element at a time (invoking the handler one time) and then return control to the calling code. Call IterativeParseInit to start, and then call IterativeParseNext to retrieve one JSON element. Use IterativeParseComplete to check for JSON document completion. --- include/rapidjson/reader.h | 111 +++++++++++++++++++++++++++---------- 1 file changed, 83 insertions(+), 28 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index dbb5e16fa..68ef128a4 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -513,6 +513,68 @@ class GenericReader { return Parse(is, handler); } + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult IterativeParseNext(InputStream& is, Handler& handler) { + while (is.Peek() != '\0') { + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return parseResult_; + } + + state_ = d; + + // Do not further consume streams if a root JSON has been parsed. + if (state_ == IterativeParsingFinishState) { + // If StopWhenDone is not set, and stray data is found post-root, flag an error. + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') + HandleError(state_, is); + } + return parseResult_; + } + + if (!IsIterativeParsingDelimiterState(n)) + return parseResult_; + } + + // Handle the end of file. + if (state_ != IterativeParsingFinishState) + HandleError(state_, is); + + stack_.Clear(); + return parseResult_; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + bool IterativeParseComplete() { + return IsIterativeParsingCompleteState(state_); + } + //! Whether a parse error has occured in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } @@ -1803,44 +1865,37 @@ class GenericReader { } } + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) { + const unsigned int delimiterStateMask = + (1 << IterativeParsingKeyValueDelimiterState) | + (1 << IterativeParsingMemberDelimiterState) | + (1 << IterativeParsingElementDelimiterState); + + return (1 << s) & delimiterStateMask; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) { + const unsigned int completeStateMask = + (1 << IterativeParsingFinishState) | + (1 << IterativeParsingErrorState); + + return (1 << s) & completeStateMask; + } + template ParseResult IterativeParse(InputStream& is, Handler& handler) { - parseResult_.Clear(); - ClearStackOnExit scope(*this); - IterativeParsingState state = IterativeParsingStartState; - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - while (is.Peek() != '\0') { - Token t = Tokenize(is.Peek()); - IterativeParsingState n = Predict(state, t); - IterativeParsingState d = Transit(state, t, n, is, handler); - - if (d == IterativeParsingErrorState) { - HandleError(state, is); + IterativeParseInit(); + while (!IterativeParseComplete()) { + if (!IterativeParseNext(is, handler)) break; - } - - state = d; - - // Do not further consume streams if a root JSON has been parsed. - if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) - break; - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } - - // Handle the end of file. - if (state != IterativeParsingFinishState) - HandleError(state, is); - return parseResult_; } static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. ParseResult parseResult_; + IterativeParsingState state_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator. From 1a7c5ea5179601308a46a21a339a146d295af78e Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Fri, 3 Feb 2017 00:29:43 -0800 Subject: [PATCH 031/117] Fix Dev Studio bool-conversion warning --- include/rapidjson/reader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 68ef128a4..9babf7572 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1871,7 +1871,7 @@ class GenericReader { (1 << IterativeParsingMemberDelimiterState) | (1 << IterativeParsingElementDelimiterState); - return (1 << s) & delimiterStateMask; + return !!((1 << s) & delimiterStateMask); } RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) { @@ -1879,7 +1879,7 @@ class GenericReader { (1 << IterativeParsingFinishState) | (1 << IterativeParsingErrorState); - return (1 << s) & completeStateMask; + return !!((1 << s) & completeStateMask); } template From 5de7258478433bf76f998fdc6a0f326709aeabc5 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Fri, 3 Feb 2017 17:14:14 -0800 Subject: [PATCH 032/117] Improve performance Slight performance improvement over previous submission --- include/rapidjson/reader.h | 172 +++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 83 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 9babf7572..065772f76 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -529,9 +529,8 @@ class GenericReader { \return Whether the parsing is successful. */ template - ParseResult IterativeParseNext(InputStream& is, Handler& handler) { + bool IterativeParseNext(InputStream& is, Handler& handler) { while (is.Peek() != '\0') { - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); SkipWhitespaceAndComments(is); Token t = Tokenize(is.Peek()); @@ -540,38 +539,52 @@ class GenericReader { if (d == IterativeParsingErrorState) { HandleError(state_, is); - return parseResult_; + return false; } state_ = d; - // Do not further consume streams if a root JSON has been parsed. - if (state_ == IterativeParsingFinishState) { - // If StopWhenDone is not set, and stray data is found post-root, flag an error. + // Do not further consume streams if we've parsed a complete object or hit an error. + if (IsIterativeParsingCompleteState(state_)) { + // If we hit an error, we are done. + if (HasParseError()) + return false; + + // If StopWhenDone is not set... if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... SkipWhitespaceAndComments(is); - if (is.Peek() != '\0') + if (is.Peek() != '\0') { + // ... this is considered an error. HandleError(state_, is); + return false; + } } - return parseResult_; + + // We are done! + return true; } + // If we found anything other than a delimiter, we invoked the handler, so we can return true now. if (!IsIterativeParsingDelimiterState(n)) - return parseResult_; + return true; } - // Handle the end of file. - if (state_ != IterativeParsingFinishState) + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { HandleError(state_, is); + return false; + } - stack_.Clear(); - return parseResult_; + return true; } //! Check if token-by-token parsing JSON text is complete /*! \return Whether the JSON has been fully decoded. */ - bool IterativeParseComplete() { + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() { return IsIterativeParsingCompleteState(state_); } @@ -1455,30 +1468,32 @@ class GenericReader { // States enum IterativeParsingState { - IterativeParsingStartState = 0, - IterativeParsingFinishState, - IterativeParsingErrorState, + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, // Object states IterativeParsingObjectInitialState, IterativeParsingMemberKeyState, - IterativeParsingKeyValueDelimiterState, IterativeParsingMemberValueState, - IterativeParsingMemberDelimiterState, IterativeParsingObjectFinishState, // Array states IterativeParsingArrayInitialState, IterativeParsingElementState, - IterativeParsingElementDelimiterState, IterativeParsingArrayFinishState, // Single value state - IterativeParsingValueState + IterativeParsingValueState, + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount }; - enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 }; - // Tokens enum Token { LeftBracketToken = 0, @@ -1529,6 +1544,18 @@ class GenericReader { RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { // current state x one lookahead token -> new state static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // Start { IterativeParsingArrayInitialState, // Left bracket @@ -1543,18 +1570,6 @@ class GenericReader { IterativeParsingValueState, // Null IterativeParsingValueState // Number }, - // Finish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Error(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, // ObjectInitial { IterativeParsingErrorState, // Left bracket @@ -1583,20 +1598,6 @@ class GenericReader { IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // KeyValueDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberValueState, // String - IterativeParsingMemberValueState, // False - IterativeParsingMemberValueState, // True - IterativeParsingMemberValueState, // Null - IterativeParsingMemberValueState // Number - }, // MemberValue { IterativeParsingErrorState, // Left bracket @@ -1611,20 +1612,6 @@ class GenericReader { IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // MemberDelimiter - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, // ObjectFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1659,6 +1646,18 @@ class GenericReader { IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // ElementDelimiter { IterativeParsingArrayInitialState, // Left bracket(push Element state) @@ -1673,18 +1672,34 @@ class GenericReader { IterativeParsingElementState, // Null IterativeParsingElementState // Number }, - // ArrayFinish(sink state) + // MemberDelimiter { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number }, - // Single Value (sink state) + // KeyValueDelimiter { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - } + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, }; // End of G return static_cast(G[state][token]); @@ -1866,20 +1881,11 @@ class GenericReader { } RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) { - const unsigned int delimiterStateMask = - (1 << IterativeParsingKeyValueDelimiterState) | - (1 << IterativeParsingMemberDelimiterState) | - (1 << IterativeParsingElementDelimiterState); - - return !!((1 << s) & delimiterStateMask); + return s >= IterativeParsingElementDelimiterState; } RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) { - const unsigned int completeStateMask = - (1 << IterativeParsingFinishState) | - (1 << IterativeParsingErrorState); - - return !!((1 << s) & completeStateMask); + return s <= IterativeParsingErrorState; } template From 116f65994b928405149ddf38c4e8d6e1399a1e0b Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Fri, 3 Feb 2017 18:58:37 -0800 Subject: [PATCH 033/117] Improve coverage and performance Further improvement to perftest and hoping to make coveralls happy. --- include/rapidjson/reader.h | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 065772f76..dcdc8cc92 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -537,18 +537,17 @@ class GenericReader { IterativeParsingState n = Predict(state_, t); IterativeParsingState d = Transit(state_, t, n, is, handler); - if (d == IterativeParsingErrorState) { - HandleError(state_, is); - return false; - } - - state_ = d; - - // Do not further consume streams if we've parsed a complete object or hit an error. - if (IsIterativeParsingCompleteState(state_)) { - // If we hit an error, we are done. - if (HasParseError()) + // If we've finished or hit an error... + if (IsIterativeParsingCompleteState(d)) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; // If StopWhenDone is not set... if (!(parseFlags & kParseStopWhenDoneFlag)) { @@ -561,11 +560,14 @@ class GenericReader { } } - // We are done! + // Success! We are done! return true; } - // If we found anything other than a delimiter, we invoked the handler, so we can return true now. + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. if (!IsIterativeParsingDelimiterState(n)) return true; } From 82a423db7d9bbc65269ddb35436ea7a46bfd2140 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Fri, 3 Feb 2017 21:12:53 -0800 Subject: [PATCH 034/117] Added unit test for pull parsing New unit test which ensures that IterativeParseNext always generates exactly one element at a time, and that calling IterativeParseNext on a complete document is harmless and generates zero events. --- test/unittest/readertest.cpp | 101 +++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index ac5a0678c..b1c0c31cf 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1157,22 +1157,22 @@ template > struct IterativeParsingReaderHandler { typedef typename Encoding::Ch Ch; - const static int LOG_NULL = -1; - const static int LOG_BOOL = -2; - const static int LOG_INT = -3; - const static int LOG_UINT = -4; - const static int LOG_INT64 = -5; - const static int LOG_UINT64 = -6; - const static int LOG_DOUBLE = -7; - const static int LOG_STRING = -8; - const static int LOG_STARTOBJECT = -9; - const static int LOG_KEY = -10; - const static int LOG_ENDOBJECT = -11; - const static int LOG_STARTARRAY = -12; - const static int LOG_ENDARRAY = -13; + const static uint32_t LOG_NULL = 0x10000000; + const static uint32_t LOG_BOOL = 0x20000000; + const static uint32_t LOG_INT = 0x30000000; + const static uint32_t LOG_UINT = 0x40000000; + const static uint32_t LOG_INT64 = 0x50000000; + const static uint32_t LOG_UINT64 = 0x60000000; + const static uint32_t LOG_DOUBLE = 0x70000000; + const static uint32_t LOG_STRING = 0x80000000; + const static uint32_t LOG_STARTOBJECT = 0x90000000; + const static uint32_t LOG_KEY = 0xA0000000; + const static uint32_t LOG_ENDOBJECT = 0xB0000000; + const static uint32_t LOG_STARTARRAY = 0xC0000000; + const static uint32_t LOG_ENDARRAY = 0xD0000000; const static size_t LogCapacity = 256; - int Logs[LogCapacity]; + uint32_t Logs[LogCapacity]; size_t LogCount; IterativeParsingReaderHandler() : LogCount(0) { @@ -1202,8 +1202,8 @@ struct IterativeParsingReaderHandler { bool EndObject(SizeType c) { RAPIDJSON_ASSERT(LogCount < LogCapacity); - Logs[LogCount++] = LOG_ENDOBJECT; - Logs[LogCount++] = static_cast(c); + RAPIDJSON_ASSERT((static_cast(c) & 0xF0000000) == 0); + Logs[LogCount++] = LOG_ENDOBJECT | static_cast(c); return true; } @@ -1211,8 +1211,8 @@ struct IterativeParsingReaderHandler { bool EndArray(SizeType c) { RAPIDJSON_ASSERT(LogCount < LogCapacity); - Logs[LogCount++] = LOG_ENDARRAY; - Logs[LogCount++] = static_cast(c); + RAPIDJSON_ASSERT((static_cast(c) & 0xF0000000) == 0); + Logs[LogCount++] = LOG_ENDARRAY | static_cast(c); return true; } }; @@ -1228,7 +1228,7 @@ TEST(Reader, IterativeParsing_General) { EXPECT_FALSE(r.IsError()); EXPECT_FALSE(reader.HasParseError()); - int e[] = { + uint32_t e[] = { handler.LOG_STARTARRAY, handler.LOG_INT, handler.LOG_STARTOBJECT, @@ -1236,14 +1236,14 @@ TEST(Reader, IterativeParsing_General) { handler.LOG_STARTARRAY, handler.LOG_INT, handler.LOG_INT, - handler.LOG_ENDARRAY, 2, - handler.LOG_ENDOBJECT, 1, + handler.LOG_ENDARRAY | 2, + handler.LOG_ENDOBJECT | 1, handler.LOG_NULL, handler.LOG_BOOL, handler.LOG_BOOL, handler.LOG_STRING, handler.LOG_DOUBLE, - handler.LOG_ENDARRAY, 7 + handler.LOG_ENDARRAY | 7 }; EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount); @@ -1265,20 +1265,20 @@ TEST(Reader, IterativeParsing_Count) { EXPECT_FALSE(r.IsError()); EXPECT_FALSE(reader.HasParseError()); - int e[] = { + uint32_t e[] = { handler.LOG_STARTARRAY, handler.LOG_STARTOBJECT, - handler.LOG_ENDOBJECT, 0, + handler.LOG_ENDOBJECT | 0, handler.LOG_STARTOBJECT, handler.LOG_KEY, handler.LOG_INT, - handler.LOG_ENDOBJECT, 1, + handler.LOG_ENDOBJECT | 1, handler.LOG_STARTARRAY, handler.LOG_INT, - handler.LOG_ENDARRAY, 1, + handler.LOG_ENDARRAY | 1, handler.LOG_STARTARRAY, - handler.LOG_ENDARRAY, 0, - handler.LOG_ENDARRAY, 4 + handler.LOG_ENDARRAY | 0, + handler.LOG_ENDARRAY | 4 }; EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount); @@ -1289,6 +1289,51 @@ TEST(Reader, IterativeParsing_Count) { } } +TEST(Reader, IterativePullParsing_General) { + { + IterativeParsingReaderHandler<> handler; + uint32_t e[] = { + handler.LOG_STARTARRAY, + handler.LOG_INT, + handler.LOG_STARTOBJECT, + handler.LOG_KEY, + handler.LOG_STARTARRAY, + handler.LOG_INT, + handler.LOG_INT, + handler.LOG_ENDARRAY | 2, + handler.LOG_ENDOBJECT | 1, + handler.LOG_NULL, + handler.LOG_BOOL, + handler.LOG_BOOL, + handler.LOG_STRING, + handler.LOG_DOUBLE, + handler.LOG_ENDARRAY | 7 + }; + + StringStream is("[1, {\"k\": [1, 2]}, null, false, true, \"string\", 1.2]"); + Reader reader; + + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + size_t oldLogCount = handler.LogCount; + EXPECT_TRUE(oldLogCount < sizeof(e) / sizeof(int)) << "overrun"; + + EXPECT_TRUE(reader.IterativeParseNext(is, handler)) << "parse fail"; + EXPECT_EQ(handler.LogCount, oldLogCount + 1) << "handler should be invoked exactly once each time"; + EXPECT_EQ(e[oldLogCount], handler.Logs[oldLogCount]) << "wrong event returned"; + } + + EXPECT_FALSE(reader.HasParseError()); + EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount) << "handler invoked wrong number of times"; + + // The handler should not be invoked when the JSON has been fully read, but it should not fail + size_t oldLogCount = handler.LogCount; + EXPECT_TRUE(reader.IterativeParseNext(is, handler)) << "parse-next past complete is allowed"; + EXPECT_EQ(handler.LogCount, oldLogCount) << "parse-next past complete should not invoke handler"; + EXPECT_FALSE(reader.HasParseError()) << "parse-next past complete should not generate parse error"; + } +} + // Test iterative parsing on kParseErrorTermination. struct HandlerTerminateAtStartObject : public IterativeParsingReaderHandler<> { bool StartObject() { return false; } From 4394b3bac7a0647f5121a533397c0fe20c89dab8 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Sat, 4 Feb 2017 00:05:34 -0800 Subject: [PATCH 035/117] Add LIKELY and UNLIKELY hints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doesn’t seem to affect timings in perftest on my machine, but it may help others. --- include/rapidjson/reader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index dcdc8cc92..df59a1e08 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -530,7 +530,7 @@ class GenericReader { */ template bool IterativeParseNext(InputStream& is, Handler& handler) { - while (is.Peek() != '\0') { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { SkipWhitespaceAndComments(is); Token t = Tokenize(is.Peek()); @@ -538,7 +538,7 @@ class GenericReader { IterativeParsingState d = Transit(state_, t, n, is, handler); // If we've finished or hit an error... - if (IsIterativeParsingCompleteState(d)) { + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { // Report errors. if (d == IterativeParsingErrorState) { HandleError(state_, is); From d84d5fe0551634d1057adf6eb163ce8a63d00f1a Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Sat, 4 Feb 2017 00:41:34 -0800 Subject: [PATCH 036/117] Add example SimplePullHandler code Example code to demonstrate how the token-pulling reader can be used. --- example/simplepullreader/simplepullreader.cpp | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 example/simplepullreader/simplepullreader.cpp diff --git a/example/simplepullreader/simplepullreader.cpp b/example/simplepullreader/simplepullreader.cpp new file mode 100644 index 000000000..0cce08b6a --- /dev/null +++ b/example/simplepullreader/simplepullreader.cpp @@ -0,0 +1,40 @@ +#include "rapidjson/reader.h" +#include + +using namespace rapidjson; +using namespace std; + +struct MyHandler { + const char* type; + std::string data; + + bool Null() { type = "Null"; data.clear(); return true; } + bool Bool(bool b) { type = "Bool"; data = b? "true": "false"; return true; } + bool Int(int i) { type = "Int"; data = std::to_string(i); return true; } + bool Uint(unsigned u) { type = "Uint"; data = std::to_string(u); return true; } + bool Int64(int64_t i) { type = "Int64"; data = std::to_string(i); return true; } + bool Uint64(uint64_t u) { type = "Uint64"; data = std::to_string(u); return true; } + bool Double(double d) { type = "Double"; data = std::to_string(d); return true; } + bool RawNumber(const char* str, SizeType length, bool) { type = "Number"; data = std::string(str, length); return true; } + bool String(const char* str, SizeType length, bool) { type = "String" data = std::string(str, length); return true; } + bool StartObject() { type = "StartObject"; data.clear(); return true; } + bool Key(const char* str, SizeType length, bool) { type = "Key" data = std::string(str, length); return true; } + bool EndObject(SizeType memberCount) { type = "EndObject"; data = std::to_string(memberCount); return true; } + bool StartArray() { type = "StartArray"; data.clear(); return true; } + bool EndArray(SizeType elementCount) { type = "EndArray"; data = std::to_string(elementCount); return true; } +}; + +int main() { + const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "; + + MyHandler handler; + Reader reader; + StringStream ss(json); + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + reader.IterativeParseNext(ss, handler); + cout << handler.type << ": " << handler.data << endl; + } + + return 0; +} From 4232e407f40a3f2f3769234c1069ebccda43ea51 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Sat, 4 Feb 2017 00:47:43 -0800 Subject: [PATCH 037/117] Clean up example code --- example/CMakeLists.txt | 1 + example/simplepullreader/simplepullreader.cpp | 24 +++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index bec6a8cbb..e16e3c9ed 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -18,6 +18,7 @@ set(EXAMPLES serialize simpledom simplereader + simplepullreader simplewriter tutorial) diff --git a/example/simplepullreader/simplepullreader.cpp b/example/simplepullreader/simplepullreader.cpp index 0cce08b6a..af8d5a7e5 100644 --- a/example/simplepullreader/simplepullreader.cpp +++ b/example/simplepullreader/simplepullreader.cpp @@ -9,19 +9,19 @@ struct MyHandler { std::string data; bool Null() { type = "Null"; data.clear(); return true; } - bool Bool(bool b) { type = "Bool"; data = b? "true": "false"; return true; } - bool Int(int i) { type = "Int"; data = std::to_string(i); return true; } - bool Uint(unsigned u) { type = "Uint"; data = std::to_string(u); return true; } - bool Int64(int64_t i) { type = "Int64"; data = std::to_string(i); return true; } - bool Uint64(uint64_t u) { type = "Uint64"; data = std::to_string(u); return true; } - bool Double(double d) { type = "Double"; data = std::to_string(d); return true; } - bool RawNumber(const char* str, SizeType length, bool) { type = "Number"; data = std::string(str, length); return true; } - bool String(const char* str, SizeType length, bool) { type = "String" data = std::string(str, length); return true; } + bool Bool(bool b) { type = "Bool:"; data = b? "true": "false"; return true; } + bool Int(int i) { type = "Int:"; data = std::to_string(i); return true; } + bool Uint(unsigned u) { type = "Uint:"; data = std::to_string(u); return true; } + bool Int64(int64_t i) { type = "Int64:"; data = std::to_string(i); return true; } + bool Uint64(uint64_t u) { type = "Uint64:"; data = std::to_string(u); return true; } + bool Double(double d) { type = "Double:"; data = std::to_string(d); return true; } + bool RawNumber(const char* str, SizeType length, bool) { type = "Number:"; data = std::string(str, length); return true; } + bool String(const char* str, SizeType length, bool) { type = "String:"; data = std::string(str, length); return true; } bool StartObject() { type = "StartObject"; data.clear(); return true; } - bool Key(const char* str, SizeType length, bool) { type = "Key" data = std::string(str, length); return true; } - bool EndObject(SizeType memberCount) { type = "EndObject"; data = std::to_string(memberCount); return true; } + bool Key(const char* str, SizeType length, bool) { type = "Key:"; data = std::string(str, length); return true; } + bool EndObject(SizeType memberCount) { type = "EndObject:"; data = std::to_string(memberCount); return true; } bool StartArray() { type = "StartArray"; data.clear(); return true; } - bool EndArray(SizeType elementCount) { type = "EndArray"; data = std::to_string(elementCount); return true; } + bool EndArray(SizeType elementCount) { type = "EndArray:"; data = std::to_string(elementCount); return true; } }; int main() { @@ -33,7 +33,7 @@ int main() { reader.IterativeParseInit(); while (!reader.IterativeParseComplete()) { reader.IterativeParseNext(ss, handler); - cout << handler.type << ": " << handler.data << endl; + cout << handler.type << handler.data << endl; } return 0; From 6288d95d1e65d6a57c480dea9edc342b4b721507 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Sat, 4 Feb 2017 01:07:00 -0800 Subject: [PATCH 038/117] SimplePullReader C++98 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit std::to_string can’t be used because it requires C++11. --- example/simplepullreader/simplepullreader.cpp | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/example/simplepullreader/simplepullreader.cpp b/example/simplepullreader/simplepullreader.cpp index af8d5a7e5..0b11b4007 100644 --- a/example/simplepullreader/simplepullreader.cpp +++ b/example/simplepullreader/simplepullreader.cpp @@ -1,27 +1,33 @@ #include "rapidjson/reader.h" #include +#include using namespace rapidjson; using namespace std; +// If you can require C++11, you could use std::to_string here +template std::string stringify(T x) { + return (std::stringstream() << x).str(); +} + struct MyHandler { const char* type; std::string data; bool Null() { type = "Null"; data.clear(); return true; } bool Bool(bool b) { type = "Bool:"; data = b? "true": "false"; return true; } - bool Int(int i) { type = "Int:"; data = std::to_string(i); return true; } - bool Uint(unsigned u) { type = "Uint:"; data = std::to_string(u); return true; } - bool Int64(int64_t i) { type = "Int64:"; data = std::to_string(i); return true; } - bool Uint64(uint64_t u) { type = "Uint64:"; data = std::to_string(u); return true; } - bool Double(double d) { type = "Double:"; data = std::to_string(d); return true; } + bool Int(int i) { type = "Int:"; data = stringify(i); return true; } + bool Uint(unsigned u) { type = "Uint:"; data = stringify(u); return true; } + bool Int64(int64_t i) { type = "Int64:"; data = stringify(i); return true; } + bool Uint64(uint64_t u) { type = "Uint64:"; data = stringify(u); return true; } + bool Double(double d) { type = "Double:"; data = stringify(d); return true; } bool RawNumber(const char* str, SizeType length, bool) { type = "Number:"; data = std::string(str, length); return true; } bool String(const char* str, SizeType length, bool) { type = "String:"; data = std::string(str, length); return true; } bool StartObject() { type = "StartObject"; data.clear(); return true; } bool Key(const char* str, SizeType length, bool) { type = "Key:"; data = std::string(str, length); return true; } - bool EndObject(SizeType memberCount) { type = "EndObject:"; data = std::to_string(memberCount); return true; } + bool EndObject(SizeType memberCount) { type = "EndObject:"; data = stringify(memberCount); return true; } bool StartArray() { type = "StartArray"; data.clear(); return true; } - bool EndArray(SizeType elementCount) { type = "EndArray:"; data = std::to_string(elementCount); return true; } + bool EndArray(SizeType elementCount) { type = "EndArray:"; data = stringify(elementCount); return true; } }; int main() { From a11ec697969eb776973c680d1eadf9ab11ac7e7b Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Sat, 4 Feb 2017 01:18:46 -0800 Subject: [PATCH 039/117] More C++98 fixes --- example/simplepullreader/simplepullreader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/simplepullreader/simplepullreader.cpp b/example/simplepullreader/simplepullreader.cpp index 0b11b4007..98566e618 100644 --- a/example/simplepullreader/simplepullreader.cpp +++ b/example/simplepullreader/simplepullreader.cpp @@ -7,7 +7,9 @@ using namespace std; // If you can require C++11, you could use std::to_string here template std::string stringify(T x) { - return (std::stringstream() << x).str(); + std::stringstream ss; + ss << x; + return ss.str(); } struct MyHandler { From 0f8389e78779cf13e5f0c8f4da2a3a780d097d42 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Tue, 7 Feb 2017 00:02:08 -0800 Subject: [PATCH 040/117] Restored original IterativeParse implementation Runs about 1-2% faster (original speed) by running in a tight loop, at the expense of slight code duplication with IterativeParseNext. --- include/rapidjson/reader.h | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index df59a1e08..d92d9fb41 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1892,11 +1892,36 @@ class GenericReader { template ParseResult IterativeParse(InputStream& is, Handler& handler) { - IterativeParseInit(); - while (!IterativeParseComplete()) { - if (!IterativeParseNext(is, handler)) + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + return parseResult_; } From bd4c282d77d4bb6f0034405a721a6fbf477b4955 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Tue, 7 Feb 2017 01:08:51 -0800 Subject: [PATCH 041/117] Test coverage up Add more tests! Good for coverage. --- test/perftest/rapidjsontest.cpp | 29 ++++++++++++++++++++ test/unittest/jsoncheckertest.cpp | 44 +++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/test/perftest/rapidjsontest.cpp b/test/perftest/rapidjsontest.cpp index 675db3182..f14e702ed 100644 --- a/test/perftest/rapidjsontest.cpp +++ b/test/perftest/rapidjsontest.cpp @@ -152,6 +152,35 @@ TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativeInsitu_DummyHandler)) { } } +TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativePull_DummyHandler)) { + for (size_t i = 0; i < kTrialCount; i++) { + StringStream s(json_); + BaseReaderHandler<> h; + Reader reader; + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + if (!reader.IterativeParseNext(s, h)) + break; + } + EXPECT_FALSE(reader.HasParseError()); + } +} + +TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativePullInsitu_DummyHandler)) { + for (size_t i = 0; i < kTrialCount; i++) { + memcpy(temp_, json_, length_ + 1); + InsituStringStream s(temp_); + BaseReaderHandler<> h; + Reader reader; + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + if (!reader.IterativeParseNext(s, h)) + break; + } + EXPECT_FALSE(reader.HasParseError()); + } +} + TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_ValidateEncoding)) { for (size_t i = 0; i < kTrialCount; i++) { StringStream s(json_); diff --git a/test/unittest/jsoncheckertest.cpp b/test/unittest/jsoncheckertest.cpp index e8f85267f..47c2b567b 100644 --- a/test/unittest/jsoncheckertest.cpp +++ b/test/unittest/jsoncheckertest.cpp @@ -48,6 +48,24 @@ static char* ReadFile(const char* filename, size_t& length) { return json; } +struct NoOpHandler { + bool Null() { return true; } + bool Bool(bool) { return true; } + bool Int(int) { return true; } + bool Uint(unsigned) { return true; } + bool Int64(int64_t) { return true; } + bool Uint64(uint64_t) { return true; } + bool Double(double) { return true; } + bool RawNumber(const char*, SizeType, bool) { return true; } + bool String(const char*, SizeType, bool) { return true; } + bool StartObject() { return true; } + bool Key(const char*, SizeType, bool) { return true; } + bool EndObject(SizeType) { return true; } + bool StartArray() { return true; } + bool EndArray(SizeType) { return true; } +}; + + TEST(JsonChecker, Reader) { char filename[256]; @@ -67,13 +85,26 @@ TEST(JsonChecker, Reader) { continue; } + // Test stack-based parsing. GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) document.Parse(json); EXPECT_TRUE(document.HasParseError()) << filename; + // Test iterative parsing. document.Parse(json); EXPECT_TRUE(document.HasParseError()) << filename; + // Test iterative pull-parsing. + Reader reader; + StringStream ss(json); + NoOpHandler h; + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + if (!reader.IterativeParseNext(ss, h)) + break; + } + EXPECT_TRUE(reader.HasParseError()) << filename; + free(json); } @@ -87,12 +118,25 @@ TEST(JsonChecker, Reader) { continue; } + // Test stack-based parsing. GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) document.Parse(json); EXPECT_FALSE(document.HasParseError()) << filename; + // Test iterative parsing. document.Parse(json); EXPECT_FALSE(document.HasParseError()) << filename; + + // Test iterative pull-parsing. + Reader reader; + StringStream ss(json); + NoOpHandler h; + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + if (!reader.IterativeParseNext(ss, h)) + break; + } + EXPECT_FALSE(reader.HasParseError()) << filename; free(json); } From c4117c68ccf45e2f80ea7693766db0c771b6d508 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Wed, 22 Feb 2017 21:54:31 -0800 Subject: [PATCH 042/117] Put in unit tests to catch parser failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noticed that the reader could over-consume “NaN” if token terminated in the middle. --- test/unittest/readertest.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index ac5a0678c..0973791df 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1832,6 +1832,10 @@ TEST(Reader, ParseNanAndInfinity) { TEST_NAN_INF("Infinity", inf); TEST_NAN_INF("-Inf", -inf); TEST_NAN_INF("-Infinity", -inf); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NInf", 1); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NaInf", 1); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "INan", 1); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "InNan", 1); TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "nan", 1); TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1); TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1); From 5e785d3db20cdad256a7c0139001b484ea37fab9 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Thu, 23 Feb 2017 00:11:12 -0800 Subject: [PATCH 043/117] Fix parsing of NaN/Inf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A failed half-consume of “NaN” now returns “value invalid” instead of attempting to consume an “Inf”. --- include/rapidjson/reader.h | 27 ++++++++++++++++++--------- test/unittest/readertest.cpp | 4 ++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index dbb5e16fa..c1d10e8b6 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1170,18 +1170,27 @@ class GenericReader { } // Parse NaN or Infinity here else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { - useNanOrInf = true; - if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) { - d = std::numeric_limits::quiet_NaN(); + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } } - else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) { - d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); - if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') - && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } } - else + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 0973791df..2217a1284 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1833,9 +1833,9 @@ TEST(Reader, ParseNanAndInfinity) { TEST_NAN_INF("-Inf", -inf); TEST_NAN_INF("-Infinity", -inf); TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NInf", 1); - TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NaInf", 1); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NaInf", 2); TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "INan", 1); - TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "InNan", 1); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "InNan", 2); TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "nan", 1); TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1); TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1); From b977fd3c9d21c758d3cf74778458d573d3897b33 Mon Sep 17 00:00:00 2001 From: ylavic Date: Fri, 24 Feb 2017 16:46:53 +0100 Subject: [PATCH 044/117] Missing "internal" namespace for StrLen include/rapidjson/pointer.h:243:40: error: 'StrLen' was not declared in this scope return Append(name, StrLen(name), allocator); --- include/rapidjson/pointer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index bc7acfd07..0f377efec 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -240,7 +240,7 @@ class GenericPointer { template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) Append(T* name, Allocator* allocator = 0) const { - return Append(name, StrLen(name), allocator); + return Append(name, internal::StrLen(name), allocator); } #if RAPIDJSON_HAS_STDSTRING From 5f92c3926b185dcaf95aaa9e524f573adbfeed36 Mon Sep 17 00:00:00 2001 From: oviradoi Date: Fri, 24 Feb 2017 19:50:36 +0200 Subject: [PATCH 045/117] Fix creating the nuget package with Raggles' fork of CoApp --- rapidjson.autopkg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rapidjson.autopkg b/rapidjson.autopkg index 70eb0d8a0..486ad1434 100644 --- a/rapidjson.autopkg +++ b/rapidjson.autopkg @@ -71,5 +71,7 @@ Changed targets { // We're trying to be standard about these sorts of thing. (Will help with config.h later :D) //Defines += HAS_EQCORE; + // Fix creating the package with Raggles' fork of CoApp + Includes += "$(MSBuildThisFileDirectory)../..${d_include}"; }; } \ No newline at end of file From 97e2f7f16f3b739a262f1391478be484e6cac8ba Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 28 Feb 2017 09:48:36 +0800 Subject: [PATCH 046/117] Try fixing Error compilation Ubuntu 14.04 #834 --- include/rapidjson/schema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 3f81d9bb4..c20a838e4 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -880,7 +880,7 @@ class Schema { #define RAPIDJSON_STRING_(name, ...) \ static const ValueType& Get##name##String() {\ static const Ch s[] = { __VA_ARGS__, '\0' };\ - static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ return v;\ } From 595b114216f8899eb72695394bff9cb5856313e6 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Mon, 27 Feb 2017 22:36:48 -0800 Subject: [PATCH 047/117] Unit test Add unit tests expecting an assertion when writing an object with a key but no value. --- test/unittest/writertest.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index d346e0f3e..398a63dca 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -442,6 +442,28 @@ TEST(Writer, InvalidEventSequence) { EXPECT_THROW(writer.Int(1), AssertException); EXPECT_FALSE(writer.IsComplete()); } + + // { 'a' } + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartObject(); + writer.Key("a"); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 'a':'b','c' } + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartObject(); + writer.Key("a"); + writer.String("b"); + writer.Key("c"); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } } TEST(Writer, NaN) { From 2e9b7b1ae6ef162b5aa0c04f2b4444be4c8b72cb Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Mon, 27 Feb 2017 22:44:13 -0800 Subject: [PATCH 048/117] Added assertion Documented existing assertions in EndObject Added new assertion in EndObject to catch error condition where objects are ended with a key but no matching value. --- include/rapidjson/writer.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 5b3004b02..43ec5dc64 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -221,8 +221,9 @@ class Writer { bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); - RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value level_stack_.template Pop(1); return EndValue(WriteEndObject()); } From fa84cd18f48294dc94f659c3f7a272b26ea5b1b5 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Mon, 27 Feb 2017 22:53:59 -0800 Subject: [PATCH 049/117] Add matching fix for PrettyWriter PrettyWriter EndObject will now also assert if called when a key is missing its matching value. --- include/rapidjson/prettywriter.h | 6 ++-- test/unittest/prettywritertest.cpp | 51 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index d663208a0..b68b687db 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -136,8 +136,10 @@ class PrettyWriter : public Writer= sizeof(typename Base::Level)); - RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index 13d1a8d93..2891c76ce 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -207,6 +207,57 @@ TEST(PrettyWriter, RawValue) { buffer.GetString()); } +TEST(PrettyWriter, InvalidEventSequence) { + // {] + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartObject(); + EXPECT_THROW(writer.EndArray(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // [} + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartArray(); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 1: + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartObject(); + EXPECT_THROW(writer.Int(1), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 'a' } + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartObject(); + writer.Key("a"); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 'a':'b','c' } + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartObject(); + writer.Key("a"); + writer.String("b"); + writer.Key("c"); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } +} + #if RAPIDJSON_HAS_CXX11_RVALUE_REFS static PrettyWriter WriterGen(StringBuffer &target) { From 0ec4e86f14a0b801cee4a707f51245a6b735fef2 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Mon, 27 Feb 2017 23:06:05 -0800 Subject: [PATCH 050/117] Unit test Add unit test for Issue 848 (segfault in ~Document) --- test/unittest/schematest.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 478051654..30b326057 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -1281,6 +1281,12 @@ TEST(SchemaValidatingWriter, Simple) { EXPECT_TRUE(validator.GetInvalidDocumentPointer() == SchemaDocument::PointerType("")); } +TEST(Schema, Issue848) { + rapidjson::Document d; + rapidjson::SchemaDocument s(d); + rapidjson::GenericSchemaValidator v(s); +} + #if RAPIDJSON_HAS_CXX11_RVALUE_REFS static SchemaDocument ReturnSchemaDocument() { From 4643104b8a0424f8f645b2777fbcdccf9a17acbf Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Mon, 27 Feb 2017 23:28:25 -0800 Subject: [PATCH 051/117] Fix null handler construction We should not malloc the null-handler object and cast to OutputHandler; we need to actually invoke the constructor via placement new. --- include/rapidjson/schema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index c20a838e4..3dddd3a1c 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1928,7 +1928,7 @@ RAPIDJSON_MULTILINEMACRO_END const Context& CurrentContext() const { return *schemaStack_.template Top(); } OutputHandler& CreateNullHandler() { - return *(nullHandler_ = static_cast(GetStateAllocator().Malloc(sizeof(OutputHandler)))); + return *(nullHandler_ = new (GetStateAllocator().Malloc(sizeof(OutputHandler))) OutputHandler); } static const size_t kDefaultSchemaStackCapacity = 1024; From 5c2bb187725f27de73cb1ca8b26750e0e139046b Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Tue, 28 Feb 2017 00:06:02 -0800 Subject: [PATCH 052/117] Add IterativeParse docs --- doc/sax.md | 51 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/doc/sax.md b/doc/sax.md index ed6d46a60..c716d3a1d 100644 --- a/doc/sax.md +++ b/doc/sax.md @@ -8,7 +8,7 @@ In RapidJSON, `Reader` (typedef of `GenericReader<...>`) is the SAX-style parser # Reader {#Reader} -`Reader` parses a JSON from a stream. While it reads characters from the stream, it analyze the characters according to the syntax of JSON, and publish events to a handler. +`Reader` parses a JSON from a stream. While it reads characters from the stream, it analyzes the characters according to the syntax of JSON, and publishes events to a handler. For example, here is a JSON. @@ -24,7 +24,7 @@ For example, here is a JSON. } ~~~~~~~~~~ -While a `Reader` parses this JSON, it publishes the following events to the handler sequentially: +When a `Reader` parses this JSON, it publishes the following events to the handler sequentially: ~~~~~~~~~~ StartObject() @@ -50,7 +50,7 @@ EndArray(4) EndObject(7) ~~~~~~~~~~ -These events can be easily matched with the JSON, except some event parameters need further explanation. Let's see the `simplereader` example which produces exactly the same output as above: +These events can be easily matched with the JSON, but some event parameters need further explanation. Let's see the `simplereader` example which produces exactly the same output as above: ~~~~~~~~~~cpp #include "rapidjson/reader.h" @@ -91,11 +91,11 @@ void main() { } ~~~~~~~~~~ -Note that, RapidJSON uses template to statically bind the `Reader` type and the handler type, instead of using class with virtual functions. This paradigm can improve the performance by inlining functions. +Note that RapidJSON uses templates to statically bind the `Reader` type and the handler type, instead of using classes with virtual functions. This paradigm can improve performance by inlining functions. ## Handler {#Handler} -As the previous example showed, user needs to implement a handler, which consumes the events (function calls) from `Reader`. The handler must contain the following member functions. +As shown in the previous example, the user needs to implement a handler which consumes the events (via function calls) from the `Reader`. The handler must contain the following member functions. ~~~~~~~~~~cpp class Handler { @@ -122,15 +122,15 @@ class Handler { When the `Reader` encounters a JSON number, it chooses a suitable C++ type mapping. And then it calls *one* function out of `Int(int)`, `Uint(unsigned)`, `Int64(int64_t)`, `Uint64(uint64_t)` and `Double(double)`. If `kParseNumbersAsStrings` is enabled, `Reader` will always calls `RawNumber()` instead. -`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `\0` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And beware that, the character type depends on the target encoding, which will be explained later. +`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `\0` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And be aware that the character type depends on the target encoding, which will be explained later. -When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `Key()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter. +When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `Key()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeat until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler; users who do not need this parameter may ignore it. -Array is similar to object but simpler. At the beginning of an array, the `Reader` calls `BeginArary()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler. +Arrays are similar to objects, but simpler. At the beginning of an array, the `Reader` calls `BeginArary()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler. -Every handler functions returns a `bool`. Normally it should returns `true`. If the handler encounters an error, it can return `false` to notify event publisher to stop further processing. +Every handler function returns a `bool`. Normally it should return `true`. If the handler encounters an error, it can return `false` to notify the event publisher to stop further processing. -For example, when we parse a JSON with `Reader` and the handler detected that the JSON does not conform to the required schema, then the handler can return `false` and let the `Reader` stop further parsing. And the `Reader` will be in error state with error code `kParseErrorTermination`. +For example, when we parse a JSON with `Reader` and the handler detects that the JSON does not conform to the required schema, the handler can return `false` and let the `Reader` stop further parsing. This will place the `Reader` in an error state, with error code `kParseErrorTermination`. ## GenericReader {#GenericReader} @@ -149,19 +149,19 @@ typedef GenericReader, UTF8<> > Reader; } // namespace rapidjson ~~~~~~~~~~ -The `Reader` uses UTF-8 as both source and target encoding. The source encoding means the encoding in the JSON stream. The target encoding means the encoding of the `str` parameter in `String()` calls. For example, to parse a UTF-8 stream and outputs UTF-16 string events, you can define a reader by: +The `Reader` uses UTF-8 as both source and target encoding. The source encoding means the encoding in the JSON stream. The target encoding means the encoding of the `str` parameter in `String()` calls. For example, to parse a UTF-8 stream and output UTF-16 string events, you can define a reader by: ~~~~~~~~~~cpp GenericReader, UTF16<> > reader; ~~~~~~~~~~ -Note that, the default character type of `UTF16` is `wchar_t`. So this `reader`needs to call `String(const wchar_t*, SizeType, bool)` of the handler. +Note that, the default character type of `UTF16` is `wchar_t`. So this `reader` needs to call `String(const wchar_t*, SizeType, bool)` of the handler. The third template parameter `Allocator` is the allocator type for internal data structure (actually a stack). ## Parsing {#SaxParsing} -The one and only one function of `Reader` is to parse JSON. +The main function of `Reader` is used to parse JSON. ~~~~~~~~~~cpp template @@ -172,7 +172,30 @@ template bool Parse(InputStream& is, Handler& handler); ~~~~~~~~~~ -If an error occurs during parsing, it will return `false`. User can also calls `bool HasParseEror()`, `ParseErrorCode GetParseErrorCode()` and `size_t GetErrorOffset()` to obtain the error states. Actually `Document` uses these `Reader` functions to obtain parse errors. Please refer to [DOM](doc/dom.md) for details about parse error. +If an error occurs during parsing, it will return `false`. User can also call `bool HasParseError()`, `ParseErrorCode GetParseErrorCode()` and `size_t GetErrorOffset()` to obtain the error states. In fact, `Document` uses these `Reader` functions to obtain parse errors. Please refer to [DOM](doc/dom.md) for details about parse errors. + +## Token-by-Token Parsing {#TokenByTokenParsing} + +Some users may wish to parse a JSON input stream a single token at a time, instead of immediately parsing an entire document without stopping. To parse JSON this way, instead of calling `Parse`, you can use the `IterativeParse` set of functions: + +~~~~~~~~~~cpp + void IterativeParseInit(); + + template + bool IterativeParseNext(InputStream& is, Handler& handler); + + bool IterativeParseComplete(); +~~~~~~~~~~ + +Here is an example of iteratively parsing JSON, token by token: + +~~~~~~~~~~cpp + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + reader.IterativeParseNext(ss, handler); + // Your handler has been called once. + } +~~~~~~~~~~ # Writer {#Writer} From 0f3bf99d58a4d704246c1bba6183838629960abd Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Tue, 28 Feb 2017 00:08:30 -0800 Subject: [PATCH 053/117] Tiny fix Make example code var names match API above for consistency --- doc/sax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sax.md b/doc/sax.md index c716d3a1d..486788071 100644 --- a/doc/sax.md +++ b/doc/sax.md @@ -192,7 +192,7 @@ Here is an example of iteratively parsing JSON, token by token: ~~~~~~~~~~cpp reader.IterativeParseInit(); while (!reader.IterativeParseComplete()) { - reader.IterativeParseNext(ss, handler); + reader.IterativeParseNext(is, handler); // Your handler has been called once. } ~~~~~~~~~~ From 6e2e5c7dbe08474249ca18b50da120b2c45ccc36 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Tue, 28 Feb 2017 01:11:30 -0800 Subject: [PATCH 054/117] Specialize StrLen for char/wchar_t Compilers generally provide a much smarter strlen than ours. --- include/rapidjson/internal/strfunc.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h index de41d8f9c..226439a76 100644 --- a/include/rapidjson/internal/strfunc.h +++ b/include/rapidjson/internal/strfunc.h @@ -16,6 +16,7 @@ #define RAPIDJSON_INTERNAL_STRFUNC_H_ #include "../stream.h" +#include RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -34,6 +35,16 @@ inline SizeType StrLen(const Ch* s) { return SizeType(p - s); } +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + //! Returns number of code points in a encoded string. template bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { From 4b822a41af98974a4ca96b79cd13afe163214d81 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Tue, 28 Feb 2017 19:31:21 -0800 Subject: [PATCH 055/117] Attempt to suppress valgrind wcslen error --- test/unittest/CMakeLists.txt | 2 +- test/valgrind.supp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 test/valgrind.supp diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index 4e2976562..fdf0ad067 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -79,7 +79,7 @@ add_test(NAME unittest if(NOT MSVC) # Not running SIMD.* unit test cases for Valgrind add_test(NAME valgrind_unittest - COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest --gtest_filter=-SIMD.* + COMMAND valgrind --suppressions=${CMAKE_SOURCE_DIR}/test/valgrind.supp --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest --gtest_filter=-SIMD.* WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/test/valgrind.supp b/test/valgrind.supp new file mode 100644 index 000000000..5a205b749 --- /dev/null +++ b/test/valgrind.supp @@ -0,0 +1,5 @@ +{ + Suppress wcslen valgrind report + Memcheck:Addr8 + fun:__wcslen_sse2 +} From 13e99d8d5ffea627a169734128d38a31a7895b29 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Tue, 28 Feb 2017 22:58:24 -0800 Subject: [PATCH 056/117] Trivial change to re-trigger Travis CI No-op blank line --- test/unittest/writertest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index 398a63dca..8fd6eb801 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -544,3 +544,4 @@ TEST(Writer, MoveCtor) { #ifdef __clang__ RAPIDJSON_DIAG_POP #endif + From 3f9ebfe9e9e9b4ac57f8d58fbce390b4f9a5977e Mon Sep 17 00:00:00 2001 From: John Stiles Date: Thu, 2 Mar 2017 21:24:03 -0800 Subject: [PATCH 057/117] Trivial change to trigger Travis CI --- include/rapidjson/writer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 43ec5dc64..12d314500 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -623,3 +623,4 @@ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_RAPIDJSON_H_ + From 534f1352619328668e8bc4ffaf9bacb5de697180 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Fri, 3 Mar 2017 00:21:10 -0800 Subject: [PATCH 058/117] Try again to suppress Valgrind --- test/valgrind.supp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/valgrind.supp b/test/valgrind.supp index 5a205b749..85523852d 100644 --- a/test/valgrind.supp +++ b/test/valgrind.supp @@ -1,5 +1,5 @@ { Suppress wcslen valgrind report - Memcheck:Addr8 + Memcheck:Cond fun:__wcslen_sse2 } From 6ae50ad6e32030c2d930b313a1c740acbbb0cca6 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Fri, 3 Mar 2017 00:27:47 -0800 Subject: [PATCH 059/117] Once again --- test/valgrind.supp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/valgrind.supp b/test/valgrind.supp index 85523852d..1fed18bea 100644 --- a/test/valgrind.supp +++ b/test/valgrind.supp @@ -1,5 +1,17 @@ { - Suppress wcslen valgrind report + Suppress wcslen valgrind report 1 Memcheck:Cond fun:__wcslen_sse2 } + +{ + Suppress wcslen valgrind report 2 + Memcheck:Addr8 + fun:__wcslen_sse2 +} + +{ + Suppress wcslen valgrind report 3 + Memcheck:Value8 + fun:__wcslen_sse2 +} From db8d3bb4d60aa20c5d6ec4be1f6e71c33d9874b9 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Fri, 3 Mar 2017 00:42:00 -0800 Subject: [PATCH 060/117] Remove unneeded change --- include/rapidjson/writer.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 12d314500..43ec5dc64 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -623,4 +623,3 @@ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_RAPIDJSON_H_ - From 66b564f385d96d7adbe1ba3073a140f5301e0af6 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Fri, 3 Mar 2017 00:42:21 -0800 Subject: [PATCH 061/117] Remove unneeded change --- test/unittest/writertest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index 8fd6eb801..398a63dca 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -544,4 +544,3 @@ TEST(Writer, MoveCtor) { #ifdef __clang__ RAPIDJSON_DIAG_POP #endif - From d6e9cf5d54ee40dcbe4cc939f33cdf41878a71d7 Mon Sep 17 00:00:00 2001 From: Erik Froseth Date: Fri, 3 Mar 2017 09:48:41 +0100 Subject: [PATCH 062/117] Remove executable bit Remove the executable bit for various .json files --- bin/types/booleans.json | 0 bin/types/floats.json | 0 bin/types/guids.json | 0 bin/types/integers.json | 0 bin/types/mixed.json | 0 bin/types/nulls.json | 0 bin/types/paragraphs.json | 0 7 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 bin/types/booleans.json mode change 100755 => 100644 bin/types/floats.json mode change 100755 => 100644 bin/types/guids.json mode change 100755 => 100644 bin/types/integers.json mode change 100755 => 100644 bin/types/mixed.json mode change 100755 => 100644 bin/types/nulls.json mode change 100755 => 100644 bin/types/paragraphs.json diff --git a/bin/types/booleans.json b/bin/types/booleans.json old mode 100755 new mode 100644 diff --git a/bin/types/floats.json b/bin/types/floats.json old mode 100755 new mode 100644 diff --git a/bin/types/guids.json b/bin/types/guids.json old mode 100755 new mode 100644 diff --git a/bin/types/integers.json b/bin/types/integers.json old mode 100755 new mode 100644 diff --git a/bin/types/mixed.json b/bin/types/mixed.json old mode 100755 new mode 100644 diff --git a/bin/types/nulls.json b/bin/types/nulls.json old mode 100755 new mode 100644 diff --git a/bin/types/paragraphs.json b/bin/types/paragraphs.json old mode 100755 new mode 100644 From dd97ede84d517e2a1d433c5bfc1610d5d769a430 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Sun, 5 Mar 2017 00:27:08 -0800 Subject: [PATCH 063/117] Quoted strings to String() or Key() are auto-sized by template No strlen call needs to be made when templates can auto-deduce the string length. No strlen = faster! Unfortunately this needs a touch of SFINAE to allow multiple overrides to coexist cleanly. --- include/rapidjson/writer.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 43ec5dc64..755f483b5 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -16,6 +16,7 @@ #define RAPIDJSON_WRITER_H_ #include "stream.h" +#include "internal/meta.h" #include "internal/stack.h" #include "internal/strfunc.h" #include "internal/dtoa.h" @@ -198,7 +199,8 @@ class Writer { return EndValue(WriteString(str, length)); } - bool String(const Ch* str, SizeType length, bool copy = false) { + template + bool String(const T* str, SizeType length, bool copy = false, RAPIDJSON_ENABLEIF((internal::IsSame))) { RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kStringType); @@ -217,7 +219,8 @@ class Writer { return WriteStartObject(); } - bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + template + bool Key(const T* str, SizeType length, bool copy = false, RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, length, copy); } bool EndObject(SizeType memberCount = 0) { (void)memberCount; @@ -247,8 +250,16 @@ class Writer { //@{ //! Simpler but slower overload. - bool String(const Ch* str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + template + bool String(const T* const& str, RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, internal::StrLen(str)); } + template + bool Key(const T* const& str, RAPIDJSON_ENABLEIF((internal::IsSame))) { return Key(str, internal::StrLen(str)); } + + //! The compiler can give us the length of quoted strings for free. + template + bool String(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, N-1); } + template + bool Key(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { return Key(str, N-1); } //@} From 61f8c4ef0df9d91fe6a684bb1b1572e0a537f66e Mon Sep 17 00:00:00 2001 From: John Stiles Date: Sun, 5 Mar 2017 00:38:34 -0800 Subject: [PATCH 064/117] Quoted strings to String() or Key() are auto-sized by template Same fix as previous commit, to prettywriter --- include/rapidjson/prettywriter.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index b68b687db..64301b8db 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -107,7 +107,8 @@ class PrettyWriter : public Writer + bool String(const T* str, SizeType length, bool copy = false, RAPIDJSON_ENABLEIF((internal::IsSame))) { RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); @@ -126,7 +127,8 @@ class PrettyWriter : public Writer + bool Key(const T* str, SizeType length, bool copy = false, RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, length, copy); } #if RAPIDJSON_HAS_STDSTRING bool Key(const std::basic_string& str) { @@ -184,8 +186,16 @@ class PrettyWriter : public Writer + bool String(const T* const& str, RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, internal::StrLen(str)); } + template + bool Key(const T* const& str, RAPIDJSON_ENABLEIF((internal::IsSame))) { return Key(str, internal::StrLen(str)); } + + //! The compiler can give us the length of quoted strings for free. + template + bool String(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, N-1); } + template + bool Key(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { return Key(str, N-1); } //@} From cdea825a0bc81531d2cd2758362b606106373477 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Sun, 5 Mar 2017 09:23:03 -0800 Subject: [PATCH 065/117] Assert that String() and Key() are given null-terminated strings Assert in case users attempt to pass a char array to String() or Key() that is not null terminated; that is not the intended use of the API. Null terminate your string buffers. --- include/rapidjson/prettywriter.h | 10 ++++++++-- include/rapidjson/writer.h | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index 64301b8db..abea4048c 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -193,9 +193,15 @@ class PrettyWriter : public Writer - bool String(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, N-1); } + bool String(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) + return String(str, N-1); + } template - bool Key(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { return Key(str, N-1); } + bool Key(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) + return Key(str, N-1); + } //@} diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 755f483b5..c438f71fb 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -257,9 +257,15 @@ class Writer { //! The compiler can give us the length of quoted strings for free. template - bool String(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, N-1); } + bool String(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) + return String(str, N-1); + } template - bool Key(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { return Key(str, N-1); } + bool Key(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) + return Key(str, N-1); + } //@} From c4e3d6243ce0321b32c9bfc7e3692753b21e46f8 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Sun, 5 Mar 2017 09:50:03 -0800 Subject: [PATCH 066/117] Fix msvc x64 compilation issue Disambiguate by putting the ENABLEIF on the return value instead of in the argument list. --- include/rapidjson/prettywriter.h | 12 ++++++------ include/rapidjson/writer.h | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index abea4048c..a9d0f02ab 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -108,7 +108,7 @@ class PrettyWriter : public Writer - bool String(const T* str, SizeType length, bool copy = false, RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); @@ -128,7 +128,7 @@ class PrettyWriter : public Writer - bool Key(const T* str, SizeType length, bool copy = false, RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, length, copy); } + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T* str, SizeType length, bool copy = false) { return String(str, length, copy); } #if RAPIDJSON_HAS_STDSTRING bool Key(const std::basic_string& str) { @@ -187,18 +187,18 @@ class PrettyWriter : public Writer - bool String(const T* const& str, RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, internal::StrLen(str)); } + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T* const& str) { return String(str, internal::StrLen(str)); } template - bool Key(const T* const& str, RAPIDJSON_ENABLEIF((internal::IsSame))) { return Key(str, internal::StrLen(str)); } + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T* const& str) { return Key(str, internal::StrLen(str)); } //! The compiler can give us the length of quoted strings for free. template - bool String(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T (&str)[N]) { RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) return String(str, N-1); } template - bool Key(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T (&str)[N]) { RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) return Key(str, N-1); } diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index c438f71fb..7a0af392a 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -200,7 +200,7 @@ class Writer { } template - bool String(const T* str, SizeType length, bool copy = false, RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kStringType); @@ -220,7 +220,7 @@ class Writer { } template - bool Key(const T* str, SizeType length, bool copy = false, RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, length, copy); } + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T* str, SizeType length, bool copy = false) { return String(str, length, copy); } bool EndObject(SizeType memberCount = 0) { (void)memberCount; @@ -251,18 +251,18 @@ class Writer { //! Simpler but slower overload. template - bool String(const T* const& str, RAPIDJSON_ENABLEIF((internal::IsSame))) { return String(str, internal::StrLen(str)); } + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T* const& str) { return String(str, internal::StrLen(str)); } template - bool Key(const T* const& str, RAPIDJSON_ENABLEIF((internal::IsSame))) { return Key(str, internal::StrLen(str)); } + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T* const& str) { return Key(str, internal::StrLen(str)); } //! The compiler can give us the length of quoted strings for free. template - bool String(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T (&str)[N]) { RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) return String(str, N-1); } template - bool Key(const T (&str)[N], RAPIDJSON_ENABLEIF((internal::IsSame))) { + RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T (&str)[N]) { RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) return Key(str, N-1); } From c64f378f16f23742b316e99d6fe40a3c14f95698 Mon Sep 17 00:00:00 2001 From: Ted Lyngmo Date: Wed, 8 Mar 2017 06:25:41 +0100 Subject: [PATCH 067/117] Fix -Werror=effc++ errors with GNU 6.3.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix "'MyHandler::type’ should be initialized in the member initialization list [-Werror=effc++]" errors. https://github.com/miloyip/rapidjson/issues/874 --- example/simplepullreader/simplepullreader.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/example/simplepullreader/simplepullreader.cpp b/example/simplepullreader/simplepullreader.cpp index 98566e618..140182951 100644 --- a/example/simplepullreader/simplepullreader.cpp +++ b/example/simplepullreader/simplepullreader.cpp @@ -16,6 +16,14 @@ struct MyHandler { const char* type; std::string data; + MyHandler() : type(), data() { Null(); } + MyHandler(const MyHandler& cpy) : type(cpy.type),data(cpy.data) {} + MyHandler& operator=(const MyHandler& cpy) { + type = cpy.type; + data = cpy.data; + return *this; + } + bool Null() { type = "Null"; data.clear(); return true; } bool Bool(bool b) { type = "Bool:"; data = b? "true": "false"; return true; } bool Int(int i) { type = "Int:"; data = stringify(i); return true; } From ef22ca17321933eb2bd54ff7975657babab18cdd Mon Sep 17 00:00:00 2001 From: Ted Lyngmo Date: Wed, 8 Mar 2017 06:25:41 +0100 Subject: [PATCH 068/117] Fix -Werror=effc++ errors with GNU 6.3.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix "'MyHandler::type’ should be initialized in the member initialization list [-Werror=effc++]" errors. https://github.com/miloyip/rapidjson/issues/874 --- example/simplepullreader/simplepullreader.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/example/simplepullreader/simplepullreader.cpp b/example/simplepullreader/simplepullreader.cpp index 140182951..a4fb1161a 100644 --- a/example/simplepullreader/simplepullreader.cpp +++ b/example/simplepullreader/simplepullreader.cpp @@ -16,13 +16,7 @@ struct MyHandler { const char* type; std::string data; - MyHandler() : type(), data() { Null(); } - MyHandler(const MyHandler& cpy) : type(cpy.type),data(cpy.data) {} - MyHandler& operator=(const MyHandler& cpy) { - type = cpy.type; - data = cpy.data; - return *this; - } + MyHandler() : type(), data() {} bool Null() { type = "Null"; data.clear(); return true; } bool Bool(bool b) { type = "Bool:"; data = b? "true": "false"; return true; } @@ -38,6 +32,9 @@ struct MyHandler { bool EndObject(SizeType memberCount) { type = "EndObject:"; data = stringify(memberCount); return true; } bool StartArray() { type = "StartArray"; data.clear(); return true; } bool EndArray(SizeType elementCount) { type = "EndArray:"; data = stringify(elementCount); return true; } +private: + MyHandler(const MyHandler& noCopyConstruction); + MyHandler& operator=(const MyHandler& noAssignment); }; int main() { From d4669bbc8e60b5c25097c21097248bd788b5f4b9 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Wed, 8 Mar 2017 01:08:41 -0800 Subject: [PATCH 069/117] Add lookahead parser example --- example/CMakeLists.txt | 1 + example/lookaheadparser/lookaheadparser.cpp | 341 ++++++++++++++++++++ 2 files changed, 342 insertions(+) create mode 100644 example/lookaheadparser/lookaheadparser.cpp diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index e16e3c9ed..e00f77aab 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -10,6 +10,7 @@ set(EXAMPLES filterkey filterkeydom jsonx + lookaheadparser messagereader parsebyparts pretty diff --git a/example/lookaheadparser/lookaheadparser.cpp b/example/lookaheadparser/lookaheadparser.cpp new file mode 100644 index 000000000..7c7f38751 --- /dev/null +++ b/example/lookaheadparser/lookaheadparser.cpp @@ -0,0 +1,341 @@ +#include "rapidjson/reader.h" +#include "rapidjson/document.h" +#include + +// This example demonstrates JSON token-by-token parsing with an API that is +// more direct; you don't need to design your logic around a handler object and +// callbacks. Instead, you retrieve values from the JSON stream by calling +// GetInt(), GetDouble(), GetString() and GetBool(), traverse into structures +// by calling EnterObject() and EnterArray(), and skip over unwanted data by +// calling SkipValue(). When you know your JSON's structure, this can be quite +// convenient. +// +// If you aren't sure of what's next in the JSON data, you can use PeekType() and +// PeekValue() to look ahead to the next object before reading it. +// +// If you call the wrong retrieval method--e.g. GetInt when the next JSON token is +// not an int, EnterObject or EnterArray when there isn't actually an object or array +// to read--the stream parsing will end immediately and no more data will be delivered. +// +// After calling EnterObject, you retrieve keys via NextObjectKey() and values via +// the normal getters. When NextObjectKey() returns null, you have exited the +// object, or you can call ExitObject() to skip to the end of the object +// immediately. If you fetch the entire object (i.e. NextObjectKey() returned null), +// you should not call ExitObject(). +// +// After calling EnterArray(), you must alternate between calling NextArrayValue() +// to see if the array has more data, and then retrieving values via the normal +// getters. You can call ExitArray() to skip to the end of the array immediately. +// If you fetch the entire array (i.e. NextArrayValue() returned null), +// you should not call ExitArray(). +// +// This parser uses in-situ strings, so the JSON buffer will be altered during the +// parse. + +using namespace rapidjson; + + +class LookaheadParserHandler { +public: + bool Null() { st_ = kHasValue; v_.SetNull(); return true; } + bool Bool(bool b) { st_ = kHasValue; v_.SetBool(b); return true; } + bool Int(int i) { st_ = kHasValue; v_.SetInt(i); return true; } + bool Uint(unsigned u) { st_ = kHasValue; v_.SetUint(u); return true; } + bool Int64(int64_t i) { st_ = kHasValue; v_.SetInt64(i); return true; } + bool Uint64(uint64_t u) { st_ = kHasValue; v_.SetUint64(u); return true; } + bool Double(double d) { st_ = kHasValue; v_.SetDouble(d); return true; } + bool RawNumber(const char*, SizeType, bool) { return false; } + bool String(const char* str, SizeType length, bool) { st_ = kHasValue; v_.SetString(str, length); return true; } + bool StartObject() { st_ = kEnteringObject; return true; } + bool Key(const char* str, SizeType length, bool) { st_ = kHasKey; v_.SetString(str, length); return true; } + bool EndObject(SizeType) { st_ = kExitingObject; return true; } + bool StartArray() { st_ = kEnteringArray; return true; } + bool EndArray(SizeType) { st_ = kExitingArray; return true; } + +protected: + LookaheadParserHandler(char* str); + void ParseNext(); + +protected: + enum LookaheadParsingState { + kError, + kHasValue, + kHasKey, + kEnteringObject, + kExitingObject, + kEnteringArray, + kExitingArray + }; + + Value v_; + LookaheadParsingState st_; + Reader r_; + InsituStringStream ss_; + + static const int parseFlags = kParseDefaultFlags | kParseInsituFlag; +}; + +LookaheadParserHandler::LookaheadParserHandler(char* str) : ss_(str) { + r_.IterativeParseInit(); + ParseNext(); +} + +void LookaheadParserHandler::ParseNext() { + if (r_.HasParseError()) { + st_ = kError; + return; + } + + r_.IterativeParseNext(ss_, *this); +} + +class LookaheadParser : protected LookaheadParserHandler { +public: + LookaheadParser(char* str) : LookaheadParserHandler(str) {} + + void EnterObject(); + void EnterArray(); + void ExitObject(); + void ExitArray(); + const char* NextObjectKey(); + bool NextArrayValue(); + int GetInt(); + double GetDouble(); + const char* GetString(); + bool GetBool(); + void GetNull(); + + void SkipValue(); + Value* PeekValue(); + int PeekType(); // returns a rapidjson::Type, or -1 for no value (at end of object/array) + + bool IsValid() { return st_ != kError; } +}; + +void LookaheadParser::EnterObject() { + if (st_ != kEnteringObject) { + st_ = kError; + return; + } + + ParseNext(); +} + +void LookaheadParser::EnterArray() { + if (st_ != kEnteringArray) { + st_ = kError; + return; + } + + ParseNext(); +} + +void LookaheadParser::ExitObject() { + while (NextObjectKey()) { + SkipValue(); + } +} + +void LookaheadParser::ExitArray() { + while (NextArrayValue()) { + SkipValue(); + } +} + +const char* LookaheadParser::NextObjectKey() { + if (st_ == kExitingObject) { + ParseNext(); + return 0; + } + + if (st_ != kHasKey || !v_.IsString()) { + st_ = kError; + return 0; + } + + const char* result = v_.GetString(); + ParseNext(); + return result; +} + +bool LookaheadParser::NextArrayValue() { + if (st_ == kExitingArray) { + ParseNext(); + return false; + } + + return true; +} + +int LookaheadParser::GetInt() { + if (st_ != kHasValue || !v_.IsInt()) { + st_ = kError; + return 0; + } + + int result = v_.GetInt(); + ParseNext(); + return result; +} + +double LookaheadParser::GetDouble() { + if (st_ != kHasValue || !v_.IsNumber()) { + st_ = kError; + return 0.; + } + + double result = v_.GetDouble(); + ParseNext(); + return result; +} + +bool LookaheadParser::GetBool() { + if (st_ != kHasValue || !v_.IsBool()) { + st_ = kError; + return false; + } + + bool result = v_.GetBool(); + ParseNext(); + return result; +} + +void LookaheadParser::GetNull() { + if (st_ != kHasValue || !v_.IsNull()) { + st_ = kError; + return; + } + + ParseNext(); +} + +const char* LookaheadParser::GetString() { + if (st_ != kHasValue || !v_.IsString()) { + st_ = kError; + return 0; + } + + const char* result = v_.GetString(); + ParseNext(); + return result; +} + +void LookaheadParser::SkipValue() { + int depth = 0; + do { + switch (st_) { + case kEnteringArray: + case kEnteringObject: + ++depth; + break; + + case kExitingArray: + case kExitingObject: + --depth; + break; + + case kError: + return; + + case kHasKey: + case kHasValue: + break; + } + ParseNext(); + } + while (depth > 0); +} + +Value* LookaheadParser::PeekValue() { + if (st_ == kHasValue || st_ == kHasKey) { + return &v_; + } + + return 0; +} + +int LookaheadParser::PeekType() { + switch (st_) { + case kHasValue: + case kHasKey: + return v_.GetType(); + + case kEnteringArray: + return kArrayType; + + case kEnteringObject: + return kObjectType; + + case kExitingArray: + case kExitingObject: + case kError: + return -1; + } +} + +//------------------------------------------------------------------------- + +int main() { + using namespace std; + + char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null," + "\"i\":123, \"pi\": 3.1416, \"a\":[-1, 2, 3, 4, \"array\", []], \"skipArrays\":[1, 2, [[[3]]]], " + "\"skipObject\":{ \"i\":0, \"t\":true, \"n\":null, \"d\":123.45 }, " + "\"skipNested\":[[[[{\"\":0}, {\"\":[-9.87]}]]], [], []], " + "\"skipString\":\"zzz\", \"reachedEnd\":null, \"t\":true }"; + + LookaheadParser r(json); + + RAPIDJSON_ASSERT(r.PeekType() == kObjectType); + + r.EnterObject(); + while (const char* key = r.NextObjectKey()) { + if (0 == strcmp(key, "hello")) { + RAPIDJSON_ASSERT(r.PeekType() == kStringType); + cout << key << ":" << r.GetString() << endl; + } + else if (0 == strcmp(key, "t") || 0 == strcmp(key, "f")) { + RAPIDJSON_ASSERT(r.PeekType() == kTrueType || r.PeekType() == kFalseType); + cout << key << ":" << r.GetBool() << endl; + continue; + } + else if (0 == strcmp(key, "n")) { + RAPIDJSON_ASSERT(r.PeekType() == kNullType); + r.GetNull(); + cout << key << endl; + continue; + } + else if (0 == strcmp(key, "pi")) { + RAPIDJSON_ASSERT(r.PeekType() == kNumberType); + cout << key << ":" << r.GetDouble() << endl; + continue; + } + else if (0 == strcmp(key, "a")) { + RAPIDJSON_ASSERT(r.PeekType() == kArrayType); + + r.EnterArray(); + + cout << key << ":[ "; + while (r.NextArrayValue()) { + if (r.PeekType() == kNumberType) { + cout << r.GetDouble() << " "; + } + else if (r.PeekType() == kStringType) { + cout << r.GetString() << " "; + } + else { + r.ExitArray(); + break; + } + } + + cout << "]" << endl; + } + else { + cout << key << ":skipped" << endl; + r.SkipValue(); + } + } + + return 0; +} From 8da89f54bd52023ab7dd8169e3aab25518012cb6 Mon Sep 17 00:00:00 2001 From: StilesCrisis Date: Wed, 8 Mar 2017 01:16:19 -0800 Subject: [PATCH 070/117] Fix GCC warning --- example/lookaheadparser/lookaheadparser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/example/lookaheadparser/lookaheadparser.cpp b/example/lookaheadparser/lookaheadparser.cpp index 7c7f38751..f4759c444 100644 --- a/example/lookaheadparser/lookaheadparser.cpp +++ b/example/lookaheadparser/lookaheadparser.cpp @@ -269,6 +269,7 @@ int LookaheadParser::PeekType() { case kExitingArray: case kExitingObject: case kError: + default: return -1; } } From 84a0356608dcef56d8a9e9f7bd2b9a008c1735b9 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Sun, 12 Mar 2017 23:40:54 -0700 Subject: [PATCH 071/117] Add unit test for Issue 889 --- test/unittest/writertest.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index 398a63dca..e630bb92e 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -100,6 +100,19 @@ TEST(Writer, String) { #endif } +TEST(Writer, Issue_889) { + char buf[100] = "Hello"; + + StringBuffer buffer; + Writer writer(buffer); + writer.StartArray(); + writer.String(buf); + writer.EndArray(); + + EXPECT_STREQ("[\"Hello\"]", buffer.GetString()); + EXPECT_TRUE(writer.IsComplete()); \ +} + TEST(Writer, ScanWriteUnescapedString) { const char json[] = "[\" \\\"0123456789ABCDEF\"]"; // ^ scanning stops here. From 55f8a32020ae45101c709b826818d429c720dff4 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Sun, 12 Mar 2017 23:47:59 -0700 Subject: [PATCH 072/117] Remove broken templatized string length optimization It did not support char arrays. --- include/rapidjson/writer.h | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 7a0af392a..b83b68ebf 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -199,8 +199,7 @@ class Writer { return EndValue(WriteString(str, length)); } - template - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T* str, SizeType length, bool copy = false) { + bool String(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kStringType); @@ -219,8 +218,7 @@ class Writer { return WriteStartObject(); } - template - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T* str, SizeType length, bool copy = false) { return String(str, length, copy); } + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } bool EndObject(SizeType memberCount = 0) { (void)memberCount; @@ -250,23 +248,9 @@ class Writer { //@{ //! Simpler but slower overload. - template - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T* const& str) { return String(str, internal::StrLen(str)); } - template - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T* const& str) { return Key(str, internal::StrLen(str)); } + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } - //! The compiler can give us the length of quoted strings for free. - template - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T (&str)[N]) { - RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) - return String(str, N-1); - } - template - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T (&str)[N]) { - RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) - return Key(str, N-1); - } - //@} //! Write a raw JSON value. From e7fd707698334b17d698be6c741990027e4f1378 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Mon, 13 Mar 2017 00:33:10 -0700 Subject: [PATCH 073/117] Improve LookaheadParser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix clang -Wswitch-enum warnings. Made NextArrayValue() more robust—now handles error state correctly, will enter error state if an unexpected state is reached. Made separate states for each value type to simplify getters. Simplified implementation of skipping arrays and objects. Skipping an object now works whether you’ve retrieved the key or not. --- example/lookaheadparser/lookaheadparser.cpp | 160 ++++++++++++-------- 1 file changed, 97 insertions(+), 63 deletions(-) diff --git a/example/lookaheadparser/lookaheadparser.cpp b/example/lookaheadparser/lookaheadparser.cpp index f4759c444..4d8e13f7f 100644 --- a/example/lookaheadparser/lookaheadparser.cpp +++ b/example/lookaheadparser/lookaheadparser.cpp @@ -19,15 +19,15 @@ // // After calling EnterObject, you retrieve keys via NextObjectKey() and values via // the normal getters. When NextObjectKey() returns null, you have exited the -// object, or you can call ExitObject() to skip to the end of the object +// object, or you can call SkipObject() to skip to the end of the object // immediately. If you fetch the entire object (i.e. NextObjectKey() returned null), -// you should not call ExitObject(). +// you should not call SkipObject(). // // After calling EnterArray(), you must alternate between calling NextArrayValue() // to see if the array has more data, and then retrieving values via the normal -// getters. You can call ExitArray() to skip to the end of the array immediately. +// getters. You can call SkipArray() to skip to the end of the array immediately. // If you fetch the entire array (i.e. NextArrayValue() returned null), -// you should not call ExitArray(). +// you should not call SkipArray(). // // This parser uses in-situ strings, so the JSON buffer will be altered during the // parse. @@ -37,15 +37,15 @@ using namespace rapidjson; class LookaheadParserHandler { public: - bool Null() { st_ = kHasValue; v_.SetNull(); return true; } - bool Bool(bool b) { st_ = kHasValue; v_.SetBool(b); return true; } - bool Int(int i) { st_ = kHasValue; v_.SetInt(i); return true; } - bool Uint(unsigned u) { st_ = kHasValue; v_.SetUint(u); return true; } - bool Int64(int64_t i) { st_ = kHasValue; v_.SetInt64(i); return true; } - bool Uint64(uint64_t u) { st_ = kHasValue; v_.SetUint64(u); return true; } - bool Double(double d) { st_ = kHasValue; v_.SetDouble(d); return true; } + bool Null() { st_ = kHasNull; v_.SetNull(); return true; } + bool Bool(bool b) { st_ = kHasBool; v_.SetBool(b); return true; } + bool Int(int i) { st_ = kHasNumber; v_.SetInt(i); return true; } + bool Uint(unsigned u) { st_ = kHasNumber; v_.SetUint(u); return true; } + bool Int64(int64_t i) { st_ = kHasNumber; v_.SetInt64(i); return true; } + bool Uint64(uint64_t u) { st_ = kHasNumber; v_.SetUint64(u); return true; } + bool Double(double d) { st_ = kHasNumber; v_.SetDouble(d); return true; } bool RawNumber(const char*, SizeType, bool) { return false; } - bool String(const char* str, SizeType length, bool) { st_ = kHasValue; v_.SetString(str, length); return true; } + bool String(const char* str, SizeType length, bool) { st_ = kHasString; v_.SetString(str, length); return true; } bool StartObject() { st_ = kEnteringObject; return true; } bool Key(const char* str, SizeType length, bool) { st_ = kHasKey; v_.SetString(str, length); return true; } bool EndObject(SizeType) { st_ = kExitingObject; return true; } @@ -59,7 +59,10 @@ class LookaheadParserHandler { protected: enum LookaheadParsingState { kError, - kHasValue, + kHasNull, + kHasBool, + kHasNumber, + kHasString, kHasKey, kEnteringObject, kExitingObject, @@ -93,10 +96,8 @@ class LookaheadParser : protected LookaheadParserHandler { public: LookaheadParser(char* str) : LookaheadParserHandler(str) {} - void EnterObject(); - void EnterArray(); - void ExitObject(); - void ExitArray(); + bool EnterObject(); + bool EnterArray(); const char* NextObjectKey(); bool NextArrayValue(); int GetInt(); @@ -105,70 +106,87 @@ class LookaheadParser : protected LookaheadParserHandler { bool GetBool(); void GetNull(); + void SkipObject(); + void SkipArray(); void SkipValue(); Value* PeekValue(); int PeekType(); // returns a rapidjson::Type, or -1 for no value (at end of object/array) bool IsValid() { return st_ != kError; } + +protected: + void SkipOut(int depth); }; -void LookaheadParser::EnterObject() { +bool LookaheadParser::EnterObject() { if (st_ != kEnteringObject) { st_ = kError; - return; + return false; } ParseNext(); + return true; } -void LookaheadParser::EnterArray() { +bool LookaheadParser::EnterArray() { if (st_ != kEnteringArray) { st_ = kError; - return; + return false; } ParseNext(); -} - -void LookaheadParser::ExitObject() { - while (NextObjectKey()) { - SkipValue(); - } -} - -void LookaheadParser::ExitArray() { - while (NextArrayValue()) { - SkipValue(); - } + return true; } const char* LookaheadParser::NextObjectKey() { - if (st_ == kExitingObject) { - ParseNext(); - return 0; - } - - if (st_ != kHasKey || !v_.IsString()) { - st_ = kError; - return 0; + switch (st_) { + case kHasKey: { + const char* result = v_.GetString(); + ParseNext(); + return result; + } + + case kExitingObject: + ParseNext(); + return 0; + + case kError: + case kHasNull: + case kHasBool: + case kHasNumber: + case kHasString: + case kEnteringObject: + case kEnteringArray: + case kExitingArray: + st_ = kError; + return 0; } - - const char* result = v_.GetString(); - ParseNext(); - return result; } bool LookaheadParser::NextArrayValue() { - if (st_ == kExitingArray) { - ParseNext(); - return false; + switch (st_) { + case kExitingArray: + ParseNext(); + return false; + + case kError: + case kExitingObject: + case kHasKey: + st_ = kError; + return false; + + case kHasNull: + case kHasBool: + case kHasNumber: + case kHasString: + case kEnteringObject: + case kEnteringArray: + return true; } - - return true; } int LookaheadParser::GetInt() { - if (st_ != kHasValue || !v_.IsInt()) { + if (st_ != kHasNumber || !v_.IsInt()) { st_ = kError; return 0; } @@ -179,7 +197,7 @@ int LookaheadParser::GetInt() { } double LookaheadParser::GetDouble() { - if (st_ != kHasValue || !v_.IsNumber()) { + if (st_ != kHasNumber || !v_.IsNumber()) { st_ = kError; return 0.; } @@ -190,7 +208,7 @@ double LookaheadParser::GetDouble() { } bool LookaheadParser::GetBool() { - if (st_ != kHasValue || !v_.IsBool()) { + if (st_ != kHasBool) { st_ = kError; return false; } @@ -201,7 +219,7 @@ bool LookaheadParser::GetBool() { } void LookaheadParser::GetNull() { - if (st_ != kHasValue || !v_.IsNull()) { + if (st_ != kHasNull) { st_ = kError; return; } @@ -210,7 +228,7 @@ void LookaheadParser::GetNull() { } const char* LookaheadParser::GetString() { - if (st_ != kHasValue || !v_.IsString()) { + if (st_ != kHasString) { st_ = kError; return 0; } @@ -220,8 +238,7 @@ const char* LookaheadParser::GetString() { return result; } -void LookaheadParser::SkipValue() { - int depth = 0; +void LookaheadParser::SkipOut(int depth) { do { switch (st_) { case kEnteringArray: @@ -237,8 +254,11 @@ void LookaheadParser::SkipValue() { case kError: return; - case kHasKey: - case kHasValue: + case kHasNull: + case kHasBool: + case kHasNumber: + case kHasString: + case kHasKey: break; } ParseNext(); @@ -246,8 +266,20 @@ void LookaheadParser::SkipValue() { while (depth > 0); } +void LookaheadParser::SkipValue() { + SkipOut(0); +} + +void LookaheadParser::SkipArray() { + SkipOut(1); +} + +void LookaheadParser::SkipObject() { + SkipOut(1); +} + Value* LookaheadParser::PeekValue() { - if (st_ == kHasValue || st_ == kHasKey) { + if (st_ >= kHasNull && st_ <= kHasKey) { return &v_; } @@ -256,7 +288,10 @@ Value* LookaheadParser::PeekValue() { int LookaheadParser::PeekType() { switch (st_) { - case kHasValue: + case kHasNull: + case kHasBool: + case kHasNumber: + case kHasString: case kHasKey: return v_.GetType(); @@ -269,7 +304,6 @@ int LookaheadParser::PeekType() { case kExitingArray: case kExitingObject: case kError: - default: return -1; } } @@ -325,7 +359,7 @@ int main() { cout << r.GetString() << " "; } else { - r.ExitArray(); + r.SkipArray(); break; } } From bf19c1a0beafa6a118b1c242d16d0c0cfe0296e2 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Mon, 13 Mar 2017 07:40:51 -0700 Subject: [PATCH 074/117] Remove switch GCC and Clang cannot agree on what constitutes a good switch statement. --- example/lookaheadparser/lookaheadparser.cpp | 61 ++++++++------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/example/lookaheadparser/lookaheadparser.cpp b/example/lookaheadparser/lookaheadparser.cpp index 4d8e13f7f..29d929946 100644 --- a/example/lookaheadparser/lookaheadparser.cpp +++ b/example/lookaheadparser/lookaheadparser.cpp @@ -139,50 +139,33 @@ bool LookaheadParser::EnterArray() { } const char* LookaheadParser::NextObjectKey() { - switch (st_) { - case kHasKey: { - const char* result = v_.GetString(); - ParseNext(); - return result; - } - - case kExitingObject: - ParseNext(); - return 0; - - case kError: - case kHasNull: - case kHasBool: - case kHasNumber: - case kHasString: - case kEnteringObject: - case kEnteringArray: - case kExitingArray: - st_ = kError; - return 0; + if (st_ == kHasKey) { + const char* result = v_.GetString(); + ParseNext(); + return result; + } + + if (st_ == kExitingObject) { + ParseNext(); + return 0; } + + st_ = kError; + return 0; } bool LookaheadParser::NextArrayValue() { - switch (st_) { - case kExitingArray: - ParseNext(); - return false; - - case kError: - case kExitingObject: - case kHasKey: - st_ = kError; - return false; - - case kHasNull: - case kHasBool: - case kHasNumber: - case kHasString: - case kEnteringObject: - case kEnteringArray: - return true; + if (st_ == kExitingArray) { + ParseNext(); + return false; + } + + if (st_ == kError || st_ == kExitingObject || st_ == kHasKey) { + st_ = kError; + return false; } + + return true; } int LookaheadParser::GetInt() { From 6723e3296a9ae52aa249bd57395c955d05d81b45 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Mon, 13 Mar 2017 07:43:26 -0700 Subject: [PATCH 075/117] Initialize v_ to placate GCC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v_ has a value assigned to it as part of ParseNext() which happens in the constructor, but that’s not soon enough for GCC --- example/lookaheadparser/lookaheadparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/lookaheadparser/lookaheadparser.cpp b/example/lookaheadparser/lookaheadparser.cpp index 29d929946..9ce843254 100644 --- a/example/lookaheadparser/lookaheadparser.cpp +++ b/example/lookaheadparser/lookaheadparser.cpp @@ -78,7 +78,7 @@ class LookaheadParserHandler { static const int parseFlags = kParseDefaultFlags | kParseInsituFlag; }; -LookaheadParserHandler::LookaheadParserHandler(char* str) : ss_(str) { +LookaheadParserHandler::LookaheadParserHandler(char* str) : v_(), ss_(str) { r_.IterativeParseInit(); ParseNext(); } From f0c108b5c9dc4b41a8ea39c8d418ff9a988edc86 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Mon, 13 Mar 2017 07:53:37 -0700 Subject: [PATCH 076/117] Remove all switch --- example/lookaheadparser/lookaheadparser.cpp | 70 ++++++++------------- 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/example/lookaheadparser/lookaheadparser.cpp b/example/lookaheadparser/lookaheadparser.cpp index 9ce843254..29469ed0b 100644 --- a/example/lookaheadparser/lookaheadparser.cpp +++ b/example/lookaheadparser/lookaheadparser.cpp @@ -58,6 +58,7 @@ class LookaheadParserHandler { protected: enum LookaheadParsingState { + kInit, kError, kHasNull, kHasBool, @@ -78,7 +79,7 @@ class LookaheadParserHandler { static const int parseFlags = kParseDefaultFlags | kParseInsituFlag; }; -LookaheadParserHandler::LookaheadParserHandler(char* str) : v_(), ss_(str) { +LookaheadParserHandler::LookaheadParserHandler(char* str) : v_(), st_(kInit), r_(), ss_(str) { r_.IterativeParseInit(); ParseNext(); } @@ -145,12 +146,12 @@ const char* LookaheadParser::NextObjectKey() { return result; } - if (st_ == kExitingObject) { - ParseNext(); + if (st_ != kExitingObject) { + st_ = kError; return 0; } - st_ = kError; + ParseNext(); return 0; } @@ -180,7 +181,7 @@ int LookaheadParser::GetInt() { } double LookaheadParser::GetDouble() { - if (st_ != kHasNumber || !v_.IsNumber()) { + if (st_ != kHasNumber) { st_ = kError; return 0.; } @@ -223,27 +224,16 @@ const char* LookaheadParser::GetString() { void LookaheadParser::SkipOut(int depth) { do { - switch (st_) { - case kEnteringArray: - case kEnteringObject: - ++depth; - break; - - case kExitingArray: - case kExitingObject: - --depth; - break; - - case kError: - return; - - case kHasNull: - case kHasBool: - case kHasNumber: - case kHasString: - case kHasKey: - break; + if (st_ == kEnteringArray || st_ == kEnteringObject) { + ++depth; } + else if (st_ == kExitingArray || st_ == kExitingObject) { + --depth; + } + else if (st_ == kError) { + return; + } + ParseNext(); } while (depth > 0); @@ -270,25 +260,19 @@ Value* LookaheadParser::PeekValue() { } int LookaheadParser::PeekType() { - switch (st_) { - case kHasNull: - case kHasBool: - case kHasNumber: - case kHasString: - case kHasKey: - return v_.GetType(); - - case kEnteringArray: - return kArrayType; - - case kEnteringObject: - return kObjectType; - - case kExitingArray: - case kExitingObject: - case kError: - return -1; + if (st_ >= kHasNull && st_ <= kHasKey) { + return v_.GetType(); + } + + if (st_ == kEnteringArray) { + return kArrayType; } + + if (st_ == kEnteringObject) { + return kObjectType; + } + + return -1; } //------------------------------------------------------------------------- From b91c515afea9f0ba6a81fc670889549d77c83db3 Mon Sep 17 00:00:00 2001 From: Clemens Arth Date: Tue, 14 Mar 2017 10:27:36 +0100 Subject: [PATCH 077/117] update to create config file which is independent from actual install location --- CMakeLists.txt | 38 +++++++++++++++++++++++++++++--------- RapidJSONConfig.cmake.in | 18 +++++++++++++++--- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9257926c1..d6823a8aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,15 +173,35 @@ install(DIRECTORY example/ # Provide config and version files to be used by other applications # =============================== -export(PACKAGE ${PROJECT_NAME}) - -# cmake-modules -CONFIGURE_FILE(${PROJECT_NAME}Config.cmake.in - ${PROJECT_NAME}Config.cmake - @ONLY) -CONFIGURE_FILE(${PROJECT_NAME}ConfigVersion.cmake.in - ${PROJECT_NAME}ConfigVersion.cmake - @ONLY) +################################################################################ +# Export package for use from the build tree +EXPORT( PACKAGE ${PROJECT_NAME} ) + +# Create the RapidJSONConfig.cmake file for other cmake projects. +# ... for the build tree +SET( CONFIG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +SET( CONFIG_DIR ${CMAKE_CURRENT_BINARY_DIR}) +CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY ) +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake @ONLY) + +# ... for the install tree +SET( CMAKECONFIG_INSTALL_DIR lib/cmake/${PROJECT_NAME} ) +FILE( RELATIVE_PATH REL_INCLUDE_DIR + "${CMAKE_INSTALL_PREFIX}/${CMAKECONFIG_INSTALL_DIR}" + "${CMAKE_INSTALL_PREFIX}/include" ) + +SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_CMAKE_DIR}/${REL_INCLUDE_DIR}" ) +SET( CONFIG_SOURCE_DIR ) +SET( CONFIG_DIR ) +CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake @ONLY ) + +INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake" + DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) + +# Install files INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake diff --git a/RapidJSONConfig.cmake.in b/RapidJSONConfig.cmake.in index 9fa12186a..e3c65a541 100644 --- a/RapidJSONConfig.cmake.in +++ b/RapidJSONConfig.cmake.in @@ -1,3 +1,15 @@ -get_filename_component(RAPIDJSON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -set(RAPIDJSON_INCLUDE_DIRS "@INCLUDE_INSTALL_DIR@") -message(STATUS "RapidJSON found. Headers: ${RAPIDJSON_INCLUDE_DIRS}") +################################################################################ +# RapidJSON source dir +set( RapidJSON_SOURCE_DIR "@CONFIG_SOURCE_DIR@") + +################################################################################ +# RapidJSON build dir +set( RapidJSON_DIR "@CONFIG_DIR@") + +################################################################################ +# Compute paths +get_filename_component(RapidJSON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +set( RapidJSON_INCLUDE_DIR "@RapidJSON_INCLUDE_DIR@" ) +set( RapidJSON_INCLUDE_DIRS "@RapidJSON_INCLUDE_DIR@" ) +message(STATUS "RapidJSON found. Headers: ${RapidJSON_INCLUDE_DIRS}") From 31c6c50ac66e5728d086260fc4a5d0993faaf683 Mon Sep 17 00:00:00 2001 From: John Stiles Date: Tue, 14 Mar 2017 23:28:59 -0700 Subject: [PATCH 078/117] Provide a Flush() API within Writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is helpful if you’re writing code that needs to control flush behavior and you don’t want to pass around your buffer object to each handler function alongside the writer. Seems like an easy convenience to add. --- include/rapidjson/prettywriter.h | 4 ++-- include/rapidjson/writer.h | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index a9d0f02ab..2d6a04f68 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -152,7 +152,7 @@ class PrettyWriter : public WriterFlush(); + Base::Flush(); return true; } @@ -176,7 +176,7 @@ class PrettyWriter : public WriterFlush(); + Base::Flush(); return true; } diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index b83b68ebf..cb7afd585 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -267,6 +267,14 @@ class Writer { return EndValue(WriteRawValue(json, length)); } + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + protected: //! Information for each nested level struct Level { @@ -473,7 +481,7 @@ class Writer { // Flush the value if it is the top level one. bool EndValue(bool ret) { if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text - os_->Flush(); + Flush(); return ret; } From d5d18cf6941518a33901a0ce522d38787269b8bb Mon Sep 17 00:00:00 2001 From: John Stiles Date: Tue, 14 Mar 2017 23:48:41 -0700 Subject: [PATCH 079/117] Fix template length optimization issue in PrettyWriter Missed PrettyWriter in the initial fix for Issue #889 --- include/rapidjson/prettywriter.h | 24 ++++-------------------- test/unittest/prettywritertest.cpp | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index a9d0f02ab..b68b687db 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -107,8 +107,7 @@ class PrettyWriter : public Writer - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T* str, SizeType length, bool copy = false) { + bool String(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); @@ -127,8 +126,7 @@ class PrettyWriter : public Writer - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T* str, SizeType length, bool copy = false) { return String(str, length, copy); } + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } #if RAPIDJSON_HAS_STDSTRING bool Key(const std::basic_string& str) { @@ -186,22 +184,8 @@ class PrettyWriter : public Writer - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T* const& str) { return String(str, internal::StrLen(str)); } - template - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T* const& str) { return Key(str, internal::StrLen(str)); } - - //! The compiler can give us the length of quoted strings for free. - template - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) String(const T (&str)[N]) { - RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) - return String(str, N-1); - } - template - RAPIDJSON_ENABLEIF_RETURN((internal::IsSame), (bool)) Key(const T (&str)[N]) { - RAPIDJSON_ASSERT(str[N-1] == '\0'); // you must pass in a null-terminated string (quoted constant strings are always null-terminated) - return Key(str, N-1); - } + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } //@} diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index 2891c76ce..bfc736f33 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -258,6 +258,20 @@ TEST(PrettyWriter, InvalidEventSequence) { } } +TEST(PrettyWriter, Issue_889) { + char buf[100] = "Hello"; + + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartArray(); + writer.String(buf); + writer.EndArray(); + + EXPECT_STREQ("[\n \"Hello\"\n]", buffer.GetString()); + EXPECT_TRUE(writer.IsComplete()); \ +} + + #if RAPIDJSON_HAS_CXX11_RVALUE_REFS static PrettyWriter WriterGen(StringBuffer &target) { From e5635fb27feab7f6e8d7b916aa20ad799045a641 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Thu, 16 Mar 2017 10:46:48 +0800 Subject: [PATCH 080/117] Fix #899 --- include/rapidjson/document.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 3873b99b4..6de441f6a 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2564,7 +2564,7 @@ class GenericObject { GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - void RemoveAllMembers() { return value_.RemoveAllMembers(); } + void RemoveAllMembers() { value_.RemoveAllMembers(); } bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } #if RAPIDJSON_HAS_STDSTRING bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } From a38104a165e3a7e4e1fc5647c6a7c19b968259b3 Mon Sep 17 00:00:00 2001 From: shadeware Date: Sun, 19 Mar 2017 03:03:36 +0300 Subject: [PATCH 081/117] fix typos in doc code --- doc/schema.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/schema.md b/doc/schema.md index 8b4195b75..29ba4f545 100644 --- a/doc/schema.md +++ b/doc/schema.md @@ -20,7 +20,7 @@ Secondly, construct a `SchemaValidator` with the `SchemaDocument`. It is similar // ... Document sd; -if (!sd.Parse(schemaJson).HasParseError()) { +if (sd.Parse(schemaJson).HasParseError()) { // the schema is not a valid JSON. // ... } @@ -28,7 +28,7 @@ SchemaDocument schema(sd); // Compile a Document to SchemaDocument // sd is no longer needed here. Document d; -if (!d.Parse(inputJson).HasParseError()) { +if (d.Parse(inputJson).HasParseError()) { // the input is not a valid JSON. // ... } From 430e8d4c9b6c52e007e57f6f1380c905c1c266df Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 20 Mar 2017 11:20:04 +0800 Subject: [PATCH 082/117] Update schema.zh-cn.md --- doc/schema.zh-cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/schema.zh-cn.md b/doc/schema.zh-cn.md index fa076de85..5df1f312f 100644 --- a/doc/schema.zh-cn.md +++ b/doc/schema.zh-cn.md @@ -20,7 +20,7 @@ RapidJSON 实现了一个 [JSON Schema Draft v4](http://json-schema.org/document // ... Document sd; -if (!sd.Parse(schemaJson).HasParseError()) { +if (sd.Parse(schemaJson).HasParseError()) { // 此 schema 不是合法的 JSON // ... } @@ -28,7 +28,7 @@ SchemaDocument schema(sd); // 把一个 Document 编译至 SchemaDocument // 之后不再需要 sd Document d; -if (!d.Parse(inputJson).HasParseError()) { +if (d.Parse(inputJson).HasParseError()) { // 输入不是一个合法的 JSON // ... } From da4fd6794c8709667137d667525e5005a091adbb Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Date: Wed, 22 Mar 2017 10:19:54 +0000 Subject: [PATCH 083/117] Fixed bug on space hexadecimal encoding --- include/rapidjson/reader.h | 12 ++++++------ include/rapidjson/writer.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 00ab6a5fc..6ba3f1713 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -990,7 +990,7 @@ class GenericReader { // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -999,7 +999,7 @@ class GenericReader { const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -1053,7 +1053,7 @@ class GenericReader { // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -1062,7 +1062,7 @@ class GenericReader { const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -1101,7 +1101,7 @@ class GenericReader { // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -1110,7 +1110,7 @@ class GenericReader { const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index cb7afd585..219da5e29 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -585,7 +585,7 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -594,7 +594,7 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped From 3c6e2cf0309c4e146c8ed18fd16096136fecbf00 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Date: Thu, 23 Mar 2017 10:14:17 +0000 Subject: [PATCH 084/117] Added unittests for invalid ascii control chars --- test/unittest/readertest.cpp | 2 ++ test/unittest/writertest.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 3555f11a5..5078f5246 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -725,6 +725,8 @@ TEST(Reader, ParseString_Error) { // Malform ASCII sequence TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x80u), '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x01u), '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x1Cu), '\"', ']', '\0')); #undef ARRAY #undef TEST_STRINGARRAY_ERROR diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index e630bb92e..bc28e0215 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -401,6 +401,16 @@ TEST(Writer, InvalidEncoding) { static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF EXPECT_FALSE(writer.String(s)); } + + // Fail in decoding invalid ASCII control bytes + { + GenericStringBuffer > buffer; + Writer >, UTF8<>, UTF16<> > writer(buffer); + writer.StartArray(); + EXPECT_FALSE(writer.String("\x01")); + EXPECT_FALSE(writer.String("\x1C")); + writer.EndArray(); + } } TEST(Writer, ValidateEncoding) { From 85500e8c8f1eabe10bcf7944e71fbb2dbcc893de Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Date: Fri, 24 Mar 2017 13:37:23 +0000 Subject: [PATCH 085/117] Changed error code for invalid special ascii chars, fixed writer tests --- include/rapidjson/reader.h | 2 +- test/unittest/writertest.cpp | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 6ba3f1713..ccc025e22 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -955,7 +955,7 @@ class GenericReader { if (c == '\0') RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } else { size_t offset = is.Tell(); diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index bc28e0215..b190c6c28 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -401,16 +401,6 @@ TEST(Writer, InvalidEncoding) { static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF EXPECT_FALSE(writer.String(s)); } - - // Fail in decoding invalid ASCII control bytes - { - GenericStringBuffer > buffer; - Writer >, UTF8<>, UTF16<> > writer(buffer); - writer.StartArray(); - EXPECT_FALSE(writer.String("\x01")); - EXPECT_FALSE(writer.String("\x1C")); - writer.EndArray(); - } } TEST(Writer, ValidateEncoding) { @@ -422,8 +412,10 @@ TEST(Writer, ValidateEncoding) { EXPECT_TRUE(writer.String("\xC2\xA2")); // Cents sign U+00A2 EXPECT_TRUE(writer.String("\xE2\x82\xAC")); // Euro sign U+20AC EXPECT_TRUE(writer.String("\xF0\x9D\x84\x9E")); // G clef sign U+1D11E + EXPECT_TRUE(writer.String("\x01")); // SOH control U+0001 + EXPECT_TRUE(writer.String("\x1B")); // Escape control U+001B writer.EndArray(); - EXPECT_STREQ("[\"\x24\",\"\xC2\xA2\",\"\xE2\x82\xAC\",\"\xF0\x9D\x84\x9E\"]", buffer.GetString()); + EXPECT_STREQ("[\"\x24\",\"\xC2\xA2\",\"\xE2\x82\xAC\",\"\xF0\x9D\x84\x9E\",\"\\u0001\",\"\\u001B\"]", buffer.GetString()); } // Fail in decoding invalid UTF-8 sequence http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt From d88be8ef1649eca4602348d1aab5c16c36f83d4f Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 27 Mar 2017 14:05:03 +0800 Subject: [PATCH 086/117] Fix #905 unable to set writeFlags for PrettyWriter --- include/rapidjson/prettywriter.h | 2 +- test/unittest/prettywritertest.cpp | 43 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index ef36a8c2a..98dfb3060 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -47,7 +47,7 @@ enum PrettyFormatOptions { template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> class PrettyWriter : public Writer { public: - typedef Writer Base; + typedef Writer Base; typedef typename Base::Ch Ch; //! Constructor diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index bfc736f33..1e1ca1ad9 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -258,6 +258,49 @@ TEST(PrettyWriter, InvalidEventSequence) { } } +TEST(PrettyWriter, NaN) { + double nan = std::numeric_limits::quiet_NaN(); + + EXPECT_TRUE(internal::Double(nan).IsNan()); + StringBuffer buffer; + { + PrettyWriter writer(buffer); + EXPECT_FALSE(writer.Double(nan)); + } + { + PrettyWriter, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer); + EXPECT_TRUE(writer.Double(nan)); + EXPECT_STREQ("NaN", buffer.GetString()); + } + GenericStringBuffer > buffer2; + PrettyWriter > > writer2(buffer2); + EXPECT_FALSE(writer2.Double(nan)); +} + +TEST(PrettyWriter, Inf) { + double inf = std::numeric_limits::infinity(); + + EXPECT_TRUE(internal::Double(inf).IsInf()); + StringBuffer buffer; + { + PrettyWriter writer(buffer); + EXPECT_FALSE(writer.Double(inf)); + } + { + PrettyWriter writer(buffer); + EXPECT_FALSE(writer.Double(-inf)); + } + { + PrettyWriter, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer); + EXPECT_TRUE(writer.Double(inf)); + } + { + PrettyWriter, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer); + EXPECT_TRUE(writer.Double(-inf)); + } + EXPECT_STREQ("Infinity-Infinity", buffer.GetString()); +} + TEST(PrettyWriter, Issue_889) { char buf[100] = "Hello"; From 77f643dc511eaa3a1ce0e9dfa2976282ecc6eede Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 7 Apr 2017 10:23:30 +0800 Subject: [PATCH 087/117] Fix #910 incorrect casting --- include/rapidjson/document.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 6de441f6a..a2b044c8d 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2293,7 +2293,7 @@ class GenericDocument : public GenericValue { template GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); - MemoryStream ms(static_cast(str), length * sizeof(typename SourceEncoding::Ch)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); EncodedInputStream is(ms); ParseStream(is); return *this; From ec90588c72ad4192d53b348298b0dd7f790524fa Mon Sep 17 00:00:00 2001 From: Zhihao Yuan Date: Sat, 8 Apr 2017 22:49:13 -0500 Subject: [PATCH 088/117] Fix a non-type template parameter type mismatch This issues a warning in gcc7. --- include/rapidjson/rapidjson.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index e667d8b69..19a30193b 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -413,7 +413,7 @@ RAPIDJSON_NAMESPACE_END RAPIDJSON_NAMESPACE_BEGIN template struct STATIC_ASSERTION_FAILURE; template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; -template struct StaticAssertTest {}; +template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END #define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) From f93a29bec2316d3b5d7c33cb6977da6690a29be8 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 14 Apr 2017 20:19:16 +0200 Subject: [PATCH 089/117] RAPIDJSON_STATIC_ASSERT: use C++11 static_assert, if available --- include/rapidjson/rapidjson.h | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index e667d8b69..a6281c0ed 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -49,6 +49,11 @@ // token stringification #define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) #define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y //!@endcond /*! \def RAPIDJSON_MAJOR_VERSION @@ -405,7 +410,15 @@ RAPIDJSON_NAMESPACE_END /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_STATIC_ASSERT -// Adopt from boost +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost #ifndef RAPIDJSON_STATIC_ASSERT #ifndef __clang__ //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN @@ -416,10 +429,6 @@ template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END -#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) -#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) -#define RAPIDJSON_DO_JOIN2(X, Y) X##Y - #if defined(__GNUC__) #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) #else @@ -438,7 +447,7 @@ RAPIDJSON_NAMESPACE_END typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE -#endif +#endif // RAPIDJSON_STATIC_ASSERT /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY From 2291258bb8adb87e5da30ed2b12fa9929d0e76f8 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Date: Tue, 11 Apr 2017 02:02:15 +0000 Subject: [PATCH 090/117] Added ARM-Neon support for SIMD.SkipWhitespace* Change-Id: Iaf210d029758723a7eeb7f28fc10cab7467889a9 Signed-off-by: Jun He --- doc/faq.md | 2 +- doc/faq.zh-cn.md | 2 +- doc/internals.md | 7 +- doc/internals.zh-cn.md | 7 +- include/rapidjson/rapidjson.h | 18 ++- include/rapidjson/reader.h | 264 +++++++++++++++++++++++++++++++- include/rapidjson/writer.h | 72 ++++++++- test/perftest/perftest.h | 3 + test/perftest/rapidjsontest.cpp | 2 + test/unittest/simdtest.cpp | 4 + 10 files changed, 365 insertions(+), 16 deletions(-) diff --git a/doc/faq.md b/doc/faq.md index 1b0541c27..4946cfeff 100644 --- a/doc/faq.md +++ b/doc/faq.md @@ -256,7 +256,7 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres 3. What is SIMD? How it is applied in RapidJSON? - [SIMD](http://en.wikipedia.org/wiki/SIMD) instructions can perform parallel computation in modern CPUs. RapidJSON support Intel's SSE2/SSE4.2 to accelerate whitespace skipping. This improves performance of parsing indent formatted JSON. Define `RAPIDJSON_SSE2` or `RAPIDJSON_SSE42` macro to enable this feature. However, running the executable on a machine without such instruction set support will make it crash. + [SIMD](http://en.wikipedia.org/wiki/SIMD) instructions can perform parallel computation in modern CPUs. RapidJSON support Intel's SSE2/SSE4.2 and ARM's Neon to accelerate whitespace/tabspace/carriage-return/line-feed skipping. This improves performance of parsing indent formatted JSON. Define `RAPIDJSON_SSE2`, `RAPIDJSON_SSE42` or `RAPIDJSON_NEON` macro to enable this feature. However, running the executable on a machine without such instruction set support will make it crash. 4. Does it consume a lot of memory? diff --git a/doc/faq.zh-cn.md b/doc/faq.zh-cn.md index f12d83073..307b02f9d 100644 --- a/doc/faq.zh-cn.md +++ b/doc/faq.zh-cn.md @@ -257,7 +257,7 @@ 3. 什是是 SIMD?它如何用于 RapidJSON? - [SIMD](http://en.wikipedia.org/wiki/SIMD) 指令可以在现代 CPU 中执行并行运算。RapidJSON 支持了 Intel 的 SSE2/SSE4.2 去加速跳过空白字符。在解析含缩进的 JSON 时,这能提升性能。只要定义名为 `RAPIDJSON_SSE2` 或 `RAPIDJSON_SSE42` 的宏,就能启动这个功能。然而,若在不支持这些指令集的机器上执行这些可执行文件,会导致崩溃。 + [SIMD](http://en.wikipedia.org/wiki/SIMD) 指令可以在现代 CPU 中执行并行运算。RapidJSON 支持使用 Intel 的 SSE2/SSE4.2 和 ARM 的 Neon 来加速对空白符、制表符、回车符和换行符的过滤处理。在解析含缩进的 JSON 时,这能提升性能。只要定义名为 `RAPIDJSON_SSE2` ,`RAPIDJSON_SSE42` 或 `RAPIDJSON_NEON` 的宏,就能启动这个功能。然而,若在不支持这些指令集的机器上执行这些可执行文件,会导致崩溃。 4. 它会消耗许多内存么? diff --git a/doc/internals.md b/doc/internals.md index 49802a0fd..2fff2d9cb 100644 --- a/doc/internals.md +++ b/doc/internals.md @@ -183,17 +183,20 @@ void SkipWhitespace(InputStream& s) { However, this requires 4 comparisons and a few branching for each character. This was found to be a hot spot. -To accelerate this process, SIMD was applied to compare 16 characters with 4 white spaces for each iteration. Currently RapidJSON only supports SSE2 and SSE4.2 instructions for this. And it is only activated for UTF-8 memory streams, including string stream or *in situ* parsing. +To accelerate this process, SIMD was applied to compare 16 characters with 4 white spaces for each iteration. Currently RapidJSON supports SSE2, SSE4.2 and ARM Neon instructions for this. And it is only activated for UTF-8 memory streams, including string stream or *in situ* parsing. -To enable this optimization, need to define `RAPIDJSON_SSE2` or `RAPIDJSON_SSE42` before including `rapidjson.h`. Some compilers can detect the setting, as in `perftest.h`: +To enable this optimization, need to define `RAPIDJSON_SSE2`, `RAPIDJSON_SSE42` or `RAPIDJSON_NEON` before including `rapidjson.h`. Some compilers can detect the setting, as in `perftest.h`: ~~~cpp // __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler. // We use -march=native with gmake to enable -msse2 and -msse4.2, if supported. +// Likewise, __ARM_NEON is used to detect Neon. #if defined(__SSE4_2__) # define RAPIDJSON_SSE42 #elif defined(__SSE2__) # define RAPIDJSON_SSE2 +#elif defined(__ARM_NEON) +# define RAPIDJSON_NEON #endif ~~~ diff --git a/doc/internals.zh-cn.md b/doc/internals.zh-cn.md index ec5795952..0c8bc068a 100644 --- a/doc/internals.zh-cn.md +++ b/doc/internals.zh-cn.md @@ -183,17 +183,20 @@ void SkipWhitespace(InputStream& s) { 但是,这需要对每个字符进行4次比较以及一些分支。这被发现是一个热点。 -为了加速这一处理,RapidJSON 使用 SIMD 来在一次迭代中比较16个字符和4个空格。目前 RapidJSON 只支持 SSE2 和 SSE4.2 指令。同时它也只会对 UTF-8 内存流启用,包括字符串流或 *原位* 解析。 +为了加速这一处理,RapidJSON 使用 SIMD 来在一次迭代中比较16个字符和4个空格。目前 RapidJSON 支持 SSE2 , SSE4.2 和 ARM Neon 指令。同时它也只会对 UTF-8 内存流启用,包括字符串流或 *原位* 解析。 -你可以通过在包含 `rapidjson.h` 之前定义 `RAPIDJSON_SSE2` 或 `RAPIDJSON_SSE42` 来启用这个优化。一些编译器可以检测这个设置,如 `perftest.h`: +你可以通过在包含 `rapidjson.h` 之前定义 `RAPIDJSON_SSE2` , `RAPIDJSON_SSE42` 或 `RAPIDJSON_NEON` 来启用这个优化。一些编译器可以检测这个设置,如 `perftest.h`: ~~~cpp // __SSE2__ 和 __SSE4_2__ 可被 gcc、clang 和 Intel 编译器识别: // 如果支持的话,我们在 gmake 中使用了 -march=native 来启用 -msse2 和 -msse4.2 +// 同样的, __ARM_NEON 被用于识别Neon #if defined(__SSE4_2__) # define RAPIDJSON_SSE42 #elif defined(__SSE2__) # define RAPIDJSON_SSE2 +#elif defined(__ARM_NEON) +# define RAPIDJSON_NEON #endif ~~~ diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index f41bb203c..57ab8514d 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -325,17 +325,17 @@ #endif /////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD /*! \def RAPIDJSON_SIMD \ingroup RAPIDJSON_CONFIG - \brief Enable SSE2/SSE4.2 optimization. + \brief Enable SSE2/SSE4.2/Neon optimization. RapidJSON supports optimized implementations for some parsing operations - based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible - processors. + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. - To enable these optimizations, two different symbols can be defined; + To enable these optimizations, three different symbols can be defined; \code // Enable SSE2 optimization. #define RAPIDJSON_SSE2 @@ -344,13 +344,17 @@ #define RAPIDJSON_SSE42 \endcode - \c RAPIDJSON_SSE42 takes precedence, if both are defined. + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. If any of these symbols is defined, RapidJSON defines the macro \c RAPIDJSON_SIMD to indicate the availability of the optimized code. */ #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ - || defined(RAPIDJSON_DOXYGEN_RUNNING) + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) #define RAPIDJSON_SIMD #endif diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index ccc025e22..120c31115 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -33,6 +33,8 @@ #include #elif defined(RAPIDJSON_SSE2) #include +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef _MSC_VER @@ -411,7 +413,92 @@ inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { return SkipWhitespace(p, end); } -#endif // RAPIDJSON_SSE2 +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz =__builtin_clzll(high);; + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low);; + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON #ifdef RAPIDJSON_SIMD //! Template function specialization for InsituStringStream @@ -1129,7 +1216,180 @@ class GenericReader { is.src_ = is.dst_ = p; } -#endif +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high);; + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low);; + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + int lz = __builtin_clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON template class NumberStream; diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 219da5e29..61cd0707e 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -32,6 +32,8 @@ #include #elif defined(RAPIDJSON_SSE2) #include +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef _MSC_VER @@ -619,7 +621,75 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz is.src_ = p; return RAPIDJSON_LIKELY(is.Tell() < length); } -#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON RAPIDJSON_NAMESPACE_END diff --git a/test/perftest/perftest.h b/test/perftest/perftest.h index b098e4147..953f95de8 100644 --- a/test/perftest/perftest.h +++ b/test/perftest/perftest.h @@ -24,10 +24,13 @@ // __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler. // We use -march=native with gmake to enable -msse2 and -msse4.2, if supported. +// Likewise, __ARM_NEON is used to detect Neon. #if defined(__SSE4_2__) # define RAPIDJSON_SSE42 #elif defined(__SSE2__) # define RAPIDJSON_SSE2 +#elif defined(__ARM_NEON) +# define RAPIDJSON_NEON #endif #define RAPIDJSON_HAS_STDSTRING 1 diff --git a/test/perftest/rapidjsontest.cpp b/test/perftest/rapidjsontest.cpp index f14e702ed..a11a557d1 100644 --- a/test/perftest/rapidjsontest.cpp +++ b/test/perftest/rapidjsontest.cpp @@ -28,6 +28,8 @@ #define SIMD_SUFFIX(name) name##_SSE2 #elif defined(RAPIDJSON_SSE42) #define SIMD_SUFFIX(name) name##_SSE42 +#elif defined(RAPIDJSON_NEON) +#define SIMD_SUFFIX(name) name##_NEON #else #define SIMD_SUFFIX(name) name #endif diff --git a/test/unittest/simdtest.cpp b/test/unittest/simdtest.cpp index b01b559f4..7b58cd05f 100644 --- a/test/unittest/simdtest.cpp +++ b/test/unittest/simdtest.cpp @@ -21,6 +21,8 @@ # define RAPIDJSON_SSE42 #elif defined(__SSE2__) # define RAPIDJSON_SSE2 +#elif defined(__ARM_NEON) +# define RAPIDJSON_NEON #endif #define RAPIDJSON_NAMESPACE rapidjson_simd @@ -41,6 +43,8 @@ using namespace rapidjson_simd; #define SIMD_SUFFIX(name) name##_SSE2 #elif defined(RAPIDJSON_SSE42) #define SIMD_SUFFIX(name) name##_SSE42 +#elif defined(RAPIDJSON_NEON) +#define SIMD_SUFFIX(name) name##_NEON #else #define SIMD_SUFFIX(name) name #endif From 63423eb6f8aa2b315d810baf8e96e7f2600745d7 Mon Sep 17 00:00:00 2001 From: Oliver Hahm Date: Fri, 21 Apr 2017 14:49:12 +0200 Subject: [PATCH 091/117] fix return values --- include/rapidjson/document.h | 2 +- include/rapidjson/schema.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index a2b044c8d..c12820eed 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -515,7 +515,7 @@ struct TypeHelper { static bool Is(const ValueType& v) { return v.IsObject(); } static ObjectType Get(ValueType& v) { return v.GetObject(); } static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } - static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } }; template diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 3dddd3a1c..44a94f8a0 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1789,7 +1789,7 @@ RAPIDJSON_MULTILINEMACRO_END } virtual void FreeState(void* p) { - return StateAllocator::Free(p); + StateAllocator::Free(p); } private: From 885b5cd2f9b0fe9596d58ee28663cb6267559f67 Mon Sep 17 00:00:00 2001 From: Oliver Hahm Date: Fri, 21 Apr 2017 14:49:30 +0200 Subject: [PATCH 092/117] common notation of empty if/else case --- include/rapidjson/schema.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 44a94f8a0..348dd379c 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1112,8 +1112,8 @@ class Schema { if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); } - else if (maximum_.IsUint64()) - /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64() + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } From 4fe02e15f9f59debf169e1d17fdf660e0ad08065 Mon Sep 17 00:00:00 2001 From: Matthew Early Date: Sat, 29 Apr 2017 16:07:23 -0400 Subject: [PATCH 093/117] typo --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4a1d64d0a..cd3021840 100644 --- a/readme.md +++ b/readme.md @@ -84,7 +84,7 @@ To generate user documentation and run tests please proceed with the steps below 3. Change to `build` directory and run `cmake ..` command to configure your build. Windows users can do the same with cmake-gui application. 4. On Windows, build the solution found in the build directory. On Linux, run `make` from the build directory. -On successfull build you will find compiled test and example binaries in `bin` +On successful build you will find compiled test and example binaries in `bin` directory. The generated documentation will be available in `doc/html` directory of the build tree. To run tests after finished build please run `make test` or `ctest` from your build tree. You can get detailed output using `ctest From fe19b7b6016d446722621fb407738209d1a911e8 Mon Sep 17 00:00:00 2001 From: Harry Wong Date: Thu, 4 May 2017 10:08:48 +0800 Subject: [PATCH 094/117] Supress implicit fallthrough in GCC --- include/rapidjson/internal/regex.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/rapidjson/internal/regex.h b/include/rapidjson/internal/regex.h index 1369ea266..6d110bdbd 100644 --- a/include/rapidjson/internal/regex.h +++ b/include/rapidjson/internal/regex.h @@ -29,6 +29,7 @@ RAPIDJSON_DIAG_OFF(implicit-fallthrough) #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(implicit-fallthrough) #endif #ifdef _MSC_VER From cba45fe9de6923b858edb0780e257b7257aa4f7b Mon Sep 17 00:00:00 2001 From: Harry Wong Date: Thu, 4 May 2017 10:32:45 +0800 Subject: [PATCH 095/117] Onley apply to GCC 7 --- include/rapidjson/internal/regex.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/rapidjson/internal/regex.h b/include/rapidjson/internal/regex.h index 6d110bdbd..e1a2faae5 100644 --- a/include/rapidjson/internal/regex.h +++ b/include/rapidjson/internal/regex.h @@ -29,8 +29,10 @@ RAPIDJSON_DIAG_OFF(implicit-fallthrough) #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 7 RAPIDJSON_DIAG_OFF(implicit-fallthrough) #endif +#endif #ifdef _MSC_VER RAPIDJSON_DIAG_PUSH From 568107e178c700fecc7eb3c0da483b1a95a01ece Mon Sep 17 00:00:00 2001 From: Hartwig Date: Wed, 10 May 2017 22:56:01 +0200 Subject: [PATCH 096/117] Add convenience method Key(std::basic_string const&) to Writer --- include/rapidjson/writer.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 61cd0707e..e610ebb60 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -222,6 +222,13 @@ class Writer { bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + bool EndObject(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object From b61bbbfe371c51c3ee86103bd1da040bcc5a0779 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Thu, 11 May 2017 16:41:26 +0800 Subject: [PATCH 097/117] Fix #947 -Weffc++ warning --- example/lookaheadparser/lookaheadparser.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/example/lookaheadparser/lookaheadparser.cpp b/example/lookaheadparser/lookaheadparser.cpp index 29469ed0b..f627f4d86 100644 --- a/example/lookaheadparser/lookaheadparser.cpp +++ b/example/lookaheadparser/lookaheadparser.cpp @@ -2,6 +2,11 @@ #include "rapidjson/document.h" #include +RAPIDJSON_DIAG_PUSH +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#endif + // This example demonstrates JSON token-by-token parsing with an API that is // more direct; you don't need to design your logic around a handler object and // callbacks. Instead, you retrieve values from the JSON stream by calling @@ -341,3 +346,5 @@ int main() { return 0; } + +RAPIDJSON_DIAG_POP From f8eb7bae89fb38f7ffe3dd69e04e95986839b0d0 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 12 May 2017 10:32:06 +0800 Subject: [PATCH 098/117] Remove -Weverything See #930 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d6823a8aa..8b90c8705 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,7 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wno-missing-field-initializers") - set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wimplicit-fallthrough -Weverything) + set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wimplicit-fallthrough) if (RAPIDJSON_BUILD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() From 56b7216efe5014a3936da2fc4d01ad1dc45a2250 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 12 May 2017 10:32:41 +0800 Subject: [PATCH 099/117] Fix #949 about -Werror=conversion --- test/unittest/pointertest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index dbddbedee..eed6fba90 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -441,8 +441,8 @@ TEST(Pointer, Stringify) { } // Construct a Pointer with static tokens, no dynamic allocation involved. -#define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } -#define INDEX(i) { #i, sizeof(#i) - 1, i } +#define NAME(s) { s, static_cast(sizeof(s) / sizeof(s[0]) - 1), kPointerInvalidIndex } +#define INDEX(i) { #i, static_cast(sizeof(#i) - 1), i } static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(0) }; // equivalent to "/foo/0" From 0033268c115cbfa224937a76685bfb1e55fdb506 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 12 May 2017 17:30:33 +0800 Subject: [PATCH 100/117] Update tutorial.zh-cn.md typo --- doc/tutorial.zh-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial.zh-cn.md b/doc/tutorial.zh-cn.md index ec1315c8f..6b2588f7e 100644 --- a/doc/tutorial.zh-cn.md +++ b/doc/tutorial.zh-cn.md @@ -343,7 +343,7 @@ Value o(kObjectType); ![转移语义不需复制。](diagram/move3.png) -在 C++11 中这称为转移赋值操作(move assignment operator)。由于 RapidJSON 支持 C++03,它在赋值操作采用转移语义,其它修改形函数如 `AddMember()`, `PushBack()` 也采用转移语义。 +在 C++11 中这称为转移赋值操作(move assignment operator)。由于 RapidJSON 支持 C++03,它在赋值操作采用转移语义,其它修改型函数如 `AddMember()`, `PushBack()` 也采用转移语义。 ### 转移语义及临时值 {#TemporaryValues} From 4ef1ff4fbac491676702cf9e4f300d504d56cee9 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 18 May 2017 19:08:23 +0200 Subject: [PATCH 101/117] GenericValue::CopyFrom: add option to force copying of strings Copying the result of an in-situ parsing into another value/document currently requires that the original buffer - still holding the strings from the parsing, outlives the destination object as well. In order to obtain a "full" copy of a GenericValue, this commit adds an optional flag `copyConstStrings` to `CopyFrom`, which then forces to take a copy of all embedded strings in the source value. This solves the problem discussed in #962. --- include/rapidjson/document.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index c12820eed..0d13b606f 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -615,10 +615,11 @@ class GenericValue { \tparam SourceAllocator allocator of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) \see CopyFrom() */ template - GenericValue(const GenericValue& rhs, Allocator& allocator) { + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { switch (rhs.GetType()) { case kObjectType: { SizeType count = rhs.data_.o.size; @@ -645,7 +646,7 @@ class GenericValue { } break; case kStringType: - if (rhs.data_.f.flags == kConstStringFlag) { + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { data_.f.flags = rhs.data_.f.flags; data_ = *reinterpret_cast(&rhs.data_); } @@ -850,12 +851,13 @@ class GenericValue { \tparam SourceAllocator Allocator type of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) */ template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); this->~GenericValue(); - new (this) GenericValue(rhs, allocator); + new (this) GenericValue(rhs, allocator, copyConstStrings); return *this; } From 77d2fadfb615687038ffeff9dac5acdfc4d5e327 Mon Sep 17 00:00:00 2001 From: "Tomasz Noczynski (Linux)" Date: Thu, 25 May 2017 13:21:57 +0200 Subject: [PATCH 102/117] If storage class is not specified as first in declaration then Intel C++ Compiler 2017 generates message: message #82: storage class is not first --- include/rapidjson/encodings.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h index ed7d44d36..0df1c3435 100644 --- a/include/rapidjson/encodings.h +++ b/include/rapidjson/encodings.h @@ -620,28 +620,28 @@ struct AutoUTF { #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template - RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; (*f[os.GetType()])(os, codepoint); } template - RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; (*f[os.GetType()])(os, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { typedef bool (*DecodeFunc)(InputStream&, unsigned*); static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; return (*f[is.GetType()])(is, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { typedef bool (*ValidateFunc)(InputStream&, OutputStream&); static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; return (*f[is.GetType()])(is, os); @@ -658,7 +658,7 @@ template struct Transcoder { //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -667,7 +667,7 @@ struct Transcoder { } template - RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -677,7 +677,7 @@ struct Transcoder { //! Validate one Unicode codepoint from an encoded stream. template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; @@ -690,19 +690,19 @@ inline void PutUnsafe(Stream& stream, typename Stream::Ch c); template struct Transcoder { template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Encoding::Validate(is, os); // source/target encoding are the same } }; From 294ad93e30abf27e99c037054e8adb1ce853d6e4 Mon Sep 17 00:00:00 2001 From: "Tomasz Noczynski (Linux)" Date: Thu, 25 May 2017 14:14:16 +0200 Subject: [PATCH 103/117] To avoid Intel C++ Compiler #1879 warnings: warning #1879: unimplemented pragma ignored: #pragma intrinsic(_BitScanReverse64) warning #1879: unimplemented pragma ignored: #pragma intrinsic(_umul128) --- include/rapidjson/internal/diyfp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h index c9fefdc61..29abf8046 100644 --- a/include/rapidjson/internal/diyfp.h +++ b/include/rapidjson/internal/diyfp.h @@ -21,7 +21,7 @@ #include "../rapidjson.h" -#if defined(_MSC_VER) && defined(_M_AMD64) +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) #include #pragma intrinsic(_BitScanReverse64) #pragma intrinsic(_umul128) From 68c96e987bd0eb67ff399904dee6b7c07042ff18 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 27 May 2017 10:26:35 +0200 Subject: [PATCH 104/117] Fixup #964 by forwarding copyConstStrings recursively As reported by @Llerd in #962, the `copyConstStrings` parameter has not been forwarded recursively to the constructors of object members and array elements. --- include/rapidjson/document.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 0d13b606f..57f0b3c23 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -626,8 +626,8 @@ class GenericValue { Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); const typename GenericValue::Member* rm = rhs.GetMembersPointer(); for (SizeType i = 0; i < count; i++) { - new (&lm[i].name) GenericValue(rm[i].name, allocator); - new (&lm[i].value) GenericValue(rm[i].value, allocator); + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); } data_.f.flags = kObjectFlag; data_.o.size = data_.o.capacity = count; @@ -639,7 +639,7 @@ class GenericValue { GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); const GenericValue* re = rhs.GetElementsPointer(); for (SizeType i = 0; i < count; i++) - new (&le[i]) GenericValue(re[i], allocator); + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); data_.f.flags = kArrayFlag; data_.a.size = data_.a.capacity = count; SetElementsPointer(le); From df6362d45060d7fde9772d21f23af969d039e920 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 9 Jun 2017 10:16:24 +0800 Subject: [PATCH 105/117] Fix patternProperties & additionalProperties lead to ASSERT Fix #825 --- include/rapidjson/schema.h | 4 +++- test/unittest/schematest.cpp | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 348dd379c..dd57edbc2 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -783,8 +783,10 @@ class Schema { if (patternProperties_) { context.patternPropertiesSchemaCount = 0; for (SizeType i = 0; i < patternPropertyCount_; i++) - if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } } SizeType index; diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 30b326057..e79fec288 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -1322,6 +1322,13 @@ TEST(SchemaValidator, Issue728_AllOfRef) { VALIDATE(s, "{\"key1\": \"abc\", \"key2\": \"def\"}", true); } +TEST(SchemaValidator, Issue825) { + Document sd; + sd.Parse("{\"type\": \"object\", \"additionalProperties\": false, \"patternProperties\": {\"^i\": { \"type\": \"string\" } } }"); + SchemaDocument s(sd); + VALIDATE(s, "{ \"item\": \"hello\" }", true); +} + #ifdef __clang__ RAPIDJSON_DIAG_POP #endif From 6e81d49b3374c1e8667438056c16ab2f3611c1fd Mon Sep 17 00:00:00 2001 From: kyb Date: Thu, 15 Jun 2017 12:36:20 +0300 Subject: [PATCH 106/117] Fixed #985 : Unittest failed with MinGWx64. And few small improvement were done while looking for mistakes. Problem was because of Windows uses backslashes '\', not Unix '/' --- test/unittest/ostreamwrappertest.cpp | 5 +++-- test/unittest/prettywritertest.cpp | 1 + test/unittest/unittest.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/unittest/ostreamwrappertest.cpp b/test/unittest/ostreamwrappertest.cpp index b1d1cd827..50f8da63e 100644 --- a/test/unittest/ostreamwrappertest.cpp +++ b/test/unittest/ostreamwrappertest.cpp @@ -69,14 +69,15 @@ static void TestFileStream() { const char* s = "Hello World!\n"; { - ofstream ofs(filename, ios::out | ios::binary); - BasicOStreamWrapper osw(ofs); + FileStreamType ofs(filename, ios::out | ios::binary); + BasicOStreamWrapper osw(ofs); for (const char* p = s; *p; p++) osw.Put(*p); osw.Flush(); } fp = fopen(filename, "r"); + ASSERT_TRUE( fp != NULL ); for (const char* p = s; *p; p++) EXPECT_EQ(*p, static_cast(fgetc(fp))); fclose(fp); diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index 1e1ca1ad9..43617a2f5 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -167,6 +167,7 @@ TEST(PrettyWriter, OStreamWrapper) { TEST(PrettyWriter, FileWriteStream) { char filename[L_tmpnam]; FILE* fp = TempFile(filename); + ASSERT_TRUE(fp!=NULL); char buffer[16]; FileWriteStream os(fp, buffer, sizeof(buffer)); PrettyWriter writer(os); diff --git a/test/unittest/unittest.h b/test/unittest/unittest.h index e125bf88d..583734578 100644 --- a/test/unittest/unittest.h +++ b/test/unittest/unittest.h @@ -78,7 +78,7 @@ inline Ch* StrDup(const Ch* str) { } inline FILE* TempFile(char *filename) { -#ifdef _MSC_VER +#if defined(__WIN32__) || defined(_MSC_VER) filename = tmpnam(filename); // For Visual Studio, tmpnam() adds a backslash in front. Remove it. From a31a380cb81c2f20baf4cd7204815e235ea8d2bd Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 21 Jun 2017 14:25:47 +0800 Subject: [PATCH 107/117] Improve readme.md Add alt text for images Use https whenever possible Update URLs Use tools.ietf.org for RFC7159 Correct indent for sublists Trim trailing whitespaces --- readme.md | 50 ++++++++++++++++++++++++------------------------- readme.zh-cn.md | 50 ++++++++++++++++++++++++------------------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/readme.md b/readme.md index cd3021840..293761990 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,8 @@ -![](doc/logo/rapidjson.png) +![RapidJSON logo](doc/logo/rapidjson.png) -![](https://img.shields.io/badge/release-v1.1.0-blue.png) +![Release version](https://img.shields.io/badge/release-v1.1.0-blue.svg) -## A fast JSON parser/generator for C++ with both SAX/DOM style API +## A fast JSON parser/generator for C++ with both SAX/DOM style API Tencent is pleased to support the open source community by making RapidJSON available. @@ -20,12 +20,12 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights | :---------------: | :-----------------: | :-------------------: | | ![lin-badge] | ![win-badge] | ![cov-badge] | -[lin-badge]: https://travis-ci.org/miloyip/rapidjson.png?branch=master "Travis build status" +[lin-badge]: https://travis-ci.org/miloyip/rapidjson.svg?branch=master "Travis build status" [lin-link]: https://travis-ci.org/miloyip/rapidjson "Travis build status" -[win-badge]: https://ci.appveyor.com/api/projects/status/u658dcuwxo14a8m9/branch/master "AppVeyor build status" +[win-badge]: https://ci.appveyor.com/api/projects/status/github/miloyip/rapidjson?branch=master&svg=true "AppVeyor build status" [win-link]: https://ci.appveyor.com/project/miloyip/rapidjson/branch/master "AppVeyor build status" -[cov-badge]: https://coveralls.io/repos/miloyip/rapidjson/badge.png?branch=master -[cov-link]: https://coveralls.io/r/miloyip/rapidjson?branch=master +[cov-badge]: https://coveralls.io/repos/miloyip/rapidjson/badge.svg?branch=master "Coveralls coverage" +[cov-link]: https://coveralls.io/r/miloyip/rapidjson?branch=master "Coveralls coverage" ## Introduction @@ -45,8 +45,8 @@ More features can be read [here](doc/features.md). JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404, with optional support of relaxed syntax. More information about JSON can be obtained at * [Introducing JSON](http://json.org/) -* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](http://www.ietf.org/rfc/rfc7159.txt) -* [Standard ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/standards/Ecma-404.htm) +* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159) +* [Standard ECMA-404: The JSON Data Interchange Format](https://www.ecma-international.org/publications/standards/Ecma-404.htm) ## Highlights in v1.1 (2016-8-25) @@ -74,8 +74,8 @@ RapidJSON is a header-only C++ library. Just copy the `include/rapidjson` folder RapidJSON uses following software as its dependencies: * [CMake](https://cmake.org/) as a general build tool -* (optional)[Doxygen](http://www.doxygen.org) to build documentation -* (optional)[googletest](https://github.com/google/googletest) for unit and performance testing +* (optional) [Doxygen](http://www.doxygen.org) to build documentation +* (optional) [googletest](https://github.com/google/googletest) for unit and performance testing To generate user documentation and run tests please proceed with the steps below: @@ -139,22 +139,22 @@ The following diagram shows the process. More [examples](https://github.com/miloyip/rapidjson/tree/master/example) are available: * DOM API - * [tutorial](https://github.com/miloyip/rapidjson/blob/master/example/tutorial/tutorial.cpp): Basic usage of DOM API. + * [tutorial](https://github.com/miloyip/rapidjson/blob/master/example/tutorial/tutorial.cpp): Basic usage of DOM API. * SAX API - * [simplereader](https://github.com/miloyip/rapidjson/blob/master/example/simplereader/simplereader.cpp): Dumps all SAX events while parsing a JSON by `Reader`. - * [condense](https://github.com/miloyip/rapidjson/blob/master/example/condense/condense.cpp): A command line tool to rewrite a JSON, with all whitespaces removed. - * [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp): A command line tool to rewrite a JSON with indents and newlines by `PrettyWriter`. - * [capitalize](https://github.com/miloyip/rapidjson/blob/master/example/capitalize/capitalize.cpp): A command line tool to capitalize strings in JSON. - * [messagereader](https://github.com/miloyip/rapidjson/blob/master/example/messagereader/messagereader.cpp): Parse a JSON message with SAX API. - * [serialize](https://github.com/miloyip/rapidjson/blob/master/example/serialize/serialize.cpp): Serialize a C++ object into JSON with SAX API. - * [jsonx](https://github.com/miloyip/rapidjson/blob/master/example/jsonx/jsonx.cpp): Implements a `JsonxWriter` which stringify SAX events into [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html) (a kind of XML) format. The example is a command line tool which converts input JSON into JSONx format. + * [simplereader](https://github.com/miloyip/rapidjson/blob/master/example/simplereader/simplereader.cpp): Dumps all SAX events while parsing a JSON by `Reader`. + * [condense](https://github.com/miloyip/rapidjson/blob/master/example/condense/condense.cpp): A command line tool to rewrite a JSON, with all whitespaces removed. + * [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp): A command line tool to rewrite a JSON with indents and newlines by `PrettyWriter`. + * [capitalize](https://github.com/miloyip/rapidjson/blob/master/example/capitalize/capitalize.cpp): A command line tool to capitalize strings in JSON. + * [messagereader](https://github.com/miloyip/rapidjson/blob/master/example/messagereader/messagereader.cpp): Parse a JSON message with SAX API. + * [serialize](https://github.com/miloyip/rapidjson/blob/master/example/serialize/serialize.cpp): Serialize a C++ object into JSON with SAX API. + * [jsonx](https://github.com/miloyip/rapidjson/blob/master/example/jsonx/jsonx.cpp): Implements a `JsonxWriter` which stringify SAX events into [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html) (a kind of XML) format. The example is a command line tool which converts input JSON into JSONx format. * Schema - * [schemavalidator](https://github.com/miloyip/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp) : A command line tool to validate a JSON with a JSON schema. - + * [schemavalidator](https://github.com/miloyip/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp) : A command line tool to validate a JSON with a JSON schema. + * Advanced - * [prettyauto](https://github.com/miloyip/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): A modified version of [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp) to automatically handle JSON with any UTF encodings. - * [parsebyparts](https://github.com/miloyip/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): Implements an `AsyncDocumentParser` which can parse JSON in parts, using C++11 thread. - * [filterkey](https://github.com/miloyip/rapidjson/blob/master/example/filterkey/filterkey.cpp): A command line tool to remove all values with user-specified key. - * [filterkeydom](https://github.com/miloyip/rapidjson/blob/master/example/filterkeydom/filterkeydom.cpp): Same tool as above, but it demonstrates how to use a generator to populate a `Document`. + * [prettyauto](https://github.com/miloyip/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): A modified version of [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp) to automatically handle JSON with any UTF encodings. + * [parsebyparts](https://github.com/miloyip/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): Implements an `AsyncDocumentParser` which can parse JSON in parts, using C++11 thread. + * [filterkey](https://github.com/miloyip/rapidjson/blob/master/example/filterkey/filterkey.cpp): A command line tool to remove all values with user-specified key. + * [filterkeydom](https://github.com/miloyip/rapidjson/blob/master/example/filterkeydom/filterkeydom.cpp): Same tool as above, but it demonstrates how to use a generator to populate a `Document`. diff --git a/readme.zh-cn.md b/readme.zh-cn.md index b62b2e132..81b84bb48 100644 --- a/readme.zh-cn.md +++ b/readme.zh-cn.md @@ -1,6 +1,6 @@ -![](doc/logo/rapidjson.png) +![RapidJSON logo](doc/logo/rapidjson.png) -![](https://img.shields.io/badge/release-v1.1.0-blue.png) +![Release version](https://img.shields.io/badge/release-v1.1.0-blue.svg) ## 高效的 C++ JSON 解析/生成器,提供 SAX 及 DOM 风格 API @@ -20,12 +20,12 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights | :---------------: | :-----------------: | :-------------------: | | ![lin-badge] | ![win-badge] | ![cov-badge] | -[lin-badge]: https://travis-ci.org/miloyip/rapidjson.png?branch=master "Travis build status" +[lin-badge]: https://travis-ci.org/miloyip/rapidjson.svg?branch=master "Travis build status" [lin-link]: https://travis-ci.org/miloyip/rapidjson "Travis build status" -[win-badge]: https://ci.appveyor.com/api/projects/status/u658dcuwxo14a8m9/branch/master "AppVeyor build status" +[win-badge]: https://ci.appveyor.com/api/projects/status/github/miloyip/rapidjson?branch=master&svg=true "AppVeyor build status" [win-link]: https://ci.appveyor.com/project/miloyip/rapidjson/branch/master "AppVeyor build status" -[cov-badge]: https://coveralls.io/repos/miloyip/rapidjson/badge.png?branch=master -[cov-link]: https://coveralls.io/r/miloyip/rapidjson?branch=master +[cov-badge]: https://coveralls.io/repos/miloyip/rapidjson/badge.svg?branch=master "Coveralls coverage" +[cov-link]: https://coveralls.io/r/miloyip/rapidjson?branch=master "Coveralls coverage" ## 简介 @@ -45,8 +45,8 @@ RapidJSON 是一个 C++ 的 JSON 解析器及生成器。它的灵感来自 [Rap JSON(JavaScript Object Notation)是一个轻量的数据交换格式。RapidJSON 应该完全遵从 RFC7159/ECMA-404,并支持可选的放宽语法。 关于 JSON 的更多信息可参考: * [Introducing JSON](http://json.org/) -* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](http://www.ietf.org/rfc/rfc7159.txt) -* [Standard ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/standards/Ecma-404.htm) +* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159) +* [Standard ECMA-404: The JSON Data Interchange Format](https://www.ecma-international.org/publications/standards/Ecma-404.htm) ## v1.1 中的亮点 (2016-8-25) @@ -73,9 +73,9 @@ RapidJSON 是跨平台的。以下是一些曾测试的平台/编译器组合 RapidJSON 是只有头文件的 C++ 库。只需把 `include/rapidjson` 目录复制至系统或项目的 include 目录中。 RapidJSON 依赖于以下软件: -* [CMake](http://www.cmake.org) 作为通用生成工具 -* (optional)[Doxygen](http://www.doxygen.org) 用于生成文档 -* (optional)[googletest](https://code.google.com/p/googletest/) 用于单元及性能测试 +* [CMake](https://cmake.org/) 作为通用生成工具 +* (optional) [Doxygen](http://www.doxygen.org) 用于生成文档 +* (optional) [googletest](https://github.com/google/googletest) 用于单元及性能测试 生成测试及例子的步骤: @@ -131,22 +131,22 @@ int main() { 还有许多 [例子](https://github.com/miloyip/rapidjson/tree/master/example) 可供参考: * DOM API - * [tutorial](https://github.com/miloyip/rapidjson/blob/master/example/tutorial/tutorial.cpp): DOM API 的基本使用方法。 + * [tutorial](https://github.com/miloyip/rapidjson/blob/master/example/tutorial/tutorial.cpp): DOM API 的基本使用方法。 * SAX API - * [simplereader](https://github.com/miloyip/rapidjson/blob/master/example/simplereader/simplereader.cpp): 使用 `Reader` 解析 JSON 时,打印所有 SAX 事件。 - * [condense](https://github.com/miloyip/rapidjson/blob/master/example/condense/condense.cpp): 移除 JSON 中所有空白符的命令行工具。 - * [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp): 为 JSON 加入缩进与换行的命令行工具,当中使用了 `PrettyWriter`。 - * [capitalize](https://github.com/miloyip/rapidjson/blob/master/example/capitalize/capitalize.cpp): 把 JSON 中所有字符串改为大写的命令行工具。 - * [messagereader](https://github.com/miloyip/rapidjson/blob/master/example/messagereader/messagereader.cpp): 使用 SAX API 去解析一个 JSON 报文。 - * [serialize](https://github.com/miloyip/rapidjson/blob/master/example/serialize/serialize.cpp): 使用 SAX API 去序列化 C++ 对象,生成 JSON。 - * [jsonx](https://github.com/miloyip/rapidjson/blob/master/example/jsonx/jsonx.cpp): 实现了一个 `JsonxWriter`,它能把 SAX 事件写成 [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html)(一种 XML)格式。这个例子是把 JSON 输入转换成 JSONx 格式的命令行工具。 + * [simplereader](https://github.com/miloyip/rapidjson/blob/master/example/simplereader/simplereader.cpp): 使用 `Reader` 解析 JSON 时,打印所有 SAX 事件。 + * [condense](https://github.com/miloyip/rapidjson/blob/master/example/condense/condense.cpp): 移除 JSON 中所有空白符的命令行工具。 + * [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp): 为 JSON 加入缩进与换行的命令行工具,当中使用了 `PrettyWriter`。 + * [capitalize](https://github.com/miloyip/rapidjson/blob/master/example/capitalize/capitalize.cpp): 把 JSON 中所有字符串改为大写的命令行工具。 + * [messagereader](https://github.com/miloyip/rapidjson/blob/master/example/messagereader/messagereader.cpp): 使用 SAX API 去解析一个 JSON 报文。 + * [serialize](https://github.com/miloyip/rapidjson/blob/master/example/serialize/serialize.cpp): 使用 SAX API 去序列化 C++ 对象,生成 JSON。 + * [jsonx](https://github.com/miloyip/rapidjson/blob/master/example/jsonx/jsonx.cpp): 实现了一个 `JsonxWriter`,它能把 SAX 事件写成 [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html)(一种 XML)格式。这个例子是把 JSON 输入转换成 JSONx 格式的命令行工具。 * Schema API - * [schemavalidator](https://github.com/miloyip/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp): 使用 JSON Schema 去校验 JSON 的命令行工具。 - + * [schemavalidator](https://github.com/miloyip/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp): 使用 JSON Schema 去校验 JSON 的命令行工具。 + * 进阶 - * [prettyauto](https://github.com/miloyip/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp) 的修改版本,可自动处理任何 UTF 编码的 JSON。 - * [parsebyparts](https://github.com/miloyip/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): 这例子中的 `AsyncDocumentParser` 类使用 C++ 线程来逐段解析 JSON。 - * [filterkey](https://github.com/miloyip/rapidjson/blob/master/example/filterkey/filterkey.cpp): 移取使用者指定的键值的命令行工具。 - * [filterkeydom](https://github.com/miloyip/rapidjson/blob/master/example/filterkey/filterkey.cpp): 如上的工具,但展示如何使用生成器(generator)去填充一个 `Document`。 \ No newline at end of file + * [prettyauto](https://github.com/miloyip/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp) 的修改版本,可自动处理任何 UTF 编码的 JSON。 + * [parsebyparts](https://github.com/miloyip/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): 这例子中的 `AsyncDocumentParser` 类使用 C++ 线程来逐段解析 JSON。 + * [filterkey](https://github.com/miloyip/rapidjson/blob/master/example/filterkey/filterkey.cpp): 移取使用者指定的键值的命令行工具。 + * [filterkeydom](https://github.com/miloyip/rapidjson/blob/master/example/filterkey/filterkey.cpp): 如上的工具,但展示如何使用生成器(generator)去填充一个 `Document`。 \ No newline at end of file From 0d62f5cd359be662a5b378b8a04862a9bce645f5 Mon Sep 17 00:00:00 2001 From: Leo Mehr Date: Thu, 29 Jun 2017 20:09:13 -0400 Subject: [PATCH 108/117] Tutorial: fix typos in examples and broken links In the move example, the code uses `contacts` when the diagrams use `contact` (no 's') The code in the example: ``` Value contacts(kArrayType); // adding elements to contacts array. // ... o.AddMember("contacts", contacts, d.GetAllocator()); // deep clone contacts (may be with lots of allocations) // destruct contacts. ``` --- doc/diagram/move2.dot | 8 ++++---- doc/diagram/move3.dot | 8 ++++---- doc/tutorial.md | 22 +++++++++++----------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/doc/diagram/move2.dot b/doc/diagram/move2.dot index 7037ea6cb..2319871b9 100644 --- a/doc/diagram/move2.dot +++ b/doc/diagram/move2.dot @@ -18,7 +18,7 @@ digraph { node [shape=Mrecord, style=filled, colorscheme=spectral7] - c1 [label="{contact:array|}", fillcolor=4] + c1 [label="{contacts:array|}", fillcolor=4] c11 [label="{|}"] c12 [label="{|}"] c13 [shape="none", label="...", style="solid"] @@ -41,13 +41,13 @@ digraph { node [shape=Mrecord, style=filled, colorscheme=spectral7] - c2 [label="{contact:array|}", fillcolor=4] + c2 [label="{contacts:array|}", fillcolor=4] c3 [label="{array|}", fillcolor=4] c21 [label="{|}"] c22 [label="{|}"] c23 [shape=none, label="...", style="solid"] o2 [label="{o:object|}", fillcolor=3] - cs [label="{string|\"contact\"}", fillcolor=5] + cs [label="{string|\"contacts\"}", fillcolor=5] c31 [label="{|}"] c32 [label="{|}"] c33 [shape="none", label="...", style="solid"] @@ -59,4 +59,4 @@ digraph { c3 -> { c31; c32; c33 } } ghost -> o2 [style=invis] -} \ No newline at end of file +} diff --git a/doc/diagram/move3.dot b/doc/diagram/move3.dot index c197b99df..57adb4f9d 100644 --- a/doc/diagram/move3.dot +++ b/doc/diagram/move3.dot @@ -19,7 +19,7 @@ digraph { node [shape=Mrecord, style=filled, colorscheme=spectral7] - c1 [label="{contact:array|}", fillcolor=4] + c1 [label="{contacts:array|}", fillcolor=4] c11 [label="{|}"] c12 [label="{|}"] c13 [shape=none, label="...", style="solid"] @@ -42,13 +42,13 @@ digraph { node [shape=Mrecord, style=filled, colorscheme=spectral7] - c2 [label="{contact:null|}", fillcolor=1] + c2 [label="{contacts:null|}", fillcolor=1] c3 [label="{array|}", fillcolor=4] c21 [label="{|}"] c22 [label="{|}"] c23 [shape="none", label="...", style="solid"] o2 [label="{o:object|}", fillcolor=3] - cs [label="{string|\"contact\"}", fillcolor=5] + cs [label="{string|\"contacts\"}", fillcolor=5] c2 -> o2 [style="dashed", constraint=false, label="AddMember", style=invis] edge [arrowhead=vee] @@ -57,4 +57,4 @@ digraph { cs -> c3 [arrowhead=none] } ghost -> o2 [style=invis] -} \ No newline at end of file +} diff --git a/doc/tutorial.md b/doc/tutorial.md index cb76b4b0b..4a06a83aa 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -2,7 +2,7 @@ This tutorial introduces the basics of the Document Object Model(DOM) API. -As shown in [Usage at a glance](@ref index), a JSON can be parsed into DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON. +As shown in [Usage at a glance](../readme.md#usage-at-a-glance), a JSON can be parsed into DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON. [TOC] @@ -55,7 +55,7 @@ printf("hello = %s\n", document["hello"].GetString()); ~~~~~~~~~~ ~~~~~~~~~~ -world +hello = world ~~~~~~~~~~ JSON true/false values are represented as `bool`. @@ -65,7 +65,7 @@ printf("t = %s\n", document["t"].GetBool() ? "true" : "false"); ~~~~~~~~~~ ~~~~~~~~~~ -true +t = true ~~~~~~~~~~ JSON null can be queryed by `IsNull()`. @@ -74,7 +74,7 @@ printf("n = %s\n", document["n"].IsNull() ? "null" : "?"); ~~~~~~~~~~ ~~~~~~~~~~ -null +n = null ~~~~~~~~~~ JSON number type represents all numeric values. However, C++ needs more specific type for manipulation. @@ -526,11 +526,11 @@ Swapping two DOM trees is fast (constant time), despite the complexity of the tr This tutorial shows the basics of DOM tree query and manipulation. There are several important concepts in RapidJSON: -1. [Streams](doc/stream.md) are channels for reading/writing JSON, which can be a in-memory string, or file stream, etc. User can also create their streams. -2. [Encoding](doc/encoding.md) defines which character encoding is used in streams and memory. RapidJSON also provide Unicode conversion/validation internally. -3. [DOM](doc/dom.md)'s basics are already covered in this tutorial. Uncover more advanced features such as *in situ* parsing, other parsing options and advanced usages. -4. [SAX](doc/sax.md) is the foundation of parsing/generating facility in RapidJSON. Learn how to use `Reader`/`Writer` to implement even faster applications. Also try `PrettyWriter` to format the JSON. -5. [Performance](doc/performance.md) shows some in-house and third-party benchmarks. -6. [Internals](doc/internals.md) describes some internal designs and techniques of RapidJSON. +1. [Streams](stream.md) are channels for reading/writing JSON, which can be a in-memory string, or file stream, etc. User can also create their streams. +2. [Encoding](encoding.md) defines which character encoding is used in streams and memory. RapidJSON also provide Unicode conversion/validation internally. +3. [DOM](dom.md)'s basics are already covered in this tutorial. Uncover more advanced features such as *in situ* parsing, other parsing options and advanced usages. +4. [SAX](sax.md) is the foundation of parsing/generating facility in RapidJSON. Learn how to use `Reader`/`Writer` to implement even faster applications. Also try `PrettyWriter` to format the JSON. +5. [Performance](performance.md) shows some in-house and third-party benchmarks. +6. [Internals](internals.md) describes some internal designs and techniques of RapidJSON. -You may also refer to the [FAQ](doc/faq.md), API documentation, examples and unit tests. +You may also refer to the [FAQ](faq.md), API documentation, examples and unit tests. From 3aafe12c91068f5403ec737ea9766eccabf17302 Mon Sep 17 00:00:00 2001 From: Leo Mehr Date: Fri, 30 Jun 2017 12:42:06 -0400 Subject: [PATCH 109/117] undo changes to links and some minor changes to make the readme more easily readable --- doc/tutorial.md | 64 ++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 4a06a83aa..167b81dd7 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -2,7 +2,7 @@ This tutorial introduces the basics of the Document Object Model(DOM) API. -As shown in [Usage at a glance](../readme.md#usage-at-a-glance), a JSON can be parsed into DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON. +As shown in [Usage at a glance](@ref index), JSON can be parsed into a DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON. [TOC] @@ -14,7 +14,7 @@ Each JSON value is stored in a type called `Value`. A `Document`, representing t In this section, we will use excerpt of `example/tutorial/tutorial.cpp`. -Assumes we have a JSON stored in a C string (`const char* json`): +Assume we have the following JSON stored in a C string (`const char* json`): ~~~~~~~~~~js { "hello": "world", @@ -68,7 +68,7 @@ printf("t = %s\n", document["t"].GetBool() ? "true" : "false"); t = true ~~~~~~~~~~ -JSON null can be queryed by `IsNull()`. +JSON null can be queryed with `IsNull()`. ~~~~~~~~~~cpp printf("n = %s\n", document["n"].IsNull() ? "null" : "?"); ~~~~~~~~~~ @@ -115,15 +115,15 @@ a[3] = 4 Note that, RapidJSON does not automatically convert values between JSON types. If a value is a string, it is invalid to call `GetInt()`, for example. In debug mode it will fail an assertion. In release mode, the behavior is undefined. -In the following, details about querying individual types are discussed. +In the following sections we discuss details about querying individual types. ## Query Array {#QueryArray} -By default, `SizeType` is typedef of `unsigned`. In most systems, array is limited to store up to 2^32-1 elements. +By default, `SizeType` is typedef of `unsigned`. In most systems, an array is limited to store up to 2^32-1 elements. -You may access the elements in array by integer literal, for example, `a[0]`, `a[1]`, `a[2]`. +You may access the elements in an array by integer literal, for example, `a[0]`, `a[1]`, `a[2]`. -Array is similar to `std::vector`, instead of using indices, you may also use iterator to access all the elements. +Array is similar to `std::vector`: instead of using indices, you may also use iterator to access all the elements. ~~~~~~~~~~cpp for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) printf("%d ", itr->GetInt()); @@ -144,7 +144,7 @@ for (auto& v : a.GetArray()) ## Query Object {#QueryObject} -Similar to array, we can access all object members by iterator: +Similar to Array, we can access all object members by iterator: ~~~~~~~~~~cpp static const char* kTypeNames[] = @@ -190,11 +190,11 @@ for (auto& m : document.GetObject()) ## Querying Number {#QueryNumber} -JSON provide a single numerical type called Number. Number can be integer or real numbers. RFC 4627 says the range of Number is specified by parser. +JSON provides a single numerical type called Number. Number can be an integer or a real number. RFC 4627 says the range of Number is specified by the parser implementation. -As C++ provides several integer and floating point number types, the DOM tries to handle these with widest possible range and good performance. +As C++ provides several integer and floating point number types, the DOM tries to handle these with the widest possible range and good performance. -When a Number is parsed, it is stored in the DOM as either one of the following type: +When a Number is parsed, it is stored in the DOM as one of the following types: Type | Description -----------|--------------------------------------- @@ -204,7 +204,7 @@ Type | Description `int64_t` | 64-bit signed integer `double` | 64-bit double precision floating point -When querying a number, you can check whether the number can be obtained as target type: +When querying a number, you can check whether the number can be obtained as the target type: Checking | Obtaining ------------------|--------------------- @@ -215,9 +215,9 @@ Checking | Obtaining `bool IsInt64()` | `int64_t GetInt64()` `bool IsDouble()` | `double GetDouble()` -Note that, an integer value may be obtained in various ways without conversion. For example, A value `x` containing 123 will make `x.IsInt() == x.IsUint() == x.IsInt64() == x.IsUint64() == true`. But a value `y` containing -3000000000 will only makes `x.IsInt64() == true`. +Note that, an integer value may be obtained in various ways without conversion. For example, A value `x` containing 123 will make `x.IsInt() == x.IsUint() == x.IsInt64() == x.IsUint64() == true`. But a value `y` containing -3000000000 will only make `x.IsInt64() == true`. -When obtaining the numeric values, `GetDouble()` will convert internal integer representation to a `double`. Note that, `int` and `unsigned` can be safely convert to `double`, but `int64_t` and `uint64_t` may lose precision (since mantissa of `double` is only 52-bits). +When obtaining the numeric values, `GetDouble()` will convert internal integer representation to a `double`. Note that, `int` and `unsigned` can be safely converted to `double`, but `int64_t` and `uint64_t` may lose precision (since mantissa of `double` is only 52-bits). ## Query String {#QueryString} @@ -225,7 +225,7 @@ In addition to `GetString()`, the `Value` class also contains `GetStringLength() According to RFC 4627, JSON strings can contain Unicode character `U+0000`, which must be escaped as `"\u0000"`. The problem is that, C/C++ often uses null-terminated string, which treats ``\0'` as the terminator symbol. -To conform RFC 4627, RapidJSON supports string containing `U+0000`. If you need to handle this, you can use `GetStringLength()` API to obtain the correct length of string. +To conform RFC 4627, RapidJSON supports string containing `U+0000`. If you need to handle this, you can use `GetStringLength()` to obtain the correct string length. For example, after parsing a the following JSON to `Document d`: @@ -360,14 +360,14 @@ a.PushBack(Value(42).Move(), allocator); // same as above ~~~~~~~~~~ ## Create String {#CreateString} -RapidJSON provide two strategies for storing string. +RapidJSON provides two strategies for storing string. 1. copy-string: allocates a buffer, and then copy the source data into it. 2. const-string: simply store a pointer of string. -Copy-string is always safe because it owns a copy of the data. Const-string can be used for storing string literal, and in-situ parsing which we will mentioned in Document section. +Copy-string is always safe because it owns a copy of the data. Const-string can be used for storing a string literal, and for in-situ parsing which will be mentioned in the DOM section. -To make memory allocation customizable, RapidJSON requires user to pass an instance of allocator, whenever an operation may require allocation. This design is needed to prevent storing a allocator (or Document) pointer per Value. +To make memory allocation customizable, RapidJSON requires users to pass an instance of allocator, whenever an operation may require allocation. This design is needed to prevent storing a allocator (or Document) pointer per Value. Therefore, when we assign a copy-string, we call this overloaded `SetString()` with allocator: @@ -385,7 +385,7 @@ In this example, we get the allocator from a `Document` instance. This is a comm Besides, the above `SetString()` requires length. This can handle null characters within a string. There is another `SetString()` overloaded function without the length parameter. And it assumes the input is null-terminated and calls a `strlen()`-like function to obtain the length. -Finally, for string literal or string with safe life-cycle can use const-string version of `SetString()`, which lacks allocator parameter. For string literals (or constant character arrays), simply passing the literal as parameter is safe and efficient: +Finally, for a string literal or string with a safe life-cycle one can use the const-string version of `SetString()`, which lacks an allocator parameter. For string literals (or constant character arrays), simply passing the literal as parameter is safe and efficient: ~~~~~~~~~~cpp Value s; @@ -393,7 +393,7 @@ s.SetString("rapidjson"); // can contain null character, length derived at co s = "rapidjson"; // shortcut, same as above ~~~~~~~~~~ -For character pointer, the RapidJSON requires to mark it as safe before using it without copying. This can be achieved by using the `StringRef` function: +For a character pointer, RapidJSON requires it to be marked as safe before using it without copying. This can be achieved by using the `StringRef` function: ~~~~~~~~~cpp const char * cstr = getenv("USER"); @@ -408,7 +408,7 @@ s = StringRef(cstr,cstr_len); // shortcut, same as above ~~~~~~~~~ ## Modify Array {#ModifyArray} -Value with array type provides similar APIs as `std::vector`. +Value with array type provides an API similar to `std::vector`. * `Clear()` * `Reserve(SizeType, Allocator&)` @@ -418,7 +418,7 @@ Value with array type provides similar APIs as `std::vector`. * `ValueIterator Erase(ConstValueIterator pos)` * `ValueIterator Erase(ConstValueIterator first, ConstValueIterator last)` -Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore require an allocator. +Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore requiring an allocator. Here is an example of `PushBack()`: @@ -433,7 +433,7 @@ for (int i = 5; i <= 10; i++) a.PushBack("Lua", allocator).PushBack("Mio", allocator); ~~~~~~~~~~ -Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called _fluent interface_. +This API differs from STL in that `PushBack()`/`PopBack()` return the array reference itself. This is called _fluent interface_. If you want to add a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)) to the array, you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place: @@ -448,7 +448,7 @@ contact.PushBack(val, document.GetAllocator()); ~~~~~~~~~~ ## Modify Object {#ModifyObject} -Object is a collection of key-value pairs (members). Each key must be a string value. To modify an object, either add or remove members. THe following APIs are for adding members: +The Object class is a collection of key-value pairs (members). Each key must be a string value. To modify an object, either add or remove members. The following API is for adding members: * `Value& AddMember(Value&, Value&, Allocator& allocator)` * `Value& AddMember(StringRefType, Value&, Allocator&)` @@ -462,7 +462,7 @@ contact.AddMember("name", "Milo", document.GetAllocator()); contact.AddMember("married", true, document.GetAllocator()); ~~~~~~~~~~ -The name parameter with `StringRefType` is similar to the interface of `SetString` function for string values. These overloads are used to avoid the need for copying the `name` string, as constant key names are very common in JSON objects. +The name parameter with `StringRefType` is similar to the interface of the `SetString` function for string values. These overloads are used to avoid the need for copying the `name` string, since constant key names are very common in JSON objects. If you need to create a name from a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)), you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place: @@ -526,11 +526,11 @@ Swapping two DOM trees is fast (constant time), despite the complexity of the tr This tutorial shows the basics of DOM tree query and manipulation. There are several important concepts in RapidJSON: -1. [Streams](stream.md) are channels for reading/writing JSON, which can be a in-memory string, or file stream, etc. User can also create their streams. -2. [Encoding](encoding.md) defines which character encoding is used in streams and memory. RapidJSON also provide Unicode conversion/validation internally. -3. [DOM](dom.md)'s basics are already covered in this tutorial. Uncover more advanced features such as *in situ* parsing, other parsing options and advanced usages. -4. [SAX](sax.md) is the foundation of parsing/generating facility in RapidJSON. Learn how to use `Reader`/`Writer` to implement even faster applications. Also try `PrettyWriter` to format the JSON. -5. [Performance](performance.md) shows some in-house and third-party benchmarks. -6. [Internals](internals.md) describes some internal designs and techniques of RapidJSON. +1. [Streams](doc/stream.md) are channels for reading/writing JSON, which can be a in-memory string, or file stream, etc. User can also create their streams. +2. [Encoding](doc/encoding.md) defines which character encoding is used in streams and memory. RapidJSON also provide Unicode conversion/validation internally. +3. [DOM](doc/dom.md)'s basics are already covered in this tutorial. Uncover more advanced features such as *in situ* parsing, other parsing options and advanced usages. +4. [SAX](doc/sax.md) is the foundation of parsing/generating facility in RapidJSON. Learn how to use `Reader`/`Writer` to implement even faster applications. Also try `PrettyWriter` to format the JSON. +5. [Performance](doc/performance.md) shows some in-house and third-party benchmarks. +6. [Internals](doc/internals.md) describes some internal designs and techniques of RapidJSON. -You may also refer to the [FAQ](faq.md), API documentation, examples and unit tests. +You may also refer to the [FAQ](doc/faq.md), API documentation, examples and unit tests. From 14218aeb0a6491130622672495bf5fe7a20ef28a Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 9 Jul 2017 11:13:31 +0200 Subject: [PATCH 110/117] ParseResult: improve bool conversion and add operator!= * Use safe-bool idiom for boolean conversion to avoid accidental misuse of ParseResult values (closes #989) * Add operator!= to support more comparison expressions (previously silently/erroneously used operator bool) --- include/rapidjson/error/error.h | 10 ++++++++-- test/unittest/documenttest.cpp | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index 95cb31a72..9311d2f03 100644 --- a/include/rapidjson/error/error.h +++ b/include/rapidjson/error/error.h @@ -104,6 +104,8 @@ enum ParseErrorCode { \see GenericReader::Parse, GenericDocument::Parse */ struct ParseResult { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; public: //! Default constructor, no error. ParseResult() : code_(kParseErrorNone), offset_(0) {} @@ -115,8 +117,8 @@ struct ParseResult { //! Get the error offset, if \ref IsError(), 0 otherwise. size_t Offset() const { return offset_; } - //! Conversion to \c bool, returns \c true, iff !\ref IsError(). - operator bool() const { return !IsError(); } + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } //! Whether the result is an error. bool IsError() const { return code_ != kParseErrorNone; } @@ -124,6 +126,10 @@ struct ParseResult { bool operator==(ParseErrorCode code) const { return code_ == code; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + //! Reset error code. void Clear() { Set(kParseErrorNone); } //! Update error code and offset. diff --git a/test/unittest/documenttest.cpp b/test/unittest/documenttest.cpp index ecd4b79bc..0ca58019b 100644 --- a/test/unittest/documenttest.cpp +++ b/test/unittest/documenttest.cpp @@ -128,8 +128,14 @@ TEST(Document, UnchangedOnParseError) { Document doc; doc.SetArray().PushBack(0, doc.GetAllocator()); + ParseResult noError; + EXPECT_TRUE(noError); + ParseResult err = doc.Parse("{]"); EXPECT_TRUE(doc.HasParseError()); + EXPECT_NE(err, noError); + EXPECT_NE(err.Code(), noError); + EXPECT_NE(noError, doc.GetParseError()); EXPECT_EQ(err.Code(), doc.GetParseError()); EXPECT_EQ(err.Offset(), doc.GetErrorOffset()); EXPECT_TRUE(doc.IsArray()); @@ -138,6 +144,9 @@ TEST(Document, UnchangedOnParseError) { err = doc.Parse("{}"); EXPECT_FALSE(doc.HasParseError()); EXPECT_FALSE(err.IsError()); + EXPECT_TRUE(err); + EXPECT_EQ(err, noError); + EXPECT_EQ(err.Code(), noError); EXPECT_EQ(err.Code(), doc.GetParseError()); EXPECT_EQ(err.Offset(), doc.GetErrorOffset()); EXPECT_TRUE(doc.IsObject()); @@ -488,15 +497,19 @@ TYPED_TEST(DocumentMove, MoveConstructorParseError) { a.Parse("{ 4 = 4]"); ParseResult error(a.GetParseError(), a.GetErrorOffset()); EXPECT_TRUE(a.HasParseError()); + EXPECT_NE(error, noError); + EXPECT_NE(error.Code(), noError); EXPECT_NE(error.Code(), noError.Code()); EXPECT_NE(error.Offset(), noError.Offset()); D b(std::move(a)); EXPECT_FALSE(a.HasParseError()); EXPECT_TRUE(b.HasParseError()); + EXPECT_EQ(a.GetParseError(), noError); EXPECT_EQ(a.GetParseError(), noError.Code()); - EXPECT_EQ(b.GetParseError(), error.Code()); EXPECT_EQ(a.GetErrorOffset(), noError.Offset()); + EXPECT_EQ(b.GetParseError(), error); + EXPECT_EQ(b.GetParseError(), error.Code()); EXPECT_EQ(b.GetErrorOffset(), error.Offset()); D c(std::move(b)); From eefb618ec947837735d08fe81fb94afdd09948d7 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 9 Jul 2017 11:40:56 +0200 Subject: [PATCH 111/117] Travis: Switch to Ubuntu 14.04 (Trusty) --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f9319f2ed..38f3a9803 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ sudo: required -dist: precise +dist: trusty +group: edge language: cpp cache: From f1ba61c7ba5989373880f14f80d2b56f8593eb81 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 9 Jul 2017 14:31:29 +0200 Subject: [PATCH 112/117] unittest.h: change RAPIDJSON_ASSERT to allow usage in expressions --- test/unittest/unittest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/unittest.h b/test/unittest/unittest.h index 583734578..aa091aa56 100644 --- a/test/unittest/unittest.h +++ b/test/unittest/unittest.h @@ -117,7 +117,7 @@ class AssertException : public std::logic_error { #pragma GCC diagnostic pop #endif -#define RAPIDJSON_ASSERT(x) if (!(x)) throw AssertException(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_ASSERT(x) (!(x) ? throw AssertException(RAPIDJSON_STRINGIFY(x)) : (void)0u) class Random { public: From 47c3c1ec9f5c3724c5befb42f8323e760acc1f69 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 9 Jul 2017 14:46:59 +0200 Subject: [PATCH 113/117] Improved handling of NULL strings * Safely assert upon passing NULL string without length (requires usage of RAPIDJSON_ASSERT within an expression) * Allow using a NULL string together with an explicit length 0 (GenericStringRef, GenericValue::SetString, ...), see #817 * Add GenericValue::SetString(StringRefType, Allocator&) overload * Add tests for the various cases --- include/rapidjson/document.h | 26 +++++++++++++++++----- test/unittest/valuetest.cpp | 43 +++++++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 57f0b3c23..06451ad28 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -308,7 +308,7 @@ struct GenericStringRef { */ #endif explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); } + : s(str), length(((RAPIDJSON_ASSERT(str != 0)), internal::StrLen(str))) {} //! Create constant string reference from pointer and length #ifndef __clang__ // -Wdocumentation @@ -320,7 +320,7 @@ struct GenericStringRef { */ #endif GenericStringRef(const CharType* str, SizeType len) - : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); } + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} @@ -331,6 +331,9 @@ struct GenericStringRef { const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + //! Disallow construction from non-const array template GenericStringRef(CharType (&str)[N]) /* = delete */; @@ -338,6 +341,9 @@ struct GenericStringRef { GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + //! Mark a character pointer as constant string /*! Mark a plain character pointer as a "string literal". This function can be used to avoid copying a character string to be referenced as a @@ -352,7 +358,7 @@ struct GenericStringRef { */ template inline GenericStringRef StringRef(const CharType* str) { - return GenericStringRef(str, internal::StrLen(str)); + return GenericStringRef(str); } //! Mark a character pointer as constant string @@ -1762,7 +1768,7 @@ class GenericValue { \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } //! Set this value as a string by copying from source string. /*! \param s source string. @@ -1770,7 +1776,15 @@ class GenericValue { \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. @@ -1780,7 +1794,7 @@ class GenericValue { \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } #endif //@} diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index fefc001d4..307e1b06d 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -857,9 +857,46 @@ TEST(Value, String) { } // Issue 226: Value of string type should not point to NULL -TEST(Value, SetStringNullException) { - Value v; - EXPECT_THROW(v.SetString(0, 0), AssertException); +TEST(Value, SetStringNull) { + + MemoryPoolAllocator<> allocator; + const char* nullPtr = 0; + { + // Construction with string type creates empty string + Value v(kStringType); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + + // Construction from/setting to null without length not allowed + EXPECT_THROW(Value(StringRef(nullPtr)), AssertException); + EXPECT_THROW(Value(StringRef(nullPtr), allocator), AssertException); + EXPECT_THROW(v.SetString(nullPtr, allocator), AssertException); + + // Non-empty length with null string is not allowed + EXPECT_THROW(v.SetString(nullPtr, 17u), AssertException); + EXPECT_THROW(v.SetString(nullPtr, 42u, allocator), AssertException); + + // Setting to null string with empty length is allowed + v.SetString(nullPtr, 0u); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + + v.SetNull(); + v.SetString(nullPtr, 0u, allocator); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + } + // Construction with null string and empty length is allowed + { + Value v(nullPtr,0u); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + } + { + Value v(nullPtr, 0u, allocator); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + } } template From 7161894f4069879642aef9eae8b6e63fbd36cfd8 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Mon, 10 Jul 2017 21:32:16 +0200 Subject: [PATCH 114/117] travis-doxygen.sh: upgrade to Doxygen 1.8.13 * Upgrade to the latest Doxygen version 1.8.13 * Drop unused variable DOXYGEN_BIN * Reenable --single-branch (travis-ci/travis-ci#5225 is closed) --- travis-doxygen.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/travis-doxygen.sh b/travis-doxygen.sh index 31a50cfa9..e5c03206a 100755 --- a/travis-doxygen.sh +++ b/travis-doxygen.sh @@ -4,10 +4,9 @@ set -e -DOXYGEN_VER=doxygen-1.8.7 +DOXYGEN_VER=doxygen-1.8.13 DOXYGEN_TAR=${DOXYGEN_VER}.linux.bin.tar.gz DOXYGEN_URL="http://ftp.stack.nl/pub/users/dimitri/${DOXYGEN_TAR}" -DOXYGEN_BIN="/usr/local/bin/doxygen" : ${GITHUB_REPO:="miloyip/rapidjson"} GITHUB_HOST="github.com" @@ -66,7 +65,7 @@ gh_pages_prepare() [ ! -d "html" ] || \ abort "Doxygen target directory already exists." git --version - git clone -b gh-pages "${GITHUB_CLONE}" html + git clone --single-branch -b gh-pages "${GITHUB_CLONE}" html cd html # setup git config (with defaults) git config user.name "${GIT_NAME-travis}" From 70171f97903752cdf4c910b94b5ee0e06570bb41 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Mon, 10 Jul 2017 22:32:18 +0200 Subject: [PATCH 115/117] GenericStringRef: move assert out of expression --- include/rapidjson/document.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 06451ad28..3169bd487 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -308,7 +308,7 @@ struct GenericStringRef { */ #endif explicit GenericStringRef(const CharType* str) - : s(str), length(((RAPIDJSON_ASSERT(str != 0)), internal::StrLen(str))) {} + : s(str), length(NotNullStrLen(str)) {} //! Create constant string reference from pointer and length #ifndef __clang__ // -Wdocumentation @@ -331,6 +331,11 @@ struct GenericStringRef { const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + /// Empty string - used when passing in a NULL pointer static const Ch emptyString[]; From fcd2e1f60cecc00d414821987b2d3d02dbd593df Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Thu, 13 Jul 2017 16:07:36 +0800 Subject: [PATCH 116/117] Fix #1017 allOf keyword fail with Writer handler Gave up using static binding for null handler, because it cannot be used with arbitrary handler type. Change `OutputHandler handler_` to pointer type. --- include/rapidjson/schema.h | 26 ++++++++------------------ test/unittest/schematest.cpp | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index dd57edbc2..abcf1a102 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1594,7 +1594,7 @@ class GenericSchemaValidator : ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), - outputHandler_(CreateNullHandler()), + outputHandler_(0), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) @@ -1622,8 +1622,7 @@ class GenericSchemaValidator : ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), - outputHandler_(outputHandler), - nullHandler_(0), + outputHandler_(&outputHandler), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) @@ -1634,10 +1633,6 @@ class GenericSchemaValidator : //! Destructor. ~GenericSchemaValidator() { Reset(); - if (nullHandler_) { - nullHandler_->~OutputHandler(); - StateAllocator::Free(nullHandler_); - } RAPIDJSON_DELETE(ownStateAllocator_); } @@ -1699,7 +1694,7 @@ RAPIDJSON_MULTILINEMACRO_END } #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ - return valid_ = EndValue() && outputHandler_.method arg2 + return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2) #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ @@ -1721,7 +1716,7 @@ RAPIDJSON_MULTILINEMACRO_END bool StartObject() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); - return valid_ = outputHandler_.StartObject(); + return valid_ = !outputHandler_ || outputHandler_->StartObject(); } bool Key(const Ch* str, SizeType len, bool copy) { @@ -1729,7 +1724,7 @@ RAPIDJSON_MULTILINEMACRO_END AppendToken(str, len); if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); - return valid_ = outputHandler_.Key(str, len, copy); + return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); } bool EndObject(SizeType memberCount) { @@ -1742,7 +1737,7 @@ RAPIDJSON_MULTILINEMACRO_END bool StartArray() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); - return valid_ = outputHandler_.StartArray(); + return valid_ = !outputHandler_ || outputHandler_->StartArray(); } bool EndArray(SizeType elementCount) { @@ -1815,7 +1810,7 @@ RAPIDJSON_MULTILINEMACRO_END ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), - outputHandler_(CreateNullHandler()), + outputHandler_(0), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(depth) @@ -1929,10 +1924,6 @@ RAPIDJSON_MULTILINEMACRO_END Context& CurrentContext() { return *schemaStack_.template Top(); } const Context& CurrentContext() const { return *schemaStack_.template Top(); } - OutputHandler& CreateNullHandler() { - return *(nullHandler_ = new (GetStateAllocator().Malloc(sizeof(OutputHandler))) OutputHandler); - } - static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultDocumentStackCapacity = 256; const SchemaDocumentType* schemaDocument_; @@ -1941,8 +1932,7 @@ RAPIDJSON_MULTILINEMACRO_END StateAllocator* ownStateAllocator_; internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) - OutputHandler& outputHandler_; - OutputHandler* nullHandler_; + OutputHandler* outputHandler_; bool valid_; #if RAPIDJSON_SCHEMA_VERBOSE unsigned depth_; diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index e79fec288..9b99ba896 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -1329,6 +1329,25 @@ TEST(SchemaValidator, Issue825) { VALIDATE(s, "{ \"item\": \"hello\" }", true); } +TEST(SchemaValidator, Issue1017_allOfHandler) { + Document sd; + sd.Parse("{\"allOf\": [{\"type\": \"object\",\"properties\": {\"cyanArray2\": {\"type\": \"array\",\"items\": { \"type\": \"string\" }}}},{\"type\": \"object\",\"properties\": {\"blackArray\": {\"type\": \"array\",\"items\": { \"type\": \"string\" }}},\"required\": [ \"blackArray\" ]}]}"); + SchemaDocument s(sd); + StringBuffer sb; + Writer writer(sb); + GenericSchemaValidator > validator(s, writer); + EXPECT_TRUE(validator.StartObject()); + EXPECT_TRUE(validator.Key("cyanArray2", 10, false)); + EXPECT_TRUE(validator.StartArray()); + EXPECT_TRUE(validator.EndArray(0)); + EXPECT_TRUE(validator.Key("blackArray", 10, false)); + EXPECT_TRUE(validator.StartArray()); + EXPECT_TRUE(validator.EndArray(0)); + EXPECT_TRUE(validator.EndObject(0)); + EXPECT_TRUE(validator.IsValid()); + EXPECT_STREQ("{\"cyanArray2\":[],\"blackArray\":[]}", sb.GetString()); +} + #ifdef __clang__ RAPIDJSON_DIAG_POP #endif From 707fd36afa54f44d4635fde0107eac1c2f4e8649 Mon Sep 17 00:00:00 2001 From: Bart Muzzin Date: Tue, 25 Jul 2017 22:13:26 -0400 Subject: [PATCH 117/117] Issue #1028: Visual Studio natvis file. --- contrib/natvis/LICENSE | 45 +++++++++++++++++++++++++++++++++ contrib/natvis/README.md | 7 +++++ contrib/natvis/rapidjson.natvis | 38 ++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 contrib/natvis/LICENSE create mode 100644 contrib/natvis/README.md create mode 100644 contrib/natvis/rapidjson.natvis diff --git a/contrib/natvis/LICENSE b/contrib/natvis/LICENSE new file mode 100644 index 000000000..f57da96cf --- /dev/null +++ b/contrib/natvis/LICENSE @@ -0,0 +1,45 @@ +The MIT License (MIT) + +Copyright (c) 2017 Bart Muzzin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Derived from: + +The MIT License (MIT) + +Copyright (c) 2015 mojmir svoboda + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/contrib/natvis/README.md b/contrib/natvis/README.md new file mode 100644 index 000000000..9685c7f7c --- /dev/null +++ b/contrib/natvis/README.md @@ -0,0 +1,7 @@ +# rapidjson.natvis + +This file can be used as a [Visual Studio Visualizer](https://docs.microsoft.com/en-gb/visualstudio/debugger/create-custom-views-of-native-objects) to aid in visualizing rapidjson structures within the Visual Studio debugger. Natvis visualizers are supported in Visual Studio 2012 and later. To install, copy the file into this directory: + +`%USERPROFILE%\Documents\Visual Studio 2012\Visualizers` + +Each version of Visual Studio has a similar directory, it must be copied into each directory to be used with that particular version. In Visual Studio 2015 and later, this can be done without restarting Visual Studio (a new debugging session must be started). diff --git a/contrib/natvis/rapidjson.natvis b/contrib/natvis/rapidjson.natvis new file mode 100644 index 000000000..a804b7bf6 --- /dev/null +++ b/contrib/natvis/rapidjson.natvis @@ -0,0 +1,38 @@ + + + + + null + true + false + {data_.ss.str} + {(const char*)((size_t)data_.s.str & 0x0000FFFFFFFFFFFF)} + {data_.n.i.i} + {data_.n.u.u} + {data_.n.i64} + {data_.n.u64} + {data_.n.d} + Object members={data_.o.size} + Array members={data_.a.size} + + data_.o.size + data_.o.capacity + + data_.o.size + + (rapidjson::GenericMember<$T1,$T2>*)(((size_t)data_.o.members) & 0x0000FFFFFFFFFFFF) + + + data_.a.size + data_.a.capacity + + data_.a.size + + (rapidjson::GenericValue<$T1,$T2>*)(((size_t)data_.a.elements) & 0x0000FFFFFFFFFFFF) + + + + + + +