10358 lines
450 KiB
Diff
10358 lines
450 KiB
Diff
From de043145d0d663f0e68157e236ad19621af0fb92 Mon Sep 17 00:00:00 2001
|
|
From: Christoph Oelckers <coelckers@users.noreply.github.com>
|
|
Date: Sun, 26 Mar 2023 08:46:23 +0200
|
|
Subject: [PATCH 44/51] - updated RapidJson library.
|
|
|
|
This still needed the Nan hotfix to be reapplied. The problem hasn't been addressed yet. :(
|
|
---
|
|
src/common/thirdparty/rapidjson/allocators.h | 534 ++++-
|
|
.../rapidjson/cursorstreamwrapper.h | 78 +
|
|
src/common/thirdparty/rapidjson/document.h | 789 +++++--
|
|
.../thirdparty/rapidjson/encodedstream.h | 4 +-
|
|
src/common/thirdparty/rapidjson/encodings.h | 88 +-
|
|
src/common/thirdparty/rapidjson/error/en.h | 118 +-
|
|
src/common/thirdparty/rapidjson/error/error.h | 146 +-
|
|
.../thirdparty/rapidjson/filereadstream.h | 6 +-
|
|
.../thirdparty/rapidjson/filewritestream.h | 6 +-
|
|
src/common/thirdparty/rapidjson/fwd.h | 4 +-
|
|
.../rapidjson/internal/biginteger.h | 25 +-
|
|
.../thirdparty/rapidjson/internal/clzll.h | 71 +
|
|
.../thirdparty/rapidjson/internal/diyfp.h | 59 +-
|
|
.../thirdparty/rapidjson/internal/dtoa.h | 18 +-
|
|
.../thirdparty/rapidjson/internal/ieee754.h | 6 +-
|
|
.../thirdparty/rapidjson/internal/itoa.h | 84 +-
|
|
.../thirdparty/rapidjson/internal/meta.h | 11 +-
|
|
.../thirdparty/rapidjson/internal/pow10.h | 2 +-
|
|
.../thirdparty/rapidjson/internal/regex.h | 246 +-
|
|
.../thirdparty/rapidjson/internal/stack.h | 12 +-
|
|
.../thirdparty/rapidjson/internal/strfunc.h | 27 +-
|
|
.../thirdparty/rapidjson/internal/strtod.h | 122 +-
|
|
.../thirdparty/rapidjson/internal/swap.h | 2 +-
|
|
.../thirdparty/rapidjson/istreamwrapper.h | 89 +-
|
|
.../thirdparty/rapidjson/memorybuffer.h | 2 +-
|
|
.../thirdparty/rapidjson/memorystream.h | 2 +-
|
|
.../thirdparty/rapidjson/ostreamwrapper.h | 2 +-
|
|
src/common/thirdparty/rapidjson/pointer.h | 194 +-
|
|
.../thirdparty/rapidjson/prettywriter.h | 58 +-
|
|
src/common/thirdparty/rapidjson/rapidjson.h | 220 +-
|
|
src/common/thirdparty/rapidjson/reader.h | 677 ++++--
|
|
src/common/thirdparty/rapidjson/schema.h | 1996 ++++++++++++++---
|
|
src/common/thirdparty/rapidjson/stream.h | 54 +-
|
|
.../thirdparty/rapidjson/stringbuffer.h | 6 +-
|
|
src/common/thirdparty/rapidjson/uri.h | 481 ++++
|
|
src/common/thirdparty/rapidjson/writer.h | 169 +-
|
|
36 files changed, 5147 insertions(+), 1261 deletions(-)
|
|
create mode 100644 src/common/thirdparty/rapidjson/cursorstreamwrapper.h
|
|
create mode 100644 src/common/thirdparty/rapidjson/internal/clzll.h
|
|
create mode 100644 src/common/thirdparty/rapidjson/uri.h
|
|
|
|
diff --git a/src/common/thirdparty/rapidjson/allocators.h b/src/common/thirdparty/rapidjson/allocators.h
|
|
index 98affe03f..ddcf4781b 100644
|
|
--- a/src/common/thirdparty/rapidjson/allocators.h
|
|
+++ b/src/common/thirdparty/rapidjson/allocators.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -16,6 +16,14 @@
|
|
#define RAPIDJSON_ALLOCATORS_H_
|
|
|
|
#include "rapidjson.h"
|
|
+#include "internal/meta.h"
|
|
+
|
|
+#include <memory>
|
|
+#include <limits>
|
|
+
|
|
+#if RAPIDJSON_HAS_CXX11
|
|
+#include <type_traits>
|
|
+#endif
|
|
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
|
|
@@ -52,6 +60,19 @@ concept Allocator {
|
|
\endcode
|
|
*/
|
|
|
|
+
|
|
+/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
|
|
+ \ingroup RAPIDJSON_CONFIG
|
|
+ \brief User-defined kDefaultChunkCapacity definition.
|
|
+
|
|
+ User can define this as any \c size that is a power of 2.
|
|
+*/
|
|
+
|
|
+#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
|
|
+#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024)
|
|
+#endif
|
|
+
|
|
+
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CrtAllocator
|
|
|
|
@@ -64,19 +85,26 @@ public:
|
|
static const bool kNeedFree = true;
|
|
void* Malloc(size_t size) {
|
|
if (size) // behavior of malloc(0) is implementation defined.
|
|
- return std::malloc(size);
|
|
+ return RAPIDJSON_MALLOC(size);
|
|
else
|
|
return NULL; // standardize to returning NULL.
|
|
}
|
|
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
|
(void)originalSize;
|
|
if (newSize == 0) {
|
|
- std::free(originalPtr);
|
|
+ RAPIDJSON_FREE(originalPtr);
|
|
return NULL;
|
|
}
|
|
- return std::realloc(originalPtr, newSize);
|
|
+ return RAPIDJSON_REALLOC(originalPtr, newSize);
|
|
+ }
|
|
+ static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); }
|
|
+
|
|
+ bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
|
|
+ return true;
|
|
+ }
|
|
+ bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
|
|
+ return false;
|
|
}
|
|
- static void Free(void *ptr) { std::free(ptr); }
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -100,16 +128,64 @@ public:
|
|
*/
|
|
template <typename BaseAllocator = CrtAllocator>
|
|
class MemoryPoolAllocator {
|
|
+ //! Chunk header for perpending to each chunk.
|
|
+ /*! Chunks are stored as a singly linked list.
|
|
+ */
|
|
+ struct ChunkHeader {
|
|
+ size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
|
|
+ size_t size; //!< Current size of allocated memory in bytes.
|
|
+ ChunkHeader *next; //!< Next chunk in the linked list.
|
|
+ };
|
|
+
|
|
+ struct SharedData {
|
|
+ ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
|
|
+ BaseAllocator* ownBaseAllocator; //!< base allocator created by this object.
|
|
+ size_t refcount;
|
|
+ bool ownBuffer;
|
|
+ };
|
|
+
|
|
+ static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData));
|
|
+ static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader));
|
|
+
|
|
+ static inline ChunkHeader *GetChunkHead(SharedData *shared)
|
|
+ {
|
|
+ return reinterpret_cast<ChunkHeader*>(reinterpret_cast<uint8_t*>(shared) + SIZEOF_SHARED_DATA);
|
|
+ }
|
|
+ static inline uint8_t *GetChunkBuffer(SharedData *shared)
|
|
+ {
|
|
+ return reinterpret_cast<uint8_t*>(shared->chunkHead) + SIZEOF_CHUNK_HEADER;
|
|
+ }
|
|
+
|
|
+ static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
|
|
+
|
|
public:
|
|
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
|
|
+ static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy
|
|
|
|
//! Constructor with chunkSize.
|
|
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
|
\param baseAllocator The allocator for allocating memory chunks.
|
|
*/
|
|
+ explicit
|
|
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
|
- chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
|
+ chunk_capacity_(chunkSize),
|
|
+ baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()),
|
|
+ shared_(static_cast<SharedData*>(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0))
|
|
{
|
|
+ RAPIDJSON_ASSERT(baseAllocator_ != 0);
|
|
+ RAPIDJSON_ASSERT(shared_ != 0);
|
|
+ if (baseAllocator) {
|
|
+ shared_->ownBaseAllocator = 0;
|
|
+ }
|
|
+ else {
|
|
+ shared_->ownBaseAllocator = baseAllocator_;
|
|
+ }
|
|
+ shared_->chunkHead = GetChunkHead(shared_);
|
|
+ shared_->chunkHead->capacity = 0;
|
|
+ shared_->chunkHead->size = 0;
|
|
+ shared_->chunkHead->next = 0;
|
|
+ shared_->ownBuffer = true;
|
|
+ shared_->refcount = 1;
|
|
}
|
|
|
|
//! Constructor with user-supplied buffer.
|
|
@@ -123,41 +199,101 @@ public:
|
|
\param baseAllocator The allocator for allocating memory chunks.
|
|
*/
|
|
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
|
- chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
|
+ chunk_capacity_(chunkSize),
|
|
+ baseAllocator_(baseAllocator),
|
|
+ shared_(static_cast<SharedData*>(AlignBuffer(buffer, size)))
|
|
{
|
|
- RAPIDJSON_ASSERT(buffer != 0);
|
|
- RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
|
|
- chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
|
|
- chunkHead_->capacity = size - sizeof(ChunkHeader);
|
|
- chunkHead_->size = 0;
|
|
- chunkHead_->next = 0;
|
|
+ RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER);
|
|
+ shared_->chunkHead = GetChunkHead(shared_);
|
|
+ shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER;
|
|
+ shared_->chunkHead->size = 0;
|
|
+ shared_->chunkHead->next = 0;
|
|
+ shared_->ownBaseAllocator = 0;
|
|
+ shared_->ownBuffer = false;
|
|
+ shared_->refcount = 1;
|
|
}
|
|
|
|
+ MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT :
|
|
+ chunk_capacity_(rhs.chunk_capacity_),
|
|
+ baseAllocator_(rhs.baseAllocator_),
|
|
+ shared_(rhs.shared_)
|
|
+ {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
|
+ ++shared_->refcount;
|
|
+ }
|
|
+ MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
|
|
+ ++rhs.shared_->refcount;
|
|
+ this->~MemoryPoolAllocator();
|
|
+ baseAllocator_ = rhs.baseAllocator_;
|
|
+ chunk_capacity_ = rhs.chunk_capacity_;
|
|
+ shared_ = rhs.shared_;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
|
+ MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT :
|
|
+ chunk_capacity_(rhs.chunk_capacity_),
|
|
+ baseAllocator_(rhs.baseAllocator_),
|
|
+ shared_(rhs.shared_)
|
|
+ {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
|
|
+ rhs.shared_ = 0;
|
|
+ }
|
|
+ MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
|
|
+ this->~MemoryPoolAllocator();
|
|
+ baseAllocator_ = rhs.baseAllocator_;
|
|
+ chunk_capacity_ = rhs.chunk_capacity_;
|
|
+ shared_ = rhs.shared_;
|
|
+ rhs.shared_ = 0;
|
|
+ return *this;
|
|
+ }
|
|
+#endif
|
|
+
|
|
//! Destructor.
|
|
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
|
|
*/
|
|
- ~MemoryPoolAllocator() {
|
|
+ ~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT {
|
|
+ if (!shared_) {
|
|
+ // do nothing if moved
|
|
+ return;
|
|
+ }
|
|
+ if (shared_->refcount > 1) {
|
|
+ --shared_->refcount;
|
|
+ return;
|
|
+ }
|
|
Clear();
|
|
- RAPIDJSON_DELETE(ownBaseAllocator_);
|
|
+ BaseAllocator *a = shared_->ownBaseAllocator;
|
|
+ if (shared_->ownBuffer) {
|
|
+ baseAllocator_->Free(shared_);
|
|
+ }
|
|
+ RAPIDJSON_DELETE(a);
|
|
}
|
|
|
|
- //! Deallocates all memory chunks, excluding the user-supplied buffer.
|
|
- void Clear() {
|
|
- while (chunkHead_ && chunkHead_ != userBuffer_) {
|
|
- ChunkHeader* next = chunkHead_->next;
|
|
- baseAllocator_->Free(chunkHead_);
|
|
- chunkHead_ = next;
|
|
+ //! Deallocates all memory chunks, excluding the first/user one.
|
|
+ void Clear() RAPIDJSON_NOEXCEPT {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
|
+ for (;;) {
|
|
+ ChunkHeader* c = shared_->chunkHead;
|
|
+ if (!c->next) {
|
|
+ break;
|
|
+ }
|
|
+ shared_->chunkHead = c->next;
|
|
+ baseAllocator_->Free(c);
|
|
}
|
|
- if (chunkHead_ && chunkHead_ == userBuffer_)
|
|
- chunkHead_->size = 0; // Clear user buffer
|
|
+ shared_->chunkHead->size = 0;
|
|
}
|
|
|
|
//! Computes the total capacity of allocated memory chunks.
|
|
/*! \return total capacity in bytes.
|
|
*/
|
|
- size_t Capacity() const {
|
|
+ size_t Capacity() const RAPIDJSON_NOEXCEPT {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
|
size_t capacity = 0;
|
|
- for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
|
+ for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
|
|
capacity += c->capacity;
|
|
return capacity;
|
|
}
|
|
@@ -165,25 +301,35 @@ public:
|
|
//! Computes the memory blocks allocated.
|
|
/*! \return total used bytes.
|
|
*/
|
|
- size_t Size() const {
|
|
+ size_t Size() const RAPIDJSON_NOEXCEPT {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
|
size_t size = 0;
|
|
- for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
|
+ for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
|
|
size += c->size;
|
|
return size;
|
|
}
|
|
|
|
+ //! Whether the allocator is shared.
|
|
+ /*! \return true or false.
|
|
+ */
|
|
+ bool Shared() const RAPIDJSON_NOEXCEPT {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
|
+ return shared_->refcount > 1;
|
|
+ }
|
|
+
|
|
//! Allocates a memory block. (concept Allocator)
|
|
void* Malloc(size_t size) {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
|
if (!size)
|
|
return NULL;
|
|
|
|
size = RAPIDJSON_ALIGN(size);
|
|
- if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
|
|
+ if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity))
|
|
if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
|
|
return NULL;
|
|
|
|
- void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
|
|
- chunkHead_->size += size;
|
|
+ void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size;
|
|
+ shared_->chunkHead->size += size;
|
|
return buffer;
|
|
}
|
|
|
|
@@ -192,6 +338,7 @@ public:
|
|
if (originalPtr == 0)
|
|
return Malloc(newSize);
|
|
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
|
if (newSize == 0)
|
|
return NULL;
|
|
|
|
@@ -203,10 +350,10 @@ public:
|
|
return originalPtr;
|
|
|
|
// Simply expand it if it is the last allocation and there is sufficient space
|
|
- if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
|
|
+ if (originalPtr == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) {
|
|
size_t increment = static_cast<size_t>(newSize - originalSize);
|
|
- if (chunkHead_->size + increment <= chunkHead_->capacity) {
|
|
- chunkHead_->size += increment;
|
|
+ if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) {
|
|
+ shared_->chunkHead->size += increment;
|
|
return originalPtr;
|
|
}
|
|
}
|
|
@@ -222,50 +369,325 @@ public:
|
|
}
|
|
|
|
//! Frees a memory block (concept Allocator)
|
|
- static void Free(void *ptr) { (void)ptr; } // Do nothing
|
|
+ static void Free(void *ptr) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing
|
|
|
|
-private:
|
|
- //! Copy constructor is not permitted.
|
|
- MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
|
|
- //! Copy assignment operator is not permitted.
|
|
- MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
|
|
+ //! Compare (equality) with another MemoryPoolAllocator
|
|
+ bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
|
|
+ return shared_ == rhs.shared_;
|
|
+ }
|
|
+ //! Compare (inequality) with another MemoryPoolAllocator
|
|
+ bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
|
|
+ return !operator==(rhs);
|
|
+ }
|
|
|
|
+private:
|
|
//! Creates a new chunk.
|
|
/*! \param capacity Capacity of the chunk in bytes.
|
|
\return true if success.
|
|
*/
|
|
bool AddChunk(size_t capacity) {
|
|
if (!baseAllocator_)
|
|
- ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
|
|
- if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
|
|
+ shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
|
|
+ if (ChunkHeader* chunk = static_cast<ChunkHeader*>(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) {
|
|
chunk->capacity = capacity;
|
|
chunk->size = 0;
|
|
- chunk->next = chunkHead_;
|
|
- chunkHead_ = chunk;
|
|
+ chunk->next = shared_->chunkHead;
|
|
+ shared_->chunkHead = chunk;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
- static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
|
|
-
|
|
- //! Chunk header for perpending to each chunk.
|
|
- /*! Chunks are stored as a singly linked list.
|
|
- */
|
|
- struct ChunkHeader {
|
|
- size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
|
|
- size_t size; //!< Current size of allocated memory in bytes.
|
|
- ChunkHeader *next; //!< Next chunk in the linked list.
|
|
- };
|
|
+ static inline void* AlignBuffer(void* buf, size_t &size)
|
|
+ {
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(buf != 0);
|
|
+ const uintptr_t mask = sizeof(void*) - 1;
|
|
+ const uintptr_t ubuf = reinterpret_cast<uintptr_t>(buf);
|
|
+ if (RAPIDJSON_UNLIKELY(ubuf & mask)) {
|
|
+ const uintptr_t abuf = (ubuf + mask) & ~mask;
|
|
+ RAPIDJSON_ASSERT(size >= abuf - ubuf);
|
|
+ buf = reinterpret_cast<void*>(abuf);
|
|
+ size -= abuf - ubuf;
|
|
+ }
|
|
+ return buf;
|
|
+ }
|
|
|
|
- ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
|
|
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
|
|
- void *userBuffer_; //!< User supplied buffer.
|
|
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
|
|
- BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
|
|
+ SharedData *shared_; //!< The shared data of the allocator
|
|
+};
|
|
+
|
|
+namespace internal {
|
|
+ template<typename, typename = void>
|
|
+ struct IsRefCounted :
|
|
+ public FalseType
|
|
+ { };
|
|
+ template<typename T>
|
|
+ struct IsRefCounted<T, typename internal::EnableIfCond<T::kRefCounted>::Type> :
|
|
+ public TrueType
|
|
+ { };
|
|
+}
|
|
+
|
|
+template<typename T, typename A>
|
|
+inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n)
|
|
+{
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(old_n <= std::numeric_limits<size_t>::max() / sizeof(T) && new_n <= std::numeric_limits<size_t>::max() / sizeof(T));
|
|
+ return static_cast<T*>(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T)));
|
|
+}
|
|
+
|
|
+template<typename T, typename A>
|
|
+inline T *Malloc(A& a, size_t n = 1)
|
|
+{
|
|
+ return Realloc<T, A>(a, NULL, 0, n);
|
|
+}
|
|
+
|
|
+template<typename T, typename A>
|
|
+inline void Free(A& a, T *p, size_t n = 1)
|
|
+{
|
|
+ static_cast<void>(Realloc<T, A>(a, p, n, 0));
|
|
+}
|
|
+
|
|
+#ifdef __GNUC__
|
|
+RAPIDJSON_DIAG_PUSH
|
|
+RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited
|
|
+#endif
|
|
+
|
|
+template <typename T, typename BaseAllocator = CrtAllocator>
|
|
+class StdAllocator :
|
|
+ public std::allocator<T>
|
|
+{
|
|
+ typedef std::allocator<T> allocator_type;
|
|
+#if RAPIDJSON_HAS_CXX11
|
|
+ typedef std::allocator_traits<allocator_type> traits_type;
|
|
+#else
|
|
+ typedef allocator_type traits_type;
|
|
+#endif
|
|
+
|
|
+public:
|
|
+ typedef BaseAllocator BaseAllocatorType;
|
|
+
|
|
+ StdAllocator() RAPIDJSON_NOEXCEPT :
|
|
+ allocator_type(),
|
|
+ baseAllocator_()
|
|
+ { }
|
|
+
|
|
+ StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
|
|
+ allocator_type(rhs),
|
|
+ baseAllocator_(rhs.baseAllocator_)
|
|
+ { }
|
|
+
|
|
+ template<typename U>
|
|
+ StdAllocator(const StdAllocator<U, BaseAllocator>& rhs) RAPIDJSON_NOEXCEPT :
|
|
+ allocator_type(rhs),
|
|
+ baseAllocator_(rhs.baseAllocator_)
|
|
+ { }
|
|
+
|
|
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
|
+ StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT :
|
|
+ allocator_type(std::move(rhs)),
|
|
+ baseAllocator_(std::move(rhs.baseAllocator_))
|
|
+ { }
|
|
+#endif
|
|
+#if RAPIDJSON_HAS_CXX11
|
|
+ using propagate_on_container_move_assignment = std::true_type;
|
|
+ using propagate_on_container_swap = std::true_type;
|
|
+#endif
|
|
+
|
|
+ /* implicit */
|
|
+ StdAllocator(const BaseAllocator& allocator) RAPIDJSON_NOEXCEPT :
|
|
+ allocator_type(),
|
|
+ baseAllocator_(allocator)
|
|
+ { }
|
|
+
|
|
+ ~StdAllocator() RAPIDJSON_NOEXCEPT
|
|
+ { }
|
|
+
|
|
+ template<typename U>
|
|
+ struct rebind {
|
|
+ typedef StdAllocator<U, BaseAllocator> other;
|
|
+ };
|
|
+
|
|
+ typedef typename traits_type::size_type size_type;
|
|
+ typedef typename traits_type::difference_type difference_type;
|
|
+
|
|
+ typedef typename traits_type::value_type value_type;
|
|
+ typedef typename traits_type::pointer pointer;
|
|
+ typedef typename traits_type::const_pointer const_pointer;
|
|
+
|
|
+#if RAPIDJSON_HAS_CXX11
|
|
+
|
|
+ typedef typename std::add_lvalue_reference<value_type>::type &reference;
|
|
+ typedef typename std::add_lvalue_reference<typename std::add_const<value_type>::type>::type &const_reference;
|
|
+
|
|
+ pointer address(reference r) const RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ return std::addressof(r);
|
|
+ }
|
|
+ const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ return std::addressof(r);
|
|
+ }
|
|
+
|
|
+ size_type max_size() const RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ return traits_type::max_size(*this);
|
|
+ }
|
|
+
|
|
+ template <typename ...Args>
|
|
+ void construct(pointer p, Args&&... args)
|
|
+ {
|
|
+ traits_type::construct(*this, p, std::forward<Args>(args)...);
|
|
+ }
|
|
+ void destroy(pointer p)
|
|
+ {
|
|
+ traits_type::destroy(*this, p);
|
|
+ }
|
|
+
|
|
+#else // !RAPIDJSON_HAS_CXX11
|
|
+
|
|
+ typedef typename allocator_type::reference reference;
|
|
+ typedef typename allocator_type::const_reference const_reference;
|
|
+
|
|
+ pointer address(reference r) const RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ return allocator_type::address(r);
|
|
+ }
|
|
+ const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ return allocator_type::address(r);
|
|
+ }
|
|
+
|
|
+ size_type max_size() const RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ return allocator_type::max_size();
|
|
+ }
|
|
+
|
|
+ void construct(pointer p, const_reference r)
|
|
+ {
|
|
+ allocator_type::construct(p, r);
|
|
+ }
|
|
+ void destroy(pointer p)
|
|
+ {
|
|
+ allocator_type::destroy(p);
|
|
+ }
|
|
+
|
|
+#endif // !RAPIDJSON_HAS_CXX11
|
|
+
|
|
+ template <typename U>
|
|
+ U* allocate(size_type n = 1, const void* = 0)
|
|
+ {
|
|
+ return RAPIDJSON_NAMESPACE::Malloc<U>(baseAllocator_, n);
|
|
+ }
|
|
+ template <typename U>
|
|
+ void deallocate(U* p, size_type n = 1)
|
|
+ {
|
|
+ RAPIDJSON_NAMESPACE::Free<U>(baseAllocator_, p, n);
|
|
+ }
|
|
+
|
|
+ pointer allocate(size_type n = 1, const void* = 0)
|
|
+ {
|
|
+ return allocate<value_type>(n);
|
|
+ }
|
|
+ void deallocate(pointer p, size_type n = 1)
|
|
+ {
|
|
+ deallocate<value_type>(p, n);
|
|
+ }
|
|
+
|
|
+#if RAPIDJSON_HAS_CXX11
|
|
+ using is_always_equal = std::is_empty<BaseAllocator>;
|
|
+#endif
|
|
+
|
|
+ template<typename U>
|
|
+ bool operator==(const StdAllocator<U, BaseAllocator>& rhs) const RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ return baseAllocator_ == rhs.baseAllocator_;
|
|
+ }
|
|
+ template<typename U>
|
|
+ bool operator!=(const StdAllocator<U, BaseAllocator>& rhs) const RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ return !operator==(rhs);
|
|
+ }
|
|
+
|
|
+ //! rapidjson Allocator concept
|
|
+ static const bool kNeedFree = BaseAllocator::kNeedFree;
|
|
+ static const bool kRefCounted = internal::IsRefCounted<BaseAllocator>::Value;
|
|
+ void* Malloc(size_t size)
|
|
+ {
|
|
+ return baseAllocator_.Malloc(size);
|
|
+ }
|
|
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize)
|
|
+ {
|
|
+ return baseAllocator_.Realloc(originalPtr, originalSize, newSize);
|
|
+ }
|
|
+ static void Free(void *ptr) RAPIDJSON_NOEXCEPT
|
|
+ {
|
|
+ BaseAllocator::Free(ptr);
|
|
+ }
|
|
+
|
|
+private:
|
|
+ template <typename, typename>
|
|
+ friend class StdAllocator; // access to StdAllocator<!T>.*
|
|
+
|
|
+ BaseAllocator baseAllocator_;
|
|
};
|
|
|
|
+#if !RAPIDJSON_HAS_CXX17 // std::allocator<void> deprecated in C++17
|
|
+template <typename BaseAllocator>
|
|
+class StdAllocator<void, BaseAllocator> :
|
|
+ public std::allocator<void>
|
|
+{
|
|
+ typedef std::allocator<void> allocator_type;
|
|
+
|
|
+public:
|
|
+ typedef BaseAllocator BaseAllocatorType;
|
|
+
|
|
+ StdAllocator() RAPIDJSON_NOEXCEPT :
|
|
+ allocator_type(),
|
|
+ baseAllocator_()
|
|
+ { }
|
|
+
|
|
+ StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
|
|
+ allocator_type(rhs),
|
|
+ baseAllocator_(rhs.baseAllocator_)
|
|
+ { }
|
|
+
|
|
+ template<typename U>
|
|
+ StdAllocator(const StdAllocator<U, BaseAllocator>& rhs) RAPIDJSON_NOEXCEPT :
|
|
+ allocator_type(rhs),
|
|
+ baseAllocator_(rhs.baseAllocator_)
|
|
+ { }
|
|
+
|
|
+ /* implicit */
|
|
+ StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT :
|
|
+ allocator_type(),
|
|
+ baseAllocator_(baseAllocator)
|
|
+ { }
|
|
+
|
|
+ ~StdAllocator() RAPIDJSON_NOEXCEPT
|
|
+ { }
|
|
+
|
|
+ template<typename U>
|
|
+ struct rebind {
|
|
+ typedef StdAllocator<U, BaseAllocator> other;
|
|
+ };
|
|
+
|
|
+ typedef typename allocator_type::value_type value_type;
|
|
+
|
|
+private:
|
|
+ template <typename, typename>
|
|
+ friend class StdAllocator; // access to StdAllocator<!T>.*
|
|
+
|
|
+ BaseAllocator baseAllocator_;
|
|
+};
|
|
+#endif
|
|
+
|
|
+#ifdef __GNUC__
|
|
+RAPIDJSON_DIAG_POP
|
|
+#endif
|
|
+
|
|
RAPIDJSON_NAMESPACE_END
|
|
|
|
#endif // RAPIDJSON_ENCODINGS_H_
|
|
diff --git a/src/common/thirdparty/rapidjson/cursorstreamwrapper.h b/src/common/thirdparty/rapidjson/cursorstreamwrapper.h
|
|
new file mode 100644
|
|
index 000000000..fd6513db1
|
|
--- /dev/null
|
|
+++ b/src/common/thirdparty/rapidjson/cursorstreamwrapper.h
|
|
@@ -0,0 +1,78 @@
|
|
+// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
+//
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
+//
|
|
+// Licensed under the MIT License (the "License"); you may not use this file except
|
|
+// in compliance with the License. You may obtain a copy of the License at
|
|
+//
|
|
+// http://opensource.org/licenses/MIT
|
|
+//
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+// specific language governing permissions and limitations under the License.
|
|
+
|
|
+#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_
|
|
+#define RAPIDJSON_CURSORSTREAMWRAPPER_H_
|
|
+
|
|
+#include "stream.h"
|
|
+
|
|
+#if defined(__GNUC__)
|
|
+RAPIDJSON_DIAG_PUSH
|
|
+RAPIDJSON_DIAG_OFF(effc++)
|
|
+#endif
|
|
+
|
|
+#if defined(_MSC_VER) && _MSC_VER <= 1800
|
|
+RAPIDJSON_DIAG_PUSH
|
|
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
|
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
|
+#endif
|
|
+
|
|
+RAPIDJSON_NAMESPACE_BEGIN
|
|
+
|
|
+
|
|
+//! Cursor stream wrapper for counting line and column number if error exists.
|
|
+/*!
|
|
+ \tparam InputStream Any stream that implements Stream Concept
|
|
+*/
|
|
+template <typename InputStream, typename Encoding = UTF8<> >
|
|
+class CursorStreamWrapper : public GenericStreamWrapper<InputStream, Encoding> {
|
|
+public:
|
|
+ typedef typename Encoding::Ch Ch;
|
|
+
|
|
+ CursorStreamWrapper(InputStream& is):
|
|
+ GenericStreamWrapper<InputStream, Encoding>(is), line_(1), col_(0) {}
|
|
+
|
|
+ // counting line and column number
|
|
+ Ch Take() {
|
|
+ Ch ch = this->is_.Take();
|
|
+ if(ch == '\n') {
|
|
+ line_ ++;
|
|
+ col_ = 0;
|
|
+ } else {
|
|
+ col_ ++;
|
|
+ }
|
|
+ return ch;
|
|
+ }
|
|
+
|
|
+ //! Get the error line number, if error exists.
|
|
+ size_t GetLine() const { return line_; }
|
|
+ //! Get the error column number, if error exists.
|
|
+ size_t GetColumn() const { return col_; }
|
|
+
|
|
+private:
|
|
+ size_t line_; //!< Current Line
|
|
+ size_t col_; //!< Current Column
|
|
+};
|
|
+
|
|
+#if defined(_MSC_VER) && _MSC_VER <= 1800
|
|
+RAPIDJSON_DIAG_POP
|
|
+#endif
|
|
+
|
|
+#if defined(__GNUC__)
|
|
+RAPIDJSON_DIAG_POP
|
|
+#endif
|
|
+
|
|
+RAPIDJSON_NAMESPACE_END
|
|
+
|
|
+#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_
|
|
diff --git a/src/common/thirdparty/rapidjson/document.h b/src/common/thirdparty/rapidjson/document.h
|
|
index 9cab4e83d..2cd9a70a6 100644
|
|
--- a/src/common/thirdparty/rapidjson/document.h
|
|
+++ b/src/common/thirdparty/rapidjson/document.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -24,33 +24,39 @@
|
|
#include "encodedstream.h"
|
|
#include <new> // placement new
|
|
#include <limits>
|
|
-
|
|
-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
|
|
-RAPIDJSON_DIAG_OFF(4996) // deprecation of std::iterator
|
|
+#ifdef __cpp_lib_three_way_comparison
|
|
+#include <compare>
|
|
#endif
|
|
|
|
+RAPIDJSON_DIAG_PUSH
|
|
#ifdef __clang__
|
|
RAPIDJSON_DIAG_OFF(padded)
|
|
RAPIDJSON_DIAG_OFF(switch-enum)
|
|
RAPIDJSON_DIAG_OFF(c++98-compat)
|
|
+#elif defined(_MSC_VER)
|
|
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
|
+RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data
|
|
#endif
|
|
|
|
#ifdef __GNUC__
|
|
RAPIDJSON_DIAG_OFF(effc++)
|
|
-#if __GNUC__ >= 6
|
|
-RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions
|
|
-#endif
|
|
#endif // __GNUC__
|
|
|
|
+#ifdef GetObject
|
|
+// see https://github.com/Tencent/rapidjson/issues/1448
|
|
+// a former included windows.h might have defined a macro called GetObject, which affects
|
|
+// GetObject defined here. This ensures the macro does not get applied
|
|
+#pragma push_macro("GetObject")
|
|
+#define RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED
|
|
+#undef GetObject
|
|
+#endif
|
|
+
|
|
#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
|
|
-#include <iterator> // std::iterator, std::random_access_iterator_tag
|
|
+#include <iterator> // std::random_access_iterator_tag
|
|
#endif
|
|
|
|
-#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
|
-#include <utility> // std::move
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+#include <map> // std::multimap
|
|
#endif
|
|
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
@@ -62,6 +68,48 @@ class GenericValue;
|
|
template <typename Encoding, typename Allocator, typename StackAllocator>
|
|
class GenericDocument;
|
|
|
|
+/*! \def RAPIDJSON_DEFAULT_ALLOCATOR
|
|
+ \ingroup RAPIDJSON_CONFIG
|
|
+ \brief Allows to choose default allocator.
|
|
+
|
|
+ User can define this to use CrtAllocator or MemoryPoolAllocator.
|
|
+*/
|
|
+#ifndef RAPIDJSON_DEFAULT_ALLOCATOR
|
|
+#define RAPIDJSON_DEFAULT_ALLOCATOR ::RAPIDJSON_NAMESPACE::MemoryPoolAllocator<::RAPIDJSON_NAMESPACE::CrtAllocator>
|
|
+#endif
|
|
+
|
|
+/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR
|
|
+ \ingroup RAPIDJSON_CONFIG
|
|
+ \brief Allows to choose default stack allocator for Document.
|
|
+
|
|
+ User can define this to use CrtAllocator or MemoryPoolAllocator.
|
|
+*/
|
|
+#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR
|
|
+#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR ::RAPIDJSON_NAMESPACE::CrtAllocator
|
|
+#endif
|
|
+
|
|
+/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY
|
|
+ \ingroup RAPIDJSON_CONFIG
|
|
+ \brief User defined kDefaultObjectCapacity value.
|
|
+
|
|
+ User can define this as any natural number.
|
|
+*/
|
|
+#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY
|
|
+// number of objects that rapidjson::Value allocates memory for by default
|
|
+#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16
|
|
+#endif
|
|
+
|
|
+/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY
|
|
+ \ingroup RAPIDJSON_CONFIG
|
|
+ \brief User defined kDefaultArrayCapacity value.
|
|
+
|
|
+ User can define this as any natural number.
|
|
+*/
|
|
+#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY
|
|
+// number of array elements that rapidjson::Value allocates memory for by default
|
|
+#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16
|
|
+#endif
|
|
+
|
|
//! Name-value pair in a JSON object value.
|
|
/*!
|
|
This class was internal to GenericValue. It used to be a inner struct.
|
|
@@ -69,9 +117,45 @@ class GenericDocument;
|
|
https://code.google.com/p/rapidjson/issues/detail?id=64
|
|
*/
|
|
template <typename Encoding, typename Allocator>
|
|
-struct GenericMember {
|
|
+class GenericMember {
|
|
+public:
|
|
GenericValue<Encoding, Allocator> name; //!< name of member (must be a string)
|
|
GenericValue<Encoding, Allocator> value; //!< value of member.
|
|
+
|
|
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
|
+ //! Move constructor in C++11
|
|
+ GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT
|
|
+ : name(std::move(rhs.name)),
|
|
+ value(std::move(rhs.value))
|
|
+ {
|
|
+ }
|
|
+
|
|
+ //! Move assignment in C++11
|
|
+ GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT {
|
|
+ return *this = static_cast<GenericMember&>(rhs);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ //! Assignment with move semantics.
|
|
+ /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment.
|
|
+ */
|
|
+ GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT {
|
|
+ if (RAPIDJSON_LIKELY(this != &rhs)) {
|
|
+ name = rhs.name;
|
|
+ value = rhs.value;
|
|
+ }
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ // swap() for std::sort() and other potential use in STL.
|
|
+ friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT {
|
|
+ a.name.Swap(b.name);
|
|
+ a.value.Swap(b.value);
|
|
+ }
|
|
+
|
|
+private:
|
|
+ //! Copy constructor is not permitted.
|
|
+ GenericMember(const GenericMember& rhs);
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -99,16 +183,13 @@ struct GenericMember {
|
|
\see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator
|
|
*/
|
|
template <bool Const, typename Encoding, typename Allocator>
|
|
-class GenericMemberIterator
|
|
- : public std::iterator<std::random_access_iterator_tag
|
|
- , typename internal::MaybeAddConst<Const,GenericMember<Encoding,Allocator> >::Type> {
|
|
+class GenericMemberIterator {
|
|
|
|
friend class GenericValue<Encoding,Allocator>;
|
|
template <bool, typename, typename> friend class GenericMemberIterator;
|
|
|
|
typedef GenericMember<Encoding,Allocator> PlainType;
|
|
typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
|
|
- typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType;
|
|
|
|
public:
|
|
//! Iterator type itself
|
|
@@ -118,12 +199,21 @@ public:
|
|
//! Non-constant iterator type
|
|
typedef GenericMemberIterator<false,Encoding,Allocator> NonConstIterator;
|
|
|
|
+ /** \name std::iterator_traits support */
|
|
+ //@{
|
|
+ typedef ValueType value_type;
|
|
+ typedef ValueType * pointer;
|
|
+ typedef ValueType & reference;
|
|
+ typedef std::ptrdiff_t difference_type;
|
|
+ typedef std::random_access_iterator_tag iterator_category;
|
|
+ //@}
|
|
+
|
|
//! Pointer to (const) GenericMember
|
|
- typedef typename BaseType::pointer Pointer;
|
|
+ typedef pointer Pointer;
|
|
//! Reference to (const) GenericMember
|
|
- typedef typename BaseType::reference Reference;
|
|
+ typedef reference Reference;
|
|
//! Signed integer type (e.g. \c ptrdiff_t)
|
|
- typedef typename BaseType::difference_type DifferenceType;
|
|
+ typedef difference_type DifferenceType;
|
|
|
|
//! Default constructor (singular value)
|
|
/*! Creates an iterator pointing to no element.
|
|
@@ -169,12 +259,16 @@ public:
|
|
|
|
//! @name relations
|
|
//@{
|
|
- bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; }
|
|
- bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; }
|
|
- bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; }
|
|
- bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; }
|
|
- bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; }
|
|
- bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; }
|
|
+ template <bool Const_> bool operator==(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ == that.ptr_; }
|
|
+ template <bool Const_> bool operator!=(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ != that.ptr_; }
|
|
+ template <bool Const_> bool operator<=(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ <= that.ptr_; }
|
|
+ template <bool Const_> bool operator>=(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ >= that.ptr_; }
|
|
+ template <bool Const_> bool operator< (const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ < that.ptr_; }
|
|
+ template <bool Const_> bool operator> (const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ > that.ptr_; }
|
|
+
|
|
+#ifdef __cpp_lib_three_way_comparison
|
|
+ template <bool Const_> std::strong_ordering operator<=>(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ <=> that.ptr_; }
|
|
+#endif
|
|
//@}
|
|
|
|
//! @name dereference
|
|
@@ -199,17 +293,19 @@ private:
|
|
// class-based member iterator implementation disabled, use plain pointers
|
|
|
|
template <bool Const, typename Encoding, typename Allocator>
|
|
-struct GenericMemberIterator;
|
|
+class GenericMemberIterator;
|
|
|
|
//! non-const GenericMemberIterator
|
|
template <typename Encoding, typename Allocator>
|
|
-struct GenericMemberIterator<false,Encoding,Allocator> {
|
|
+class GenericMemberIterator<false,Encoding,Allocator> {
|
|
+public:
|
|
//! use plain pointer as iterator type
|
|
typedef GenericMember<Encoding,Allocator>* Iterator;
|
|
};
|
|
//! const GenericMemberIterator
|
|
template <typename Encoding, typename Allocator>
|
|
-struct GenericMemberIterator<true,Encoding,Allocator> {
|
|
+class GenericMemberIterator<true,Encoding,Allocator> {
|
|
+public:
|
|
//! use plain const pointer as iterator type
|
|
typedef const GenericMember<Encoding,Allocator>* Iterator;
|
|
};
|
|
@@ -301,7 +397,7 @@ struct GenericStringRef {
|
|
*/
|
|
#endif
|
|
explicit GenericStringRef(const CharType* str)
|
|
- : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); }
|
|
+ : s(str), length(NotNullStrLen(str)) {}
|
|
|
|
//! Create constant string reference from pointer and length
|
|
#ifndef __clang__ // -Wdocumentation
|
|
@@ -313,7 +409,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) {}
|
|
|
|
@@ -324,6 +420,14 @@ 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[];
|
|
+
|
|
//! Disallow construction from non-const array
|
|
template<SizeType N>
|
|
GenericStringRef(CharType (&str)[N]) /* = delete */;
|
|
@@ -331,6 +435,9 @@ private:
|
|
GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */;
|
|
};
|
|
|
|
+template<typename CharType>
|
|
+const CharType GenericStringRef<CharType>::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
|
|
@@ -345,7 +452,7 @@ private:
|
|
*/
|
|
template<typename CharType>
|
|
inline GenericStringRef<CharType> StringRef(const CharType* str) {
|
|
- return GenericStringRef<CharType>(str, internal::StrLen(str));
|
|
+ return GenericStringRef<CharType>(str);
|
|
}
|
|
|
|
//! Mark a character pointer as constant string
|
|
@@ -435,6 +542,26 @@ struct TypeHelper<ValueType, unsigned> {
|
|
static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); }
|
|
};
|
|
|
|
+#ifdef _MSC_VER
|
|
+RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int));
|
|
+template<typename ValueType>
|
|
+struct TypeHelper<ValueType, long> {
|
|
+ static bool Is(const ValueType& v) { return v.IsInt(); }
|
|
+ static long Get(const ValueType& v) { return v.GetInt(); }
|
|
+ static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); }
|
|
+ static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); }
|
|
+};
|
|
+
|
|
+RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned));
|
|
+template<typename ValueType>
|
|
+struct TypeHelper<ValueType, unsigned long> {
|
|
+ static bool Is(const ValueType& v) { return v.IsUint(); }
|
|
+ static unsigned long Get(const ValueType& v) { return v.GetUint(); }
|
|
+ static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); }
|
|
+ static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); }
|
|
+};
|
|
+#endif
|
|
+
|
|
template<typename ValueType>
|
|
struct TypeHelper<ValueType, int64_t> {
|
|
static bool Is(const ValueType& v) { return v.IsInt64(); }
|
|
@@ -508,7 +635,7 @@ struct TypeHelper<ValueType, typename ValueType::Object> {
|
|
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<typename ValueType>
|
|
@@ -537,7 +664,7 @@ template <bool, typename> class GenericObject;
|
|
\tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document)
|
|
\tparam Allocator Allocator type for allocating memory of object, array and string.
|
|
*/
|
|
-template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
|
|
+template <typename Encoding, typename Allocator = RAPIDJSON_DEFAULT_ALLOCATOR >
|
|
class GenericValue {
|
|
public:
|
|
//! Name-value pair in an object.
|
|
@@ -591,11 +718,11 @@ public:
|
|
\note Default content for number is zero.
|
|
*/
|
|
explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() {
|
|
- static const uint16_t defaultFlags[7] = {
|
|
+ static const uint16_t defaultFlags[] = {
|
|
kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag,
|
|
kNumberAnyFlag
|
|
};
|
|
- RAPIDJSON_ASSERT(type <= kNumberType);
|
|
+ RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType);
|
|
data_.f.flags = defaultFlags[type];
|
|
|
|
// Use ShortString to store empty string.
|
|
@@ -608,10 +735,40 @@ public:
|
|
\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< typename SourceAllocator >
|
|
- GenericValue(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator & allocator);
|
|
+ template <typename SourceAllocator>
|
|
+ GenericValue(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator, bool copyConstStrings = false) {
|
|
+ switch (rhs.GetType()) {
|
|
+ case kObjectType:
|
|
+ DoCopyMembers(rhs, allocator, copyConstStrings);
|
|
+ break;
|
|
+ case kArrayType: {
|
|
+ SizeType count = rhs.data_.a.size;
|
|
+ GenericValue* le = reinterpret_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
|
|
+ const GenericValue<Encoding,SourceAllocator>* re = rhs.GetElementsPointer();
|
|
+ for (SizeType i = 0; i < count; i++)
|
|
+ new (&le[i]) GenericValue(re[i], allocator, copyConstStrings);
|
|
+ data_.f.flags = kArrayFlag;
|
|
+ data_.a.size = data_.a.capacity = count;
|
|
+ SetElementsPointer(le);
|
|
+ }
|
|
+ break;
|
|
+ case kStringType:
|
|
+ if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) {
|
|
+ data_.f.flags = rhs.data_.f.flags;
|
|
+ data_ = *reinterpret_cast<const Data*>(&rhs.data_);
|
|
+ }
|
|
+ else
|
|
+ SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator);
|
|
+ break;
|
|
+ default:
|
|
+ data_.f.flags = rhs.data_.f.flags;
|
|
+ data_ = *reinterpret_cast<const Data*>(&rhs.data_);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
|
|
//! Constructor for boolean value.
|
|
/*! \param b Boolean value
|
|
@@ -673,6 +830,9 @@ public:
|
|
//! Constructor for double value.
|
|
explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; }
|
|
|
|
+ //! Constructor for float value.
|
|
+ explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast<double>(f); data_.f.flags = kNumberDoubleFlag; }
|
|
+
|
|
//! Constructor for constant string (i.e. do not make a copy of string)
|
|
GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); }
|
|
|
|
@@ -718,25 +878,30 @@ public:
|
|
/*! Need to destruct elements of array, members of object, or copy-string.
|
|
*/
|
|
~GenericValue() {
|
|
- if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
|
|
+ // With RAPIDJSON_USE_MEMBERSMAP, the maps need to be destroyed to release
|
|
+ // their Allocator if it's refcounted (e.g. MemoryPoolAllocator).
|
|
+ if (Allocator::kNeedFree || (RAPIDJSON_USE_MEMBERSMAP+0 &&
|
|
+ internal::IsRefCounted<Allocator>::Value)) {
|
|
switch(data_.f.flags) {
|
|
case kArrayFlag:
|
|
{
|
|
GenericValue* e = GetElementsPointer();
|
|
for (GenericValue* v = e; v != e + data_.a.size; ++v)
|
|
v->~GenericValue();
|
|
- Allocator::Free(e);
|
|
+ if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
|
|
+ Allocator::Free(e);
|
|
+ }
|
|
}
|
|
break;
|
|
|
|
case kObjectFlag:
|
|
- for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
|
|
- m->~Member();
|
|
- Allocator::Free(GetMembersPointer());
|
|
+ DoFreeMembers();
|
|
break;
|
|
|
|
case kCopyStringFlag:
|
|
- Allocator::Free(const_cast<Ch*>(GetStringPointer()));
|
|
+ if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
|
|
+ Allocator::Free(const_cast<Ch*>(GetStringPointer()));
|
|
+ }
|
|
break;
|
|
|
|
default:
|
|
@@ -754,9 +919,15 @@ public:
|
|
/*! \param rhs Source of the assignment. It will become a null value after assignment.
|
|
*/
|
|
GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
|
|
- RAPIDJSON_ASSERT(this != &rhs);
|
|
- this->~GenericValue();
|
|
- RawAssign(rhs);
|
|
+ if (RAPIDJSON_LIKELY(this != &rhs)) {
|
|
+ // Can't destroy "this" before assigning "rhs", otherwise "rhs"
|
|
+ // could be used after free if it's an sub-Value of "this",
|
|
+ // hence the temporary danse.
|
|
+ GenericValue temp;
|
|
+ temp.RawAssign(rhs);
|
|
+ this->~GenericValue();
|
|
+ RawAssign(temp);
|
|
+ }
|
|
return *this;
|
|
}
|
|
|
|
@@ -801,12 +972,13 @@ public:
|
|
\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 <typename SourceAllocator>
|
|
- GenericValue& CopyFrom(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator& allocator) {
|
|
+ GenericValue& CopyFrom(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator& allocator, bool copyConstStrings = false) {
|
|
RAPIDJSON_ASSERT(static_cast<void*>(this) != static_cast<void const*>(&rhs));
|
|
this->~GenericValue();
|
|
- new (this) GenericValue(rhs, allocator);
|
|
+ new (this) GenericValue(rhs, allocator, copyConstStrings);
|
|
return *this;
|
|
}
|
|
|
|
@@ -847,7 +1019,7 @@ public:
|
|
//! Equal-to operator
|
|
/*!
|
|
\note If an object contains duplicated named member, comparing equality with any object is always \c false.
|
|
- \note Linear time complexity (number of all values in the subtree and total lengths of all strings).
|
|
+ \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings).
|
|
*/
|
|
template <typename SourceAllocator>
|
|
bool operator==(const GenericValue<Encoding, SourceAllocator>& rhs) const {
|
|
@@ -906,6 +1078,7 @@ public:
|
|
*/
|
|
template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>,internal::IsGenericValue<T> >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); }
|
|
|
|
+#ifndef __cpp_impl_three_way_comparison
|
|
//! Not-equal-to operator
|
|
/*! \return !(*this == rhs)
|
|
*/
|
|
@@ -930,6 +1103,7 @@ public:
|
|
*/
|
|
template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); }
|
|
//@}
|
|
+#endif
|
|
|
|
//!@name Type
|
|
//@{
|
|
@@ -956,14 +1130,14 @@ public:
|
|
uint64_t u = GetUint64();
|
|
volatile double d = static_cast<double>(u);
|
|
return (d >= 0.0)
|
|
- && (d < static_cast<double>(std::numeric_limits<uint64_t>::max()))
|
|
+ && (d < static_cast<double>((std::numeric_limits<uint64_t>::max)()))
|
|
&& (u == static_cast<uint64_t>(d));
|
|
}
|
|
if (IsInt64()) {
|
|
int64_t i = GetInt64();
|
|
volatile double d = static_cast<double>(i);
|
|
- return (d >= static_cast<double>(std::numeric_limits<int64_t>::min()))
|
|
- && (d < static_cast<double>(std::numeric_limits<int64_t>::max()))
|
|
+ return (d >= static_cast<double>((std::numeric_limits<int64_t>::min)()))
|
|
+ && (d < static_cast<double>((std::numeric_limits<int64_t>::max)()))
|
|
&& (i == static_cast<int64_t>(d));
|
|
}
|
|
return true; // double, int, uint are always lossless
|
|
@@ -980,8 +1154,8 @@ public:
|
|
bool IsLosslessFloat() const {
|
|
if (!IsNumber()) return false;
|
|
double a = GetDouble();
|
|
- if (a < static_cast<double>(-std::numeric_limits<float>::max())
|
|
- || a > static_cast<double>(std::numeric_limits<float>::max()))
|
|
+ if (a < static_cast<double>(-(std::numeric_limits<float>::max)())
|
|
+ || a > static_cast<double>((std::numeric_limits<float>::max)()))
|
|
return false;
|
|
double b = static_cast<double>(static_cast<float>(a));
|
|
return a >= b && a <= b; // Prevent -Wfloat-equal
|
|
@@ -1016,6 +1190,9 @@ public:
|
|
//! Get the number of members in the object.
|
|
SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; }
|
|
|
|
+ //! Get the capacity of object.
|
|
+ SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; }
|
|
+
|
|
//! Check whether the object is empty.
|
|
bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; }
|
|
|
|
@@ -1053,13 +1230,28 @@ public:
|
|
else {
|
|
RAPIDJSON_ASSERT(false); // see above note
|
|
|
|
- // This will generate -Wexit-time-destructors in clang
|
|
- // static GenericValue NullValue;
|
|
- // return NullValue;
|
|
-
|
|
- // Use static buffer and placement-new to prevent destruction
|
|
- static char buffer[sizeof(GenericValue)];
|
|
+#if RAPIDJSON_HAS_CXX11
|
|
+ // Use thread-local storage to prevent races between threads.
|
|
+ // Use static buffer and placement-new to prevent destruction, with
|
|
+ // alignas() to ensure proper alignment.
|
|
+ alignas(GenericValue) thread_local static char buffer[sizeof(GenericValue)];
|
|
+ return *new (buffer) GenericValue();
|
|
+#elif defined(_MSC_VER) && _MSC_VER < 1900
|
|
+ // There's no way to solve both thread locality and proper alignment
|
|
+ // simultaneously.
|
|
+ __declspec(thread) static char buffer[sizeof(GenericValue)];
|
|
return *new (buffer) GenericValue();
|
|
+#elif defined(__GNUC__) || defined(__clang__)
|
|
+ // This will generate -Wexit-time-destructors in clang, but that's
|
|
+ // better than having under-alignment.
|
|
+ __thread static GenericValue buffer;
|
|
+ return buffer;
|
|
+#else
|
|
+ // Don't know what compiler this is, so don't know how to ensure
|
|
+ // thread-locality.
|
|
+ static GenericValue buffer;
|
|
+ return buffer;
|
|
+#endif
|
|
}
|
|
}
|
|
template <typename SourceAllocator>
|
|
@@ -1084,6 +1276,18 @@ public:
|
|
/*! \pre IsObject() == true */
|
|
MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); }
|
|
|
|
+ //! Request the object to have enough capacity to store members.
|
|
+ /*! \param newCapacity The capacity that the object at least need to have.
|
|
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
|
|
+ \return The value itself for fluent API.
|
|
+ \note Linear time complexity.
|
|
+ */
|
|
+ GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) {
|
|
+ RAPIDJSON_ASSERT(IsObject());
|
|
+ DoReserveMembers(newCapacity, allocator);
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
//! Check whether a member exists in the object.
|
|
/*!
|
|
\param name Member name to be searched.
|
|
@@ -1154,11 +1358,7 @@ public:
|
|
MemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) {
|
|
RAPIDJSON_ASSERT(IsObject());
|
|
RAPIDJSON_ASSERT(name.IsString());
|
|
- MemberIterator member = MemberBegin();
|
|
- for ( ; member != MemberEnd(); ++member)
|
|
- if (name.StringEqual(member->name))
|
|
- break;
|
|
- return member;
|
|
+ return DoFindMember(name);
|
|
}
|
|
template <typename SourceAllocator> ConstMemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
|
|
|
|
@@ -1187,23 +1387,7 @@ public:
|
|
GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
|
|
RAPIDJSON_ASSERT(IsObject());
|
|
RAPIDJSON_ASSERT(name.IsString());
|
|
-
|
|
- ObjectData& o = data_.o;
|
|
- if (o.size >= o.capacity) {
|
|
- if (o.capacity == 0) {
|
|
- o.capacity = kDefaultObjectCapacity;
|
|
- SetMembersPointer(reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member))));
|
|
- }
|
|
- else {
|
|
- SizeType oldCapacity = o.capacity;
|
|
- o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5
|
|
- SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member))));
|
|
- }
|
|
- }
|
|
- Member* members = GetMembersPointer();
|
|
- members[o.size].name.RawAssign(name);
|
|
- members[o.size].value.RawAssign(value);
|
|
- o.size++;
|
|
+ DoAddMember(name, value, allocator);
|
|
return *this;
|
|
}
|
|
|
|
@@ -1337,9 +1521,7 @@ public:
|
|
*/
|
|
void RemoveAllMembers() {
|
|
RAPIDJSON_ASSERT(IsObject());
|
|
- for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
|
|
- m->~Member();
|
|
- data_.o.size = 0;
|
|
+ DoClearMembers();
|
|
}
|
|
|
|
//! Remove a member in object by its name.
|
|
@@ -1383,14 +1565,7 @@ public:
|
|
RAPIDJSON_ASSERT(data_.o.size > 0);
|
|
RAPIDJSON_ASSERT(GetMembersPointer() != 0);
|
|
RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd());
|
|
-
|
|
- MemberIterator last(GetMembersPointer() + (data_.o.size - 1));
|
|
- if (data_.o.size > 1 && m != last)
|
|
- *m = *last; // Move the last one to this place
|
|
- else
|
|
- m->~Member(); // Only one left, just destroy
|
|
- --data_.o.size;
|
|
- return m;
|
|
+ return DoRemoveMember(m);
|
|
}
|
|
|
|
//! Remove a member from an object by iterator.
|
|
@@ -1422,13 +1597,7 @@ public:
|
|
RAPIDJSON_ASSERT(first >= MemberBegin());
|
|
RAPIDJSON_ASSERT(first <= last);
|
|
RAPIDJSON_ASSERT(last <= MemberEnd());
|
|
-
|
|
- MemberIterator pos = MemberBegin() + (first - MemberBegin());
|
|
- for (MemberIterator itr = pos; itr != last; ++itr)
|
|
- itr->~Member();
|
|
- std::memmove(&*pos, &*last, static_cast<size_t>(MemberEnd() - last) * sizeof(Member));
|
|
- data_.o.size -= static_cast<SizeType>(last - first);
|
|
- return pos;
|
|
+ return DoEraseMembers(first, last);
|
|
}
|
|
|
|
//! Erase a member in object by its name.
|
|
@@ -1457,7 +1626,9 @@ public:
|
|
}
|
|
|
|
Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
|
|
+ Object GetObj() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
|
|
ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); }
|
|
+ ConstObject GetObj() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); }
|
|
|
|
//@}
|
|
|
|
@@ -1629,8 +1800,8 @@ public:
|
|
RAPIDJSON_ASSERT(last <= End());
|
|
ValueIterator pos = Begin() + (first - Begin());
|
|
for (ValueIterator itr = pos; itr != last; ++itr)
|
|
- itr->~GenericValue();
|
|
- std::memmove(pos, last, static_cast<size_t>(End() - last) * sizeof(GenericValue));
|
|
+ itr->~GenericValue();
|
|
+ std::memmove(static_cast<void*>(pos), last, static_cast<size_t>(End() - last) * sizeof(GenericValue));
|
|
data_.a.size -= static_cast<SizeType>(last - first);
|
|
return pos;
|
|
}
|
|
@@ -1672,19 +1843,19 @@ public:
|
|
GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; }
|
|
GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; }
|
|
GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; }
|
|
- GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; }
|
|
+ GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast<double>(f)); return *this; }
|
|
|
|
//@}
|
|
|
|
//!@name String
|
|
//@{
|
|
|
|
- const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); }
|
|
+ const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return DataString(data_); }
|
|
|
|
//! Get the length of string.
|
|
/*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength().
|
|
*/
|
|
- SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); }
|
|
+ SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return DataStringLength(data_); }
|
|
|
|
//! Set this value as a string without copying source string.
|
|
/*! This version has better performance with supplied length, and also support string containing null character.
|
|
@@ -1711,7 +1882,7 @@ public:
|
|
\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.
|
|
@@ -1719,7 +1890,15 @@ public:
|
|
\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.
|
|
@@ -1729,7 +1908,7 @@ public:
|
|
\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<Ch>& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); }
|
|
+ GenericValue& SetString(const std::basic_string<Ch>& s, Allocator& allocator) { return SetString(StringRef(s), allocator); }
|
|
#endif
|
|
|
|
//@}
|
|
@@ -1787,7 +1966,7 @@ public:
|
|
case kArrayType:
|
|
if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
|
|
return false;
|
|
- for (const GenericValue* v = Begin(); v != End(); ++v)
|
|
+ for (ConstValueIterator v = Begin(); v != End(); ++v)
|
|
if (RAPIDJSON_UNLIKELY(!v->Accept(handler)))
|
|
return false;
|
|
return handler.EndArray(data_.a.size);
|
|
@@ -1823,25 +2002,26 @@ private:
|
|
|
|
// Initial flags of different types.
|
|
kNullFlag = kNullType,
|
|
- kTrueFlag = kTrueType | kBoolFlag,
|
|
- kFalseFlag = kFalseType | kBoolFlag,
|
|
- kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag,
|
|
- kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag,
|
|
- kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag,
|
|
- kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag,
|
|
- kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag,
|
|
- kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag,
|
|
- kConstStringFlag = kStringType | kStringFlag,
|
|
- kCopyStringFlag = kStringType | kStringFlag | kCopyFlag,
|
|
- kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag,
|
|
+ // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types.
|
|
+ kTrueFlag = static_cast<int>(kTrueType) | static_cast<int>(kBoolFlag),
|
|
+ kFalseFlag = static_cast<int>(kFalseType) | static_cast<int>(kBoolFlag),
|
|
+ kNumberIntFlag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kIntFlag | kInt64Flag),
|
|
+ kNumberUintFlag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag),
|
|
+ kNumberInt64Flag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kInt64Flag),
|
|
+ kNumberUint64Flag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kUint64Flag),
|
|
+ kNumberDoubleFlag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kDoubleFlag),
|
|
+ kNumberAnyFlag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag),
|
|
+ kConstStringFlag = static_cast<int>(kStringType) | static_cast<int>(kStringFlag),
|
|
+ kCopyStringFlag = static_cast<int>(kStringType) | static_cast<int>(kStringFlag | kCopyFlag),
|
|
+ kShortStringFlag = static_cast<int>(kStringType) | static_cast<int>(kStringFlag | kCopyFlag | kInlineStrFlag),
|
|
kObjectFlag = kObjectType,
|
|
kArrayFlag = kArrayType,
|
|
|
|
kTypeMask = 0x07
|
|
};
|
|
|
|
- static const SizeType kDefaultArrayCapacity = 16;
|
|
- static const SizeType kDefaultObjectCapacity = 16;
|
|
+ static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY;
|
|
+ static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY;
|
|
|
|
struct Flag {
|
|
#if RAPIDJSON_48BITPOINTER_OPTIMIZATION
|
|
@@ -1924,6 +2104,13 @@ private:
|
|
Flag f;
|
|
}; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION
|
|
|
|
+ static RAPIDJSON_FORCEINLINE const Ch* DataString(const Data& data) {
|
|
+ return (data.f.flags & kInlineStrFlag) ? data.ss.str : RAPIDJSON_GETPOINTER(Ch, data.s.str);
|
|
+ }
|
|
+ static RAPIDJSON_FORCEINLINE SizeType DataStringLength(const Data& data) {
|
|
+ return (data.f.flags & kInlineStrFlag) ? data.ss.GetLength() : data.s.length;
|
|
+ }
|
|
+
|
|
RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); }
|
|
RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); }
|
|
RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); }
|
|
@@ -1931,13 +2118,293 @@ private:
|
|
RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); }
|
|
RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); }
|
|
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+
|
|
+ struct MapTraits {
|
|
+ struct Less {
|
|
+ bool operator()(const Data& s1, const Data& s2) const {
|
|
+ SizeType n1 = DataStringLength(s1), n2 = DataStringLength(s2);
|
|
+ int cmp = std::memcmp(DataString(s1), DataString(s2), sizeof(Ch) * (n1 < n2 ? n1 : n2));
|
|
+ return cmp < 0 || (cmp == 0 && n1 < n2);
|
|
+ }
|
|
+ };
|
|
+ typedef std::pair<const Data, SizeType> Pair;
|
|
+ typedef std::multimap<Data, SizeType, Less, StdAllocator<Pair, Allocator> > Map;
|
|
+ typedef typename Map::iterator Iterator;
|
|
+ };
|
|
+ typedef typename MapTraits::Map Map;
|
|
+ typedef typename MapTraits::Less MapLess;
|
|
+ typedef typename MapTraits::Pair MapPair;
|
|
+ typedef typename MapTraits::Iterator MapIterator;
|
|
+
|
|
+ //
|
|
+ // Layout of the members' map/array, re(al)located according to the needed capacity:
|
|
+ //
|
|
+ // {Map*}<>{capacity}<>{Member[capacity]}<>{MapIterator[capacity]}
|
|
+ //
|
|
+ // (where <> stands for the RAPIDJSON_ALIGN-ment, if needed)
|
|
+ //
|
|
+
|
|
+ static RAPIDJSON_FORCEINLINE size_t GetMapLayoutSize(SizeType capacity) {
|
|
+ return RAPIDJSON_ALIGN(sizeof(Map*)) +
|
|
+ RAPIDJSON_ALIGN(sizeof(SizeType)) +
|
|
+ RAPIDJSON_ALIGN(capacity * sizeof(Member)) +
|
|
+ capacity * sizeof(MapIterator);
|
|
+ }
|
|
+
|
|
+ static RAPIDJSON_FORCEINLINE SizeType &GetMapCapacity(Map* &map) {
|
|
+ return *reinterpret_cast<SizeType*>(reinterpret_cast<uintptr_t>(&map) +
|
|
+ RAPIDJSON_ALIGN(sizeof(Map*)));
|
|
+ }
|
|
+
|
|
+ static RAPIDJSON_FORCEINLINE Member* GetMapMembers(Map* &map) {
|
|
+ return reinterpret_cast<Member*>(reinterpret_cast<uintptr_t>(&map) +
|
|
+ RAPIDJSON_ALIGN(sizeof(Map*)) +
|
|
+ RAPIDJSON_ALIGN(sizeof(SizeType)));
|
|
+ }
|
|
+
|
|
+ static RAPIDJSON_FORCEINLINE MapIterator* GetMapIterators(Map* &map) {
|
|
+ return reinterpret_cast<MapIterator*>(reinterpret_cast<uintptr_t>(&map) +
|
|
+ RAPIDJSON_ALIGN(sizeof(Map*)) +
|
|
+ RAPIDJSON_ALIGN(sizeof(SizeType)) +
|
|
+ RAPIDJSON_ALIGN(GetMapCapacity(map) * sizeof(Member)));
|
|
+ }
|
|
+
|
|
+ static RAPIDJSON_FORCEINLINE Map* &GetMap(Member* members) {
|
|
+ RAPIDJSON_ASSERT(members != 0);
|
|
+ return *reinterpret_cast<Map**>(reinterpret_cast<uintptr_t>(members) -
|
|
+ RAPIDJSON_ALIGN(sizeof(SizeType)) -
|
|
+ RAPIDJSON_ALIGN(sizeof(Map*)));
|
|
+ }
|
|
+
|
|
+ // Some compilers' debug mechanisms want all iterators to be destroyed, for their accounting..
|
|
+ RAPIDJSON_FORCEINLINE MapIterator DropMapIterator(MapIterator& rhs) {
|
|
+#if RAPIDJSON_HAS_CXX11
|
|
+ MapIterator ret = std::move(rhs);
|
|
+#else
|
|
+ MapIterator ret = rhs;
|
|
+#endif
|
|
+ rhs.~MapIterator();
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ Map* &DoReallocMap(Map** oldMap, SizeType newCapacity, Allocator& allocator) {
|
|
+ Map **newMap = static_cast<Map**>(allocator.Malloc(GetMapLayoutSize(newCapacity)));
|
|
+ GetMapCapacity(*newMap) = newCapacity;
|
|
+ if (!oldMap) {
|
|
+ *newMap = new (allocator.Malloc(sizeof(Map))) Map(MapLess(), allocator);
|
|
+ }
|
|
+ else {
|
|
+ *newMap = *oldMap;
|
|
+ size_t count = (*oldMap)->size();
|
|
+ std::memcpy(static_cast<void*>(GetMapMembers(*newMap)),
|
|
+ static_cast<void*>(GetMapMembers(*oldMap)),
|
|
+ count * sizeof(Member));
|
|
+ MapIterator *oldIt = GetMapIterators(*oldMap),
|
|
+ *newIt = GetMapIterators(*newMap);
|
|
+ while (count--) {
|
|
+ new (&newIt[count]) MapIterator(DropMapIterator(oldIt[count]));
|
|
+ }
|
|
+ Allocator::Free(oldMap);
|
|
+ }
|
|
+ return *newMap;
|
|
+ }
|
|
+
|
|
+ RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) {
|
|
+ return GetMapMembers(DoReallocMap(0, capacity, allocator));
|
|
+ }
|
|
+
|
|
+ void DoReserveMembers(SizeType newCapacity, Allocator& allocator) {
|
|
+ ObjectData& o = data_.o;
|
|
+ if (newCapacity > o.capacity) {
|
|
+ Member* oldMembers = GetMembersPointer();
|
|
+ Map **oldMap = oldMembers ? &GetMap(oldMembers) : 0,
|
|
+ *&newMap = DoReallocMap(oldMap, newCapacity, allocator);
|
|
+ RAPIDJSON_SETPOINTER(Member, o.members, GetMapMembers(newMap));
|
|
+ o.capacity = newCapacity;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ template <typename SourceAllocator>
|
|
+ MemberIterator DoFindMember(const GenericValue<Encoding, SourceAllocator>& name) {
|
|
+ if (Member* members = GetMembersPointer()) {
|
|
+ Map* &map = GetMap(members);
|
|
+ MapIterator mit = map->find(reinterpret_cast<const Data&>(name.data_));
|
|
+ if (mit != map->end()) {
|
|
+ return MemberIterator(&members[mit->second]);
|
|
+ }
|
|
+ }
|
|
+ return MemberEnd();
|
|
+ }
|
|
+
|
|
+ void DoClearMembers() {
|
|
+ if (Member* members = GetMembersPointer()) {
|
|
+ Map* &map = GetMap(members);
|
|
+ MapIterator* mit = GetMapIterators(map);
|
|
+ for (SizeType i = 0; i < data_.o.size; i++) {
|
|
+ map->erase(DropMapIterator(mit[i]));
|
|
+ members[i].~Member();
|
|
+ }
|
|
+ data_.o.size = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void DoFreeMembers() {
|
|
+ if (Member* members = GetMembersPointer()) {
|
|
+ GetMap(members)->~Map();
|
|
+ for (SizeType i = 0; i < data_.o.size; i++) {
|
|
+ members[i].~Member();
|
|
+ }
|
|
+ if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
|
|
+ Map** map = &GetMap(members);
|
|
+ Allocator::Free(*map);
|
|
+ Allocator::Free(map);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+#else // !RAPIDJSON_USE_MEMBERSMAP
|
|
+
|
|
+ RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) {
|
|
+ return Malloc<Member>(allocator, capacity);
|
|
+ }
|
|
+
|
|
+ void DoReserveMembers(SizeType newCapacity, Allocator& allocator) {
|
|
+ ObjectData& o = data_.o;
|
|
+ if (newCapacity > o.capacity) {
|
|
+ Member* newMembers = Realloc<Member>(allocator, GetMembersPointer(), o.capacity, newCapacity);
|
|
+ RAPIDJSON_SETPOINTER(Member, o.members, newMembers);
|
|
+ o.capacity = newCapacity;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ template <typename SourceAllocator>
|
|
+ MemberIterator DoFindMember(const GenericValue<Encoding, SourceAllocator>& name) {
|
|
+ MemberIterator member = MemberBegin();
|
|
+ for ( ; member != MemberEnd(); ++member)
|
|
+ if (name.StringEqual(member->name))
|
|
+ break;
|
|
+ return member;
|
|
+ }
|
|
+
|
|
+ void DoClearMembers() {
|
|
+ for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
|
|
+ m->~Member();
|
|
+ data_.o.size = 0;
|
|
+ }
|
|
+
|
|
+ void DoFreeMembers() {
|
|
+ for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
|
|
+ m->~Member();
|
|
+ Allocator::Free(GetMembersPointer());
|
|
+ }
|
|
+
|
|
+#endif // !RAPIDJSON_USE_MEMBERSMAP
|
|
+
|
|
+ void DoAddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
|
|
+ ObjectData& o = data_.o;
|
|
+ if (o.size >= o.capacity)
|
|
+ DoReserveMembers(o.capacity ? (o.capacity + (o.capacity + 1) / 2) : kDefaultObjectCapacity, allocator);
|
|
+ Member* members = GetMembersPointer();
|
|
+ Member* m = members + o.size;
|
|
+ m->name.RawAssign(name);
|
|
+ m->value.RawAssign(value);
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+ Map* &map = GetMap(members);
|
|
+ MapIterator* mit = GetMapIterators(map);
|
|
+ new (&mit[o.size]) MapIterator(map->insert(MapPair(m->name.data_, o.size)));
|
|
+#endif
|
|
+ ++o.size;
|
|
+ }
|
|
+
|
|
+ MemberIterator DoRemoveMember(MemberIterator m) {
|
|
+ ObjectData& o = data_.o;
|
|
+ Member* members = GetMembersPointer();
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+ Map* &map = GetMap(members);
|
|
+ MapIterator* mit = GetMapIterators(map);
|
|
+ SizeType mpos = static_cast<SizeType>(&*m - members);
|
|
+ map->erase(DropMapIterator(mit[mpos]));
|
|
+#endif
|
|
+ MemberIterator last(members + (o.size - 1));
|
|
+ if (o.size > 1 && m != last) {
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+ new (&mit[mpos]) MapIterator(DropMapIterator(mit[&*last - members]));
|
|
+ mit[mpos]->second = mpos;
|
|
+#endif
|
|
+ *m = *last; // Move the last one to this place
|
|
+ }
|
|
+ else {
|
|
+ m->~Member(); // Only one left, just destroy
|
|
+ }
|
|
+ --o.size;
|
|
+ return m;
|
|
+ }
|
|
+
|
|
+ MemberIterator DoEraseMembers(ConstMemberIterator first, ConstMemberIterator last) {
|
|
+ ObjectData& o = data_.o;
|
|
+ MemberIterator beg = MemberBegin(),
|
|
+ pos = beg + (first - beg),
|
|
+ end = MemberEnd();
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+ Map* &map = GetMap(GetMembersPointer());
|
|
+ MapIterator* mit = GetMapIterators(map);
|
|
+#endif
|
|
+ for (MemberIterator itr = pos; itr != last; ++itr) {
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+ map->erase(DropMapIterator(mit[itr - beg]));
|
|
+#endif
|
|
+ itr->~Member();
|
|
+ }
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+ if (first != last) {
|
|
+ // Move remaining members/iterators
|
|
+ MemberIterator next = pos + (last - first);
|
|
+ for (MemberIterator itr = pos; next != end; ++itr, ++next) {
|
|
+ std::memcpy(static_cast<void*>(&*itr), &*next, sizeof(Member));
|
|
+ SizeType mpos = static_cast<SizeType>(itr - beg);
|
|
+ new (&mit[mpos]) MapIterator(DropMapIterator(mit[next - beg]));
|
|
+ mit[mpos]->second = mpos;
|
|
+ }
|
|
+ }
|
|
+#else
|
|
+ std::memmove(static_cast<void*>(&*pos), &*last,
|
|
+ static_cast<size_t>(end - last) * sizeof(Member));
|
|
+#endif
|
|
+ o.size -= static_cast<SizeType>(last - first);
|
|
+ return pos;
|
|
+ }
|
|
+
|
|
+ template <typename SourceAllocator>
|
|
+ void DoCopyMembers(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator, bool copyConstStrings) {
|
|
+ RAPIDJSON_ASSERT(rhs.GetType() == kObjectType);
|
|
+
|
|
+ data_.f.flags = kObjectFlag;
|
|
+ SizeType count = rhs.data_.o.size;
|
|
+ Member* lm = DoAllocMembers(count, allocator);
|
|
+ const typename GenericValue<Encoding,SourceAllocator>::Member* rm = rhs.GetMembersPointer();
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+ Map* &map = GetMap(lm);
|
|
+ MapIterator* mit = GetMapIterators(map);
|
|
+#endif
|
|
+ for (SizeType i = 0; i < count; i++) {
|
|
+ new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings);
|
|
+ new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings);
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+ new (&mit[i]) MapIterator(map->insert(MapPair(lm[i].name.data_, i)));
|
|
+#endif
|
|
+ }
|
|
+ data_.o.size = data_.o.capacity = count;
|
|
+ SetMembersPointer(lm);
|
|
+ }
|
|
+
|
|
// Initialize this value as array with initial data, without calling destructor.
|
|
void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
|
|
data_.f.flags = kArrayFlag;
|
|
if (count) {
|
|
GenericValue* e = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
|
|
SetElementsPointer(e);
|
|
- std::memcpy((void*)e, (void*)values, count * sizeof(GenericValue));
|
|
+ std::memcpy(static_cast<void*>(e), values, count * sizeof(GenericValue));
|
|
}
|
|
else
|
|
SetElementsPointer(0);
|
|
@@ -1948,9 +2415,16 @@ private:
|
|
void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
|
|
data_.f.flags = kObjectFlag;
|
|
if (count) {
|
|
- Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
|
|
+ Member* m = DoAllocMembers(count, allocator);
|
|
SetMembersPointer(m);
|
|
- std::memcpy((void*)m, (void*)members, count * sizeof(Member));
|
|
+ std::memcpy(static_cast<void*>(m), members, count * sizeof(Member));
|
|
+#if RAPIDJSON_USE_MEMBERSMAP
|
|
+ Map* &map = GetMap(m);
|
|
+ MapIterator* mit = GetMapIterators(map);
|
|
+ for (SizeType i = 0; i < count; i++) {
|
|
+ new (&mit[i]) MapIterator(map->insert(MapPair(m[i].name.data_, i)));
|
|
+ }
|
|
+#endif
|
|
}
|
|
else
|
|
SetMembersPointer(0);
|
|
@@ -2021,12 +2495,13 @@ typedef GenericValue<UTF8<> > Value;
|
|
\tparam StackAllocator Allocator for allocating memory for stack during parsing.
|
|
\warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue.
|
|
*/
|
|
-template <typename Encoding, typename Allocator = MemoryPoolAllocator<>, typename StackAllocator = CrtAllocator>
|
|
+template <typename Encoding, typename Allocator = RAPIDJSON_DEFAULT_ALLOCATOR, typename StackAllocator = RAPIDJSON_DEFAULT_STACK_ALLOCATOR >
|
|
class GenericDocument : public GenericValue<Encoding, Allocator> {
|
|
public:
|
|
typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding.
|
|
typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of the document.
|
|
typedef Allocator AllocatorType; //!< Allocator type from template parameter.
|
|
+ typedef StackAllocator StackAllocatorType; //!< StackAllocator type from template parameter.
|
|
|
|
//! Constructor
|
|
/*! Creates an empty document of specified type.
|
|
@@ -2039,7 +2514,7 @@ public:
|
|
GenericValue<Encoding, Allocator>(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_()
|
|
{
|
|
if (!allocator_)
|
|
- ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
|
|
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
|
}
|
|
|
|
//! Constructor
|
|
@@ -2052,7 +2527,7 @@ public:
|
|
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
|
|
@@ -2071,6 +2546,13 @@ public:
|
|
#endif
|
|
|
|
~GenericDocument() {
|
|
+ // Clear the ::ValueType before ownAllocator is destroyed, ~ValueType()
|
|
+ // runs last and may access its elements or members which would be freed
|
|
+ // with an allocator like MemoryPoolAllocator (CrtAllocator does not
|
|
+ // free its data when destroyed, but MemoryPoolAllocator does).
|
|
+ if (ownAllocator_) {
|
|
+ ValueType::SetNull();
|
|
+ }
|
|
Destroy();
|
|
}
|
|
|
|
@@ -2113,6 +2595,10 @@ public:
|
|
return *this;
|
|
}
|
|
|
|
+ // Allow Swap with ValueType.
|
|
+ // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names.
|
|
+ using ValueType::Swap;
|
|
+
|
|
//! free-standing swap function helper
|
|
/*!
|
|
Helper function to enable support for common swap implementation pattern based on \c std::swap:
|
|
@@ -2244,7 +2730,7 @@ public:
|
|
template <unsigned parseFlags, typename SourceEncoding>
|
|
GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) {
|
|
RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
|
|
- MemoryStream ms(static_cast<const char*>(str), length * sizeof(typename SourceEncoding::Ch));
|
|
+ MemoryStream ms(reinterpret_cast<const char*>(str), length * sizeof(typename SourceEncoding::Ch));
|
|
EncodedInputStream<SourceEncoding, MemoryStream> is(ms);
|
|
ParseStream<parseFlags, SourceEncoding>(is);
|
|
return *this;
|
|
@@ -2281,7 +2767,7 @@ public:
|
|
//!@name Handling parse errors
|
|
//!@{
|
|
|
|
- //! Whether a parse error has occured in the last parsing.
|
|
+ //! Whether a parse error has occurred in the last parsing.
|
|
bool HasParseError() const { return parseResult_.IsError(); }
|
|
|
|
//! Get the \ref ParseErrorCode of last parsing.
|
|
@@ -2402,34 +2888,6 @@ private:
|
|
//! GenericDocument with UTF8 encoding
|
|
typedef GenericDocument<UTF8<> > Document;
|
|
|
|
-// defined here due to the dependency on GenericDocument
|
|
-template <typename Encoding, typename Allocator>
|
|
-template <typename SourceAllocator>
|
|
-inline
|
|
-GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator)
|
|
-{
|
|
- switch (rhs.GetType()) {
|
|
- case kObjectType:
|
|
- case kArrayType: { // perform deep copy via SAX Handler
|
|
- GenericDocument<Encoding,Allocator> d(&allocator);
|
|
- rhs.Accept(d);
|
|
- RawAssign(*d.stack_.template Pop<GenericValue>(1));
|
|
- }
|
|
- break;
|
|
- case kStringType:
|
|
- if (rhs.data_.f.flags == kConstStringFlag) {
|
|
- data_.f.flags = rhs.data_.f.flags;
|
|
- data_ = *reinterpret_cast<const Data*>(&rhs.data_);
|
|
- } else {
|
|
- SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator);
|
|
- }
|
|
- break;
|
|
- default:
|
|
- data_.f.flags = rhs.data_.f.flags;
|
|
- data_ = *reinterpret_cast<const Data*>(&rhs.data_);
|
|
- break;
|
|
- }
|
|
-}
|
|
|
|
//! Helper class for accessing Value of array type.
|
|
/*!
|
|
@@ -2455,6 +2913,7 @@ public:
|
|
GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; }
|
|
~GenericArray() {}
|
|
|
|
+ operator ValueType&() const { return value_; }
|
|
SizeType Size() const { return value_.Size(); }
|
|
SizeType Capacity() const { return value_.Capacity(); }
|
|
bool Empty() const { return value_.Empty(); }
|
|
@@ -2510,7 +2969,9 @@ public:
|
|
GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; }
|
|
~GenericObject() {}
|
|
|
|
+ operator ValueType&() const { return value_; }
|
|
SizeType MemberCount() const { return value_.MemberCount(); }
|
|
+ SizeType MemberCapacity() const { return value_.MemberCapacity(); }
|
|
bool ObjectEmpty() const { return value_.ObjectEmpty(); }
|
|
template <typename T> ValueType& operator[](T* name) const { return value_[name]; }
|
|
template <typename SourceAllocator> ValueType& operator[](const GenericValue<EncodingType, SourceAllocator>& name) const { return value_[name]; }
|
|
@@ -2519,6 +2980,7 @@ public:
|
|
#endif
|
|
MemberIterator MemberBegin() const { return value_.MemberBegin(); }
|
|
MemberIterator MemberEnd() const { return value_.MemberEnd(); }
|
|
+ GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; }
|
|
bool HasMember(const Ch* name) const { return value_.HasMember(name); }
|
|
#if RAPIDJSON_HAS_STDSTRING
|
|
bool HasMember(const std::basic_string<Ch>& name) const { return value_.HasMember(name); }
|
|
@@ -2544,7 +3006,7 @@ public:
|
|
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 <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (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<Ch>& name) const { return value_.RemoveMember(name); }
|
|
@@ -2573,4 +3035,9 @@ private:
|
|
RAPIDJSON_NAMESPACE_END
|
|
RAPIDJSON_DIAG_POP
|
|
|
|
+#ifdef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED
|
|
+#pragma pop_macro("GetObject")
|
|
+#undef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED
|
|
+#endif
|
|
+
|
|
#endif // RAPIDJSON_DOCUMENT_H_
|
|
diff --git a/src/common/thirdparty/rapidjson/encodedstream.h b/src/common/thirdparty/rapidjson/encodedstream.h
|
|
index 145068386..cf046b892 100644
|
|
--- a/src/common/thirdparty/rapidjson/encodedstream.h
|
|
+++ b/src/common/thirdparty/rapidjson/encodedstream.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -200,7 +200,7 @@ private:
|
|
// 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/src/common/thirdparty/rapidjson/encodings.h b/src/common/thirdparty/rapidjson/encodings.h
|
|
index baa7c2b17..50ad18bdc 100644
|
|
--- a/src/common/thirdparty/rapidjson/encodings.h
|
|
+++ b/src/common/thirdparty/rapidjson/encodings.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -17,7 +17,7 @@
|
|
|
|
#include "rapidjson.h"
|
|
|
|
-#ifdef _MSC_VER
|
|
+#if defined(_MSC_VER) && !defined(__clang__)
|
|
RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
|
|
RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
|
@@ -144,9 +144,9 @@ struct UTF8 {
|
|
|
|
template <typename InputStream>
|
|
static bool Decode(InputStream& is, unsigned* codepoint) {
|
|
-#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
|
|
-#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
|
-#define TAIL() COPY(); TRANS(0x70)
|
|
+#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
|
|
+#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
|
+#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
|
|
typename InputStream::Ch c = is.Take();
|
|
if (!(c & 0x80)) {
|
|
*codepoint = static_cast<unsigned char>(c);
|
|
@@ -157,48 +157,48 @@ struct UTF8 {
|
|
if (type >= 32) {
|
|
*codepoint = 0;
|
|
} else {
|
|
- *codepoint = (0xFF >> type) & static_cast<unsigned char>(c);
|
|
+ *codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
|
|
}
|
|
bool result = true;
|
|
switch (type) {
|
|
- case 2: TAIL(); return result;
|
|
- case 3: TAIL(); TAIL(); return result;
|
|
- case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
|
- case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
|
- case 6: TAIL(); TAIL(); TAIL(); return result;
|
|
- case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
|
- case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
|
+ case 2: RAPIDJSON_TAIL(); return result;
|
|
+ case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
|
+ case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
|
|
+ case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
|
+ case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
|
+ case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
|
|
+ case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
|
default: return false;
|
|
}
|
|
-#undef COPY
|
|
-#undef TRANS
|
|
-#undef TAIL
|
|
+#undef RAPIDJSON_COPY
|
|
+#undef RAPIDJSON_TRANS
|
|
+#undef RAPIDJSON_TAIL
|
|
}
|
|
|
|
template <typename InputStream, typename OutputStream>
|
|
static bool Validate(InputStream& is, OutputStream& os) {
|
|
-#define COPY() os.Put(c = is.Take())
|
|
-#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
|
-#define TAIL() COPY(); TRANS(0x70)
|
|
+#define RAPIDJSON_COPY() os.Put(c = is.Take())
|
|
+#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
|
+#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
|
|
Ch c;
|
|
- COPY();
|
|
+ RAPIDJSON_COPY();
|
|
if (!(c & 0x80))
|
|
return true;
|
|
|
|
bool result = true;
|
|
switch (GetRange(static_cast<unsigned char>(c))) {
|
|
- case 2: TAIL(); return result;
|
|
- case 3: TAIL(); TAIL(); return result;
|
|
- case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
|
- case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
|
- case 6: TAIL(); TAIL(); TAIL(); return result;
|
|
- case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
|
- case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
|
+ case 2: RAPIDJSON_TAIL(); return result;
|
|
+ case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
|
+ case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
|
|
+ case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
|
+ case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
|
+ case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
|
|
+ case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
|
default: return false;
|
|
}
|
|
-#undef COPY
|
|
-#undef TRANS
|
|
-#undef TAIL
|
|
+#undef RAPIDJSON_COPY
|
|
+#undef RAPIDJSON_TRANS
|
|
+#undef RAPIDJSON_TAIL
|
|
}
|
|
|
|
static unsigned char GetRange(unsigned char c) {
|
|
@@ -283,7 +283,7 @@ struct UTF16 {
|
|
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
|
unsigned v = codepoint - 0x10000;
|
|
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
|
- os.Put((v & 0x3FF) | 0xDC00);
|
|
+ os.Put(static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
|
|
}
|
|
}
|
|
|
|
@@ -299,7 +299,7 @@ struct UTF16 {
|
|
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
|
unsigned v = codepoint - 0x10000;
|
|
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
|
- PutUnsafe(os, (v & 0x3FF) | 0xDC00);
|
|
+ PutUnsafe(os, static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
|
|
}
|
|
}
|
|
|
|
@@ -384,7 +384,7 @@ struct UTF16BE : UTF16<CharType> {
|
|
static CharType Take(InputByteStream& is) {
|
|
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
|
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
|
- c |= static_cast<uint8_t>(is.Take());
|
|
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
|
|
return static_cast<CharType>(c);
|
|
}
|
|
|
|
@@ -620,28 +620,28 @@ struct AutoUTF {
|
|
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
|
|
|
template<typename OutputStream>
|
|
- 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<typename OutputStream>
|
|
- 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 <typename InputStream>
|
|
- 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 <typename InputStream, typename OutputStream>
|
|
- 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<typename SourceEncoding, typename TargetEncoding>
|
|
struct Transcoder {
|
|
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
|
|
template<typename InputStream, typename OutputStream>
|
|
- 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<typename InputStream, typename OutputStream>
|
|
- 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<typename InputStream, typename OutputStream>
|
|
- 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,26 +690,26 @@ inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
|
|
template<typename Encoding>
|
|
struct Transcoder<Encoding, Encoding> {
|
|
template<typename InputStream, typename OutputStream>
|
|
- 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<typename InputStream, typename OutputStream>
|
|
- 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<typename InputStream, typename OutputStream>
|
|
- 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
|
|
}
|
|
};
|
|
|
|
RAPIDJSON_NAMESPACE_END
|
|
|
|
-#if defined(__GNUC__) || defined(_MSC_VER)
|
|
+#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__))
|
|
RAPIDJSON_DIAG_POP
|
|
#endif
|
|
|
|
diff --git a/src/common/thirdparty/rapidjson/error/en.h b/src/common/thirdparty/rapidjson/error/en.h
|
|
index 2db838bff..c87b04eb1 100644
|
|
--- a/src/common/thirdparty/rapidjson/error/en.h
|
|
+++ b/src/common/thirdparty/rapidjson/error/en.h
|
|
@@ -1,15 +1,15 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
-//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+//
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://opensource.org/licenses/MIT
|
|
//
|
|
-// Unless required by applicable law or agreed to in writing, software distributed
|
|
-// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
-// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
#ifndef RAPIDJSON_ERROR_EN_H_
|
|
@@ -39,13 +39,13 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro
|
|
|
|
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
|
|
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
|
|
-
|
|
+
|
|
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
|
|
-
|
|
+
|
|
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
|
|
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
|
|
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
|
|
-
|
|
+
|
|
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
|
|
|
|
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
|
|
@@ -65,6 +65,108 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro
|
|
}
|
|
}
|
|
|
|
+//! Maps error code of validation into error message.
|
|
+/*!
|
|
+ \ingroup RAPIDJSON_ERRORS
|
|
+ \param validateErrorCode Error code obtained from validator.
|
|
+ \return the error message.
|
|
+ \note User can make a copy of this function for localization.
|
|
+ Using switch-case is safer for future modification of error codes.
|
|
+*/
|
|
+inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) {
|
|
+ switch (validateErrorCode) {
|
|
+ case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred");
|
|
+ case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
|
+
|
|
+ case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'.");
|
|
+ case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'.");
|
|
+ case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'.");
|
|
+ case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'.");
|
|
+ case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'.");
|
|
+
|
|
+ case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'.");
|
|
+ case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'.");
|
|
+ case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression.");
|
|
+
|
|
+ case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'.");
|
|
+ case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'.");
|
|
+ case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true.");
|
|
+ case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema.");
|
|
+
|
|
+ case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'.");
|
|
+ case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'.");
|
|
+ case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'.");
|
|
+ case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema.");
|
|
+ case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema.");
|
|
+ case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors.");
|
|
+
|
|
+ case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values.");
|
|
+ case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'.");
|
|
+
|
|
+ case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors.");
|
|
+ case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf', indices '%matches'.");
|
|
+ case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors.");
|
|
+ case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors.");
|
|
+ case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'.");
|
|
+
|
|
+ case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing.");
|
|
+ case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading.");
|
|
+
|
|
+ default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
|
+ }
|
|
+}
|
|
+
|
|
+//! Maps error code of schema document compilation into error message.
|
|
+/*!
|
|
+ \ingroup RAPIDJSON_ERRORS
|
|
+ \param schemaErrorCode Error code obtained from compiling the schema document.
|
|
+ \return the error message.
|
|
+ \note User can make a copy of this function for localization.
|
|
+ Using switch-case is safer for future modification of error codes.
|
|
+*/
|
|
+ inline const RAPIDJSON_ERROR_CHARTYPE* GetSchemaError_En(SchemaErrorCode schemaErrorCode) {
|
|
+ switch (schemaErrorCode) {
|
|
+ case kSchemaErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
|
+
|
|
+ case kSchemaErrorStartUnknown: return RAPIDJSON_ERROR_STRING("Pointer '%value' to start of schema does not resolve to a location in the document.");
|
|
+ case kSchemaErrorRefPlainName: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' must be a JSON pointer.");
|
|
+ case kSchemaErrorRefInvalid: return RAPIDJSON_ERROR_STRING("$ref must not be an empty string.");
|
|
+ case kSchemaErrorRefPointerInvalid: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' is not a valid JSON pointer at offset '%offset'.");
|
|
+ case kSchemaErrorRefUnknown: return RAPIDJSON_ERROR_STRING("$ref '%value' does not resolve to a location in the target document.");
|
|
+ case kSchemaErrorRefCyclical: return RAPIDJSON_ERROR_STRING("$ref '%value' is cyclical.");
|
|
+ case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider.");
|
|
+ case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema.");
|
|
+ case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'.");
|
|
+ case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized.");
|
|
+ case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported.");
|
|
+ case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document.");
|
|
+ case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'.");
|
|
+
|
|
+ default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
|
+ }
|
|
+ }
|
|
+
|
|
+//! Maps error code of pointer parse into error message.
|
|
+/*!
|
|
+ \ingroup RAPIDJSON_ERRORS
|
|
+ \param pointerParseErrorCode Error code obtained from pointer parse.
|
|
+ \return the error message.
|
|
+ \note User can make a copy of this function for localization.
|
|
+ Using switch-case is safer for future modification of error codes.
|
|
+*/
|
|
+inline const RAPIDJSON_ERROR_CHARTYPE* GetPointerParseError_En(PointerParseErrorCode pointerParseErrorCode) {
|
|
+ switch (pointerParseErrorCode) {
|
|
+ case kPointerParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
|
+
|
|
+ case kPointerParseErrorTokenMustBeginWithSolidus: return RAPIDJSON_ERROR_STRING("A token must begin with a '/'.");
|
|
+ case kPointerParseErrorInvalidEscape: return RAPIDJSON_ERROR_STRING("Invalid escape.");
|
|
+ case kPointerParseErrorInvalidPercentEncoding: return RAPIDJSON_ERROR_STRING("Invalid percent encoding in URI fragment.");
|
|
+ case kPointerParseErrorCharacterMustPercentEncode: return RAPIDJSON_ERROR_STRING("A character must be percent encoded in a URI fragment.");
|
|
+
|
|
+ default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
|
+ }
|
|
+}
|
|
+
|
|
RAPIDJSON_NAMESPACE_END
|
|
|
|
#ifdef __clang__
|
|
diff --git a/src/common/thirdparty/rapidjson/error/error.h b/src/common/thirdparty/rapidjson/error/error.h
|
|
index 95cb31a72..cae345db3 100644
|
|
--- a/src/common/thirdparty/rapidjson/error/error.h
|
|
+++ b/src/common/thirdparty/rapidjson/error/error.h
|
|
@@ -1,15 +1,15 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
-//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+//
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://opensource.org/licenses/MIT
|
|
//
|
|
-// Unless required by applicable law or agreed to in writing, software distributed
|
|
-// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
-// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
#ifndef RAPIDJSON_ERROR_ERROR_H_
|
|
@@ -42,7 +42,7 @@ RAPIDJSON_DIAG_OFF(padded)
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RAPIDJSON_ERROR_STRING
|
|
|
|
-//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[].
|
|
+//! Macro for converting string literal to \ref RAPIDJSON_ERROR_CHARTYPE[].
|
|
/*! \ingroup RAPIDJSON_ERRORS
|
|
By default this conversion macro does nothing.
|
|
On Windows, user can define this macro as \c _T(x) for supporting both
|
|
@@ -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 @@ public:
|
|
//! 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 @@ public:
|
|
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.
|
|
@@ -146,6 +152,130 @@ private:
|
|
*/
|
|
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
|
|
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// ValidateErrorCode
|
|
+
|
|
+//! Error codes when validating.
|
|
+/*! \ingroup RAPIDJSON_ERRORS
|
|
+ \see GenericSchemaValidator
|
|
+*/
|
|
+enum ValidateErrorCode {
|
|
+ kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set.
|
|
+ kValidateErrorNone = 0, //!< No error.
|
|
+
|
|
+ kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value.
|
|
+ kValidateErrorMaximum, //!< Number is greater than the 'maximum' value.
|
|
+ kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value.
|
|
+ kValidateErrorMinimum, //!< Number is less than the 'minimum' value.
|
|
+ kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value.
|
|
+
|
|
+ kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value.
|
|
+ kValidateErrorMinLength, //!< String is longer than the 'maxLength' value.
|
|
+ kValidateErrorPattern, //!< String does not match the 'pattern' regular expression.
|
|
+
|
|
+ kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value.
|
|
+ kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value.
|
|
+ kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true.
|
|
+ kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema.
|
|
+
|
|
+ kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value.
|
|
+ kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value.
|
|
+ kValidateErrorRequired, //!< Object is missing one or more members required by the schema.
|
|
+ kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema.
|
|
+ kValidateErrorPatternProperties, //!< See other errors.
|
|
+ kValidateErrorDependencies, //!< Object has missing property or schema dependencies.
|
|
+
|
|
+ kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values.
|
|
+ kValidateErrorType, //!< Property has a type that is not allowed by the schema.
|
|
+
|
|
+ kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'.
|
|
+ kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'.
|
|
+ kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'.
|
|
+ kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'.
|
|
+ kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'.
|
|
+
|
|
+ kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing
|
|
+ kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading
|
|
+};
|
|
+
|
|
+//! Function pointer type of GetValidateError().
|
|
+/*! \ingroup RAPIDJSON_ERRORS
|
|
+
|
|
+ This is the prototype for \c GetValidateError_X(), where \c X is a locale.
|
|
+ User can dynamically change locale in runtime, e.g.:
|
|
+\code
|
|
+ GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever
|
|
+ const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode());
|
|
+\endcode
|
|
+*/
|
|
+typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode);
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// SchemaErrorCode
|
|
+
|
|
+//! Error codes when validating.
|
|
+/*! \ingroup RAPIDJSON_ERRORS
|
|
+ \see GenericSchemaValidator
|
|
+*/
|
|
+enum SchemaErrorCode {
|
|
+ kSchemaErrorNone = 0, //!< No error.
|
|
+
|
|
+ kSchemaErrorStartUnknown, //!< Pointer to start of schema does not resolve to a location in the document
|
|
+ kSchemaErrorRefPlainName, //!< $ref fragment must be a JSON pointer
|
|
+ kSchemaErrorRefInvalid, //!< $ref must not be an empty string
|
|
+ kSchemaErrorRefPointerInvalid, //!< $ref fragment is not a valid JSON pointer at offset
|
|
+ kSchemaErrorRefUnknown, //!< $ref does not resolve to a location in the target document
|
|
+ kSchemaErrorRefCyclical, //!< $ref is cyclical
|
|
+ kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider
|
|
+ kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema
|
|
+ kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties'
|
|
+ kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized
|
|
+ kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported
|
|
+ kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document
|
|
+ kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly'
|
|
+};
|
|
+
|
|
+//! Function pointer type of GetSchemaError().
|
|
+/*! \ingroup RAPIDJSON_ERRORS
|
|
+
|
|
+ This is the prototype for \c GetSchemaError_X(), where \c X is a locale.
|
|
+ User can dynamically change locale in runtime, e.g.:
|
|
+\code
|
|
+ GetSchemaErrorFunc GetSchemaError = GetSchemaError_En; // or whatever
|
|
+ const RAPIDJSON_ERROR_CHARTYPE* s = GetSchemaError(validator.GetInvalidSchemaCode());
|
|
+\endcode
|
|
+*/
|
|
+typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetSchemaErrorFunc)(SchemaErrorCode);
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// PointerParseErrorCode
|
|
+
|
|
+//! Error code of JSON pointer parsing.
|
|
+/*! \ingroup RAPIDJSON_ERRORS
|
|
+ \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode
|
|
+*/
|
|
+enum PointerParseErrorCode {
|
|
+ kPointerParseErrorNone = 0, //!< The parse is successful
|
|
+
|
|
+ kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/'
|
|
+ kPointerParseErrorInvalidEscape, //!< Invalid escape
|
|
+ kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment
|
|
+ kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment
|
|
+};
|
|
+
|
|
+//! Function pointer type of GetPointerParseError().
|
|
+/*! \ingroup RAPIDJSON_ERRORS
|
|
+
|
|
+ This is the prototype for \c GetPointerParseError_X(), where \c X is a locale.
|
|
+ User can dynamically change locale in runtime, e.g.:
|
|
+\code
|
|
+ GetPointerParseErrorFunc GetPointerParseError = GetPointerParseError_En; // or whatever
|
|
+ const RAPIDJSON_ERROR_CHARTYPE* s = GetPointerParseError(pointer.GetParseErrorCode());
|
|
+\endcode
|
|
+*/
|
|
+typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetPointerParseErrorFunc)(PointerParseErrorCode);
|
|
+
|
|
+
|
|
RAPIDJSON_NAMESPACE_END
|
|
|
|
#ifdef __clang__
|
|
diff --git a/src/common/thirdparty/rapidjson/filereadstream.h b/src/common/thirdparty/rapidjson/filereadstream.h
|
|
index b56ea13b3..f8bb43cb0 100644
|
|
--- a/src/common/thirdparty/rapidjson/filereadstream.h
|
|
+++ b/src/common/thirdparty/rapidjson/filereadstream.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -59,7 +59,7 @@ public:
|
|
|
|
// For encoding detection only.
|
|
const Ch* Peek4() const {
|
|
- return (current_ + 4 <= bufferLast_) ? current_ : 0;
|
|
+ return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0;
|
|
}
|
|
|
|
private:
|
|
@@ -68,7 +68,7 @@ private:
|
|
++current_;
|
|
else if (!eof_) {
|
|
count_ += readCount_;
|
|
- readCount_ = fread(buffer_, 1, bufferSize_, fp_);
|
|
+ readCount_ = std::fread(buffer_, 1, bufferSize_, fp_);
|
|
bufferLast_ = buffer_ + readCount_ - 1;
|
|
current_ = buffer_;
|
|
|
|
diff --git a/src/common/thirdparty/rapidjson/filewritestream.h b/src/common/thirdparty/rapidjson/filewritestream.h
|
|
index 6378dd60e..5d89588c2 100644
|
|
--- a/src/common/thirdparty/rapidjson/filewritestream.h
|
|
+++ b/src/common/thirdparty/rapidjson/filewritestream.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -25,7 +25,7 @@ RAPIDJSON_DIAG_OFF(unreachable-code)
|
|
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
|
|
-//! Wrapper of C file stream for input using fread().
|
|
+//! Wrapper of C file stream for output using fwrite().
|
|
/*!
|
|
\note implements Stream concept
|
|
*/
|
|
@@ -62,7 +62,7 @@ public:
|
|
|
|
void Flush() {
|
|
if (current_ != buffer_) {
|
|
- size_t result = fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
|
|
+ size_t result = std::fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
|
|
if (result < static_cast<size_t>(current_ - buffer_)) {
|
|
// failure deliberately ignored at this time
|
|
// added to avoid warn_unused_result build errors
|
|
diff --git a/src/common/thirdparty/rapidjson/fwd.h b/src/common/thirdparty/rapidjson/fwd.h
|
|
index e8104e841..d62f77f0e 100644
|
|
--- a/src/common/thirdparty/rapidjson/fwd.h
|
|
+++ b/src/common/thirdparty/rapidjson/fwd.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -102,7 +102,7 @@ class PrettyWriter;
|
|
// document.h
|
|
|
|
template <typename Encoding, typename Allocator>
|
|
-struct GenericMember;
|
|
+class GenericMember;
|
|
|
|
template <bool Const, typename Encoding, typename Allocator>
|
|
class GenericMemberIterator;
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/biginteger.h b/src/common/thirdparty/rapidjson/internal/biginteger.h
|
|
index 9d3e88c99..af4873803 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/biginteger.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/biginteger.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -17,9 +17,13 @@
|
|
|
|
#include "../rapidjson.h"
|
|
|
|
-#if defined(_MSC_VER) && defined(_M_AMD64)
|
|
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64)
|
|
#include <intrin.h> // for _umul128
|
|
+#if !defined(_ARM64EC_)
|
|
#pragma intrinsic(_umul128)
|
|
+#else
|
|
+#pragma comment(lib,"softintrin")
|
|
+#endif
|
|
#endif
|
|
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
@@ -37,7 +41,8 @@ public:
|
|
digits_[0] = u;
|
|
}
|
|
|
|
- BigInteger(const char* decimals, size_t length) : count_(1) {
|
|
+ template<typename Ch>
|
|
+ BigInteger(const Ch* decimals, size_t length) : count_(1) {
|
|
RAPIDJSON_ASSERT(length > 0);
|
|
digits_[0] = 0;
|
|
size_t i = 0;
|
|
@@ -133,7 +138,7 @@ public:
|
|
RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
|
|
|
|
if (interShift == 0) {
|
|
- std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type));
|
|
+ std::memmove(digits_ + offset, digits_, count_ * sizeof(Type));
|
|
count_ += offset;
|
|
}
|
|
else {
|
|
@@ -221,7 +226,8 @@ public:
|
|
bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
|
|
|
|
private:
|
|
- void AppendDecimal64(const char* begin, const char* end) {
|
|
+ template<typename Ch>
|
|
+ void AppendDecimal64(const Ch* begin, const Ch* end) {
|
|
uint64_t u = ParseUint64(begin, end);
|
|
if (IsZero())
|
|
*this = u;
|
|
@@ -236,11 +242,12 @@ private:
|
|
digits_[count_++] = digit;
|
|
}
|
|
|
|
- static uint64_t ParseUint64(const char* begin, const char* end) {
|
|
+ template<typename Ch>
|
|
+ static uint64_t ParseUint64(const Ch* begin, const Ch* end) {
|
|
uint64_t r = 0;
|
|
- for (const char* p = begin; p != end; ++p) {
|
|
- RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
|
|
- r = r * 10u + static_cast<unsigned>(*p - '0');
|
|
+ for (const Ch* p = begin; p != end; ++p) {
|
|
+ RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9'));
|
|
+ r = r * 10u + static_cast<unsigned>(*p - Ch('0'));
|
|
}
|
|
return r;
|
|
}
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/clzll.h b/src/common/thirdparty/rapidjson/internal/clzll.h
|
|
new file mode 100644
|
|
index 000000000..8fc5118aa
|
|
--- /dev/null
|
|
+++ b/src/common/thirdparty/rapidjson/internal/clzll.h
|
|
@@ -0,0 +1,71 @@
|
|
+// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
+//
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
+//
|
|
+// Licensed under the MIT License (the "License"); you may not use this file except
|
|
+// in compliance with the License. You may obtain a copy of the License at
|
|
+//
|
|
+// http://opensource.org/licenses/MIT
|
|
+//
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+// specific language governing permissions and limitations under the License.
|
|
+
|
|
+#ifndef RAPIDJSON_CLZLL_H_
|
|
+#define RAPIDJSON_CLZLL_H_
|
|
+
|
|
+#include "../rapidjson.h"
|
|
+
|
|
+#if defined(_MSC_VER) && !defined(UNDER_CE)
|
|
+#include <intrin.h>
|
|
+#if defined(_WIN64)
|
|
+#pragma intrinsic(_BitScanReverse64)
|
|
+#else
|
|
+#pragma intrinsic(_BitScanReverse)
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+RAPIDJSON_NAMESPACE_BEGIN
|
|
+namespace internal {
|
|
+
|
|
+inline uint32_t clzll(uint64_t x) {
|
|
+ // Passing 0 to __builtin_clzll is UB in GCC and results in an
|
|
+ // infinite loop in the software implementation.
|
|
+ RAPIDJSON_ASSERT(x != 0);
|
|
+
|
|
+#if defined(_MSC_VER) && !defined(UNDER_CE)
|
|
+ unsigned long r = 0;
|
|
+#if defined(_WIN64)
|
|
+ _BitScanReverse64(&r, x);
|
|
+#else
|
|
+ // Scan the high 32 bits.
|
|
+ if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
|
|
+ return 63 - (r + 32);
|
|
+
|
|
+ // Scan the low 32 bits.
|
|
+ _BitScanReverse(&r, static_cast<uint32_t>(x & 0xFFFFFFFF));
|
|
+#endif // _WIN64
|
|
+
|
|
+ return 63 - r;
|
|
+#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll)
|
|
+ // __builtin_clzll wrapper
|
|
+ return static_cast<uint32_t>(__builtin_clzll(x));
|
|
+#else
|
|
+ // naive version
|
|
+ uint32_t r = 0;
|
|
+ while (!(x & (static_cast<uint64_t>(1) << 63))) {
|
|
+ x <<= 1;
|
|
+ ++r;
|
|
+ }
|
|
+
|
|
+ return r;
|
|
+#endif // _MSC_VER
|
|
+}
|
|
+
|
|
+#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll
|
|
+
|
|
+} // namespace internal
|
|
+RAPIDJSON_NAMESPACE_END
|
|
+
|
|
+#endif // RAPIDJSON_CLZLL_H_
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/diyfp.h b/src/common/thirdparty/rapidjson/internal/diyfp.h
|
|
index c9fefdc61..f7d46539a 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/diyfp.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/diyfp.h
|
|
@@ -1,15 +1,15 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
-//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+//
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://opensource.org/licenses/MIT
|
|
//
|
|
-// Unless required by applicable law or agreed to in writing, software distributed
|
|
-// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
-// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
|
@@ -20,11 +20,16 @@
|
|
#define RAPIDJSON_DIYFP_H_
|
|
|
|
#include "../rapidjson.h"
|
|
+#include "clzll.h"
|
|
+#include <limits>
|
|
|
|
-#if defined(_MSC_VER) && defined(_M_AMD64)
|
|
+#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER)
|
|
#include <intrin.h>
|
|
-#pragma intrinsic(_BitScanReverse64)
|
|
+#if !defined(_ARM64EC_)
|
|
#pragma intrinsic(_umul128)
|
|
+#else
|
|
+#pragma comment(lib,"softintrin")
|
|
+#endif
|
|
#endif
|
|
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
@@ -56,7 +61,7 @@ struct DiyFp {
|
|
if (biased_e != 0) {
|
|
f = significand + kDpHiddenBit;
|
|
e = biased_e - kDpExponentBias;
|
|
- }
|
|
+ }
|
|
else {
|
|
f = significand;
|
|
e = kDpMinExponent + 1;
|
|
@@ -99,21 +104,8 @@ struct DiyFp {
|
|
}
|
|
|
|
DiyFp Normalize() const {
|
|
-#if defined(_MSC_VER) && defined(_M_AMD64)
|
|
- unsigned long index;
|
|
- _BitScanReverse64(&index, f);
|
|
- return DiyFp(f << (63 - index), e - (63 - index));
|
|
-#elif defined(__GNUC__) && __GNUC__ >= 4
|
|
- int s = __builtin_clzll(f);
|
|
+ int s = static_cast<int>(clzll(f));
|
|
return DiyFp(f << s, e - s);
|
|
-#else
|
|
- DiyFp res = *this;
|
|
- while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
|
|
- res.f <<= 1;
|
|
- res.e--;
|
|
- }
|
|
- return res;
|
|
-#endif
|
|
}
|
|
|
|
DiyFp NormalizeBoundary() const {
|
|
@@ -141,7 +133,16 @@ struct DiyFp {
|
|
double d;
|
|
uint64_t u64;
|
|
}u;
|
|
- const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
|
|
+ RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask);
|
|
+ if (e < kDpDenormalExponent) {
|
|
+ // Underflow.
|
|
+ return 0.0;
|
|
+ }
|
|
+ if (e >= kDpMaxExponent) {
|
|
+ // Overflow.
|
|
+ return std::numeric_limits<double>::infinity();
|
|
+ }
|
|
+ const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
|
|
static_cast<uint64_t>(e + kDpExponentBias);
|
|
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
|
|
return u.d;
|
|
@@ -220,9 +221,10 @@ inline DiyFp GetCachedPowerByIndex(size_t index) {
|
|
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
|
|
907, 933, 960, 986, 1013, 1039, 1066
|
|
};
|
|
+ RAPIDJSON_ASSERT(index < 87);
|
|
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
|
|
}
|
|
-
|
|
+
|
|
inline DiyFp GetCachedPower(int e, int* K) {
|
|
|
|
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
|
|
@@ -238,10 +240,11 @@ inline DiyFp GetCachedPower(int e, int* K) {
|
|
}
|
|
|
|
inline DiyFp GetCachedPower10(int exp, int *outExp) {
|
|
- unsigned index = (static_cast<unsigned>(exp) + 348u) / 8u;
|
|
- *outExp = -348 + static_cast<int>(index) * 8;
|
|
- return GetCachedPowerByIndex(index);
|
|
- }
|
|
+ RAPIDJSON_ASSERT(exp >= -348);
|
|
+ unsigned index = static_cast<unsigned>(exp + 348) / 8u;
|
|
+ *outExp = -348 + static_cast<int>(index) * 8;
|
|
+ return GetCachedPowerByIndex(index);
|
|
+}
|
|
|
|
#ifdef __GNUC__
|
|
RAPIDJSON_DIAG_POP
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/dtoa.h b/src/common/thirdparty/rapidjson/internal/dtoa.h
|
|
index 8d6350e62..cd456721a 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/dtoa.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/dtoa.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -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;
|
|
@@ -58,12 +58,16 @@ inline unsigned CountDecimalDigit32(uint32_t n) {
|
|
}
|
|
|
|
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
|
|
- static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
|
+ static const uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL,
|
|
+ 1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL,
|
|
+ 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL,
|
|
+ 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL,
|
|
+ 10000000000000000000ULL };
|
|
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
|
|
const DiyFp wp_w = Mp - W;
|
|
uint32_t p1 = static_cast<uint32_t>(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) {
|
|
@@ -86,7 +90,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff
|
|
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
|
|
if (tmp <= delta) {
|
|
*K += kappa;
|
|
- GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
|
|
+ GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f);
|
|
return;
|
|
}
|
|
}
|
|
@@ -102,8 +106,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<int>(kappa);
|
|
- GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
|
|
+ int index = -kappa;
|
|
+ GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0));
|
|
return;
|
|
}
|
|
}
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/ieee754.h b/src/common/thirdparty/rapidjson/internal/ieee754.h
|
|
index 82bb0b99e..68c9e9664 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/ieee754.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/ieee754.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -48,13 +48,13 @@ public:
|
|
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<unsigned>(order) + 1074;
|
|
+ return order + 1074;
|
|
}
|
|
|
|
private:
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/itoa.h b/src/common/thirdparty/rapidjson/internal/itoa.h
|
|
index 01a4e7e72..9fe8c932f 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/itoa.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/itoa.h
|
|
@@ -1,15 +1,15 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
-//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+//
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://opensource.org/licenses/MIT
|
|
//
|
|
-// Unless required by applicable law or agreed to in writing, software distributed
|
|
-// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
-// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
#ifndef RAPIDJSON_ITOA_
|
|
@@ -37,12 +37,14 @@ inline const char* GetDigitsLut() {
|
|
}
|
|
|
|
inline char* u32toa(uint32_t value, char* buffer) {
|
|
+ RAPIDJSON_ASSERT(buffer != 0);
|
|
+
|
|
const char* cDigitsLut = GetDigitsLut();
|
|
|
|
if (value < 10000) {
|
|
const uint32_t d1 = (value / 100) << 1;
|
|
const uint32_t d2 = (value % 100) << 1;
|
|
-
|
|
+
|
|
if (value >= 1000)
|
|
*buffer++ = cDigitsLut[d1];
|
|
if (value >= 100)
|
|
@@ -55,13 +57,13 @@ inline char* u32toa(uint32_t value, char* buffer) {
|
|
// value = bbbbcccc
|
|
const uint32_t b = value / 10000;
|
|
const uint32_t c = value % 10000;
|
|
-
|
|
+
|
|
const uint32_t d1 = (b / 100) << 1;
|
|
const uint32_t d2 = (b % 100) << 1;
|
|
-
|
|
+
|
|
const uint32_t d3 = (c / 100) << 1;
|
|
const uint32_t d4 = (c % 100) << 1;
|
|
-
|
|
+
|
|
if (value >= 10000000)
|
|
*buffer++ = cDigitsLut[d1];
|
|
if (value >= 1000000)
|
|
@@ -69,7 +71,7 @@ inline char* u32toa(uint32_t value, char* buffer) {
|
|
if (value >= 100000)
|
|
*buffer++ = cDigitsLut[d2];
|
|
*buffer++ = cDigitsLut[d2 + 1];
|
|
-
|
|
+
|
|
*buffer++ = cDigitsLut[d3];
|
|
*buffer++ = cDigitsLut[d3 + 1];
|
|
*buffer++ = cDigitsLut[d4];
|
|
@@ -77,10 +79,10 @@ inline char* u32toa(uint32_t value, char* buffer) {
|
|
}
|
|
else {
|
|
// value = aabbbbcccc in decimal
|
|
-
|
|
+
|
|
const uint32_t a = value / 100000000; // 1 to 42
|
|
value %= 100000000;
|
|
-
|
|
+
|
|
if (a >= 10) {
|
|
const unsigned i = a << 1;
|
|
*buffer++ = cDigitsLut[i];
|
|
@@ -91,13 +93,13 @@ inline char* u32toa(uint32_t value, char* buffer) {
|
|
|
|
const uint32_t b = value / 10000; // 0 to 9999
|
|
const uint32_t c = value % 10000; // 0 to 9999
|
|
-
|
|
+
|
|
const uint32_t d1 = (b / 100) << 1;
|
|
const uint32_t d2 = (b % 100) << 1;
|
|
-
|
|
+
|
|
const uint32_t d3 = (c / 100) << 1;
|
|
const uint32_t d4 = (c % 100) << 1;
|
|
-
|
|
+
|
|
*buffer++ = cDigitsLut[d1];
|
|
*buffer++ = cDigitsLut[d1 + 1];
|
|
*buffer++ = cDigitsLut[d2];
|
|
@@ -111,6 +113,7 @@ inline char* u32toa(uint32_t value, char* buffer) {
|
|
}
|
|
|
|
inline char* i32toa(int32_t value, char* buffer) {
|
|
+ RAPIDJSON_ASSERT(buffer != 0);
|
|
uint32_t u = static_cast<uint32_t>(value);
|
|
if (value < 0) {
|
|
*buffer++ = '-';
|
|
@@ -121,6 +124,7 @@ inline char* i32toa(int32_t value, char* buffer) {
|
|
}
|
|
|
|
inline char* u64toa(uint64_t value, char* buffer) {
|
|
+ RAPIDJSON_ASSERT(buffer != 0);
|
|
const char* cDigitsLut = GetDigitsLut();
|
|
const uint64_t kTen8 = 100000000;
|
|
const uint64_t kTen9 = kTen8 * 10;
|
|
@@ -131,13 +135,13 @@ inline char* u64toa(uint64_t value, char* buffer) {
|
|
const uint64_t kTen14 = kTen8 * 1000000;
|
|
const uint64_t kTen15 = kTen8 * 10000000;
|
|
const uint64_t kTen16 = kTen8 * kTen8;
|
|
-
|
|
+
|
|
if (value < kTen8) {
|
|
uint32_t v = static_cast<uint32_t>(value);
|
|
if (v < 10000) {
|
|
const uint32_t d1 = (v / 100) << 1;
|
|
const uint32_t d2 = (v % 100) << 1;
|
|
-
|
|
+
|
|
if (v >= 1000)
|
|
*buffer++ = cDigitsLut[d1];
|
|
if (v >= 100)
|
|
@@ -150,13 +154,13 @@ inline char* u64toa(uint64_t value, char* buffer) {
|
|
// value = bbbbcccc
|
|
const uint32_t b = v / 10000;
|
|
const uint32_t c = v % 10000;
|
|
-
|
|
+
|
|
const uint32_t d1 = (b / 100) << 1;
|
|
const uint32_t d2 = (b % 100) << 1;
|
|
-
|
|
+
|
|
const uint32_t d3 = (c / 100) << 1;
|
|
const uint32_t d4 = (c % 100) << 1;
|
|
-
|
|
+
|
|
if (value >= 10000000)
|
|
*buffer++ = cDigitsLut[d1];
|
|
if (value >= 1000000)
|
|
@@ -164,7 +168,7 @@ inline char* u64toa(uint64_t value, char* buffer) {
|
|
if (value >= 100000)
|
|
*buffer++ = cDigitsLut[d2];
|
|
*buffer++ = cDigitsLut[d2 + 1];
|
|
-
|
|
+
|
|
*buffer++ = cDigitsLut[d3];
|
|
*buffer++ = cDigitsLut[d3 + 1];
|
|
*buffer++ = cDigitsLut[d4];
|
|
@@ -174,22 +178,22 @@ inline char* u64toa(uint64_t value, char* buffer) {
|
|
else if (value < kTen16) {
|
|
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
|
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
|
-
|
|
+
|
|
const uint32_t b0 = v0 / 10000;
|
|
const uint32_t c0 = v0 % 10000;
|
|
-
|
|
+
|
|
const uint32_t d1 = (b0 / 100) << 1;
|
|
const uint32_t d2 = (b0 % 100) << 1;
|
|
-
|
|
+
|
|
const uint32_t d3 = (c0 / 100) << 1;
|
|
const uint32_t d4 = (c0 % 100) << 1;
|
|
|
|
const uint32_t b1 = v1 / 10000;
|
|
const uint32_t c1 = v1 % 10000;
|
|
-
|
|
+
|
|
const uint32_t d5 = (b1 / 100) << 1;
|
|
const uint32_t d6 = (b1 % 100) << 1;
|
|
-
|
|
+
|
|
const uint32_t d7 = (c1 / 100) << 1;
|
|
const uint32_t d8 = (c1 % 100) << 1;
|
|
|
|
@@ -207,9 +211,8 @@ inline char* u64toa(uint64_t value, char* buffer) {
|
|
*buffer++ = cDigitsLut[d3 + 1];
|
|
if (value >= kTen9)
|
|
*buffer++ = cDigitsLut[d4];
|
|
- if (value >= kTen8)
|
|
- *buffer++ = cDigitsLut[d4 + 1];
|
|
-
|
|
+
|
|
+ *buffer++ = cDigitsLut[d4 + 1];
|
|
*buffer++ = cDigitsLut[d5];
|
|
*buffer++ = cDigitsLut[d5 + 1];
|
|
*buffer++ = cDigitsLut[d6];
|
|
@@ -222,7 +225,7 @@ inline char* u64toa(uint64_t value, char* buffer) {
|
|
else {
|
|
const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
|
|
value %= kTen16;
|
|
-
|
|
+
|
|
if (a < 10)
|
|
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
|
else if (a < 100) {
|
|
@@ -232,7 +235,7 @@ inline char* u64toa(uint64_t value, char* buffer) {
|
|
}
|
|
else if (a < 1000) {
|
|
*buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
|
|
-
|
|
+
|
|
const uint32_t i = (a % 100) << 1;
|
|
*buffer++ = cDigitsLut[i];
|
|
*buffer++ = cDigitsLut[i + 1];
|
|
@@ -245,28 +248,28 @@ inline char* u64toa(uint64_t value, char* buffer) {
|
|
*buffer++ = cDigitsLut[j];
|
|
*buffer++ = cDigitsLut[j + 1];
|
|
}
|
|
-
|
|
+
|
|
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
|
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
|
-
|
|
+
|
|
const uint32_t b0 = v0 / 10000;
|
|
const uint32_t c0 = v0 % 10000;
|
|
-
|
|
+
|
|
const uint32_t d1 = (b0 / 100) << 1;
|
|
const uint32_t d2 = (b0 % 100) << 1;
|
|
-
|
|
+
|
|
const uint32_t d3 = (c0 / 100) << 1;
|
|
const uint32_t d4 = (c0 % 100) << 1;
|
|
-
|
|
+
|
|
const uint32_t b1 = v1 / 10000;
|
|
const uint32_t c1 = v1 % 10000;
|
|
-
|
|
+
|
|
const uint32_t d5 = (b1 / 100) << 1;
|
|
const uint32_t d6 = (b1 % 100) << 1;
|
|
-
|
|
+
|
|
const uint32_t d7 = (c1 / 100) << 1;
|
|
const uint32_t d8 = (c1 % 100) << 1;
|
|
-
|
|
+
|
|
*buffer++ = cDigitsLut[d1];
|
|
*buffer++ = cDigitsLut[d1 + 1];
|
|
*buffer++ = cDigitsLut[d2];
|
|
@@ -284,11 +287,12 @@ inline char* u64toa(uint64_t value, char* buffer) {
|
|
*buffer++ = cDigitsLut[d8];
|
|
*buffer++ = cDigitsLut[d8 + 1];
|
|
}
|
|
-
|
|
+
|
|
return buffer;
|
|
}
|
|
|
|
inline char* i64toa(int64_t value, char* buffer) {
|
|
+ RAPIDJSON_ASSERT(buffer != 0);
|
|
uint64_t u = static_cast<uint64_t>(value);
|
|
if (value < 0) {
|
|
*buffer++ = '-';
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/meta.h b/src/common/thirdparty/rapidjson/internal/meta.h
|
|
index 5a9aaa428..27092dc0d 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/meta.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/meta.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -21,7 +21,8 @@
|
|
RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(effc++)
|
|
#endif
|
|
-#if defined(_MSC_VER)
|
|
+
|
|
+#if defined(_MSC_VER) && !defined(__clang__)
|
|
RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(6334)
|
|
#endif
|
|
@@ -174,7 +175,11 @@ template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type;
|
|
RAPIDJSON_NAMESPACE_END
|
|
//@endcond
|
|
|
|
-#if defined(__GNUC__) || defined(_MSC_VER)
|
|
+#if defined(_MSC_VER) && !defined(__clang__)
|
|
+RAPIDJSON_DIAG_POP
|
|
+#endif
|
|
+
|
|
+#ifdef __GNUC__
|
|
RAPIDJSON_DIAG_POP
|
|
#endif
|
|
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/pow10.h b/src/common/thirdparty/rapidjson/internal/pow10.h
|
|
index 02f475d70..eae1a43ed 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/pow10.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/pow10.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/regex.h b/src/common/thirdparty/rapidjson/internal/regex.h
|
|
index 422a5240b..6446c403a 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/regex.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/regex.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -23,7 +23,9 @@
|
|
RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(padded)
|
|
RAPIDJSON_DIAG_OFF(switch-enum)
|
|
-RAPIDJSON_DIAG_OFF(implicit-fallthrough)
|
|
+#elif defined(_MSC_VER)
|
|
+RAPIDJSON_DIAG_PUSH
|
|
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
|
#endif
|
|
|
|
#ifdef __GNUC__
|
|
@@ -31,11 +33,6 @@ RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(effc++)
|
|
#endif
|
|
|
|
-#ifdef _MSC_VER
|
|
-RAPIDJSON_DIAG_PUSH
|
|
-RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
|
-#endif
|
|
-
|
|
#ifndef RAPIDJSON_REGEX_VERBOSE
|
|
#define RAPIDJSON_REGEX_VERBOSE 0
|
|
#endif
|
|
@@ -43,12 +40,40 @@ RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
namespace internal {
|
|
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// DecodedStream
|
|
+
|
|
+template <typename SourceStream, typename Encoding>
|
|
+class DecodedStream {
|
|
+public:
|
|
+ DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
|
|
+ unsigned Peek() { return codepoint_; }
|
|
+ unsigned Take() {
|
|
+ unsigned c = codepoint_;
|
|
+ if (c) // No further decoding when '\0'
|
|
+ Decode();
|
|
+ return c;
|
|
+ }
|
|
+
|
|
+private:
|
|
+ void Decode() {
|
|
+ if (!Encoding::Decode(ss_, &codepoint_))
|
|
+ codepoint_ = 0;
|
|
+ }
|
|
+
|
|
+ SourceStream& ss_;
|
|
+ unsigned codepoint_;
|
|
+};
|
|
+
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// GenericRegex
|
|
|
|
static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
|
|
static const SizeType kRegexInvalidRange = ~SizeType(0);
|
|
|
|
+template <typename Encoding, typename Allocator>
|
|
+class GenericRegexSearch;
|
|
+
|
|
//! Regular expression engine with subset of ECMAscript grammar.
|
|
/*!
|
|
Supported regular expression syntax:
|
|
@@ -84,45 +109,29 @@ static const SizeType kRegexInvalidRange = ~SizeType(0);
|
|
template <typename Encoding, typename Allocator = CrtAllocator>
|
|
class GenericRegex {
|
|
public:
|
|
+ typedef Encoding EncodingType;
|
|
typedef typename Encoding::Ch Ch;
|
|
+ template <typename, typename> friend class GenericRegexSearch;
|
|
|
|
GenericRegex(const Ch* source, Allocator* allocator = 0) :
|
|
- states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
|
|
- stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_()
|
|
+ ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_),
|
|
+ states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
|
|
+ anchorBegin_(), anchorEnd_()
|
|
{
|
|
GenericStringStream<Encoding> ss(source);
|
|
- DecodedStream<GenericStringStream<Encoding> > ds(ss);
|
|
+ DecodedStream<GenericStringStream<Encoding>, Encoding> ds(ss);
|
|
Parse(ds);
|
|
}
|
|
|
|
- ~GenericRegex() {
|
|
- Allocator::Free(stateSet_);
|
|
+ ~GenericRegex()
|
|
+ {
|
|
+ RAPIDJSON_DELETE(ownAllocator_);
|
|
}
|
|
|
|
bool IsValid() const {
|
|
return root_ != kRegexInvalidState;
|
|
}
|
|
|
|
- template <typename InputStream>
|
|
- bool Match(InputStream& is) const {
|
|
- return SearchWithAnchoring(is, true, true);
|
|
- }
|
|
-
|
|
- bool Match(const Ch* s) const {
|
|
- GenericStringStream<Encoding> is(s);
|
|
- return Match(is);
|
|
- }
|
|
-
|
|
- template <typename InputStream>
|
|
- bool Search(InputStream& is) const {
|
|
- return SearchWithAnchoring(is, anchorBegin_, anchorEnd_);
|
|
- }
|
|
-
|
|
- bool Search(const Ch* s) const {
|
|
- GenericStringStream<Encoding> is(s);
|
|
- return Search(is);
|
|
- }
|
|
-
|
|
private:
|
|
enum Operator {
|
|
kZeroOrOne,
|
|
@@ -157,28 +166,6 @@ private:
|
|
SizeType minIndex;
|
|
};
|
|
|
|
- template <typename SourceStream>
|
|
- class DecodedStream {
|
|
- public:
|
|
- DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
|
|
- unsigned Peek() { return codepoint_; }
|
|
- unsigned Take() {
|
|
- unsigned c = codepoint_;
|
|
- if (c) // No further decoding when '\0'
|
|
- Decode();
|
|
- return c;
|
|
- }
|
|
-
|
|
- private:
|
|
- void Decode() {
|
|
- if (!Encoding::Decode(ss_, &codepoint_))
|
|
- codepoint_ = 0;
|
|
- }
|
|
-
|
|
- SourceStream& ss_;
|
|
- unsigned codepoint_;
|
|
- };
|
|
-
|
|
State& GetState(SizeType index) {
|
|
RAPIDJSON_ASSERT(index < stateCount_);
|
|
return states_.template Bottom<State>()[index];
|
|
@@ -200,11 +187,10 @@ private:
|
|
}
|
|
|
|
template <typename InputStream>
|
|
- void Parse(DecodedStream<InputStream>& ds) {
|
|
- Allocator allocator;
|
|
- Stack<Allocator> operandStack(&allocator, 256); // Frag
|
|
- Stack<Allocator> operatorStack(&allocator, 256); // Operator
|
|
- Stack<Allocator> atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis)
|
|
+ void Parse(DecodedStream<InputStream, Encoding>& ds) {
|
|
+ Stack<Allocator> operandStack(allocator_, 256); // Frag
|
|
+ Stack<Allocator> operatorStack(allocator_, 256); // Operator
|
|
+ Stack<Allocator> atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis)
|
|
|
|
*atomCountStack.template Push<unsigned>() = 0;
|
|
|
|
@@ -301,6 +287,7 @@ private:
|
|
if (!CharacterEscape(ds, &codepoint))
|
|
return; // Unsupported escape character
|
|
// fall through to default
|
|
+ RAPIDJSON_DELIBERATE_FALLTHROUGH;
|
|
|
|
default: // Pattern character
|
|
PushOperand(operandStack, codepoint);
|
|
@@ -327,14 +314,6 @@ private:
|
|
printf("\n");
|
|
#endif
|
|
}
|
|
-
|
|
- // Preallocate buffer for SearchWithAnchoring()
|
|
- RAPIDJSON_ASSERT(stateSet_ == 0);
|
|
- if (stateCount_ > 0) {
|
|
- stateSet_ = static_cast<unsigned*>(states_.GetAllocator().Malloc(GetStateSetSize()));
|
|
- state0_.template Reserve<SizeType>(stateCount_);
|
|
- state1_.template Reserve<SizeType>(stateCount_);
|
|
- }
|
|
}
|
|
|
|
SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
|
|
@@ -413,8 +392,7 @@ private:
|
|
}
|
|
return false;
|
|
|
|
- default:
|
|
- RAPIDJSON_ASSERT(op == kOneOrMore);
|
|
+ case kOneOrMore:
|
|
if (operandStack.GetSize() >= sizeof(Frag)) {
|
|
Frag e = *operandStack.template Pop<Frag>(1);
|
|
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
|
@@ -423,6 +401,10 @@ private:
|
|
return true;
|
|
}
|
|
return false;
|
|
+
|
|
+ default:
|
|
+ // syntax error (e.g. unclosed kLeftParenthesis)
|
|
+ return false;
|
|
}
|
|
}
|
|
|
|
@@ -483,7 +465,7 @@ private:
|
|
}
|
|
|
|
template <typename InputStream>
|
|
- bool ParseUnsigned(DecodedStream<InputStream>& ds, unsigned* u) {
|
|
+ bool ParseUnsigned(DecodedStream<InputStream, Encoding>& ds, unsigned* u) {
|
|
unsigned r = 0;
|
|
if (ds.Peek() < '0' || ds.Peek() > '9')
|
|
return false;
|
|
@@ -497,7 +479,7 @@ private:
|
|
}
|
|
|
|
template <typename InputStream>
|
|
- bool ParseRange(DecodedStream<InputStream>& ds, SizeType* range) {
|
|
+ bool ParseRange(DecodedStream<InputStream, Encoding>& ds, SizeType* range) {
|
|
bool isBegin = true;
|
|
bool negate = false;
|
|
int step = 0;
|
|
@@ -535,6 +517,7 @@ private:
|
|
else if (!CharacterEscape(ds, &codepoint))
|
|
return false;
|
|
// fall through to default
|
|
+ RAPIDJSON_DELIBERATE_FALLTHROUGH;
|
|
|
|
default:
|
|
switch (step) {
|
|
@@ -544,6 +527,7 @@ private:
|
|
break;
|
|
}
|
|
// fall through to step 0 for other characters
|
|
+ RAPIDJSON_DELIBERATE_FALLTHROUGH;
|
|
|
|
case 0:
|
|
{
|
|
@@ -575,7 +559,7 @@ private:
|
|
}
|
|
|
|
template <typename InputStream>
|
|
- bool CharacterEscape(DecodedStream<InputStream>& ds, unsigned* escapedCodepoint) {
|
|
+ bool CharacterEscape(DecodedStream<InputStream, Encoding>& ds, unsigned* escapedCodepoint) {
|
|
unsigned codepoint;
|
|
switch (codepoint = ds.Take()) {
|
|
case '^':
|
|
@@ -603,34 +587,95 @@ private:
|
|
}
|
|
}
|
|
|
|
+ Allocator* ownAllocator_;
|
|
+ Allocator* allocator_;
|
|
+ Stack<Allocator> states_;
|
|
+ Stack<Allocator> ranges_;
|
|
+ SizeType root_;
|
|
+ SizeType stateCount_;
|
|
+ SizeType rangeCount_;
|
|
+
|
|
+ static const unsigned kInfinityQuantifier = ~0u;
|
|
+
|
|
+ // For SearchWithAnchoring()
|
|
+ bool anchorBegin_;
|
|
+ bool anchorEnd_;
|
|
+};
|
|
+
|
|
+template <typename RegexType, typename Allocator = CrtAllocator>
|
|
+class GenericRegexSearch {
|
|
+public:
|
|
+ typedef typename RegexType::EncodingType Encoding;
|
|
+ typedef typename Encoding::Ch Ch;
|
|
+
|
|
+ GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) :
|
|
+ regex_(regex), allocator_(allocator), ownAllocator_(0),
|
|
+ state0_(allocator, 0), state1_(allocator, 0), stateSet_()
|
|
+ {
|
|
+ RAPIDJSON_ASSERT(regex_.IsValid());
|
|
+ if (!allocator_)
|
|
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
|
+ stateSet_ = static_cast<unsigned*>(allocator_->Malloc(GetStateSetSize()));
|
|
+ state0_.template Reserve<SizeType>(regex_.stateCount_);
|
|
+ state1_.template Reserve<SizeType>(regex_.stateCount_);
|
|
+ }
|
|
+
|
|
+ ~GenericRegexSearch() {
|
|
+ Allocator::Free(stateSet_);
|
|
+ RAPIDJSON_DELETE(ownAllocator_);
|
|
+ }
|
|
+
|
|
template <typename InputStream>
|
|
- bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const {
|
|
- RAPIDJSON_ASSERT(IsValid());
|
|
- DecodedStream<InputStream> ds(is);
|
|
+ bool Match(InputStream& is) {
|
|
+ return SearchWithAnchoring(is, true, true);
|
|
+ }
|
|
+
|
|
+ bool Match(const Ch* s) {
|
|
+ GenericStringStream<Encoding> is(s);
|
|
+ return Match(is);
|
|
+ }
|
|
+
|
|
+ template <typename InputStream>
|
|
+ bool Search(InputStream& is) {
|
|
+ return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_);
|
|
+ }
|
|
+
|
|
+ bool Search(const Ch* s) {
|
|
+ GenericStringStream<Encoding> is(s);
|
|
+ return Search(is);
|
|
+ }
|
|
+
|
|
+private:
|
|
+ typedef typename RegexType::State State;
|
|
+ typedef typename RegexType::Range Range;
|
|
+
|
|
+ template <typename InputStream>
|
|
+ bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) {
|
|
+ DecodedStream<InputStream, Encoding> ds(is);
|
|
|
|
state0_.Clear();
|
|
Stack<Allocator> *current = &state0_, *next = &state1_;
|
|
const size_t stateSetSize = GetStateSetSize();
|
|
std::memset(stateSet_, 0, stateSetSize);
|
|
|
|
- bool matched = AddState(*current, root_);
|
|
+ bool matched = AddState(*current, regex_.root_);
|
|
unsigned codepoint;
|
|
while (!current->Empty() && (codepoint = ds.Take()) != 0) {
|
|
std::memset(stateSet_, 0, stateSetSize);
|
|
next->Clear();
|
|
matched = false;
|
|
for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
|
|
- const State& sr = GetState(*s);
|
|
+ const State& sr = regex_.GetState(*s);
|
|
if (sr.codepoint == codepoint ||
|
|
- sr.codepoint == kAnyCharacterClass ||
|
|
- (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
|
|
+ sr.codepoint == RegexType::kAnyCharacterClass ||
|
|
+ (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
|
|
{
|
|
matched = AddState(*next, sr.out) || matched;
|
|
if (!anchorEnd && matched)
|
|
return true;
|
|
}
|
|
if (!anchorBegin)
|
|
- AddState(*next, root_);
|
|
+ AddState(*next, regex_.root_);
|
|
}
|
|
internal::Swap(current, next);
|
|
}
|
|
@@ -639,62 +684,55 @@ private:
|
|
}
|
|
|
|
size_t GetStateSetSize() const {
|
|
- return (stateCount_ + 31) / 32 * 4;
|
|
+ return (regex_.stateCount_ + 31) / 32 * 4;
|
|
}
|
|
|
|
// Return whether the added states is a match state
|
|
- bool AddState(Stack<Allocator>& l, SizeType index) const {
|
|
+ bool AddState(Stack<Allocator>& l, SizeType index) {
|
|
RAPIDJSON_ASSERT(index != kRegexInvalidState);
|
|
|
|
- const State& s = GetState(index);
|
|
+ const State& s = regex_.GetState(index);
|
|
if (s.out1 != kRegexInvalidState) { // Split
|
|
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<SizeType>() = index;
|
|
}
|
|
return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
|
|
}
|
|
|
|
bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
|
|
- bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0;
|
|
+ bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0;
|
|
while (rangeIndex != kRegexInvalidRange) {
|
|
- const Range& r = GetRange(rangeIndex);
|
|
- if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end)
|
|
+ const Range& r = regex_.GetRange(rangeIndex);
|
|
+ if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end)
|
|
return yes;
|
|
rangeIndex = r.next;
|
|
}
|
|
return !yes;
|
|
}
|
|
|
|
- Stack<Allocator> states_;
|
|
- Stack<Allocator> ranges_;
|
|
- SizeType root_;
|
|
- SizeType stateCount_;
|
|
- SizeType rangeCount_;
|
|
-
|
|
- static const unsigned kInfinityQuantifier = ~0u;
|
|
-
|
|
- // For SearchWithAnchoring()
|
|
- uint32_t* stateSet_; // allocated by states_.GetAllocator()
|
|
- mutable Stack<Allocator> state0_;
|
|
- mutable Stack<Allocator> state1_;
|
|
- bool anchorBegin_;
|
|
- bool anchorEnd_;
|
|
+ const RegexType& regex_;
|
|
+ Allocator* allocator_;
|
|
+ Allocator* ownAllocator_;
|
|
+ Stack<Allocator> state0_;
|
|
+ Stack<Allocator> state1_;
|
|
+ uint32_t* stateSet_;
|
|
};
|
|
|
|
typedef GenericRegex<UTF8<> > Regex;
|
|
+typedef GenericRegexSearch<Regex> RegexSearch;
|
|
|
|
} // namespace internal
|
|
RAPIDJSON_NAMESPACE_END
|
|
|
|
-#ifdef __clang__
|
|
+#ifdef __GNUC__
|
|
RAPIDJSON_DIAG_POP
|
|
#endif
|
|
|
|
-#ifdef _MSC_VER
|
|
+#if defined(__clang__) || defined(_MSC_VER)
|
|
RAPIDJSON_DIAG_POP
|
|
#endif
|
|
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/stack.h b/src/common/thirdparty/rapidjson/internal/stack.h
|
|
index 022c9aab4..73abd706e 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/stack.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/stack.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -17,6 +17,7 @@
|
|
|
|
#include "../allocators.h"
|
|
#include "swap.h"
|
|
+#include <cstddef>
|
|
|
|
#if defined(__clang__)
|
|
RAPIDJSON_DIAG_PUSH
|
|
@@ -100,7 +101,7 @@ public:
|
|
void ShrinkToFit() {
|
|
if (Empty()) {
|
|
// If the stack is empty, completely deallocate the memory.
|
|
- Allocator::Free(stack_);
|
|
+ Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc)
|
|
stack_ = 0;
|
|
stackTop_ = 0;
|
|
stackEnd_ = 0;
|
|
@@ -114,7 +115,7 @@ public:
|
|
template<typename T>
|
|
RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
|
|
// Expand the stack if needed
|
|
- if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_))
|
|
+ if (RAPIDJSON_UNLIKELY(static_cast<std::ptrdiff_t>(sizeof(T) * count) > (stackEnd_ - stackTop_)))
|
|
Expand<T>(count);
|
|
}
|
|
|
|
@@ -126,7 +127,8 @@ public:
|
|
|
|
template<typename T>
|
|
RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
|
|
- RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_);
|
|
+ RAPIDJSON_ASSERT(stackTop_);
|
|
+ RAPIDJSON_ASSERT(static_cast<std::ptrdiff_t>(sizeof(T) * count) <= (stackEnd_ - stackTop_));
|
|
T* ret = reinterpret_cast<T*>(stackTop_);
|
|
stackTop_ += sizeof(T) * count;
|
|
return ret;
|
|
@@ -183,7 +185,7 @@ private:
|
|
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/src/common/thirdparty/rapidjson/internal/strfunc.h b/src/common/thirdparty/rapidjson/internal/strfunc.h
|
|
index de41d8f9c..b698a8f43 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/strfunc.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/strfunc.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -16,6 +16,7 @@
|
|
#define RAPIDJSON_INTERNAL_STRFUNC_H_
|
|
|
|
#include "../stream.h"
|
|
+#include <cwchar>
|
|
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
namespace internal {
|
|
@@ -34,6 +35,30 @@ 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));
|
|
+}
|
|
+
|
|
+//! Custom strcmpn() which works on different character types.
|
|
+/*! \tparam Ch Character type (e.g. char, wchar_t, short)
|
|
+ \param s1 Null-terminated input string.
|
|
+ \param s2 Null-terminated input string.
|
|
+ \return 0 if equal
|
|
+*/
|
|
+template<typename Ch>
|
|
+inline int StrCmp(const Ch* s1, const Ch* s2) {
|
|
+ RAPIDJSON_ASSERT(s1 != 0);
|
|
+ RAPIDJSON_ASSERT(s2 != 0);
|
|
+ while(*s1 && (*s1 == *s2)) { s1++; s2++; }
|
|
+ return static_cast<unsigned>(*s1) < static_cast<unsigned>(*s2) ? -1 : static_cast<unsigned>(*s1) > static_cast<unsigned>(*s2);
|
|
+}
|
|
+
|
|
//! Returns number of code points in a encoded string.
|
|
template<typename Encoding>
|
|
bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/strtod.h b/src/common/thirdparty/rapidjson/internal/strtod.h
|
|
index 289c413b0..55f0e380b 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/strtod.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/strtod.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -19,6 +19,8 @@
|
|
#include "biginteger.h"
|
|
#include "diyfp.h"
|
|
#include "pow10.h"
|
|
+#include <climits>
|
|
+#include <limits>
|
|
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
namespace internal {
|
|
@@ -126,46 +128,47 @@ inline bool StrtodFast(double d, int p, double* result) {
|
|
}
|
|
|
|
// Compute an approximation and see if it is within 1/2 ULP
|
|
-inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
|
|
+template<typename Ch>
|
|
+inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) {
|
|
uint64_t significand = 0;
|
|
- size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
|
|
- for (; i < length; i++) {
|
|
+ int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
|
|
+ for (; i < dLen; i++) {
|
|
if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
|
|
- (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
|
|
+ (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5')))
|
|
break;
|
|
- significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
|
|
+ significand = significand * 10u + static_cast<unsigned>(decimals[i] - Ch('0'));
|
|
}
|
|
|
|
- if (i < length && decimals[i] >= '5') // Rounding
|
|
+ if (i < dLen && decimals[i] >= Ch('5')) // Rounding
|
|
significand++;
|
|
|
|
- size_t remaining = length - i;
|
|
- const unsigned kUlpShift = 3;
|
|
- const unsigned kUlp = 1 << kUlpShift;
|
|
+ int remaining = dLen - i;
|
|
+ const int kUlpShift = 3;
|
|
+ const int kUlp = 1 << kUlpShift;
|
|
int64_t error = (remaining == 0) ? 0 : kUlp / 2;
|
|
|
|
DiyFp v(significand, 0);
|
|
v = v.Normalize();
|
|
error <<= -v.e;
|
|
|
|
- const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(i) + exp;
|
|
+ dExp += remaining;
|
|
|
|
int actualExp;
|
|
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
|
|
if (actualExp != dExp) {
|
|
static const DiyFp kPow10[] = {
|
|
- DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1
|
|
- DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2
|
|
- DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3
|
|
- DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4
|
|
- DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5
|
|
- DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6
|
|
- DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7
|
|
+ DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1
|
|
+ DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2
|
|
+ DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3
|
|
+ DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4
|
|
+ DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5
|
|
+ DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6
|
|
+ DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7
|
|
};
|
|
- int adjustment = dExp - actualExp - 1;
|
|
- RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7);
|
|
- v = v * kPow10[adjustment];
|
|
- if (length + static_cast<unsigned>(adjustment)> 19u) // has more digits than decimal digits in 64-bit
|
|
+ int adjustment = dExp - actualExp;
|
|
+ RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8);
|
|
+ v = v * kPow10[adjustment - 1];
|
|
+ if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit
|
|
error += kUlp / 2;
|
|
}
|
|
|
|
@@ -177,17 +180,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<int>(kUlp);
|
|
+ error = (error >> scaleExp) + 1 + kUlp;
|
|
precisionSize -= scaleExp;
|
|
}
|
|
|
|
- DiyFp rounded(v.f >> precisionSize, v.e + static_cast<int>(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<unsigned>(error)) {
|
|
@@ -203,9 +206,10 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit
|
|
return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
|
|
}
|
|
|
|
-inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
|
- const BigInteger dInt(decimals, length);
|
|
- const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(length) + exp;
|
|
+template<typename Ch>
|
|
+inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) {
|
|
+ RAPIDJSON_ASSERT(dLen >= 0);
|
|
+ const BigInteger dInt(decimals, static_cast<unsigned>(dLen));
|
|
Double a(approx);
|
|
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
|
|
if (cmp < 0)
|
|
@@ -221,46 +225,66 @@ inline double StrtodBigInteger(double approx, const char* decimals, size_t lengt
|
|
return a.NextPositiveDouble();
|
|
}
|
|
|
|
-inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
|
+template<typename Ch>
|
|
+inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) {
|
|
RAPIDJSON_ASSERT(d >= 0.0);
|
|
RAPIDJSON_ASSERT(length >= 1);
|
|
|
|
- double result;
|
|
+ double result = 0.0;
|
|
if (StrtodFast(d, p, &result))
|
|
return result;
|
|
|
|
+ RAPIDJSON_ASSERT(length <= INT_MAX);
|
|
+ int dLen = static_cast<int>(length);
|
|
+
|
|
+ RAPIDJSON_ASSERT(length >= decimalPosition);
|
|
+ RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX);
|
|
+ int dExpAdjust = static_cast<int>(length - decimalPosition);
|
|
+
|
|
+ RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust);
|
|
+ int dExp = exp - dExpAdjust;
|
|
+
|
|
+ // Make sure length+dExp does not overflow
|
|
+ RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen);
|
|
+
|
|
// Trim leading zeros
|
|
- while (*decimals == '0' && length > 1) {
|
|
- length--;
|
|
+ while (dLen > 0 && *decimals == '0') {
|
|
+ dLen--;
|
|
decimals++;
|
|
- decimalPosition--;
|
|
}
|
|
|
|
// Trim trailing zeros
|
|
- while (decimals[length - 1] == '0' && length > 1) {
|
|
- length--;
|
|
- decimalPosition--;
|
|
- exp++;
|
|
+ while (dLen > 0 && decimals[dLen - 1] == '0') {
|
|
+ dLen--;
|
|
+ dExp++;
|
|
+ }
|
|
+
|
|
+ if (dLen == 0) { // Buffer only contains zeros.
|
|
+ return 0.0;
|
|
}
|
|
|
|
// Trim right-most digits
|
|
- const int kMaxDecimalDigit = 780;
|
|
- if (static_cast<int>(length) > kMaxDecimalDigit) {
|
|
- int delta = (static_cast<int>(length) - kMaxDecimalDigit);
|
|
- exp += delta;
|
|
- decimalPosition -= static_cast<unsigned>(delta);
|
|
- length = kMaxDecimalDigit;
|
|
+ const int kMaxDecimalDigit = 767 + 1;
|
|
+ if (dLen > kMaxDecimalDigit) {
|
|
+ dExp += dLen - kMaxDecimalDigit;
|
|
+ dLen = kMaxDecimalDigit;
|
|
}
|
|
|
|
- // If too small, underflow to zero
|
|
- if (int(length) + exp < -324)
|
|
+ // If too small, underflow to zero.
|
|
+ // Any x <= 10^-324 is interpreted as zero.
|
|
+ if (dLen + dExp <= -324)
|
|
return 0.0;
|
|
|
|
- if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result))
|
|
+ // If too large, overflow to infinity.
|
|
+ // Any x >= 10^309 is interpreted as +infinity.
|
|
+ if (dLen + dExp > 309)
|
|
+ return std::numeric_limits<double>::infinity();
|
|
+
|
|
+ if (StrtodDiyFp(decimals, dLen, dExp, &result))
|
|
return result;
|
|
|
|
// Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
|
|
- return StrtodBigInteger(result, decimals, length, decimalPosition, exp);
|
|
+ return StrtodBigInteger(result, decimals, dLen, dExp);
|
|
}
|
|
|
|
} // namespace internal
|
|
diff --git a/src/common/thirdparty/rapidjson/internal/swap.h b/src/common/thirdparty/rapidjson/internal/swap.h
|
|
index 666e49f97..2cf92f93a 100644
|
|
--- a/src/common/thirdparty/rapidjson/internal/swap.h
|
|
+++ b/src/common/thirdparty/rapidjson/internal/swap.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
diff --git a/src/common/thirdparty/rapidjson/istreamwrapper.h b/src/common/thirdparty/rapidjson/istreamwrapper.h
|
|
index f5fe28977..01437ec01 100644
|
|
--- a/src/common/thirdparty/rapidjson/istreamwrapper.h
|
|
+++ b/src/common/thirdparty/rapidjson/istreamwrapper.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -17,13 +17,12 @@
|
|
|
|
#include "stream.h"
|
|
#include <iosfwd>
|
|
+#include <ios>
|
|
|
|
#ifdef __clang__
|
|
RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(padded)
|
|
-#endif
|
|
-
|
|
-#ifdef _MSC_VER
|
|
+#elif defined(_MSC_VER)
|
|
RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized
|
|
#endif
|
|
@@ -50,57 +49,71 @@ template <typename StreamType>
|
|
class BasicIStreamWrapper {
|
|
public:
|
|
typedef typename StreamType::char_type Ch;
|
|
- BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {}
|
|
|
|
- Ch Peek() const {
|
|
- typename StreamType::int_type c = stream_.peek();
|
|
- return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast<Ch>(c) : '\0';
|
|
+ //! Constructor.
|
|
+ /*!
|
|
+ \param stream stream opened for read.
|
|
+ */
|
|
+ BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
|
+ Read();
|
|
}
|
|
|
|
- Ch Take() {
|
|
- typename StreamType::int_type c = stream_.get();
|
|
- if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) {
|
|
- count_++;
|
|
- return static_cast<Ch>(c);
|
|
- }
|
|
- else
|
|
- return '\0';
|
|
+ //! Constructor.
|
|
+ /*!
|
|
+ \param stream stream opened for read.
|
|
+ \param buffer user-supplied buffer.
|
|
+ \param bufferSize size of buffer in bytes. Must >=4 bytes.
|
|
+ */
|
|
+ BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
|
+ RAPIDJSON_ASSERT(bufferSize >= 4);
|
|
+ Read();
|
|
}
|
|
|
|
- // tellg() may return -1 when failed. So we count by ourself.
|
|
- size_t Tell() const { return count_; }
|
|
+ Ch Peek() const { return *current_; }
|
|
+ Ch Take() { Ch c = *current_; Read(); return c; }
|
|
+ size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
|
|
|
|
- Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
|
+ // Not implemented
|
|
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
|
- void Flush() { RAPIDJSON_ASSERT(false); }
|
|
+ void Flush() { RAPIDJSON_ASSERT(false); }
|
|
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
|
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
|
|
|
// For encoding detection only.
|
|
const Ch* Peek4() const {
|
|
- RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream.
|
|
- int i;
|
|
- bool hasError = false;
|
|
- for (i = 0; i < 4; ++i) {
|
|
- typename StreamType::int_type c = stream_.get();
|
|
- if (c == StreamType::traits_type::eof()) {
|
|
- hasError = true;
|
|
- stream_.clear();
|
|
- break;
|
|
- }
|
|
- peekBuffer_[i] = static_cast<Ch>(c);
|
|
- }
|
|
- for (--i; i >= 0; --i)
|
|
- stream_.putback(peekBuffer_[i]);
|
|
- return !hasError ? peekBuffer_ : 0;
|
|
+ return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0;
|
|
}
|
|
|
|
private:
|
|
+ BasicIStreamWrapper();
|
|
BasicIStreamWrapper(const BasicIStreamWrapper&);
|
|
BasicIStreamWrapper& operator=(const BasicIStreamWrapper&);
|
|
|
|
- StreamType& stream_;
|
|
- size_t count_; //!< Number of characters read. Note:
|
|
- mutable Ch peekBuffer_[4];
|
|
+ void Read() {
|
|
+ if (current_ < bufferLast_)
|
|
+ ++current_;
|
|
+ else if (!eof_) {
|
|
+ count_ += readCount_;
|
|
+ readCount_ = bufferSize_;
|
|
+ bufferLast_ = buffer_ + readCount_ - 1;
|
|
+ current_ = buffer_;
|
|
+
|
|
+ if (!stream_.read(buffer_, static_cast<std::streamsize>(bufferSize_))) {
|
|
+ readCount_ = static_cast<size_t>(stream_.gcount());
|
|
+ *(bufferLast_ = buffer_ + readCount_) = '\0';
|
|
+ eof_ = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ StreamType &stream_;
|
|
+ Ch peekBuffer_[4], *buffer_;
|
|
+ size_t bufferSize_;
|
|
+ Ch *bufferLast_;
|
|
+ Ch *current_;
|
|
+ size_t readCount_;
|
|
+ size_t count_; //!< Number of characters read
|
|
+ bool eof_;
|
|
};
|
|
|
|
typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
|
|
diff --git a/src/common/thirdparty/rapidjson/memorybuffer.h b/src/common/thirdparty/rapidjson/memorybuffer.h
|
|
index 39bee1dec..ffbc41ed1 100644
|
|
--- a/src/common/thirdparty/rapidjson/memorybuffer.h
|
|
+++ b/src/common/thirdparty/rapidjson/memorybuffer.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
diff --git a/src/common/thirdparty/rapidjson/memorystream.h b/src/common/thirdparty/rapidjson/memorystream.h
|
|
index 1d71d8a4f..77af6c999 100644
|
|
--- a/src/common/thirdparty/rapidjson/memorystream.h
|
|
+++ b/src/common/thirdparty/rapidjson/memorystream.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
diff --git a/src/common/thirdparty/rapidjson/ostreamwrapper.h b/src/common/thirdparty/rapidjson/ostreamwrapper.h
|
|
index 6f4667c08..11ed4d33f 100644
|
|
--- a/src/common/thirdparty/rapidjson/ostreamwrapper.h
|
|
+++ b/src/common/thirdparty/rapidjson/ostreamwrapper.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
diff --git a/src/common/thirdparty/rapidjson/pointer.h b/src/common/thirdparty/rapidjson/pointer.h
|
|
index 0206ac1c8..05b1704dd 100644
|
|
--- a/src/common/thirdparty/rapidjson/pointer.h
|
|
+++ b/src/common/thirdparty/rapidjson/pointer.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -16,14 +16,14 @@
|
|
#define RAPIDJSON_POINTER_H_
|
|
|
|
#include "document.h"
|
|
+#include "uri.h"
|
|
#include "internal/itoa.h"
|
|
+#include "error/error.h" // PointerParseErrorCode
|
|
|
|
#ifdef __clang__
|
|
RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(switch-enum)
|
|
-#endif
|
|
-
|
|
-#ifdef _MSC_VER
|
|
+#elif defined(_MSC_VER)
|
|
RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
|
#endif
|
|
@@ -32,19 +32,6 @@ RAPIDJSON_NAMESPACE_BEGIN
|
|
|
|
static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token
|
|
|
|
-//! Error code of parsing.
|
|
-/*! \ingroup RAPIDJSON_ERRORS
|
|
- \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode
|
|
-*/
|
|
-enum PointerParseErrorCode {
|
|
- kPointerParseErrorNone = 0, //!< The parse is successful
|
|
-
|
|
- kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/'
|
|
- kPointerParseErrorInvalidEscape, //!< Invalid escape
|
|
- kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment
|
|
- kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment
|
|
-};
|
|
-
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// GenericPointer
|
|
|
|
@@ -70,10 +57,10 @@ enum PointerParseErrorCode {
|
|
supplied tokens eliminates these.
|
|
|
|
GenericPointer depends on GenericDocument and GenericValue.
|
|
-
|
|
+
|
|
\tparam ValueType The value type of the DOM tree. E.g. GenericValue<UTF8<> >
|
|
\tparam Allocator The allocator type for allocating memory for internal representation.
|
|
-
|
|
+
|
|
\note GenericPointer uses same encoding of ValueType.
|
|
However, Allocator of GenericPointer is independent of Allocator of Value.
|
|
*/
|
|
@@ -82,8 +69,10 @@ class GenericPointer {
|
|
public:
|
|
typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value
|
|
typedef typename ValueType::Ch Ch; //!< Character type from Value
|
|
+ typedef GenericUri<ValueType, Allocator> UriType;
|
|
|
|
- //! A token is the basic units of internal representation.
|
|
+
|
|
+ //! A token is the basic units of internal representation.
|
|
/*!
|
|
A JSON pointer string representation "/foo/123" is parsed to two tokens:
|
|
"foo" and 123. 123 will be represented in both numeric form and string form.
|
|
@@ -165,7 +154,12 @@ public:
|
|
GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast<Token*>(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
|
|
|
|
//! Copy constructor.
|
|
- GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
|
|
+ GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
|
|
+ *this = rhs;
|
|
+ }
|
|
+
|
|
+ //! Copy constructor.
|
|
+ GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
|
|
*this = rhs;
|
|
}
|
|
|
|
@@ -197,6 +191,36 @@ public:
|
|
return *this;
|
|
}
|
|
|
|
+ //! Swap the content of this pointer with an other.
|
|
+ /*!
|
|
+ \param other The pointer to swap with.
|
|
+ \note Constant complexity.
|
|
+ */
|
|
+ GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT {
|
|
+ internal::Swap(allocator_, other.allocator_);
|
|
+ internal::Swap(ownAllocator_, other.ownAllocator_);
|
|
+ internal::Swap(nameBuffer_, other.nameBuffer_);
|
|
+ internal::Swap(tokens_, other.tokens_);
|
|
+ internal::Swap(tokenCount_, other.tokenCount_);
|
|
+ internal::Swap(parseErrorOffset_, other.parseErrorOffset_);
|
|
+ internal::Swap(parseErrorCode_, other.parseErrorCode_);
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ //! free-standing swap function helper
|
|
+ /*!
|
|
+ Helper function to enable support for common swap implementation pattern based on \c std::swap:
|
|
+ \code
|
|
+ void swap(MyClass& a, MyClass& b) {
|
|
+ using std::swap;
|
|
+ swap(a.pointer, b.pointer);
|
|
+ // ...
|
|
+ }
|
|
+ \endcode
|
|
+ \see Swap()
|
|
+ */
|
|
+ friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); }
|
|
+
|
|
//@}
|
|
|
|
//!@name Append token
|
|
@@ -240,7 +264,7 @@ public:
|
|
template <typename T>
|
|
RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::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
|
|
@@ -274,7 +298,7 @@ public:
|
|
else {
|
|
Ch name[21];
|
|
for (size_t i = 0; i <= length; i++)
|
|
- name[i] = buffer[i];
|
|
+ name[i] = static_cast<Ch>(buffer[i]);
|
|
Token token = { name, length, index };
|
|
return Append(token, allocator);
|
|
}
|
|
@@ -353,6 +377,33 @@ public:
|
|
*/
|
|
bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); }
|
|
|
|
+ //! Less than operator.
|
|
+ /*!
|
|
+ \note Invalid pointers are always greater than valid ones.
|
|
+ */
|
|
+ bool operator<(const GenericPointer& rhs) const {
|
|
+ if (!IsValid())
|
|
+ return false;
|
|
+ if (!rhs.IsValid())
|
|
+ return true;
|
|
+
|
|
+ if (tokenCount_ != rhs.tokenCount_)
|
|
+ return tokenCount_ < rhs.tokenCount_;
|
|
+
|
|
+ for (size_t i = 0; i < tokenCount_; i++) {
|
|
+ if (tokens_[i].index != rhs.tokens_[i].index)
|
|
+ return tokens_[i].index < rhs.tokens_[i].index;
|
|
+
|
|
+ if (tokens_[i].length != rhs.tokens_[i].length)
|
|
+ return tokens_[i].length < rhs.tokens_[i].length;
|
|
+
|
|
+ if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length))
|
|
+ return cmp < 0;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+
|
|
//@}
|
|
|
|
//!@name Stringify
|
|
@@ -428,10 +479,11 @@ public:
|
|
v = &((*v)[t->index]);
|
|
}
|
|
else {
|
|
- typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
|
|
+ typename ValueType::MemberIterator m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
|
|
if (m == v->MemberEnd()) {
|
|
v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator);
|
|
- v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end
|
|
+ m = v->MemberEnd();
|
|
+ v = &(--m)->value; // Assumes AddMember() appends at the end
|
|
exist = false;
|
|
}
|
|
else
|
|
@@ -459,6 +511,70 @@ public:
|
|
|
|
//@}
|
|
|
|
+ //!@name Compute URI
|
|
+ //@{
|
|
+
|
|
+ //! Compute the in-scope URI for a subtree.
|
|
+ // For use with JSON pointers into JSON schema documents.
|
|
+ /*!
|
|
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
|
|
+ \param rootUri Root URI
|
|
+ \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token.
|
|
+ \param allocator Allocator for Uris
|
|
+ \return Uri if it can be resolved. Otherwise null.
|
|
+
|
|
+ \note
|
|
+ There are only 3 situations when a URI cannot be resolved:
|
|
+ 1. A value in the path is not an array nor object.
|
|
+ 2. An object value does not contain the token.
|
|
+ 3. A token is out of range of an array value.
|
|
+
|
|
+ Use unresolvedTokenIndex to retrieve the token index.
|
|
+ */
|
|
+ UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const {
|
|
+ static const Ch kIdString[] = { 'i', 'd', '\0' };
|
|
+ static const ValueType kIdValue(kIdString, 2);
|
|
+ UriType base = UriType(rootUri, allocator);
|
|
+ RAPIDJSON_ASSERT(IsValid());
|
|
+ ValueType* v = &root;
|
|
+ for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
|
|
+ switch (v->GetType()) {
|
|
+ case kObjectType:
|
|
+ {
|
|
+ // See if we have an id, and if so resolve with the current base
|
|
+ typename ValueType::MemberIterator m = v->FindMember(kIdValue);
|
|
+ if (m != v->MemberEnd() && (m->value).IsString()) {
|
|
+ UriType here = UriType(m->value, allocator).Resolve(base, allocator);
|
|
+ base = here;
|
|
+ }
|
|
+ m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
|
|
+ if (m == v->MemberEnd())
|
|
+ break;
|
|
+ v = &m->value;
|
|
+ }
|
|
+ continue;
|
|
+ case kArrayType:
|
|
+ if (t->index == kPointerInvalidIndex || t->index >= v->Size())
|
|
+ break;
|
|
+ v = &((*v)[t->index]);
|
|
+ continue;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ // Error: unresolved token
|
|
+ if (unresolvedTokenIndex)
|
|
+ *unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
|
|
+ return UriType(allocator);
|
|
+ }
|
|
+ return base;
|
|
+ }
|
|
+
|
|
+ UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const {
|
|
+ return GetUri(const_cast<ValueType&>(root), rootUri, unresolvedTokenIndex, allocator);
|
|
+ }
|
|
+
|
|
+
|
|
//!@name Query value
|
|
//@{
|
|
|
|
@@ -483,7 +599,7 @@ public:
|
|
switch (v->GetType()) {
|
|
case kObjectType:
|
|
{
|
|
- typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
|
|
+ typename ValueType::MemberIterator m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
|
|
if (m == v->MemberEnd())
|
|
break;
|
|
v = &m->value;
|
|
@@ -532,14 +648,14 @@ public:
|
|
*/
|
|
ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const {
|
|
bool alreadyExist;
|
|
- Value& v = Create(root, allocator, &alreadyExist);
|
|
+ ValueType& v = Create(root, allocator, &alreadyExist);
|
|
return alreadyExist ? v : v.CopyFrom(defaultValue, allocator);
|
|
}
|
|
|
|
//! Query a value in a subtree with default null-terminated string.
|
|
ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const {
|
|
bool alreadyExist;
|
|
- Value& v = Create(root, allocator, &alreadyExist);
|
|
+ ValueType& v = Create(root, allocator, &alreadyExist);
|
|
return alreadyExist ? v : v.SetString(defaultValue, allocator);
|
|
}
|
|
|
|
@@ -547,7 +663,7 @@ public:
|
|
//! Query a value in a subtree with default std::basic_string.
|
|
ValueType& GetWithDefault(ValueType& root, const std::basic_string<Ch>& defaultValue, typename ValueType::AllocatorType& allocator) const {
|
|
bool alreadyExist;
|
|
- Value& v = Create(root, allocator, &alreadyExist);
|
|
+ ValueType& v = Create(root, allocator, &alreadyExist);
|
|
return alreadyExist ? v : v.SetString(defaultValue, allocator);
|
|
}
|
|
#endif
|
|
@@ -573,7 +689,7 @@ public:
|
|
ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* defaultValue) const {
|
|
return GetWithDefault(document, defaultValue, document.GetAllocator());
|
|
}
|
|
-
|
|
+
|
|
#if RAPIDJSON_HAS_STDSTRING
|
|
//! Query a value in a document with default std::basic_string.
|
|
template <typename stackAllocator>
|
|
@@ -719,7 +835,7 @@ public:
|
|
switch (v->GetType()) {
|
|
case kObjectType:
|
|
{
|
|
- typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
|
|
+ typename ValueType::MemberIterator m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
|
|
if (m == v->MemberEnd())
|
|
return false;
|
|
v = &m->value;
|
|
@@ -758,7 +874,7 @@ private:
|
|
*/
|
|
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 +922,7 @@ private:
|
|
|
|
// 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;
|
|
@@ -867,7 +983,7 @@ private:
|
|
}
|
|
|
|
i++;
|
|
-
|
|
+
|
|
// Escaping "~0" -> '~', "~1" -> '/'
|
|
if (c == '~') {
|
|
if (i < length) {
|
|
@@ -1029,8 +1145,8 @@ private:
|
|
unsigned char u = static_cast<unsigned char>(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<typename OutputStream::Ch>(hexDigits[u >> 4]));
|
|
+ os_.Put(static_cast<typename OutputStream::Ch>(hexDigits[u & 15]));
|
|
}
|
|
private:
|
|
OutputStream& os_;
|
|
@@ -1347,11 +1463,7 @@ bool EraseValueByPointer(T& root, const CharType(&source)[N]) {
|
|
|
|
RAPIDJSON_NAMESPACE_END
|
|
|
|
-#ifdef __clang__
|
|
-RAPIDJSON_DIAG_POP
|
|
-#endif
|
|
-
|
|
-#ifdef _MSC_VER
|
|
+#if defined(__clang__) || defined(_MSC_VER)
|
|
RAPIDJSON_DIAG_POP
|
|
#endif
|
|
|
|
diff --git a/src/common/thirdparty/rapidjson/prettywriter.h b/src/common/thirdparty/rapidjson/prettywriter.h
|
|
index c6f0216e9..fe45df1d1 100644
|
|
--- a/src/common/thirdparty/rapidjson/prettywriter.h
|
|
+++ b/src/common/thirdparty/rapidjson/prettywriter.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -22,6 +22,11 @@ RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(effc++)
|
|
#endif
|
|
|
|
+#if defined(__clang__)
|
|
+RAPIDJSON_DIAG_PUSH
|
|
+RAPIDJSON_DIAG_OFF(c++98-compat)
|
|
+#endif
|
|
+
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
|
|
//! Combination of PrettyWriter format flags.
|
|
@@ -34,7 +39,7 @@ enum PrettyFormatOptions {
|
|
|
|
//! Writer with indentation and spacing.
|
|
/*!
|
|
- \tparam OutputStream Type of ouptut os.
|
|
+ \tparam OutputStream Type of output os.
|
|
\tparam SourceEncoding Encoding of source string.
|
|
\tparam TargetEncoding Encoding of output stream.
|
|
\tparam StackAllocator Type of allocator for allocating memory of stack.
|
|
@@ -42,7 +47,7 @@ enum PrettyFormatOptions {
|
|
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
|
|
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
|
|
public:
|
|
- typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base;
|
|
+ typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> Base;
|
|
typedef typename Base::Ch Ch;
|
|
|
|
//! Constructor
|
|
@@ -55,7 +60,12 @@ public:
|
|
|
|
|
|
explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
|
- Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
|
|
+ Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
|
|
+
|
|
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
|
+ PrettyWriter(PrettyWriter&& rhs) :
|
|
+ Base(std::forward<PrettyWriter>(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {}
|
|
+#endif
|
|
|
|
//! Set custom indentation.
|
|
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
|
|
@@ -82,26 +92,26 @@ public:
|
|
*/
|
|
//@{
|
|
|
|
- bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
|
|
- bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
|
|
- bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
|
|
- bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
|
|
- bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
|
|
- bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
|
|
- bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
|
|
+ bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); }
|
|
+ bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); }
|
|
+ bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); }
|
|
+ bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); }
|
|
+ bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); }
|
|
+ bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); }
|
|
+ bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); }
|
|
|
|
bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
|
|
RAPIDJSON_ASSERT(str != 0);
|
|
(void)copy;
|
|
PrettyPrefix(kNumberType);
|
|
- return Base::WriteString(str, length);
|
|
+ return Base::EndValue(Base::WriteString(str, length));
|
|
}
|
|
|
|
bool String(const Ch* str, SizeType length, bool copy = false) {
|
|
RAPIDJSON_ASSERT(str != 0);
|
|
(void)copy;
|
|
PrettyPrefix(kStringType);
|
|
- return Base::WriteString(str, length);
|
|
+ return Base::EndValue(Base::WriteString(str, length));
|
|
}
|
|
|
|
#if RAPIDJSON_HAS_STDSTRING
|
|
@@ -126,19 +136,21 @@ public:
|
|
|
|
bool EndObject(SizeType memberCount = 0) {
|
|
(void)memberCount;
|
|
- RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
|
- RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
|
+ RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object
|
|
+ RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); // currently inside an Array, not Object
|
|
+ RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top<typename Base::Level>()->valueCount % 2); // Object has a Key without a Value
|
|
+
|
|
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
|
|
|
if (!empty) {
|
|
Base::os_->Put('\n');
|
|
WriteIndent();
|
|
}
|
|
- bool ret = Base::WriteEndObject();
|
|
+ bool ret = Base::EndValue(Base::WriteEndObject());
|
|
(void)ret;
|
|
RAPIDJSON_ASSERT(ret == true);
|
|
if (Base::level_stack_.Empty()) // end of json text
|
|
- Base::os_->Flush();
|
|
+ Base::Flush();
|
|
return true;
|
|
}
|
|
|
|
@@ -158,11 +170,11 @@ public:
|
|
Base::os_->Put('\n');
|
|
WriteIndent();
|
|
}
|
|
- bool ret = Base::WriteEndArray();
|
|
+ bool ret = Base::EndValue(Base::WriteEndArray());
|
|
(void)ret;
|
|
RAPIDJSON_ASSERT(ret == true);
|
|
if (Base::level_stack_.Empty()) // end of json text
|
|
- Base::os_->Flush();
|
|
+ Base::Flush();
|
|
return true;
|
|
}
|
|
|
|
@@ -189,7 +201,7 @@ public:
|
|
bool RawValue(const Ch* json, size_t length, Type type) {
|
|
RAPIDJSON_ASSERT(json != 0);
|
|
PrettyPrefix(type);
|
|
- return Base::WriteRawValue(json, length);
|
|
+ return Base::EndValue(Base::WriteRawValue(json, length));
|
|
}
|
|
|
|
protected:
|
|
@@ -239,7 +251,7 @@ protected:
|
|
|
|
void WriteIndent() {
|
|
size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
|
|
- PutN(*Base::os_, static_cast<typename TargetEncoding::Ch>(indentChar_), count);
|
|
+ PutN(*Base::os_, static_cast<typename OutputStream::Ch>(indentChar_), count);
|
|
}
|
|
|
|
Ch indentChar_;
|
|
@@ -254,6 +266,10 @@ private:
|
|
|
|
RAPIDJSON_NAMESPACE_END
|
|
|
|
+#if defined(__clang__)
|
|
+RAPIDJSON_DIAG_POP
|
|
+#endif
|
|
+
|
|
#ifdef __GNUC__
|
|
RAPIDJSON_DIAG_POP
|
|
#endif
|
|
diff --git a/src/common/thirdparty/rapidjson/rapidjson.h b/src/common/thirdparty/rapidjson/rapidjson.h
|
|
index d5b7630b5..5ea694795 100644
|
|
--- a/src/common/thirdparty/rapidjson/rapidjson.h
|
|
+++ b/src/common/thirdparty/rapidjson/rapidjson.h
|
|
@@ -1,15 +1,15 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
-//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+//
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://opensource.org/licenses/MIT
|
|
//
|
|
-// Unless required by applicable law or agreed to in writing, software distributed
|
|
-// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
-// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
#ifndef RAPIDJSON_RAPIDJSON_H_
|
|
@@ -17,7 +17,7 @@
|
|
|
|
/*!\file rapidjson.h
|
|
\brief common definitions and configuration
|
|
-
|
|
+
|
|
\see RAPIDJSON_CONFIG
|
|
*/
|
|
|
|
@@ -26,7 +26,7 @@
|
|
|
|
Some RapidJSON features are configurable to adapt the library to a wide
|
|
variety of platforms, environments and usage scenarios. Most of the
|
|
- features can be configured in terms of overriden or predefined
|
|
+ features can be configured in terms of overridden or predefined
|
|
preprocessor macros at compile-time.
|
|
|
|
Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs.
|
|
@@ -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
|
|
@@ -119,6 +124,19 @@
|
|
#define RAPIDJSON_NAMESPACE_END }
|
|
#endif
|
|
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// __cplusplus macro
|
|
+
|
|
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
|
+
|
|
+#if defined(_MSC_VER)
|
|
+#define RAPIDJSON_CPLUSPLUS _MSVC_LANG
|
|
+#else
|
|
+#define RAPIDJSON_CPLUSPLUS __cplusplus
|
|
+#endif
|
|
+
|
|
+//!@endcond
|
|
+
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RAPIDJSON_HAS_STDSTRING
|
|
|
|
@@ -144,6 +162,24 @@
|
|
#include <string>
|
|
#endif // RAPIDJSON_HAS_STDSTRING
|
|
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// RAPIDJSON_USE_MEMBERSMAP
|
|
+
|
|
+/*! \def RAPIDJSON_USE_MEMBERSMAP
|
|
+ \ingroup RAPIDJSON_CONFIG
|
|
+ \brief Enable RapidJSON support for object members handling in a \c std::multimap
|
|
+
|
|
+ By defining this preprocessor symbol to \c 1, \ref rapidjson::GenericValue object
|
|
+ members are stored in a \c std::multimap for faster lookup and deletion times, a
|
|
+ trade off with a slightly slower insertion time and a small object allocat(or)ed
|
|
+ memory overhead.
|
|
+
|
|
+ \hideinitializer
|
|
+*/
|
|
+#ifndef RAPIDJSON_USE_MEMBERSMAP
|
|
+#define RAPIDJSON_USE_MEMBERSMAP 0 // not by default
|
|
+#endif
|
|
+
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RAPIDJSON_NO_INT64DEFINE
|
|
|
|
@@ -159,7 +195,7 @@
|
|
*/
|
|
#ifndef RAPIDJSON_NO_INT64DEFINE
|
|
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
|
-#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013
|
|
+#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013
|
|
#include "msinttypes/stdint.h"
|
|
#include "msinttypes/inttypes.h"
|
|
#else
|
|
@@ -214,7 +250,7 @@
|
|
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
|
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
|
# else
|
|
-# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
|
|
+# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN.
|
|
# endif // __BYTE_ORDER__
|
|
// Detect with GLIBC's endian.h
|
|
# elif defined(__GLIBC__)
|
|
@@ -224,7 +260,7 @@
|
|
# elif (__BYTE_ORDER == __BIG_ENDIAN)
|
|
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
|
# else
|
|
-# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
|
|
+# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN.
|
|
# endif // __GLIBC__
|
|
// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
|
|
# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
|
|
@@ -236,14 +272,12 @@
|
|
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
|
# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
|
|
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
|
-# elif defined(_MSC_VER) && defined(_M_ARM)
|
|
-# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
|
-# elif defined(_MSC_VER) && defined(_M_ARM64)
|
|
+# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
|
|
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
|
# elif defined(RAPIDJSON_DOXYGEN_RUNNING)
|
|
# define RAPIDJSON_ENDIAN
|
|
# else
|
|
-# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
|
|
+# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN.
|
|
# endif
|
|
#endif // RAPIDJSON_ENDIAN
|
|
|
|
@@ -266,16 +300,11 @@
|
|
/*! \ingroup RAPIDJSON_CONFIG
|
|
\param x pointer to align
|
|
|
|
- Some machines require strict data alignment. Currently the default uses 4 bytes
|
|
- alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms.
|
|
+ Some machines require strict data alignment. The default is 8 bytes.
|
|
User can customize by defining the RAPIDJSON_ALIGN function macro.
|
|
*/
|
|
#ifndef RAPIDJSON_ALIGN
|
|
-#if RAPIDJSON_64BIT == 1
|
|
-#define RAPIDJSON_ALIGN(x) (((x) + static_cast<uint64_t>(7u)) & ~static_cast<uint64_t>(7u))
|
|
-#else
|
|
-#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u)
|
|
-#endif
|
|
+#define RAPIDJSON_ALIGN(x) (((x) + static_cast<size_t>(7u)) & ~static_cast<size_t>(7u))
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -322,17 +351,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
|
|
@@ -341,13 +370,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
|
|
|
|
@@ -407,7 +440,15 @@ RAPIDJSON_NAMESPACE_END
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RAPIDJSON_STATIC_ASSERT
|
|
|
|
-// Adopt from boost
|
|
+// Prefer C++11 static_assert, if available
|
|
+#ifndef RAPIDJSON_STATIC_ASSERT
|
|
+#if RAPIDJSON_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
|
|
@@ -415,14 +456,10 @@ RAPIDJSON_NAMESPACE_END
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
template <bool x> struct STATIC_ASSERTION_FAILURE;
|
|
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
|
|
-template<int x> struct StaticAssertTest {};
|
|
+template <size_t x> 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__)
|
|
+#if defined(__GNUC__) || defined(__clang__)
|
|
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
|
|
#else
|
|
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
|
@@ -440,7 +477,7 @@ RAPIDJSON_NAMESPACE_END
|
|
typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \
|
|
sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
|
|
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
|
-#endif
|
|
+#endif // RAPIDJSON_STATIC_ASSERT
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY
|
|
@@ -476,7 +513,7 @@ RAPIDJSON_NAMESPACE_END
|
|
|
|
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
|
|
|
-#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
|
|
+#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
|
|
#define RAPIDJSON_MULTILINEMACRO_END \
|
|
} while((void)0, 0)
|
|
|
|
@@ -484,6 +521,12 @@ RAPIDJSON_NAMESPACE_END
|
|
#define RAPIDJSON_VERSION_CODE(x,y,z) \
|
|
(((x)*100000) + ((y)*100) + (z))
|
|
|
|
+#if defined(__has_builtin)
|
|
+#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x)
|
|
+#else
|
|
+#define RAPIDJSON_HAS_BUILTIN(x) 0
|
|
+#endif
|
|
+
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
|
|
|
|
@@ -529,16 +572,23 @@ RAPIDJSON_NAMESPACE_END
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// C++11 features
|
|
|
|
+#ifndef RAPIDJSON_HAS_CXX11
|
|
+#define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L)
|
|
+#endif
|
|
+
|
|
#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
|
-#if defined(__clang__)
|
|
+#if RAPIDJSON_HAS_CXX11
|
|
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
|
+#elif defined(__clang__)
|
|
#if __has_feature(cxx_rvalue_references) && \
|
|
- (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
|
|
+ (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
|
|
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
|
#else
|
|
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
|
|
#endif
|
|
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
|
- (defined(_MSC_VER) && _MSC_VER >= 1600)
|
|
+ (defined(_MSC_VER) && _MSC_VER >= 1600) || \
|
|
+ (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
|
|
|
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
|
#else
|
|
@@ -546,46 +596,120 @@ RAPIDJSON_NAMESPACE_END
|
|
#endif
|
|
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
|
|
|
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
|
+#include <utility> // std::move
|
|
+#endif
|
|
+
|
|
#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
|
|
-#if defined(__clang__)
|
|
+#if RAPIDJSON_HAS_CXX11
|
|
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
|
|
+#elif defined(__clang__)
|
|
#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
|
|
-#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
|
-// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported
|
|
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
|
+ (defined(_MSC_VER) && _MSC_VER >= 1900) || \
|
|
+ (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
|
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
|
|
#else
|
|
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
|
|
#endif
|
|
#endif
|
|
+#ifndef RAPIDJSON_NOEXCEPT
|
|
#if RAPIDJSON_HAS_CXX11_NOEXCEPT
|
|
#define RAPIDJSON_NOEXCEPT noexcept
|
|
#else
|
|
-#define RAPIDJSON_NOEXCEPT /* noexcept */
|
|
+#define RAPIDJSON_NOEXCEPT throw()
|
|
#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
|
|
+#endif
|
|
|
|
// no automatic detection, yet
|
|
#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
|
|
+#if (defined(_MSC_VER) && _MSC_VER >= 1700)
|
|
+#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1
|
|
+#else
|
|
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
|
|
#endif
|
|
+#endif
|
|
|
|
#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__)) || \
|
|
- (defined(_MSC_VER) && _MSC_VER >= 1700)
|
|
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
|
+ (defined(_MSC_VER) && _MSC_VER >= 1700) || \
|
|
+ (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
|
#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
|
|
#else
|
|
#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0
|
|
#endif
|
|
#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR
|
|
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// C++17 features
|
|
+
|
|
+#ifndef RAPIDJSON_HAS_CXX17
|
|
+#define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L)
|
|
+#endif
|
|
+
|
|
+#if RAPIDJSON_HAS_CXX17
|
|
+# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]]
|
|
+#elif defined(__has_cpp_attribute)
|
|
+# if __has_cpp_attribute(clang::fallthrough)
|
|
+# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]]
|
|
+# elif __has_cpp_attribute(fallthrough)
|
|
+# define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough))
|
|
+# else
|
|
+# define RAPIDJSON_DELIBERATE_FALLTHROUGH
|
|
+# endif
|
|
+#else
|
|
+# define RAPIDJSON_DELIBERATE_FALLTHROUGH
|
|
+#endif
|
|
+
|
|
//!@endcond
|
|
|
|
+//! Assertion (in non-throwing contexts).
|
|
+ /*! \ingroup RAPIDJSON_CONFIG
|
|
+ Some functions provide a \c noexcept guarantee, if the compiler supports it.
|
|
+ In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to
|
|
+ throw an exception. This macro adds a separate customization point for
|
|
+ such cases.
|
|
+
|
|
+ Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is
|
|
+ supported, and to \ref RAPIDJSON_ASSERT otherwise.
|
|
+ */
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// RAPIDJSON_NOEXCEPT_ASSERT
|
|
+
|
|
+#ifndef RAPIDJSON_NOEXCEPT_ASSERT
|
|
+#ifdef RAPIDJSON_ASSERT_THROWS
|
|
+#include <cassert>
|
|
+#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x)
|
|
+#else
|
|
+#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x)
|
|
+#endif // RAPIDJSON_ASSERT_THROWS
|
|
+#endif // RAPIDJSON_NOEXCEPT_ASSERT
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// malloc/realloc/free
|
|
+
|
|
+#ifndef RAPIDJSON_MALLOC
|
|
+///! customization point for global \c malloc
|
|
+#define RAPIDJSON_MALLOC(size) std::malloc(size)
|
|
+#endif
|
|
+#ifndef RAPIDJSON_REALLOC
|
|
+///! customization point for global \c realloc
|
|
+#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size)
|
|
+#endif
|
|
+#ifndef RAPIDJSON_FREE
|
|
+///! customization point for global \c free
|
|
+#define RAPIDJSON_FREE(ptr) std::free(ptr)
|
|
+#endif
|
|
+
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// new/delete
|
|
|
|
#ifndef RAPIDJSON_NEW
|
|
///! customization point for global \c new
|
|
-#define RAPIDJSON_NEW(x) new x
|
|
+#define RAPIDJSON_NEW(TypeName) new TypeName
|
|
#endif
|
|
#ifndef RAPIDJSON_DELETE
|
|
///! customization point for global \c delete
|
|
@@ -607,7 +731,7 @@ enum Type {
|
|
kFalseType = 1, //!< false
|
|
kTrueType = 2, //!< true
|
|
kObjectType = 3, //!< object
|
|
- kArrayType = 4, //!< array
|
|
+ kArrayType = 4, //!< array
|
|
kStringType = 5, //!< string
|
|
kNumberType = 6 //!< number
|
|
};
|
|
diff --git a/src/common/thirdparty/rapidjson/reader.h b/src/common/thirdparty/rapidjson/reader.h
|
|
index 19f8849b1..55546601e 100644
|
|
--- a/src/common/thirdparty/rapidjson/reader.h
|
|
+++ b/src/common/thirdparty/rapidjson/reader.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -20,6 +20,7 @@
|
|
#include "allocators.h"
|
|
#include "stream.h"
|
|
#include "encodedstream.h"
|
|
+#include "internal/clzll.h"
|
|
#include "internal/meta.h"
|
|
#include "internal/stack.h"
|
|
#include "internal/strtod.h"
|
|
@@ -33,12 +34,8 @@
|
|
#include <nmmintrin.h>
|
|
#elif defined(RAPIDJSON_SSE2)
|
|
#include <emmintrin.h>
|
|
-#endif
|
|
-
|
|
-#ifdef _MSC_VER
|
|
-RAPIDJSON_DIAG_PUSH
|
|
-RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
|
-RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
|
+#elif defined(RAPIDJSON_NEON)
|
|
+#include <arm_neon.h>
|
|
#endif
|
|
|
|
#ifdef __clang__
|
|
@@ -46,6 +43,10 @@ RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(old-style-cast)
|
|
RAPIDJSON_DIAG_OFF(padded)
|
|
RAPIDJSON_DIAG_OFF(switch-enum)
|
|
+#elif defined(_MSC_VER)
|
|
+RAPIDJSON_DIAG_PUSH
|
|
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
|
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
|
#endif
|
|
|
|
#ifdef __GNUC__
|
|
@@ -153,6 +154,7 @@ enum ParseFlag {
|
|
kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings.
|
|
kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
|
|
kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles.
|
|
+ kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings.
|
|
kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS
|
|
};
|
|
|
|
@@ -299,16 +301,9 @@ inline const char *SkipWhitespace_SIMD(const char* p) {
|
|
|
|
for (;; p += 16) {
|
|
const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
|
|
- const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY));
|
|
- if (r != 0) { // some of characters is non-whitespace
|
|
-#ifdef _MSC_VER // Find the index of first non-whitespace
|
|
- unsigned long offset;
|
|
- _BitScanForward(&offset, r);
|
|
- return p + offset;
|
|
-#else
|
|
- return p + __builtin_ffs(r) - 1;
|
|
-#endif
|
|
- }
|
|
+ const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY);
|
|
+ if (r != 16) // some of characters is non-whitespace
|
|
+ return p + r;
|
|
}
|
|
}
|
|
|
|
@@ -325,16 +320,9 @@ inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
|
|
|
|
for (; p <= end - 16; p += 16) {
|
|
const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
|
|
- const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY));
|
|
- if (r != 0) { // some of characters is non-whitespace
|
|
-#ifdef _MSC_VER // Find the index of first non-whitespace
|
|
- unsigned long offset;
|
|
- _BitScanForward(&offset, r);
|
|
- return p + offset;
|
|
-#else
|
|
- return p + __builtin_ffs(r) - 1;
|
|
-#endif
|
|
- }
|
|
+ const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY);
|
|
+ if (r != 16) // some of characters is non-whitespace
|
|
+ return p + r;
|
|
}
|
|
|
|
return SkipWhitespace(p, end);
|
|
@@ -425,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<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~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<const uint8_t *>(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(vreinterpretq_u64_u8(x), 0); // extract
|
|
+ uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract
|
|
+
|
|
+ if (low == 0) {
|
|
+ if (high != 0) {
|
|
+ uint32_t lz = internal::clzll(high);
|
|
+ return p + 8 + (lz >> 3);
|
|
+ }
|
|
+ } else {
|
|
+ uint32_t lz = internal::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<const uint8_t *>(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(vreinterpretq_u64_u8(x), 0); // extract
|
|
+ uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract
|
|
+
|
|
+ if (low == 0) {
|
|
+ if (high != 0) {
|
|
+ uint32_t lz = internal::clzll(high);
|
|
+ return p + 8 + (lz >> 3);
|
|
+ }
|
|
+ } else {
|
|
+ uint32_t lz = internal::clzll(low);
|
|
+ return p + (lz >> 3);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return SkipWhitespace(p, end);
|
|
+}
|
|
+
|
|
+#endif // RAPIDJSON_NEON
|
|
|
|
#ifdef RAPIDJSON_SIMD
|
|
//! Template function specialization for InsituStringStream
|
|
@@ -471,7 +544,8 @@ public:
|
|
/*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
|
|
\param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing)
|
|
*/
|
|
- GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {}
|
|
+ GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) :
|
|
+ stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {}
|
|
|
|
//! Parse JSON text.
|
|
/*! \tparam parseFlags Combination of \ref ParseFlag.
|
|
@@ -527,7 +601,84 @@ public:
|
|
return Parse<kParseDefaultFlags>(is, handler);
|
|
}
|
|
|
|
- //! Whether a parse error has occured in the last parsing.
|
|
+ //! 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 <unsigned parseFlags, typename InputStream, typename Handler>
|
|
+ bool IterativeParseNext(InputStream& is, Handler& handler) {
|
|
+ while (RAPIDJSON_LIKELY(is.Peek() != '\0')) {
|
|
+ SkipWhitespaceAndComments<parseFlags>(is);
|
|
+
|
|
+ Token t = Tokenize(is.Peek());
|
|
+ IterativeParsingState n = Predict(state_, t);
|
|
+ IterativeParsingState d = Transit<parseFlags>(state_, t, n, is, handler);
|
|
+
|
|
+ // If we've finished or hit an error...
|
|
+ if (RAPIDJSON_UNLIKELY(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)) {
|
|
+ // ... and extra non-whitespace data is found...
|
|
+ SkipWhitespaceAndComments<parseFlags>(is);
|
|
+ if (is.Peek() != '\0') {
|
|
+ // ... this is considered an error.
|
|
+ HandleError(state_, is);
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Success! We are done!
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ // 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;
|
|
+ }
|
|
+
|
|
+ // We reached the end of file.
|
|
+ stack_.Clear();
|
|
+
|
|
+ if (state_ != IterativeParsingFinishState) {
|
|
+ HandleError(state_, is);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ //! Check if token-by-token parsing JSON text is complete
|
|
+ /*! \return Whether the JSON has been fully decoded.
|
|
+ */
|
|
+ RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const {
|
|
+ return IsIterativeParsingCompleteState(state_);
|
|
+ }
|
|
+
|
|
+ //! Whether a parse error has occurred in the last parsing.
|
|
bool HasParseError() const { return parseResult_.IsError(); }
|
|
|
|
//! Get the \ref ParseErrorCode of last parsing.
|
|
@@ -575,7 +726,7 @@ private:
|
|
}
|
|
}
|
|
else if (RAPIDJSON_LIKELY(Consume(is, '/')))
|
|
- while (is.Peek() != '\0' && is.Take() != '\n');
|
|
+ while (is.Peek() != '\0' && is.Take() != '\n') {}
|
|
else
|
|
RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
|
|
|
|
@@ -750,7 +901,7 @@ private:
|
|
return false;
|
|
}
|
|
|
|
- // Helper function to parse four hexidecimal digits in \uXXXX in ParseString().
|
|
+ // Helper function to parse four hexadecimal digits in \uXXXX in ParseString().
|
|
template<typename InputStream>
|
|
unsigned ParseHex4(InputStream& is, size_t escapeOffset) {
|
|
unsigned codepoint = 0;
|
|
@@ -841,7 +992,7 @@ private:
|
|
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
|
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
static const char escape[256] = {
|
|
- Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/',
|
|
+ Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/',
|
|
Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
|
|
0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0,
|
|
0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
@@ -857,26 +1008,38 @@ private:
|
|
|
|
Ch c = is.Peek();
|
|
if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape
|
|
- size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset
|
|
+ size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset
|
|
is.Take();
|
|
Ch e = is.Peek();
|
|
if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast<unsigned char>(e)])) {
|
|
is.Take();
|
|
os.Put(static_cast<typename TEncoding::Ch>(escape[static_cast<unsigned char>(e)]));
|
|
}
|
|
+ else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe
|
|
+ is.Take();
|
|
+ os.Put('\'');
|
|
+ }
|
|
else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode
|
|
is.Take();
|
|
unsigned codepoint = ParseHex4(is, escapeOffset);
|
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
|
- if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) {
|
|
- // Handle UTF-16 surrogate pair
|
|
- if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u')))
|
|
- RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
|
|
- unsigned codepoint2 = ParseHex4(is, escapeOffset);
|
|
- RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
|
- if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF))
|
|
+ if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) {
|
|
+ // high surrogate, check if followed by valid low surrogate
|
|
+ if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) {
|
|
+ // Handle UTF-16 surrogate pair
|
|
+ if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u')))
|
|
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
|
|
+ unsigned codepoint2 = ParseHex4(is, escapeOffset);
|
|
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
|
+ if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF))
|
|
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
|
|
+ codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000;
|
|
+ }
|
|
+ // single low surrogate
|
|
+ else
|
|
+ {
|
|
RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
|
|
- codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000;
|
|
+ }
|
|
}
|
|
TEncoding::Encode(os, codepoint);
|
|
}
|
|
@@ -892,7 +1055,7 @@ private:
|
|
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();
|
|
@@ -927,7 +1090,7 @@ private:
|
|
// 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<const __m128i *>(&dquote[0]));
|
|
const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
|
|
const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
|
|
@@ -936,7 +1099,7 @@ private:
|
|
const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(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<unsigned short>(_mm_movemask_epi8(x));
|
|
if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
|
|
@@ -948,11 +1111,13 @@ private:
|
|
#else
|
|
length = static_cast<SizeType>(__builtin_ffs(r) - 1);
|
|
#endif
|
|
- char* q = reinterpret_cast<char*>(os.Push(length));
|
|
- for (size_t i = 0; i < length; i++)
|
|
- q[i] = p[i];
|
|
+ if (length != 0) {
|
|
+ char* q = reinterpret_cast<char*>(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);
|
|
@@ -988,7 +1153,7 @@ private:
|
|
// 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<const __m128i *>(&dquote[0]));
|
|
const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
|
|
const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
|
|
@@ -997,7 +1162,7 @@ private:
|
|
const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(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<unsigned short>(_mm_movemask_epi8(x));
|
|
if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
|
|
@@ -1036,7 +1201,7 @@ private:
|
|
// 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<const __m128i *>(&dquote[0]));
|
|
const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
|
|
const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
|
|
@@ -1045,7 +1210,7 @@ private:
|
|
const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(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<unsigned short>(_mm_movemask_epi8(x));
|
|
if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
|
|
@@ -1064,27 +1229,199 @@ private:
|
|
|
|
is.src_ = is.dst_ = p;
|
|
}
|
|
-#endif
|
|
+#elif defined(RAPIDJSON_NEON)
|
|
+ // StringStream -> StackStream<char>
|
|
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream<char>& 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<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
|
|
+ while (p != nextAligned)
|
|
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
|
|
+ is.src_ = p;
|
|
+ return;
|
|
+ }
|
|
+ else
|
|
+ os.Put(*p++);
|
|
|
|
- template<typename InputStream, bool backup, bool pushOnTake>
|
|
+ // 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<const uint8_t *>(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(vreinterpretq_u64_u8(x), 0); // extract
|
|
+ uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract
|
|
+
|
|
+ SizeType length = 0;
|
|
+ bool escaped = false;
|
|
+ if (low == 0) {
|
|
+ if (high != 0) {
|
|
+ uint32_t lz = internal::clzll(high);
|
|
+ length = 8 + (lz >> 3);
|
|
+ escaped = true;
|
|
+ }
|
|
+ } else {
|
|
+ uint32_t lz = internal::clzll(low);
|
|
+ length = lz >> 3;
|
|
+ escaped = true;
|
|
+ }
|
|
+ if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped
|
|
+ if (length != 0) {
|
|
+ char* q = reinterpret_cast<char*>(os.Push(length));
|
|
+ for (size_t i = 0; i < length; i++)
|
|
+ q[i] = p[i];
|
|
+
|
|
+ p += length;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ vst1q_u8(reinterpret_cast<uint8_t *>(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<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
|
|
+ while (p != nextAligned)
|
|
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*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<uint8_t *>(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(vreinterpretq_u64_u8(x), 0); // extract
|
|
+ uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract
|
|
+
|
|
+ SizeType length = 0;
|
|
+ bool escaped = false;
|
|
+ if (low == 0) {
|
|
+ if (high != 0) {
|
|
+ uint32_t lz = internal::clzll(high);
|
|
+ length = 8 + (lz >> 3);
|
|
+ escaped = true;
|
|
+ }
|
|
+ } else {
|
|
+ uint32_t lz = internal::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<uint8_t *>(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<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
|
|
+ for (; p != nextAligned; p++)
|
|
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*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<uint8_t *>(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(vreinterpretq_u64_u8(x), 0); // extract
|
|
+ uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract
|
|
+
|
|
+ if (low == 0) {
|
|
+ if (high != 0) {
|
|
+ uint32_t lz = internal::clzll(high);
|
|
+ p += 8 + (lz >> 3);
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ uint32_t lz = internal::clzll(low);
|
|
+ p += lz >> 3;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ is.src_ = is.dst_ = p;
|
|
+ }
|
|
+#endif // RAPIDJSON_NEON
|
|
+
|
|
+ template<typename InputStream, typename StackCharacter, bool backup, bool pushOnTake>
|
|
class NumberStream;
|
|
|
|
- template<typename InputStream>
|
|
- class NumberStream<InputStream, false, false> {
|
|
+ template<typename InputStream, typename StackCharacter>
|
|
+ class NumberStream<InputStream, StackCharacter, false, false> {
|
|
public:
|
|
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(); }
|
|
RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); }
|
|
- RAPIDJSON_FORCEINLINE void Push(char) {}
|
|
+ RAPIDJSON_FORCEINLINE void Push(char) {}
|
|
|
|
size_t Tell() { return is.Tell(); }
|
|
size_t Length() { return 0; }
|
|
- const char* Pop() { return 0; }
|
|
+ const StackCharacter* Pop() { return 0; }
|
|
|
|
protected:
|
|
NumberStream& operator=(const NumberStream&);
|
|
@@ -1092,47 +1429,47 @@ private:
|
|
InputStream& is;
|
|
};
|
|
|
|
- template<typename InputStream>
|
|
- class NumberStream<InputStream, true, false> : public NumberStream<InputStream, false, false> {
|
|
- typedef NumberStream<InputStream, false, false> Base;
|
|
+ template<typename InputStream, typename StackCharacter>
|
|
+ class NumberStream<InputStream, StackCharacter, true, false> : public NumberStream<InputStream, StackCharacter, false, false> {
|
|
+ typedef NumberStream<InputStream, StackCharacter, false, false> Base;
|
|
public:
|
|
- NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {}
|
|
- ~NumberStream() {}
|
|
+ NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s), stackStream(reader.stack_) {}
|
|
|
|
RAPIDJSON_FORCEINLINE Ch TakePush() {
|
|
- stackStream.Put(static_cast<char>(Base::is.Peek()));
|
|
+ stackStream.Put(static_cast<StackCharacter>(Base::is.Peek()));
|
|
return Base::is.Take();
|
|
}
|
|
|
|
- RAPIDJSON_FORCEINLINE void Push(char c) {
|
|
+ RAPIDJSON_FORCEINLINE void Push(StackCharacter c) {
|
|
stackStream.Put(c);
|
|
}
|
|
|
|
size_t Length() { return stackStream.Length(); }
|
|
|
|
- const char* Pop() {
|
|
+ const StackCharacter* Pop() {
|
|
stackStream.Put('\0');
|
|
return stackStream.Pop();
|
|
}
|
|
|
|
private:
|
|
- StackStream<char> stackStream;
|
|
+ StackStream<StackCharacter> stackStream;
|
|
};
|
|
|
|
- template<typename InputStream>
|
|
- class NumberStream<InputStream, true, true> : public NumberStream<InputStream, true, false> {
|
|
- typedef NumberStream<InputStream, true, false> Base;
|
|
+ template<typename InputStream, typename StackCharacter>
|
|
+ class NumberStream<InputStream, StackCharacter, true, true> : public NumberStream<InputStream, StackCharacter, true, false> {
|
|
+ typedef NumberStream<InputStream, StackCharacter, true, false> Base;
|
|
public:
|
|
- NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {}
|
|
- ~NumberStream() {}
|
|
+ NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s) {}
|
|
|
|
RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); }
|
|
};
|
|
|
|
template<unsigned parseFlags, typename InputStream, typename Handler>
|
|
void ParseNumber(InputStream& is, Handler& handler) {
|
|
+ typedef typename internal::SelectIf<internal::BoolType<(parseFlags & kParseNumbersAsStringsFlag) != 0>, typename TargetEncoding::Ch, char>::Type NumberCharacter;
|
|
+
|
|
internal::StreamLocalCopy<InputStream> copy(is);
|
|
- NumberStream<InputStream,
|
|
+ NumberStream<InputStream, NumberCharacter,
|
|
((parseFlags & kParseNumbersAsStringsFlag) != 0) ?
|
|
((parseFlags & kParseInsituFlag) == 0) :
|
|
((parseFlags & kParseFullPrecisionFlag) != 0),
|
|
@@ -1185,18 +1522,27 @@ private:
|
|
}
|
|
// 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<double>::quiet_NaN();
|
|
+ if (Consume(s, 'N')) {
|
|
+ if (Consume(s, 'a') && Consume(s, 'N')) {
|
|
+ d = std::numeric_limits<double>::quiet_NaN();
|
|
+ useNanOrInf = true;
|
|
+ }
|
|
}
|
|
- else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) {
|
|
- d = (minus ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::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<double>::infinity() : std::numeric_limits<double>::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());
|
|
@@ -1231,8 +1577,6 @@ private:
|
|
// Force double for big integer
|
|
if (useDouble) {
|
|
while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
|
|
- if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0
|
|
- RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
|
|
d = d * 10 + (s.TakePush() - '0');
|
|
}
|
|
}
|
|
@@ -1302,9 +1646,18 @@ private:
|
|
if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
|
|
exp = static_cast<int>(s.Take() - '0');
|
|
if (expMinus) {
|
|
+ // (exp + expFrac) must not underflow int => we're detecting when -exp gets
|
|
+ // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into
|
|
+ // underflow territory):
|
|
+ //
|
|
+ // -(exp * 10 + 9) + expFrac >= INT_MIN
|
|
+ // <=> exp <= (expFrac - INT_MIN - 9) / 10
|
|
+ RAPIDJSON_ASSERT(expFrac <= 0);
|
|
+ int maxExp = (expFrac + 2147483639) / 10;
|
|
+
|
|
while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
|
|
exp = exp * 10 + static_cast<int>(s.Take() - '0');
|
|
- if (exp >= 214748364) { // Issue #313: prevent overflow exponent
|
|
+ if (RAPIDJSON_UNLIKELY(exp > maxExp)) {
|
|
while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent
|
|
s.Take();
|
|
}
|
|
@@ -1341,10 +1694,10 @@ private:
|
|
}
|
|
else {
|
|
SizeType numCharsToCopy = static_cast<SizeType>(s.Length());
|
|
- StringStream srcStream(s.Pop());
|
|
+ GenericStringStream<UTF8<NumberCharacter> > srcStream(s.Pop());
|
|
StackStream<typename TargetEncoding::Ch> dstStream(stack_);
|
|
while (numCharsToCopy--) {
|
|
- Transcoder<UTF8<>, TargetEncoding>::Transcode(srcStream, dstStream);
|
|
+ Transcoder<UTF8<typename TargetEncoding::Ch>, TargetEncoding>::Transcode(srcStream, dstStream);
|
|
}
|
|
dstStream.Put('\0');
|
|
const typename TargetEncoding::Ch* str = dstStream.Pop();
|
|
@@ -1354,7 +1707,7 @@ private:
|
|
}
|
|
else {
|
|
size_t length = s.Length();
|
|
- const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not.
|
|
+ const NumberCharacter* decimal = s.Pop(); // Pop stack no matter if it will be used or not.
|
|
|
|
if (useDouble) {
|
|
int p = exp + expFrac;
|
|
@@ -1363,6 +1716,13 @@ private:
|
|
else
|
|
d = internal::StrtodNormalPrecision(d, p);
|
|
|
|
+ // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal
|
|
+ if (d > (std::numeric_limits<double>::max)()) {
|
|
+ // Overflow
|
|
+ // TODO: internal::StrtodX should report overflow (or underflow)
|
|
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
|
|
+ }
|
|
+
|
|
cont = handler.Double(minus ? -d : d);
|
|
}
|
|
else if (useNanOrInf) {
|
|
@@ -1408,29 +1768,31 @@ private:
|
|
|
|
// 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,
|
|
|
|
- enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 };
|
|
+ cIterativeParsingStateCount
|
|
+ };
|
|
|
|
// Tokens
|
|
enum Token {
|
|
@@ -1452,7 +1814,7 @@ private:
|
|
kTokenCount
|
|
};
|
|
|
|
- RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) {
|
|
+ RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const {
|
|
|
|
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
|
#define N NumberToken
|
|
@@ -1479,9 +1841,21 @@ private:
|
|
return NumberToken;
|
|
}
|
|
|
|
- RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) {
|
|
+ RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const {
|
|
// 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
|
|
@@ -1496,18 +1870,6 @@ private:
|
|
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
|
|
@@ -1536,20 +1898,6 @@ private:
|
|
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
|
|
@@ -1564,20 +1912,6 @@ private:
|
|
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,
|
|
@@ -1612,6 +1946,18 @@ private:
|
|
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)
|
|
@@ -1626,18 +1972,34 @@ private:
|
|
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<IterativeParsingState>(G[state][token]);
|
|
@@ -1818,6 +2180,14 @@ private:
|
|
}
|
|
}
|
|
|
|
+ RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const {
|
|
+ return s >= IterativeParsingElementDelimiterState;
|
|
+ }
|
|
+
|
|
+ RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const {
|
|
+ return s <= IterativeParsingErrorState;
|
|
+ }
|
|
+
|
|
template <unsigned parseFlags, typename InputStream, typename Handler>
|
|
ParseResult IterativeParse(InputStream& is, Handler& handler) {
|
|
parseResult_.Clear();
|
|
@@ -1856,6 +2226,7 @@ private:
|
|
static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
|
|
internal::Stack<StackAllocator> 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.
|
|
@@ -1863,7 +2234,7 @@ typedef GenericReader<UTF8<>, UTF8<> > Reader;
|
|
|
|
RAPIDJSON_NAMESPACE_END
|
|
|
|
-#ifdef __clang__
|
|
+#if defined(__clang__) || defined(_MSC_VER)
|
|
RAPIDJSON_DIAG_POP
|
|
#endif
|
|
|
|
@@ -1872,8 +2243,4 @@ RAPIDJSON_DIAG_POP
|
|
RAPIDJSON_DIAG_POP
|
|
#endif
|
|
|
|
-#ifdef _MSC_VER
|
|
-RAPIDJSON_DIAG_POP
|
|
-#endif
|
|
-
|
|
#endif // RAPIDJSON_READER_H_
|
|
diff --git a/src/common/thirdparty/rapidjson/schema.h b/src/common/thirdparty/rapidjson/schema.h
|
|
index 8497d3031..439133fa6 100644
|
|
--- a/src/common/thirdparty/rapidjson/schema.h
|
|
+++ b/src/common/thirdparty/rapidjson/schema.h
|
|
@@ -1,5 +1,5 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available->
|
|
-//
|
|
+//
|
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
@@ -7,9 +7,9 @@
|
|
//
|
|
// http://opensource->org/licenses/MIT
|
|
//
|
|
-// Unless required by applicable law or agreed to in writing, software distributed
|
|
-// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
-// CONDITIONS OF ANY KIND, either express or implied-> See the License for the
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied-> See the License for the
|
|
// specific language governing permissions and limitations under the License->
|
|
|
|
#ifndef RAPIDJSON_SCHEMA_H_
|
|
@@ -17,6 +17,9 @@
|
|
|
|
#include "document.h"
|
|
#include "pointer.h"
|
|
+#include "stringbuffer.h"
|
|
+#include "error/en.h"
|
|
+#include "uri.h"
|
|
#include <cmath> // abs, floor
|
|
|
|
#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
|
|
@@ -25,7 +28,7 @@
|
|
#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
|
|
#endif
|
|
|
|
-#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
|
|
+#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
|
|
#define RAPIDJSON_SCHEMA_USE_STDREGEX 1
|
|
#else
|
|
#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
|
|
@@ -47,10 +50,6 @@
|
|
#define RAPIDJSON_SCHEMA_VERBOSE 0
|
|
#endif
|
|
|
|
-#if RAPIDJSON_SCHEMA_VERBOSE
|
|
-#include "stringbuffer.h"
|
|
-#endif
|
|
-
|
|
RAPIDJSON_DIAG_PUSH
|
|
|
|
#if defined(__GNUC__)
|
|
@@ -62,9 +61,7 @@ RAPIDJSON_DIAG_OFF(weak-vtables)
|
|
RAPIDJSON_DIAG_OFF(exit-time-destructors)
|
|
RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
|
|
RAPIDJSON_DIAG_OFF(variadic-macros)
|
|
-#endif
|
|
-
|
|
-#ifdef _MSC_VER
|
|
+#elif defined(_MSC_VER)
|
|
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
|
#endif
|
|
|
|
@@ -77,50 +74,163 @@ RAPIDJSON_NAMESPACE_BEGIN
|
|
|
|
namespace internal {
|
|
|
|
-inline void PrintInvalidKeyword(const char* keyword) {
|
|
- printf("Fail keyword: %s\n", keyword);
|
|
+inline void PrintInvalidKeywordData(const char* keyword) {
|
|
+ printf(" Fail keyword: '%s'\n", keyword);
|
|
+}
|
|
+
|
|
+inline void PrintInvalidKeywordData(const wchar_t* keyword) {
|
|
+ wprintf(L" Fail keyword: '%ls'\n", keyword);
|
|
+}
|
|
+
|
|
+inline void PrintInvalidDocumentData(const char* document) {
|
|
+ printf(" Fail document: '%s'\n", document);
|
|
+}
|
|
+
|
|
+inline void PrintInvalidDocumentData(const wchar_t* document) {
|
|
+ wprintf(L" Fail document: '%ls'\n", document);
|
|
+}
|
|
+
|
|
+inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) {
|
|
+ printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d);
|
|
+}
|
|
+
|
|
+inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) {
|
|
+ wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d);
|
|
+}
|
|
+
|
|
+inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) {
|
|
+ printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved);
|
|
+}
|
|
+
|
|
+inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) {
|
|
+ wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved);
|
|
+}
|
|
+
|
|
+inline void PrintMethodData(const char* method) {
|
|
+ printf("%s\n", method);
|
|
+}
|
|
+
|
|
+inline void PrintMethodData(const char* method, bool b) {
|
|
+ printf("%s, Data: '%s'\n", method, b ? "true" : "false");
|
|
+}
|
|
+
|
|
+inline void PrintMethodData(const char* method, int64_t i) {
|
|
+ printf("%s, Data: '%" PRId64 "'\n", method, i);
|
|
}
|
|
|
|
-inline void PrintInvalidKeyword(const wchar_t* keyword) {
|
|
- wprintf(L"Fail keyword: %ls\n", keyword);
|
|
+inline void PrintMethodData(const char* method, uint64_t u) {
|
|
+ printf("%s, Data: '%" PRIu64 "'\n", method, u);
|
|
}
|
|
|
|
-inline void PrintInvalidDocument(const char* document) {
|
|
- printf("Fail document: %s\n\n", document);
|
|
+inline void PrintMethodData(const char* method, double d) {
|
|
+ printf("%s, Data: '%lf'\n", method, d);
|
|
}
|
|
|
|
-inline void PrintInvalidDocument(const wchar_t* document) {
|
|
- wprintf(L"Fail document: %ls\n\n", document);
|
|
+inline void PrintMethodData(const char* method, const char* s) {
|
|
+ printf("%s, Data: '%s'\n", method, s);
|
|
}
|
|
|
|
-inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
|
|
- printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
|
|
+inline void PrintMethodData(const char* method, const wchar_t* s) {
|
|
+ wprintf(L"%hs, Data: '%ls'\n", method, s);
|
|
}
|
|
|
|
-inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
|
|
- wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
|
|
+inline void PrintMethodData(const char* method, const char* s1, const char* s2) {
|
|
+ printf("%s, Data: '%s', '%s'\n", method, s1, s2);
|
|
+}
|
|
+
|
|
+inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) {
|
|
+ wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2);
|
|
}
|
|
|
|
} // namespace internal
|
|
|
|
#endif // RAPIDJSON_SCHEMA_VERBOSE
|
|
|
|
-///////////////////////////////////////////////////////////////////////////////
|
|
-// RAPIDJSON_INVALID_KEYWORD_RETURN
|
|
-
|
|
+#ifndef RAPIDJSON_SCHEMA_PRINT
|
|
#if RAPIDJSON_SCHEMA_VERBOSE
|
|
-#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
|
|
+#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__)
|
|
#else
|
|
-#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
|
|
+#define RAPIDJSON_SCHEMA_PRINT(name, ...)
|
|
+#endif
|
|
#endif
|
|
|
|
-#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// RAPIDJSON_INVALID_KEYWORD_RETURN
|
|
+
|
|
+#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\
|
|
RAPIDJSON_MULTILINEMACRO_BEGIN\
|
|
- context.invalidKeyword = keyword.GetString();\
|
|
- RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
|
|
+ context.invalidCode = code;\
|
|
+ context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\
|
|
+ RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\
|
|
return false;\
|
|
RAPIDJSON_MULTILINEMACRO_END
|
|
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// ValidateFlag
|
|
+
|
|
+/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS
|
|
+ \ingroup RAPIDJSON_CONFIG
|
|
+ \brief User-defined kValidateDefaultFlags definition.
|
|
+
|
|
+ User can define this as any \c ValidateFlag combinations.
|
|
+*/
|
|
+#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS
|
|
+#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags
|
|
+#endif
|
|
+
|
|
+//! Combination of validate flags
|
|
+/*! \see
|
|
+ */
|
|
+enum ValidateFlag {
|
|
+ kValidateNoFlags = 0, //!< No flags are set.
|
|
+ kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error.
|
|
+ kValidateReadFlag = 2, //!< Validation is for a read semantic.
|
|
+ kValidateWriteFlag = 4, //!< Validation is for a write semantic.
|
|
+ kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS
|
|
+};
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// Specification
|
|
+enum SchemaDraft {
|
|
+ kDraftUnknown = -1,
|
|
+ kDraftNone = 0,
|
|
+ kDraft03 = 3,
|
|
+ kDraftMin = 4, //!< Current minimum supported draft
|
|
+ kDraft04 = 4,
|
|
+ kDraft05 = 5,
|
|
+ kDraftMax = 5, //!< Current maximum supported draft
|
|
+ kDraft06 = 6,
|
|
+ kDraft07 = 7,
|
|
+ kDraft2019_09 = 8,
|
|
+ kDraft2020_12 = 9
|
|
+};
|
|
+
|
|
+enum OpenApiVersion {
|
|
+ kVersionUnknown = -1,
|
|
+ kVersionNone = 0,
|
|
+ kVersionMin = 2, //!< Current minimum supported version
|
|
+ kVersion20 = 2,
|
|
+ kVersion30 = 3,
|
|
+ kVersionMax = 3, //!< Current maximum supported version
|
|
+ kVersion31 = 4,
|
|
+};
|
|
+
|
|
+struct Specification {
|
|
+ Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {}
|
|
+ Specification(OpenApiVersion o) : oapi(o) {
|
|
+ if (oapi == kVersion20) draft = kDraft04;
|
|
+ else if (oapi == kVersion30) draft = kDraft05;
|
|
+ else if (oapi == kVersion31) draft = kDraft2020_12;
|
|
+ else draft = kDraft04;
|
|
+ }
|
|
+ ~Specification() {}
|
|
+ bool IsSupported() const {
|
|
+ return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax)));
|
|
+ }
|
|
+ SchemaDraft draft;
|
|
+ OpenApiVersion oapi;
|
|
+};
|
|
+
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Forward declarations
|
|
|
|
@@ -139,6 +249,8 @@ class ISchemaValidator {
|
|
public:
|
|
virtual ~ISchemaValidator() {}
|
|
virtual bool IsValid() const = 0;
|
|
+ virtual void SetValidateFlags(unsigned flags) = 0;
|
|
+ virtual unsigned GetValidateFlags() const = 0;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -148,7 +260,7 @@ template <typename SchemaType>
|
|
class ISchemaStateFactory {
|
|
public:
|
|
virtual ~ISchemaStateFactory() {}
|
|
- virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
|
|
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0;
|
|
virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
|
|
virtual void* CreateHasher() = 0;
|
|
virtual uint64_t GetHashCode(void* hasher) = 0;
|
|
@@ -157,6 +269,65 @@ public:
|
|
virtual void FreeState(void* p) = 0;
|
|
};
|
|
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// IValidationErrorHandler
|
|
+
|
|
+template <typename SchemaType>
|
|
+class IValidationErrorHandler {
|
|
+public:
|
|
+ typedef typename SchemaType::Ch Ch;
|
|
+ typedef typename SchemaType::SValue SValue;
|
|
+
|
|
+ virtual ~IValidationErrorHandler() {}
|
|
+
|
|
+ virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0;
|
|
+ virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0;
|
|
+ virtual void NotMultipleOf(double actual, const SValue& expected) = 0;
|
|
+ virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0;
|
|
+ virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
|
|
+ virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0;
|
|
+ virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0;
|
|
+ virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
|
|
+ virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0;
|
|
+
|
|
+ virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0;
|
|
+ virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0;
|
|
+ virtual void DoesNotMatch(const Ch* str, SizeType length) = 0;
|
|
+
|
|
+ virtual void DisallowedItem(SizeType index) = 0;
|
|
+ virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0;
|
|
+ virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0;
|
|
+ virtual void DuplicateItems(SizeType index1, SizeType index2) = 0;
|
|
+
|
|
+ virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0;
|
|
+ virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0;
|
|
+ virtual void StartMissingProperties() = 0;
|
|
+ virtual void AddMissingProperty(const SValue& name) = 0;
|
|
+ virtual bool EndMissingProperties() = 0;
|
|
+ virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0;
|
|
+ virtual void DisallowedProperty(const Ch* name, SizeType length) = 0;
|
|
+
|
|
+ virtual void StartDependencyErrors() = 0;
|
|
+ virtual void StartMissingDependentProperties() = 0;
|
|
+ virtual void AddMissingDependentProperty(const SValue& targetName) = 0;
|
|
+ virtual void EndMissingDependentProperties(const SValue& sourceName) = 0;
|
|
+ virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
|
|
+ virtual bool EndDependencyErrors() = 0;
|
|
+
|
|
+ virtual void DisallowedValue(const ValidateErrorCode code) = 0;
|
|
+ virtual void StartDisallowedType() = 0;
|
|
+ virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
|
|
+ virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
|
|
+ virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
|
+ virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
|
+ virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
|
+ virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0;
|
|
+ virtual void Disallowed() = 0;
|
|
+ virtual void DisallowedWhenWriting() = 0;
|
|
+ virtual void DisallowedWhenReading() = 0;
|
|
+};
|
|
+
|
|
+
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Hasher
|
|
|
|
@@ -174,10 +345,10 @@ public:
|
|
bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
|
|
bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
|
|
bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
|
|
- bool Double(double d) {
|
|
- Number n;
|
|
+ bool Double(double d) {
|
|
+ Number n;
|
|
if (d < 0) n.u.i = static_cast<int64_t>(d);
|
|
- else n.u.u = static_cast<uint64_t>(d);
|
|
+ else n.u.u = static_cast<uint64_t>(d);
|
|
n.d = d;
|
|
return WriteNumber(n);
|
|
}
|
|
@@ -261,6 +432,7 @@ template <typename SchemaDocumentType>
|
|
struct SchemaValidationContext {
|
|
typedef Schema<SchemaDocumentType> SchemaType;
|
|
typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
|
|
+ typedef IValidationErrorHandler<SchemaType> ErrorHandlerType;
|
|
typedef typename SchemaType::ValueType ValueType;
|
|
typedef typename ValueType::Ch Ch;
|
|
|
|
@@ -270,11 +442,14 @@ struct SchemaValidationContext {
|
|
kPatternValidatorWithAdditionalProperty
|
|
};
|
|
|
|
- SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
|
|
+ SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) :
|
|
factory(f),
|
|
+ error_handler(eh),
|
|
schema(s),
|
|
+ flags(fl),
|
|
valueSchema(),
|
|
invalidKeyword(),
|
|
+ invalidCode(),
|
|
hasher(),
|
|
arrayElementHashCodes(),
|
|
validators(),
|
|
@@ -295,13 +470,19 @@ struct SchemaValidationContext {
|
|
if (hasher)
|
|
factory.DestroryHasher(hasher);
|
|
if (validators) {
|
|
- for (SizeType i = 0; i < validatorCount; i++)
|
|
- factory.DestroySchemaValidator(validators[i]);
|
|
+ for (SizeType i = 0; i < validatorCount; i++) {
|
|
+ if (validators[i]) {
|
|
+ factory.DestroySchemaValidator(validators[i]);
|
|
+ }
|
|
+ }
|
|
factory.FreeState(validators);
|
|
}
|
|
if (patternPropertiesValidators) {
|
|
- for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
|
|
- factory.DestroySchemaValidator(patternPropertiesValidators[i]);
|
|
+ for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) {
|
|
+ if (patternPropertiesValidators[i]) {
|
|
+ factory.DestroySchemaValidator(patternPropertiesValidators[i]);
|
|
+ }
|
|
+ }
|
|
factory.FreeState(patternPropertiesValidators);
|
|
}
|
|
if (patternPropertiesSchemas)
|
|
@@ -311,9 +492,12 @@ struct SchemaValidationContext {
|
|
}
|
|
|
|
SchemaValidatorFactoryType& factory;
|
|
+ ErrorHandlerType& error_handler;
|
|
const SchemaType* schema;
|
|
+ unsigned flags;
|
|
const SchemaType* valueSchema;
|
|
const Ch* invalidKeyword;
|
|
+ ValidateErrorCode invalidCode;
|
|
void* hasher; // Only validator access
|
|
void* arrayElementHashCodes; // Only validator access this
|
|
ISchemaValidator** validators;
|
|
@@ -345,15 +529,23 @@ public:
|
|
typedef SchemaValidationContext<SchemaDocumentType> Context;
|
|
typedef Schema<SchemaDocumentType> SchemaType;
|
|
typedef GenericValue<EncodingType, AllocatorType> SValue;
|
|
+ typedef IValidationErrorHandler<Schema> ErrorHandler;
|
|
+ typedef GenericUri<ValueType, AllocatorType> UriType;
|
|
friend class GenericSchemaDocument<ValueType, AllocatorType>;
|
|
|
|
- Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
|
|
+ Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) :
|
|
allocator_(allocator),
|
|
+ uri_(schemaDocument->GetURI(), *allocator),
|
|
+ id_(id, allocator),
|
|
+ spec_(schemaDocument->GetSpecification()),
|
|
+ pointer_(p, allocator),
|
|
+ typeless_(schemaDocument->GetTypeless()),
|
|
enum_(),
|
|
enumCount_(),
|
|
not_(),
|
|
type_((1 << kTotalSchemaType) - 1), // typeless
|
|
validatorCount_(),
|
|
+ notValidatorIndex_(),
|
|
properties_(),
|
|
additionalPropertiesSchema_(),
|
|
patternProperties_(),
|
|
@@ -377,15 +569,44 @@ public:
|
|
minLength_(0),
|
|
maxLength_(~SizeType(0)),
|
|
exclusiveMinimum_(false),
|
|
- exclusiveMaximum_(false)
|
|
+ exclusiveMaximum_(false),
|
|
+ defaultValueLength_(0),
|
|
+ readOnly_(false),
|
|
+ writeOnly_(false),
|
|
+ nullable_(false)
|
|
{
|
|
- typedef typename SchemaDocumentType::ValueType ValueType;
|
|
+ GenericStringBuffer<EncodingType> sb;
|
|
+ p.StringifyUriFragment(sb);
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString());
|
|
+
|
|
typedef typename ValueType::ConstValueIterator ConstValueIterator;
|
|
typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
|
|
|
|
+ // PR #1393
|
|
+ // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite
|
|
+ // recursion (with recursive schemas), since schemaDocument->getSchema() is always
|
|
+ // checked before creating a new one. Don't cache typeless_, though.
|
|
+ if (this != typeless_) {
|
|
+ typedef typename SchemaDocumentType::SchemaEntry SchemaEntry;
|
|
+ SchemaEntry *entry = schemaDocument->schemaMap_.template Push<SchemaEntry>();
|
|
+ new (entry) SchemaEntry(pointer_, this, true, allocator_);
|
|
+ schemaDocument->AddSchemaRefs(this);
|
|
+ }
|
|
+
|
|
if (!value.IsObject())
|
|
return;
|
|
|
|
+ // If we have an id property, resolve it with the in-scope id
|
|
+ // Not supported for open api 2.0 or 3.0
|
|
+ if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
|
+ if (const ValueType* v = GetMember(value, GetIdString())) {
|
|
+ if (v->IsString()) {
|
|
+ UriType local(*v, allocator);
|
|
+ id_ = local.Resolve(id_, allocator);
|
|
+ RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString());
|
|
+ }
|
|
+ }
|
|
+
|
|
if (const ValueType* v = GetMember(value, GetTypeString())) {
|
|
type_ = 0;
|
|
if (v->IsString())
|
|
@@ -395,29 +616,33 @@ public:
|
|
AddType(*itr);
|
|
}
|
|
|
|
- if (const ValueType* v = GetMember(value, GetEnumString()))
|
|
+ if (const ValueType* v = GetMember(value, GetEnumString())) {
|
|
if (v->IsArray() && v->Size() > 0) {
|
|
enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
|
|
for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
|
|
- typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
|
|
- char buffer[256 + 24];
|
|
- MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
|
|
+ typedef Hasher<EncodingType, MemoryPoolAllocator<AllocatorType> > EnumHasherType;
|
|
+ char buffer[256u + 24];
|
|
+ MemoryPoolAllocator<AllocatorType> hasherAllocator(buffer, sizeof(buffer));
|
|
EnumHasherType h(&hasherAllocator, 256);
|
|
itr->Accept(h);
|
|
enum_[enumCount_++] = h.GetHashCode();
|
|
}
|
|
}
|
|
+ }
|
|
|
|
- if (schemaDocument) {
|
|
+ if (schemaDocument)
|
|
AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
|
|
+
|
|
+ // AnyOf, OneOf, Not not supported for open api 2.0
|
|
+ if (schemaDocument && spec_.oapi != kVersion20) {
|
|
AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
|
|
AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
|
|
- }
|
|
|
|
- if (const ValueType* v = GetMember(value, GetNotString())) {
|
|
- schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document);
|
|
- notValidatorIndex_ = validatorCount_;
|
|
- validatorCount_++;
|
|
+ if (const ValueType* v = GetMember(value, GetNotString())) {
|
|
+ schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document, id_);
|
|
+ notValidatorIndex_ = validatorCount_;
|
|
+ validatorCount_++;
|
|
+ }
|
|
}
|
|
|
|
// Object
|
|
@@ -432,12 +657,14 @@ public:
|
|
if (properties && properties->IsObject())
|
|
for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
|
|
AddUniqueElement(allProperties, itr->name);
|
|
-
|
|
+
|
|
if (required && required->IsArray())
|
|
for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
|
|
if (itr->IsString())
|
|
AddUniqueElement(allProperties, *itr);
|
|
|
|
+ // Dependencies not supported for open api 2.0 and 3.0
|
|
+ if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
|
if (dependencies && dependencies->IsObject())
|
|
for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
|
|
AddUniqueElement(allProperties, itr->name);
|
|
@@ -453,7 +680,7 @@ public:
|
|
for (SizeType i = 0; i < propertyCount_; i++) {
|
|
new (&properties_[i]) Property();
|
|
properties_[i].name = allProperties[i];
|
|
- properties_[i].schema = GetTypeless();
|
|
+ properties_[i].schema = typeless_;
|
|
}
|
|
}
|
|
}
|
|
@@ -463,10 +690,12 @@ public:
|
|
for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
|
|
SizeType index;
|
|
if (FindPropertyIndex(itr->name, &index))
|
|
- schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
|
|
+ schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_);
|
|
}
|
|
}
|
|
|
|
+ // PatternProperties not supported for open api 2.0 and 3.0
|
|
+ if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
|
if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
|
|
PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
|
|
patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
|
|
@@ -474,8 +703,9 @@ public:
|
|
|
|
for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
|
|
new (&patternProperties_[patternPropertyCount_]) PatternProperty();
|
|
- patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
|
|
- schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
|
|
+ PointerType r = q.Append(itr->name, allocator_);
|
|
+ patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r);
|
|
+ schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_);
|
|
patternPropertyCount_++;
|
|
}
|
|
}
|
|
@@ -490,6 +720,8 @@ public:
|
|
}
|
|
}
|
|
|
|
+ // Dependencies not supported for open api 2.0 and 3.0
|
|
+ if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
|
if (dependencies && dependencies->IsObject()) {
|
|
PointerType q = p.Append(GetDependenciesString(), allocator_);
|
|
hasDependencies_ = true;
|
|
@@ -507,7 +739,7 @@ public:
|
|
}
|
|
else if (itr->value.IsObject()) {
|
|
hasSchemaDependencies_ = true;
|
|
- schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
|
|
+ schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_);
|
|
properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
|
|
validatorCount_++;
|
|
}
|
|
@@ -519,7 +751,7 @@ public:
|
|
if (v->IsBool())
|
|
additionalProperties_ = v->GetBool();
|
|
else if (v->IsObject())
|
|
- schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
|
|
+ schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_);
|
|
}
|
|
|
|
AssignIfExist(minProperties_, value, GetMinPropertiesString());
|
|
@@ -529,23 +761,25 @@ public:
|
|
if (const ValueType* v = GetMember(value, GetItemsString())) {
|
|
PointerType q = p.Append(GetItemsString(), allocator_);
|
|
if (v->IsObject()) // List validation
|
|
- schemaDocument->CreateSchema(&itemsList_, q, *v, document);
|
|
+ schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_);
|
|
else if (v->IsArray()) { // Tuple validation
|
|
itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
|
|
SizeType index = 0;
|
|
for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
|
|
- schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
|
|
+ schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_);
|
|
}
|
|
}
|
|
|
|
AssignIfExist(minItems_, value, GetMinItemsString());
|
|
AssignIfExist(maxItems_, value, GetMaxItemsString());
|
|
|
|
+ // AdditionalItems not supported for openapi 2.0 and 3.0
|
|
+ if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
|
if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
|
|
if (v->IsBool())
|
|
additionalItems_ = v->GetBool();
|
|
else if (v->IsObject())
|
|
- schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
|
|
+ schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_);
|
|
}
|
|
|
|
AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
|
|
@@ -555,7 +789,7 @@ public:
|
|
AssignIfExist(maxLength_, value, GetMaxLengthString());
|
|
|
|
if (const ValueType* v = GetMember(value, GetPatternString()))
|
|
- pattern_ = CreatePattern(*v);
|
|
+ pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_));
|
|
|
|
// Number
|
|
if (const ValueType* v = GetMember(value, GetMinimumString()))
|
|
@@ -572,12 +806,33 @@ public:
|
|
if (const ValueType* v = GetMember(value, GetMultipleOfString()))
|
|
if (v->IsNumber() && v->GetDouble() > 0.0)
|
|
multipleOf_.CopyFrom(*v, *allocator_);
|
|
+
|
|
+ // Default
|
|
+ if (const ValueType* v = GetMember(value, GetDefaultValueString()))
|
|
+ if (v->IsString())
|
|
+ defaultValueLength_ = v->GetStringLength();
|
|
+
|
|
+ // ReadOnly - open api only (until draft 7 supported)
|
|
+ // WriteOnly - open api 3 only (until draft 7 supported)
|
|
+ // Both can't be true
|
|
+ if (spec_.oapi != kVersionNone)
|
|
+ AssignIfExist(readOnly_, value, GetReadOnlyString());
|
|
+ if (spec_.oapi >= kVersion30)
|
|
+ AssignIfExist(writeOnly_, value, GetWriteOnlyString());
|
|
+ if (readOnly_ && writeOnly_)
|
|
+ schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p);
|
|
+
|
|
+ // Nullable - open api 3 only
|
|
+ // If true add 'null' as allowable type
|
|
+ if (spec_.oapi >= kVersion30) {
|
|
+ AssignIfExist(nullable_, value, GetNullableString());
|
|
+ if (nullable_)
|
|
+ AddType(GetNullString());
|
|
+ }
|
|
}
|
|
|
|
~Schema() {
|
|
- if (allocator_) {
|
|
- allocator_->Free(enum_);
|
|
- }
|
|
+ AllocatorType::Free(enum_);
|
|
if (properties_) {
|
|
for (SizeType i = 0; i < propertyCount_; i++)
|
|
properties_[i].~Property();
|
|
@@ -592,12 +847,29 @@ public:
|
|
#if RAPIDJSON_SCHEMA_HAS_REGEX
|
|
if (pattern_) {
|
|
pattern_->~RegexType();
|
|
- allocator_->Free(pattern_);
|
|
+ AllocatorType::Free(pattern_);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
+ const SValue& GetURI() const {
|
|
+ return uri_;
|
|
+ }
|
|
+
|
|
+ const UriType& GetId() const {
|
|
+ return id_;
|
|
+ }
|
|
+
|
|
+ const Specification& GetSpecification() const {
|
|
+ return spec_;
|
|
+ }
|
|
+
|
|
+ const PointerType& GetPointer() const {
|
|
+ return pointer_;
|
|
+ }
|
|
+
|
|
bool BeginValue(Context& context) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue");
|
|
if (context.inArray) {
|
|
if (uniqueItems_)
|
|
context.valueUniqueness = true;
|
|
@@ -610,12 +882,18 @@ public:
|
|
else if (additionalItemsSchema_)
|
|
context.valueSchema = additionalItemsSchema_;
|
|
else if (additionalItems_)
|
|
- context.valueSchema = GetTypeless();
|
|
- else
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
|
|
+ context.valueSchema = typeless_;
|
|
+ else {
|
|
+ context.error_handler.DisallowedItem(context.arrayElementIndex);
|
|
+ // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
|
|
+ context.valueSchema = typeless_;
|
|
+ // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set
|
|
+ context.arrayElementIndex++;
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems);
|
|
+ }
|
|
}
|
|
else
|
|
- context.valueSchema = GetTypeless();
|
|
+ context.valueSchema = typeless_;
|
|
|
|
context.arrayElementIndex++;
|
|
}
|
|
@@ -623,6 +901,8 @@ public:
|
|
}
|
|
|
|
RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue");
|
|
+ // Only check pattern properties if we have validators
|
|
if (context.patternPropertiesValidatorCount > 0) {
|
|
bool otherValid = false;
|
|
SizeType count = context.patternPropertiesValidatorCount;
|
|
@@ -637,133 +917,178 @@ public:
|
|
}
|
|
|
|
if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
|
|
- if (!patternValid)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
|
|
+ if (!patternValid) {
|
|
+ context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
|
|
+ }
|
|
}
|
|
else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
|
|
- if (!patternValid || !otherValid)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
|
|
+ if (!patternValid || !otherValid) {
|
|
+ context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
|
|
+ }
|
|
+ }
|
|
+ else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
|
|
+ context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
|
|
}
|
|
- else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
|
|
}
|
|
|
|
- if (enum_) {
|
|
+ // For enums only check if we have a hasher
|
|
+ if (enum_ && context.hasher) {
|
|
const uint64_t h = context.factory.GetHashCode(context.hasher);
|
|
for (SizeType i = 0; i < enumCount_; i++)
|
|
if (enum_[i] == h)
|
|
goto foundEnum;
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
|
|
+ context.error_handler.DisallowedValue(kValidateErrorEnum);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum);
|
|
foundEnum:;
|
|
}
|
|
|
|
- if (allOf_.schemas)
|
|
- for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
|
|
- if (!context.validators[i]->IsValid())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
|
|
-
|
|
- if (anyOf_.schemas) {
|
|
- for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
|
|
- if (context.validators[i]->IsValid())
|
|
- goto foundAny;
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
|
|
- foundAny:;
|
|
- }
|
|
-
|
|
- if (oneOf_.schemas) {
|
|
- bool oneValid = false;
|
|
- for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
|
|
- if (context.validators[i]->IsValid()) {
|
|
- if (oneValid)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
|
|
- else
|
|
- oneValid = true;
|
|
+ // Only check allOf etc if we have validators
|
|
+ if (context.validatorCount > 0) {
|
|
+ if (allOf_.schemas)
|
|
+ for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
|
|
+ if (!context.validators[i]->IsValid()) {
|
|
+ context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf);
|
|
+ }
|
|
+
|
|
+ if (anyOf_.schemas) {
|
|
+ for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
|
|
+ if (context.validators[i]->IsValid())
|
|
+ goto foundAny;
|
|
+ context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf);
|
|
+ foundAny:;
|
|
+ }
|
|
+
|
|
+ if (oneOf_.schemas) {
|
|
+ bool oneValid = false;
|
|
+ SizeType firstMatch = 0;
|
|
+ for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
|
|
+ if (context.validators[i]->IsValid()) {
|
|
+ if (oneValid) {
|
|
+ context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch);
|
|
+ } else {
|
|
+ oneValid = true;
|
|
+ firstMatch = i - oneOf_.begin;
|
|
+ }
|
|
+ }
|
|
+ if (!oneValid) {
|
|
+ context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf);
|
|
}
|
|
- if (!oneValid)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
|
|
- }
|
|
+ }
|
|
|
|
- if (not_ && context.validators[notValidatorIndex_]->IsValid())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
|
|
+ if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
|
|
+ context.error_handler.Disallowed();
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot);
|
|
+ }
|
|
+ }
|
|
|
|
return true;
|
|
}
|
|
|
|
- bool Null(Context& context) const {
|
|
- if (!(type_ & (1 << kNullSchemaType)))
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
|
+ bool Null(Context& context) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null");
|
|
+ if (!(type_ & (1 << kNullSchemaType))) {
|
|
+ DisallowedType(context, GetNullString());
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
|
+ }
|
|
return CreateParallelValidator(context);
|
|
}
|
|
-
|
|
- bool Bool(Context& context, bool) const {
|
|
- if (!(type_ & (1 << kBooleanSchemaType)))
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
|
+
|
|
+ bool Bool(Context& context, bool b) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b);
|
|
+ if (!CheckBool(context, b))
|
|
+ return false;
|
|
return CreateParallelValidator(context);
|
|
}
|
|
|
|
bool Int(Context& context, int i) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i);
|
|
if (!CheckInt(context, i))
|
|
return false;
|
|
return CreateParallelValidator(context);
|
|
}
|
|
|
|
bool Uint(Context& context, unsigned u) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u);
|
|
if (!CheckUint(context, u))
|
|
return false;
|
|
return CreateParallelValidator(context);
|
|
}
|
|
|
|
bool Int64(Context& context, int64_t i) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i);
|
|
if (!CheckInt(context, i))
|
|
return false;
|
|
return CreateParallelValidator(context);
|
|
}
|
|
|
|
bool Uint64(Context& context, uint64_t u) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u);
|
|
if (!CheckUint(context, u))
|
|
return false;
|
|
return CreateParallelValidator(context);
|
|
}
|
|
|
|
bool Double(Context& context, double d) const {
|
|
- if (!(type_ & (1 << kNumberSchemaType)))
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d);
|
|
+ if (!(type_ & (1 << kNumberSchemaType))) {
|
|
+ DisallowedType(context, GetNumberString());
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
|
+ }
|
|
|
|
if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
|
|
return false;
|
|
|
|
if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
|
|
return false;
|
|
-
|
|
+
|
|
if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
|
|
return false;
|
|
-
|
|
+
|
|
return CreateParallelValidator(context);
|
|
}
|
|
-
|
|
+
|
|
bool String(Context& context, const Ch* str, SizeType length, bool) const {
|
|
- if (!(type_ & (1 << kStringSchemaType)))
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str);
|
|
+ if (!(type_ & (1 << kStringSchemaType))) {
|
|
+ DisallowedType(context, GetStringString());
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
|
+ }
|
|
|
|
if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
|
|
SizeType count;
|
|
if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
|
|
- if (count < minLength_)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
|
|
- if (count > maxLength_)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
|
|
+ if (count < minLength_) {
|
|
+ context.error_handler.TooShort(str, length, minLength_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength);
|
|
+ }
|
|
+ if (count > maxLength_) {
|
|
+ context.error_handler.TooLong(str, length, maxLength_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
- if (pattern_ && !IsPatternMatch(pattern_, str, length))
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
|
|
+ if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
|
|
+ context.error_handler.DoesNotMatch(str, length);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern);
|
|
+ }
|
|
|
|
return CreateParallelValidator(context);
|
|
}
|
|
|
|
- bool StartObject(Context& context) const {
|
|
- if (!(type_ & (1 << kObjectSchemaType)))
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
|
+ bool StartObject(Context& context) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject");
|
|
+ if (!(type_ & (1 << kObjectSchemaType))) {
|
|
+ DisallowedType(context, GetObjectString());
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
|
+ }
|
|
|
|
if (hasDependencies_ || hasRequired_) {
|
|
context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
|
|
@@ -779,20 +1104,24 @@ public:
|
|
|
|
return CreateParallelValidator(context);
|
|
}
|
|
-
|
|
+
|
|
bool Key(Context& context, const Ch* str, SizeType len, bool) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str);
|
|
+
|
|
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;
|
|
+ SizeType index = 0;
|
|
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
|
|
@@ -805,9 +1134,9 @@ public:
|
|
}
|
|
|
|
if (additionalPropertiesSchema_) {
|
|
- if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
|
|
+ if (context.patternPropertiesSchemaCount > 0) {
|
|
context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
|
|
- context.valueSchema = GetTypeless();
|
|
+ context.valueSchema = typeless_;
|
|
context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
|
|
}
|
|
else
|
|
@@ -815,73 +1144,144 @@ public:
|
|
return true;
|
|
}
|
|
else if (additionalProperties_) {
|
|
- context.valueSchema = GetTypeless();
|
|
+ context.valueSchema = typeless_;
|
|
return true;
|
|
}
|
|
|
|
- if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
|
|
+ if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
|
|
+ // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
|
|
+ context.valueSchema = typeless_;
|
|
+ context.error_handler.DisallowedProperty(str, len);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties);
|
|
+ }
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EndObject(Context& context, SizeType memberCount) const {
|
|
- if (hasRequired_)
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject");
|
|
+ if (hasRequired_) {
|
|
+ context.error_handler.StartMissingProperties();
|
|
for (SizeType index = 0; index < propertyCount_; index++)
|
|
- if (properties_[index].required)
|
|
- if (!context.propertyExist[index])
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
|
|
+ if (properties_[index].required && !context.propertyExist[index])
|
|
+ if (properties_[index].schema->defaultValueLength_ == 0 )
|
|
+ context.error_handler.AddMissingProperty(properties_[index].name);
|
|
+ if (context.error_handler.EndMissingProperties())
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired);
|
|
+ }
|
|
|
|
- if (memberCount < minProperties_)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
|
|
+ if (memberCount < minProperties_) {
|
|
+ context.error_handler.TooFewProperties(memberCount, minProperties_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties);
|
|
+ }
|
|
|
|
- if (memberCount > maxProperties_)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
|
|
+ if (memberCount > maxProperties_) {
|
|
+ context.error_handler.TooManyProperties(memberCount, maxProperties_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties);
|
|
+ }
|
|
|
|
if (hasDependencies_) {
|
|
- for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
|
|
+ context.error_handler.StartDependencyErrors();
|
|
+ for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) {
|
|
+ const Property& source = properties_[sourceIndex];
|
|
if (context.propertyExist[sourceIndex]) {
|
|
- if (properties_[sourceIndex].dependencies) {
|
|
+ if (source.dependencies) {
|
|
+ context.error_handler.StartMissingDependentProperties();
|
|
for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
|
|
- if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
|
|
+ if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex])
|
|
+ context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name);
|
|
+ context.error_handler.EndMissingDependentProperties(source.name);
|
|
+ }
|
|
+ else if (source.dependenciesSchema) {
|
|
+ ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex];
|
|
+ if (!dependenciesValidator->IsValid())
|
|
+ context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator);
|
|
}
|
|
- else if (properties_[sourceIndex].dependenciesSchema)
|
|
- if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
|
|
}
|
|
+ }
|
|
+ if (context.error_handler.EndDependencyErrors())
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
- bool StartArray(Context& context) const {
|
|
- if (!(type_ & (1 << kArraySchemaType)))
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
|
-
|
|
+ bool StartArray(Context& context) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray");
|
|
context.arrayElementIndex = 0;
|
|
- context.inArray = true;
|
|
+ context.inArray = true; // Ensure we note that we are in an array
|
|
+
|
|
+ if (!(type_ & (1 << kArraySchemaType))) {
|
|
+ DisallowedType(context, GetArrayString());
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
|
+ }
|
|
|
|
return CreateParallelValidator(context);
|
|
}
|
|
|
|
- bool EndArray(Context& context, SizeType elementCount) const {
|
|
+ bool EndArray(Context& context, SizeType elementCount) const {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray");
|
|
context.inArray = false;
|
|
-
|
|
- if (elementCount < minItems_)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
|
|
-
|
|
- if (elementCount > maxItems_)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
|
|
+
|
|
+ if (elementCount < minItems_) {
|
|
+ context.error_handler.TooFewItems(elementCount, minItems_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems);
|
|
+ }
|
|
+
|
|
+ if (elementCount > maxItems_) {
|
|
+ context.error_handler.TooManyItems(elementCount, maxItems_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems);
|
|
+ }
|
|
|
|
return true;
|
|
}
|
|
|
|
+ static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) {
|
|
+ switch (validateErrorCode) {
|
|
+ case kValidateErrorMultipleOf: return GetMultipleOfString();
|
|
+ case kValidateErrorMaximum: return GetMaximumString();
|
|
+ case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same
|
|
+ case kValidateErrorMinimum: return GetMinimumString();
|
|
+ case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same
|
|
+
|
|
+ case kValidateErrorMaxLength: return GetMaxLengthString();
|
|
+ case kValidateErrorMinLength: return GetMinLengthString();
|
|
+ case kValidateErrorPattern: return GetPatternString();
|
|
+
|
|
+ case kValidateErrorMaxItems: return GetMaxItemsString();
|
|
+ case kValidateErrorMinItems: return GetMinItemsString();
|
|
+ case kValidateErrorUniqueItems: return GetUniqueItemsString();
|
|
+ case kValidateErrorAdditionalItems: return GetAdditionalItemsString();
|
|
+
|
|
+ case kValidateErrorMaxProperties: return GetMaxPropertiesString();
|
|
+ case kValidateErrorMinProperties: return GetMinPropertiesString();
|
|
+ case kValidateErrorRequired: return GetRequiredString();
|
|
+ case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString();
|
|
+ case kValidateErrorPatternProperties: return GetPatternPropertiesString();
|
|
+ case kValidateErrorDependencies: return GetDependenciesString();
|
|
+
|
|
+ case kValidateErrorEnum: return GetEnumString();
|
|
+ case kValidateErrorType: return GetTypeString();
|
|
+
|
|
+ case kValidateErrorOneOf: return GetOneOfString();
|
|
+ case kValidateErrorOneOfMatch: return GetOneOfString(); // Same
|
|
+ case kValidateErrorAllOf: return GetAllOfString();
|
|
+ case kValidateErrorAnyOf: return GetAnyOfString();
|
|
+ case kValidateErrorNot: return GetNotString();
|
|
+
|
|
+ case kValidateErrorReadOnly: return GetReadOnlyString();
|
|
+ case kValidateErrorWriteOnly: return GetWriteOnlyString();
|
|
+
|
|
+ default: return GetNullString();
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
// Generate functions for string literal according to Ch
|
|
#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<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
|
|
return v;\
|
|
}
|
|
|
|
@@ -918,6 +1318,15 @@ public:
|
|
RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
|
|
RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
|
|
RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
|
|
+ RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't')
|
|
+ RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a')
|
|
+ RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f')
|
|
+ RAPIDJSON_STRING_(Id, 'i', 'd')
|
|
+ RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r')
|
|
+ RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i')
|
|
+ RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y')
|
|
+ RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
|
|
+ RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e')
|
|
|
|
#undef RAPIDJSON_STRING_
|
|
|
|
@@ -934,7 +1343,7 @@ private:
|
|
};
|
|
|
|
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
|
|
- typedef internal::GenericRegex<EncodingType> RegexType;
|
|
+ typedef internal::GenericRegex<EncodingType, AllocatorType> RegexType;
|
|
#elif RAPIDJSON_SCHEMA_USE_STDREGEX
|
|
typedef std::basic_regex<Ch> RegexType;
|
|
#else
|
|
@@ -949,11 +1358,6 @@ private:
|
|
SizeType count;
|
|
};
|
|
|
|
- static const SchemaType* GetTypeless() {
|
|
- static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
|
|
- return &typeless;
|
|
- }
|
|
-
|
|
template <typename V1, typename V2>
|
|
void AddUniqueElement(V1& a, const V2& v) {
|
|
for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
|
|
@@ -988,7 +1392,7 @@ private:
|
|
out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
|
|
memset(out.schemas, 0, sizeof(Schema*)* out.count);
|
|
for (SizeType i = 0; i < out.count; i++)
|
|
- schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
|
|
+ schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_);
|
|
out.begin = validatorCount_;
|
|
validatorCount_ += out.count;
|
|
}
|
|
@@ -997,10 +1401,11 @@ private:
|
|
|
|
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
|
|
template <typename ValueType>
|
|
- RegexType* CreatePattern(const ValueType& value) {
|
|
+ RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
|
|
if (value.IsString()) {
|
|
- RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
|
|
+ RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_);
|
|
if (!r->IsValid()) {
|
|
+ sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
|
|
r->~RegexType();
|
|
AllocatorType::Free(r);
|
|
r = 0;
|
|
@@ -1011,17 +1416,22 @@ private:
|
|
}
|
|
|
|
static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
|
|
- return pattern->Search(str);
|
|
+ GenericRegexSearch<RegexType> rs(*pattern);
|
|
+ return rs.Search(str);
|
|
}
|
|
#elif RAPIDJSON_SCHEMA_USE_STDREGEX
|
|
template <typename ValueType>
|
|
- RegexType* CreatePattern(const ValueType& value) {
|
|
- if (value.IsString())
|
|
+ RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
|
|
+ if (value.IsString()) {
|
|
+ RegexType *r = static_cast<RegexType*>(allocator_->Malloc(sizeof(RegexType)));
|
|
try {
|
|
- return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
|
|
+ return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
|
|
}
|
|
- catch (const std::regex_error&) {
|
|
+ catch (const std::regex_error& e) {
|
|
+ sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
|
|
+ AllocatorType::Free(r);
|
|
}
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -1031,7 +1441,9 @@ private:
|
|
}
|
|
#else
|
|
template <typename ValueType>
|
|
- RegexType* CreatePattern(const ValueType&) { return 0; }
|
|
+ RegexType* CreatePattern(const ValueType&) {
|
|
+ return 0;
|
|
+ }
|
|
|
|
static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
|
|
#endif // RAPIDJSON_SCHEMA_USE_STDREGEX
|
|
@@ -1046,6 +1458,9 @@ private:
|
|
else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
|
|
}
|
|
|
|
+ // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required.
|
|
+ // Also creates a hasher for enums and array uniqueness, if required.
|
|
+ // Also a useful place to add type-independent error checks.
|
|
bool CreateParallelValidator(Context& context) const {
|
|
if (enum_ || context.arrayUniqueness)
|
|
context.hasher = context.factory.CreateHasher();
|
|
@@ -1053,33 +1468,45 @@ private:
|
|
if (validatorCount_) {
|
|
RAPIDJSON_ASSERT(context.validators == 0);
|
|
context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
|
|
+ std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_);
|
|
context.validatorCount = validatorCount_;
|
|
|
|
+ // Always return after first failure for these sub-validators
|
|
if (allOf_.schemas)
|
|
- CreateSchemaValidators(context, allOf_);
|
|
+ CreateSchemaValidators(context, allOf_, false);
|
|
|
|
if (anyOf_.schemas)
|
|
- CreateSchemaValidators(context, anyOf_);
|
|
-
|
|
+ CreateSchemaValidators(context, anyOf_, false);
|
|
+
|
|
if (oneOf_.schemas)
|
|
- CreateSchemaValidators(context, oneOf_);
|
|
-
|
|
+ CreateSchemaValidators(context, oneOf_, false);
|
|
+
|
|
if (not_)
|
|
- context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
|
|
-
|
|
+ context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false);
|
|
+
|
|
if (hasSchemaDependencies_) {
|
|
for (SizeType i = 0; i < propertyCount_; i++)
|
|
if (properties_[i].dependenciesSchema)
|
|
- context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
|
|
+ context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false);
|
|
}
|
|
}
|
|
|
|
+ // Add any other type-independent checks here
|
|
+ if (readOnly_ && (context.flags & kValidateWriteFlag)) {
|
|
+ context.error_handler.DisallowedWhenWriting();
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly);
|
|
+ }
|
|
+ if (writeOnly_ && (context.flags & kValidateReadFlag)) {
|
|
+ context.error_handler.DisallowedWhenReading();
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly);
|
|
+ }
|
|
+
|
|
return true;
|
|
}
|
|
|
|
- void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
|
|
+ void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const {
|
|
for (SizeType i = 0; i < schemas.count; i++)
|
|
- context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
|
|
+ context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors);
|
|
}
|
|
|
|
// O(n)
|
|
@@ -1087,7 +1514,7 @@ private:
|
|
SizeType len = name.GetStringLength();
|
|
const Ch* str = name.GetString();
|
|
for (SizeType index = 0; index < propertyCount_; index++)
|
|
- if (properties_[index].name.GetStringLength() == len &&
|
|
+ if (properties_[index].name.GetStringLength() == len &&
|
|
(std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
|
|
{
|
|
*outIndex = index;
|
|
@@ -1096,17 +1523,30 @@ private:
|
|
return false;
|
|
}
|
|
|
|
+ bool CheckBool(Context& context, bool) const {
|
|
+ if (!(type_ & (1 << kBooleanSchemaType))) {
|
|
+ DisallowedType(context, GetBooleanString());
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
bool CheckInt(Context& context, int64_t i) const {
|
|
- if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
|
+ if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
|
|
+ DisallowedType(context, GetIntegerString());
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
|
+ }
|
|
|
|
if (!minimum_.IsNull()) {
|
|
if (minimum_.IsInt64()) {
|
|
- if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
|
|
+ if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
|
|
+ context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
|
|
+ }
|
|
}
|
|
else if (minimum_.IsUint64()) {
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
|
|
+ context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64()
|
|
}
|
|
else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
|
|
return false;
|
|
@@ -1114,19 +1554,23 @@ private:
|
|
|
|
if (!maximum_.IsNull()) {
|
|
if (maximum_.IsInt64()) {
|
|
- if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
|
|
+ if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
|
|
+ context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
|
|
+ }
|
|
}
|
|
- 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<double>(i)))
|
|
return false;
|
|
}
|
|
|
|
if (!multipleOf_.IsNull()) {
|
|
if (multipleOf_.IsUint64()) {
|
|
- if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
|
|
+ if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
|
|
+ context.error_handler.NotMultipleOf(i, multipleOf_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
|
|
+ }
|
|
}
|
|
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
|
|
return false;
|
|
@@ -1136,13 +1580,17 @@ private:
|
|
}
|
|
|
|
bool CheckUint(Context& context, uint64_t i) const {
|
|
- if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
|
+ if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
|
|
+ DisallowedType(context, GetIntegerString());
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
|
+ }
|
|
|
|
if (!minimum_.IsNull()) {
|
|
if (minimum_.IsUint64()) {
|
|
- if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
|
|
+ if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
|
|
+ context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
|
|
+ }
|
|
}
|
|
else if (minimum_.IsInt64())
|
|
/* do nothing */; // i >= 0 > minimum.Getint64()
|
|
@@ -1152,19 +1600,25 @@ private:
|
|
|
|
if (!maximum_.IsNull()) {
|
|
if (maximum_.IsUint64()) {
|
|
- if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
|
|
+ if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
|
|
+ context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
|
|
+ }
|
|
+ }
|
|
+ else if (maximum_.IsInt64()) {
|
|
+ context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_
|
|
}
|
|
- else if (maximum_.IsInt64())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
|
|
else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
|
|
return false;
|
|
}
|
|
|
|
if (!multipleOf_.IsNull()) {
|
|
if (multipleOf_.IsUint64()) {
|
|
- if (i % multipleOf_.GetUint64() != 0)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
|
|
+ if (i % multipleOf_.GetUint64() != 0) {
|
|
+ context.error_handler.NotMultipleOf(i, multipleOf_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
|
|
+ }
|
|
}
|
|
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
|
|
return false;
|
|
@@ -1174,14 +1628,18 @@ private:
|
|
}
|
|
|
|
bool CheckDoubleMinimum(Context& context, double d) const {
|
|
- if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
|
|
+ if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
|
|
+ context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
|
|
+ }
|
|
return true;
|
|
}
|
|
|
|
bool CheckDoubleMaximum(Context& context, double d) const {
|
|
- if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
|
|
+ if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
|
|
+ context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
|
|
+ }
|
|
return true;
|
|
}
|
|
|
|
@@ -1189,11 +1647,29 @@ private:
|
|
double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
|
|
double q = std::floor(a / b);
|
|
double r = a - q * b;
|
|
- if (r > 0.0)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
|
|
+ if (r > 0.0) {
|
|
+ context.error_handler.NotMultipleOf(d, multipleOf_);
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
|
|
+ }
|
|
return true;
|
|
}
|
|
|
|
+ void DisallowedType(Context& context, const ValueType& actualType) const {
|
|
+ ErrorHandler& eh = context.error_handler;
|
|
+ eh.StartDisallowedType();
|
|
+
|
|
+ if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString());
|
|
+ if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString());
|
|
+ if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString());
|
|
+ if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString());
|
|
+ if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString());
|
|
+
|
|
+ if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString());
|
|
+ else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString());
|
|
+
|
|
+ eh.EndDisallowedType(actualType);
|
|
+ }
|
|
+
|
|
struct Property {
|
|
Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
|
|
~Property() { AllocatorType::Free(dependencies); }
|
|
@@ -1207,7 +1683,7 @@ private:
|
|
|
|
struct PatternProperty {
|
|
PatternProperty() : schema(), pattern() {}
|
|
- ~PatternProperty() {
|
|
+ ~PatternProperty() {
|
|
if (pattern) {
|
|
pattern->~RegexType();
|
|
AllocatorType::Free(pattern);
|
|
@@ -1218,6 +1694,11 @@ private:
|
|
};
|
|
|
|
AllocatorType* allocator_;
|
|
+ SValue uri_;
|
|
+ UriType id_;
|
|
+ Specification spec_;
|
|
+ PointerType pointer_;
|
|
+ const SchemaType* typeless_;
|
|
uint64_t* enum_;
|
|
SizeType enumCount_;
|
|
SchemaArray allOf_;
|
|
@@ -1258,6 +1739,12 @@ private:
|
|
SValue multipleOf_;
|
|
bool exclusiveMinimum_;
|
|
bool exclusiveMaximum_;
|
|
+
|
|
+ SizeType defaultValueLength_;
|
|
+
|
|
+ bool readOnly_;
|
|
+ bool writeOnly_;
|
|
+ bool nullable_;
|
|
};
|
|
|
|
template<typename Stack, typename Ch>
|
|
@@ -1267,7 +1754,7 @@ struct TokenHelper {
|
|
char buffer[21];
|
|
size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
|
|
for (size_t i = 0; i < length; i++)
|
|
- *documentStack.template Push<Ch>() = buffer[i];
|
|
+ *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
|
|
}
|
|
};
|
|
|
|
@@ -1299,9 +1786,18 @@ template <typename SchemaDocumentType>
|
|
class IGenericRemoteSchemaDocumentProvider {
|
|
public:
|
|
typedef typename SchemaDocumentType::Ch Ch;
|
|
+ typedef typename SchemaDocumentType::ValueType ValueType;
|
|
+ typedef typename SchemaDocumentType::AllocatorType AllocatorType;
|
|
|
|
virtual ~IGenericRemoteSchemaDocumentProvider() {}
|
|
virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
|
|
+ virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri<ValueType, AllocatorType> uri, Specification& spec) {
|
|
+ // Default implementation just calls through for compatibility
|
|
+ // Following line suppresses unused parameter warning
|
|
+ (void)spec;
|
|
+ // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi);
|
|
+ return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength());
|
|
+ }
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1326,6 +1822,9 @@ public:
|
|
typedef typename EncodingType::Ch Ch;
|
|
typedef internal::Schema<GenericSchemaDocument> SchemaType;
|
|
typedef GenericPointer<ValueType, Allocator> PointerType;
|
|
+ typedef GenericValue<EncodingType, AllocatorType> GValue;
|
|
+ typedef GenericUri<ValueType, Allocator> UriType;
|
|
+ typedef GenericStringRef<Ch> StringRefType;
|
|
friend class internal::Schema<GenericSchemaDocument>;
|
|
template <typename, typename, typename>
|
|
friend class GenericSchemaValidator;
|
|
@@ -1335,37 +1834,57 @@ public:
|
|
Compile a JSON document into schema document.
|
|
|
|
\param document A JSON document as source.
|
|
+ \param uri The base URI of this schema document for purposes of violation reporting.
|
|
+ \param uriLength Length of \c name, in code points.
|
|
\param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
|
|
\param allocator An optional allocator instance for allocating memory. Can be null.
|
|
+ \param pointer An optional JSON pointer to the start of the schema document
|
|
+ \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04.
|
|
*/
|
|
- explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
|
|
+ explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
|
|
+ IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0,
|
|
+ const PointerType& pointer = PointerType(), // PR #1393
|
|
+ const Specification& spec = Specification(kDraft04)) :
|
|
remoteProvider_(remoteProvider),
|
|
allocator_(allocator),
|
|
ownAllocator_(),
|
|
root_(),
|
|
+ typeless_(),
|
|
schemaMap_(allocator, kInitialSchemaMapSize),
|
|
- schemaRef_(allocator, kInitialSchemaRefSize)
|
|
+ schemaRef_(allocator, kInitialSchemaRefSize),
|
|
+ spec_(spec),
|
|
+ error_(kObjectType),
|
|
+ currentError_()
|
|
{
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument");
|
|
if (!allocator_)
|
|
- ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
|
|
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
|
|
|
- // Generate root schema, it will call CreateSchema() to create sub-schemas,
|
|
- // And call AddRefSchema() if there are $ref.
|
|
- CreateSchemaRecursive(&root_, PointerType(), document, document);
|
|
+ Ch noUri[1] = {0};
|
|
+ uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
|
|
+ docId_ = UriType(uri_, allocator_);
|
|
|
|
- // Resolve $ref
|
|
- while (!schemaRef_.Empty()) {
|
|
- SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
|
|
- if (const SchemaType* s = GetSchema(refEntry->target)) {
|
|
- if (refEntry->schema)
|
|
- *refEntry->schema = s;
|
|
-
|
|
- // Create entry in map if not exist
|
|
- if (!GetSchema(refEntry->source)) {
|
|
- new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
|
|
- }
|
|
- }
|
|
- refEntry->~SchemaRefEntry();
|
|
+ typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
|
|
+ new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_);
|
|
+
|
|
+ // Establish the schema draft or open api version.
|
|
+ // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document.
|
|
+ SetSchemaSpecification(document);
|
|
+
|
|
+ // Generate root schema, it will call CreateSchema() to create sub-schemas,
|
|
+ // And call HandleRefSchema() if there are $ref.
|
|
+ // PR #1393 use input pointer if supplied
|
|
+ root_ = typeless_;
|
|
+ if (pointer.GetTokenCount() == 0) {
|
|
+ CreateSchemaRecursive(&root_, pointer, document, document, docId_);
|
|
+ }
|
|
+ else if (const ValueType* v = pointer.Get(document)) {
|
|
+ CreateSchema(&root_, pointer, *v, document, docId_);
|
|
+ }
|
|
+ else {
|
|
+ GenericStringBuffer<EncodingType> sb;
|
|
+ pointer.StringifyUriFragment(sb);
|
|
+ SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)));
|
|
}
|
|
|
|
RAPIDJSON_ASSERT(root_ != 0);
|
|
@@ -1380,12 +1899,19 @@ public:
|
|
allocator_(rhs.allocator_),
|
|
ownAllocator_(rhs.ownAllocator_),
|
|
root_(rhs.root_),
|
|
+ typeless_(rhs.typeless_),
|
|
schemaMap_(std::move(rhs.schemaMap_)),
|
|
- schemaRef_(std::move(rhs.schemaRef_))
|
|
+ schemaRef_(std::move(rhs.schemaRef_)),
|
|
+ uri_(std::move(rhs.uri_)),
|
|
+ docId_(std::move(rhs.docId_)),
|
|
+ spec_(rhs.spec_),
|
|
+ error_(std::move(rhs.error_)),
|
|
+ currentError_(std::move(rhs.currentError_))
|
|
{
|
|
rhs.remoteProvider_ = 0;
|
|
rhs.allocator_ = 0;
|
|
rhs.ownAllocator_ = 0;
|
|
+ rhs.typeless_ = 0;
|
|
}
|
|
#endif
|
|
|
|
@@ -1394,24 +1920,92 @@ public:
|
|
while (!schemaMap_.Empty())
|
|
schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
|
|
|
|
+ if (typeless_) {
|
|
+ typeless_->~SchemaType();
|
|
+ Allocator::Free(typeless_);
|
|
+ }
|
|
+
|
|
+ // these may contain some allocator data so clear before deleting ownAllocator_
|
|
+ uri_.SetNull();
|
|
+ error_.SetNull();
|
|
+ currentError_.SetNull();
|
|
+
|
|
RAPIDJSON_DELETE(ownAllocator_);
|
|
}
|
|
|
|
+ const GValue& GetURI() const { return uri_; }
|
|
+
|
|
+ const Specification& GetSpecification() const { return spec_; }
|
|
+ bool IsSupportedSpecification() const { return spec_.IsSupported(); }
|
|
+
|
|
+ //! Static method to get the specification of any schema document
|
|
+ // Returns kDraftNone if document is silent
|
|
+ static const Specification GetSpecification(const ValueType& document) {
|
|
+ SchemaDraft draft = GetSchemaDraft(document);
|
|
+ if (draft != kDraftNone)
|
|
+ return Specification(draft);
|
|
+ else {
|
|
+ OpenApiVersion oapi = GetOpenApiVersion(document);
|
|
+ if (oapi != kVersionNone)
|
|
+ return Specification(oapi);
|
|
+ }
|
|
+ return Specification(kDraftNone);
|
|
+ }
|
|
+
|
|
//! Get the root schema.
|
|
const SchemaType& GetRoot() const { return *root_; }
|
|
|
|
-private:
|
|
+ //! Gets the error object.
|
|
+ GValue& GetError() { return error_; }
|
|
+ const GValue& GetError() const { return error_; }
|
|
+
|
|
+ static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) {
|
|
+ switch (schemaErrorCode) {
|
|
+ case kSchemaErrorStartUnknown: return GetStartUnknownString();
|
|
+ case kSchemaErrorRefPlainName: return GetRefPlainNameString();
|
|
+ case kSchemaErrorRefInvalid: return GetRefInvalidString();
|
|
+ case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString();
|
|
+ case kSchemaErrorRefUnknown: return GetRefUnknownString();
|
|
+ case kSchemaErrorRefCyclical: return GetRefCyclicalString();
|
|
+ case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString();
|
|
+ case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString();
|
|
+ case kSchemaErrorRegexInvalid: return GetRegexInvalidString();
|
|
+ case kSchemaErrorSpecUnknown: return GetSpecUnknownString();
|
|
+ case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString();
|
|
+ case kSchemaErrorSpecIllegal: return GetSpecIllegalString();
|
|
+ case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString();
|
|
+ default: return GetNullString();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //! Default error method
|
|
+ void SchemaError(const SchemaErrorCode code, const PointerType& location) {
|
|
+ currentError_ = GValue(kObjectType);
|
|
+ AddCurrentError(code, location);
|
|
+ }
|
|
+
|
|
+ //! Method for error with single string value insert
|
|
+ void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) {
|
|
+ currentError_ = GValue(kObjectType);
|
|
+ currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
|
|
+ AddCurrentError(code, location);
|
|
+ }
|
|
+
|
|
+ //! Method for error with invalid pointer
|
|
+ void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) {
|
|
+ currentError_ = GValue(kObjectType);
|
|
+ currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
|
|
+ currentError_.AddMember(GetOffsetString(), static_cast<SizeType>(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_);
|
|
+ AddCurrentError(code, location);
|
|
+ }
|
|
+
|
|
+ private:
|
|
//! Prohibit copying
|
|
GenericSchemaDocument(const GenericSchemaDocument&);
|
|
//! Prohibit assignment
|
|
GenericSchemaDocument& operator=(const GenericSchemaDocument&);
|
|
|
|
- struct SchemaRefEntry {
|
|
- SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
|
|
- PointerType source;
|
|
- PointerType target;
|
|
- const SchemaType** schema;
|
|
- };
|
|
+ typedef const PointerType* SchemaRefPtr; // PR #1393
|
|
|
|
struct SchemaEntry {
|
|
SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
|
|
@@ -1426,78 +2020,361 @@ private:
|
|
bool owned;
|
|
};
|
|
|
|
- void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
|
|
- if (schema)
|
|
- *schema = SchemaType::GetTypeless();
|
|
+ void AddErrorInstanceLocation(GValue& result, const PointerType& location) {
|
|
+ GenericStringBuffer<EncodingType> sb;
|
|
+ location.StringifyUriFragment(sb);
|
|
+ GValue instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)), *allocator_);
|
|
+ result.AddMember(GetInstanceRefString(), instanceRef, *allocator_);
|
|
+ }
|
|
+
|
|
+ void AddError(GValue& keyword, GValue& error) {
|
|
+ typename GValue::MemberIterator member = error_.FindMember(keyword);
|
|
+ if (member == error_.MemberEnd())
|
|
+ error_.AddMember(keyword, error, *allocator_);
|
|
+ else {
|
|
+ if (member->value.IsObject()) {
|
|
+ GValue errors(kArrayType);
|
|
+ errors.PushBack(member->value, *allocator_);
|
|
+ member->value = errors;
|
|
+ }
|
|
+ member->value.PushBack(error, *allocator_);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void AddCurrentError(const SchemaErrorCode code, const PointerType& location) {
|
|
+ RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code));
|
|
+ currentError_.AddMember(GetErrorCodeString(), code, *allocator_);
|
|
+ AddErrorInstanceLocation(currentError_, location);
|
|
+ AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_);
|
|
+ }
|
|
+
|
|
+#define RAPIDJSON_STRING_(name, ...) \
|
|
+ static const StringRefType& Get##name##String() {\
|
|
+ static const Ch s[] = { __VA_ARGS__, '\0' };\
|
|
+ static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
|
|
+ return v;\
|
|
+ }
|
|
+
|
|
+ RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
|
|
+ RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
|
|
+ RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e')
|
|
+ RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't')
|
|
+
|
|
+ RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
|
|
+ RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
|
|
+ RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd')
|
|
+ RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l')
|
|
+ RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
|
|
+ RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e')
|
|
+ RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
|
|
+ RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
|
|
+ RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
|
|
+ RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l')
|
|
+ RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r')
|
|
+ RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a')
|
|
+ RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
|
|
+ RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
|
|
|
|
+#undef RAPIDJSON_STRING_
|
|
+
|
|
+ // Static method to get schema draft of any schema document
|
|
+ static SchemaDraft GetSchemaDraft(const ValueType& document) {
|
|
+ static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
|
+ static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
|
+ static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
|
+ static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
|
+ static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
|
+ static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' };
|
|
+ static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' };
|
|
+
|
|
+ if (!document.IsObject()) {
|
|
+ return kDraftNone;
|
|
+ }
|
|
+
|
|
+ // Get the schema draft from the $schema keyword at the supplied location
|
|
+ typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString());
|
|
+ if (itr != document.MemberEnd()) {
|
|
+ if (!itr->value.IsString()) return kDraftUnknown;
|
|
+ const UriType draftUri(itr->value);
|
|
+ // Check base uri for match
|
|
+ if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04;
|
|
+ if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05;
|
|
+ if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06;
|
|
+ if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07;
|
|
+ if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03;
|
|
+ if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09;
|
|
+ if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12;
|
|
+ return kDraftUnknown;
|
|
+ }
|
|
+ // $schema not found
|
|
+ return kDraftNone;
|
|
+ }
|
|
+
|
|
+
|
|
+ // Get open api version of any schema document
|
|
+ static OpenApiVersion GetOpenApiVersion(const ValueType& document) {
|
|
+ static const Ch kVersion20String[] = { '2', '.', '0', '\0' };
|
|
+ static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level
|
|
+ static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level
|
|
+ static SizeType len = internal::StrLen<Ch>(kVersion30String);
|
|
+
|
|
+ if (!document.IsObject()) {
|
|
+ return kVersionNone;
|
|
+ }
|
|
+
|
|
+ // Get the open api version from the swagger / openapi keyword at the supplied location
|
|
+ typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString());
|
|
+ if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString());
|
|
+ if (itr != document.MemberEnd()) {
|
|
+ if (!itr->value.IsString()) return kVersionUnknown;
|
|
+ const ValueType kVersion20Value(kVersion20String);
|
|
+ if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly
|
|
+ const ValueType kVersion30Value(kVersion30String);
|
|
+ if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x
|
|
+ const ValueType kVersion31Value(kVersion31String);
|
|
+ if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x
|
|
+ return kVersionUnknown;
|
|
+ }
|
|
+ // swagger or openapi not found
|
|
+ return kVersionNone;
|
|
+ }
|
|
+
|
|
+ // Get the draft of the schema or the open api version (which implies the draft).
|
|
+ // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on.
|
|
+ void SetSchemaSpecification(const ValueType& document) {
|
|
+ // Look for '$schema', 'swagger' or 'openapi' keyword at document root
|
|
+ SchemaDraft docDraft = GetSchemaDraft(document);
|
|
+ OpenApiVersion docOapi = GetOpenApiVersion(document);
|
|
+ // Error if both in document
|
|
+ if (docDraft != kDraftNone && docOapi != kVersionNone)
|
|
+ SchemaError(kSchemaErrorSpecIllegal, PointerType());
|
|
+ // Use document draft or open api version if present or use spec from constructor
|
|
+ if (docDraft != kDraftNone)
|
|
+ spec_ = Specification(docDraft);
|
|
+ else if (docOapi != kVersionNone)
|
|
+ spec_ = Specification(docOapi);
|
|
+ // Error if draft or version unknown
|
|
+ if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown)
|
|
+ SchemaError(kSchemaErrorSpecUnknown, PointerType());
|
|
+ else if (!spec_.IsSupported())
|
|
+ SchemaError(kSchemaErrorSpecUnsupported, PointerType());
|
|
+ }
|
|
+
|
|
+ // Changed by PR #1393
|
|
+ void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
|
|
if (v.GetType() == kObjectType) {
|
|
- const SchemaType* s = GetSchema(pointer);
|
|
- if (!s)
|
|
- CreateSchema(schema, pointer, v, document);
|
|
+ UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_);
|
|
|
|
for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
|
|
- CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
|
|
+ CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid);
|
|
}
|
|
else if (v.GetType() == kArrayType)
|
|
for (SizeType i = 0; i < v.Size(); i++)
|
|
- CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
|
|
+ CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id);
|
|
}
|
|
|
|
- void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
|
|
+ // Changed by PR #1393
|
|
+ const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
|
|
RAPIDJSON_ASSERT(pointer.IsValid());
|
|
+ GenericStringBuffer<EncodingType> sb;
|
|
+ pointer.StringifyUriFragment(sb);
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString());
|
|
if (v.IsObject()) {
|
|
- if (!HandleRefSchema(pointer, schema, v, document)) {
|
|
- SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
|
|
- new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
|
|
+ if (const SchemaType* sc = GetSchema(pointer)) {
|
|
+ if (schema)
|
|
+ *schema = sc;
|
|
+ AddSchemaRefs(const_cast<SchemaType*>(sc));
|
|
+ }
|
|
+ else if (!HandleRefSchema(pointer, schema, v, document, id)) {
|
|
+ // The new schema constructor adds itself and its $ref(s) to schemaMap_
|
|
+ SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id);
|
|
if (schema)
|
|
*schema = s;
|
|
+ return s->GetId();
|
|
}
|
|
}
|
|
+ else {
|
|
+ if (schema)
|
|
+ *schema = typeless_;
|
|
+ AddSchemaRefs(typeless_);
|
|
+ }
|
|
+ return id;
|
|
}
|
|
|
|
- bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
|
|
- static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
|
|
- static const ValueType kRefValue(kRefString, 4);
|
|
-
|
|
- typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
|
|
+ // Changed by PR #1393
|
|
+ // TODO should this return a UriType& ?
|
|
+ bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) {
|
|
+ typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString());
|
|
if (itr == v.MemberEnd())
|
|
return false;
|
|
|
|
+ GenericStringBuffer<EncodingType> sb;
|
|
+ source.StringifyUriFragment(sb);
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString());
|
|
+ // Resolve the source pointer to the $ref'ed schema (finally)
|
|
+ new (schemaRef_.template Push<SchemaRefPtr>()) SchemaRefPtr(&source);
|
|
+
|
|
if (itr->value.IsString()) {
|
|
SizeType len = itr->value.GetStringLength();
|
|
- if (len > 0) {
|
|
- const Ch* s = itr->value.GetString();
|
|
- SizeType i = 0;
|
|
- while (i < len && s[i] != '#') // Find the first #
|
|
- i++;
|
|
-
|
|
- if (i > 0) { // Remote reference, resolve immediately
|
|
- if (remoteProvider_) {
|
|
- if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) {
|
|
- PointerType pointer(&s[i], len - i, allocator_);
|
|
- if (pointer.IsValid()) {
|
|
- if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
|
|
- if (schema)
|
|
- *schema = sc;
|
|
+ if (len == 0)
|
|
+ SchemaError(kSchemaErrorRefInvalid, source);
|
|
+ else {
|
|
+ // First resolve $ref against the in-scope id
|
|
+ UriType scopeId = UriType(id, allocator_);
|
|
+ UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_);
|
|
+ RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString());
|
|
+ // See if the resolved $ref minus the fragment matches a resolved id in this document
|
|
+ // Search from the root. Returns the subschema in the document and its absolute JSON pointer.
|
|
+ PointerType basePointer = PointerType();
|
|
+ const ValueType *base = FindId(document, ref, basePointer, docId_, false);
|
|
+ if (!base) {
|
|
+ // Remote reference - call the remote document provider
|
|
+ if (!remoteProvider_)
|
|
+ SchemaError(kSchemaErrorRefNoRemoteProvider, source);
|
|
+ else {
|
|
+ if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) {
|
|
+ const Ch* s = ref.GetFragString();
|
|
+ len = ref.GetFragStringLength();
|
|
+ if (len <= 1 || s[1] == '/') {
|
|
+ // JSON pointer fragment, absolute in the remote schema
|
|
+ const PointerType pointer(s, len, allocator_);
|
|
+ if (!pointer.IsValid())
|
|
+ SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer);
|
|
+ else {
|
|
+ // Get the subschema
|
|
+ if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) {
|
|
+ if (schema)
|
|
+ *schema = sc;
|
|
+ AddSchemaRefs(const_cast<SchemaType *>(sc));
|
|
+ return true;
|
|
+ } else
|
|
+ SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
|
|
+ }
|
|
+ } else
|
|
+ // Plain name fragment, not allowed in remote schema
|
|
+ SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
|
|
+ } else
|
|
+ SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength());
|
|
+ }
|
|
+ }
|
|
+ else { // Local reference
|
|
+ const Ch* s = ref.GetFragString();
|
|
+ len = ref.GetFragStringLength();
|
|
+ if (len <= 1 || s[1] == '/') {
|
|
+ // JSON pointer fragment, relative to the resolved URI
|
|
+ const PointerType relPointer(s, len, allocator_);
|
|
+ if (!relPointer.IsValid())
|
|
+ SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer);
|
|
+ else {
|
|
+ // Get the subschema
|
|
+ if (const ValueType *pv = relPointer.Get(*base)) {
|
|
+ // Now get the absolute JSON pointer by adding relative to base
|
|
+ PointerType pointer(basePointer, allocator_);
|
|
+ for (SizeType i = 0; i < relPointer.GetTokenCount(); i++)
|
|
+ pointer = pointer.Append(relPointer.GetTokens()[i], allocator_);
|
|
+ if (IsCyclicRef(pointer))
|
|
+ SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
|
|
+ else {
|
|
+ // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
|
|
+ // TODO: cache pointer <-> id mapping
|
|
+ size_t unresolvedTokenIndex;
|
|
+ scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
|
|
+ CreateSchema(schema, pointer, *pv, document, scopeId);
|
|
return true;
|
|
}
|
|
- }
|
|
+ } else
|
|
+ SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
|
|
}
|
|
+ } else {
|
|
+ // Plain name fragment, relative to the resolved URI
|
|
+ // Not supported in open api 2.0 and 3.0
|
|
+ PointerType pointer(allocator_);
|
|
+ if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30)
|
|
+ SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
|
|
+ // See if the fragment matches an id in this document.
|
|
+ // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer.
|
|
+ else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
|
|
+ if (IsCyclicRef(pointer))
|
|
+ SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
|
|
+ else {
|
|
+ // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
|
|
+ // TODO: cache pointer <-> id mapping
|
|
+ size_t unresolvedTokenIndex;
|
|
+ scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
|
|
+ CreateSchema(schema, pointer, *pv, document, scopeId);
|
|
+ return true;
|
|
+ }
|
|
+ } else
|
|
+ SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
|
|
}
|
|
}
|
|
- else if (s[i] == '#') { // Local reference, defer resolution
|
|
- PointerType pointer(&s[i], len - i, allocator_);
|
|
- if (pointer.IsValid()) {
|
|
- if (const ValueType* nv = pointer.Get(document))
|
|
- if (HandleRefSchema(source, schema, *nv, document))
|
|
- return true;
|
|
+ }
|
|
+ }
|
|
|
|
- new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
|
|
- return true;
|
|
- }
|
|
+ // Invalid/Unknown $ref
|
|
+ if (schema)
|
|
+ *schema = typeless_;
|
|
+ AddSchemaRefs(typeless_);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ //! Find the first subschema with a resolved 'id' that matches the specified URI.
|
|
+ // If full specified use all URI else ignore fragment.
|
|
+ // If found, return a pointer to the subschema and its JSON pointer.
|
|
+ // TODO cache pointer <-> id mapping
|
|
+ ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const {
|
|
+ SizeType i = 0;
|
|
+ ValueType* resval = 0;
|
|
+ UriType tempuri = UriType(finduri, allocator_);
|
|
+ UriType localuri = UriType(baseuri, allocator_);
|
|
+ if (doc.GetType() == kObjectType) {
|
|
+ // Establish the base URI of this object
|
|
+ typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString());
|
|
+ if (m != doc.MemberEnd() && m->value.GetType() == kStringType) {
|
|
+ localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_);
|
|
+ }
|
|
+ // See if it matches
|
|
+ if (localuri.Match(finduri, full)) {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString());
|
|
+ resval = const_cast<ValueType *>(&doc);
|
|
+ resptr = here;
|
|
+ return resval;
|
|
+ }
|
|
+ // No match, continue looking
|
|
+ for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) {
|
|
+ if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) {
|
|
+ resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_));
|
|
+ }
|
|
+ if (resval) break;
|
|
+ }
|
|
+ } else if (doc.GetType() == kArrayType) {
|
|
+ // Continue looking
|
|
+ for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) {
|
|
+ if (v->GetType() == kObjectType || v->GetType() == kArrayType) {
|
|
+ resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_));
|
|
}
|
|
+ if (resval) break;
|
|
+ i++;
|
|
}
|
|
}
|
|
+ return resval;
|
|
+ }
|
|
+
|
|
+ // Added by PR #1393
|
|
+ void AddSchemaRefs(SchemaType* schema) {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs");
|
|
+ while (!schemaRef_.Empty()) {
|
|
+ SchemaRefPtr *ref = schemaRef_.template Pop<SchemaRefPtr>(1);
|
|
+ SchemaEntry *entry = schemaMap_.template Push<SchemaEntry>();
|
|
+ new (entry) SchemaEntry(**ref, schema, false, allocator_);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Added by PR #1393
|
|
+ bool IsCyclicRef(const PointerType& pointer) const {
|
|
+ for (const SchemaRefPtr* ref = schemaRef_.template Bottom<SchemaRefPtr>(); ref != schemaRef_.template End<SchemaRefPtr>(); ++ref)
|
|
+ if (pointer == **ref)
|
|
+ return true;
|
|
return false;
|
|
}
|
|
|
|
@@ -1515,6 +2392,8 @@ private:
|
|
return PointerType();
|
|
}
|
|
|
|
+ const SchemaType* GetTypeless() const { return typeless_; }
|
|
+
|
|
static const size_t kInitialSchemaMapSize = 64;
|
|
static const size_t kInitialSchemaRefSize = 64;
|
|
|
|
@@ -1522,8 +2401,14 @@ private:
|
|
Allocator *allocator_;
|
|
Allocator *ownAllocator_;
|
|
const SchemaType* root_; //!< Root schema.
|
|
+ SchemaType* typeless_;
|
|
internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
|
|
- internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
|
|
+ internal::Stack<Allocator> schemaRef_; // Stores Pointer(s) from $ref(s) until resolved
|
|
+ GValue uri_; // Schema document URI
|
|
+ UriType docId_;
|
|
+ Specification spec_;
|
|
+ GValue error_;
|
|
+ GValue currentError_;
|
|
};
|
|
|
|
//! GenericSchemaDocument using Value type.
|
|
@@ -1552,13 +2437,16 @@ template <
|
|
typename StateAllocator = CrtAllocator>
|
|
class GenericSchemaValidator :
|
|
public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
|
|
- public internal::ISchemaValidator
|
|
-{
|
|
+ public internal::ISchemaValidator,
|
|
+ public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType> {
|
|
public:
|
|
typedef typename SchemaDocumentType::SchemaType SchemaType;
|
|
typedef typename SchemaDocumentType::PointerType PointerType;
|
|
typedef typename SchemaType::EncodingType EncodingType;
|
|
+ typedef typename SchemaType::SValue SValue;
|
|
typedef typename EncodingType::Ch Ch;
|
|
+ typedef GenericStringRef<Ch> StringRefType;
|
|
+ typedef GenericValue<EncodingType, StateAllocator> ValueType;
|
|
|
|
//! Constructor without output handler.
|
|
/*!
|
|
@@ -1575,16 +2463,19 @@ public:
|
|
:
|
|
schemaDocument_(&schemaDocument),
|
|
root_(schemaDocument.GetRoot()),
|
|
- outputHandler_(GetNullHandler()),
|
|
stateAllocator_(allocator),
|
|
ownStateAllocator_(0),
|
|
schemaStack_(allocator, schemaStackCapacity),
|
|
documentStack_(allocator, documentStackCapacity),
|
|
- valid_(true)
|
|
-#if RAPIDJSON_SCHEMA_VERBOSE
|
|
- , depth_(0)
|
|
-#endif
|
|
+ outputHandler_(0),
|
|
+ error_(kObjectType),
|
|
+ currentError_(),
|
|
+ missingDependents_(),
|
|
+ valid_(true),
|
|
+ flags_(kValidateDefaultFlags),
|
|
+ depth_(0)
|
|
{
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator");
|
|
}
|
|
|
|
//! Constructor with output handler.
|
|
@@ -1603,16 +2494,19 @@ public:
|
|
:
|
|
schemaDocument_(&schemaDocument),
|
|
root_(schemaDocument.GetRoot()),
|
|
- outputHandler_(outputHandler),
|
|
stateAllocator_(allocator),
|
|
ownStateAllocator_(0),
|
|
schemaStack_(allocator, schemaStackCapacity),
|
|
documentStack_(allocator, documentStackCapacity),
|
|
- valid_(true)
|
|
-#if RAPIDJSON_SCHEMA_VERBOSE
|
|
- , depth_(0)
|
|
-#endif
|
|
+ outputHandler_(&outputHandler),
|
|
+ error_(kObjectType),
|
|
+ currentError_(),
|
|
+ missingDependents_(),
|
|
+ valid_(true),
|
|
+ flags_(kValidateDefaultFlags),
|
|
+ depth_(0)
|
|
{
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)");
|
|
}
|
|
|
|
//! Destructor.
|
|
@@ -1626,44 +2520,291 @@ public:
|
|
while (!schemaStack_.Empty())
|
|
PopSchema();
|
|
documentStack_.Clear();
|
|
+ ResetError();
|
|
+ }
|
|
+
|
|
+ //! Reset the error state.
|
|
+ void ResetError() {
|
|
+ error_.SetObject();
|
|
+ currentError_.SetNull();
|
|
+ missingDependents_.SetNull();
|
|
valid_ = true;
|
|
}
|
|
|
|
- //! Checks whether the current state is valid.
|
|
- // Implementation of ISchemaValidator
|
|
- virtual bool IsValid() const { return valid_; }
|
|
+ //! Implementation of ISchemaValidator
|
|
+ void SetValidateFlags(unsigned flags) {
|
|
+ flags_ = flags;
|
|
+ }
|
|
+ virtual unsigned GetValidateFlags() const {
|
|
+ return flags_;
|
|
+ }
|
|
+
|
|
+ virtual bool IsValid() const {
|
|
+ if (!valid_) return false;
|
|
+ if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false;
|
|
+ return true;
|
|
+ }
|
|
+ //! End of Implementation of ISchemaValidator
|
|
+
|
|
+ //! Gets the error object.
|
|
+ ValueType& GetError() { return error_; }
|
|
+ const ValueType& GetError() const { return error_; }
|
|
|
|
//! Gets the JSON pointer pointed to the invalid schema.
|
|
+ // If reporting all errors, the stack will be empty.
|
|
PointerType GetInvalidSchemaPointer() const {
|
|
- return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
|
|
+ return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
|
|
}
|
|
|
|
//! Gets the keyword of invalid schema.
|
|
+ // If reporting all errors, the stack will be empty, so return "errors".
|
|
const Ch* GetInvalidSchemaKeyword() const {
|
|
- return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
|
|
+ if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword;
|
|
+ if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString();
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ //! Gets the error code of invalid schema.
|
|
+ // If reporting all errors, the stack will be empty, so return kValidateErrors.
|
|
+ ValidateErrorCode GetInvalidSchemaCode() const {
|
|
+ if (!schemaStack_.Empty()) return CurrentContext().invalidCode;
|
|
+ if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors;
|
|
+ return kValidateErrorNone;
|
|
}
|
|
|
|
//! Gets the JSON pointer pointed to the invalid value.
|
|
+ // If reporting all errors, the stack will be empty.
|
|
PointerType GetInvalidDocumentPointer() const {
|
|
- return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
|
|
+ if (documentStack_.Empty()) {
|
|
+ return PointerType();
|
|
+ }
|
|
+ else {
|
|
+ return PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
|
|
+ }
|
|
}
|
|
|
|
-#if RAPIDJSON_SCHEMA_VERBOSE
|
|
-#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
|
|
-RAPIDJSON_MULTILINEMACRO_BEGIN\
|
|
- *documentStack_.template Push<Ch>() = '\0';\
|
|
- documentStack_.template Pop<Ch>(1);\
|
|
- internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
|
|
-RAPIDJSON_MULTILINEMACRO_END
|
|
-#else
|
|
-#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
|
|
-#endif
|
|
+ void NotMultipleOf(int64_t actual, const SValue& expected) {
|
|
+ AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
|
|
+ }
|
|
+ void NotMultipleOf(uint64_t actual, const SValue& expected) {
|
|
+ AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
|
|
+ }
|
|
+ void NotMultipleOf(double actual, const SValue& expected) {
|
|
+ AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
|
|
+ }
|
|
+ void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
|
|
+ AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
|
|
+ exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
|
|
+ }
|
|
+ void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
|
|
+ AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
|
|
+ exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
|
|
+ }
|
|
+ void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
|
|
+ AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
|
|
+ exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
|
|
+ }
|
|
+ void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
|
|
+ AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
|
|
+ exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
|
|
+ }
|
|
+ void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
|
|
+ AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
|
|
+ exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
|
|
+ }
|
|
+ void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
|
|
+ AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
|
|
+ exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
|
|
+ }
|
|
+
|
|
+ void TooLong(const Ch* str, SizeType length, SizeType expected) {
|
|
+ AddNumberError(kValidateErrorMaxLength,
|
|
+ ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
|
|
+ }
|
|
+ void TooShort(const Ch* str, SizeType length, SizeType expected) {
|
|
+ AddNumberError(kValidateErrorMinLength,
|
|
+ ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
|
|
+ }
|
|
+ void DoesNotMatch(const Ch* str, SizeType length) {
|
|
+ currentError_.SetObject();
|
|
+ currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
|
|
+ AddCurrentError(kValidateErrorPattern);
|
|
+ }
|
|
+
|
|
+ void DisallowedItem(SizeType index) {
|
|
+ currentError_.SetObject();
|
|
+ currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
|
|
+ AddCurrentError(kValidateErrorAdditionalItems, true);
|
|
+ }
|
|
+ void TooFewItems(SizeType actualCount, SizeType expectedCount) {
|
|
+ AddNumberError(kValidateErrorMinItems,
|
|
+ ValueType(actualCount).Move(), SValue(expectedCount).Move());
|
|
+ }
|
|
+ void TooManyItems(SizeType actualCount, SizeType expectedCount) {
|
|
+ AddNumberError(kValidateErrorMaxItems,
|
|
+ ValueType(actualCount).Move(), SValue(expectedCount).Move());
|
|
+ }
|
|
+ void DuplicateItems(SizeType index1, SizeType index2) {
|
|
+ ValueType duplicates(kArrayType);
|
|
+ duplicates.PushBack(index1, GetStateAllocator());
|
|
+ duplicates.PushBack(index2, GetStateAllocator());
|
|
+ currentError_.SetObject();
|
|
+ currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
|
|
+ AddCurrentError(kValidateErrorUniqueItems, true);
|
|
+ }
|
|
+
|
|
+ void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
|
|
+ AddNumberError(kValidateErrorMaxProperties,
|
|
+ ValueType(actualCount).Move(), SValue(expectedCount).Move());
|
|
+ }
|
|
+ void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
|
|
+ AddNumberError(kValidateErrorMinProperties,
|
|
+ ValueType(actualCount).Move(), SValue(expectedCount).Move());
|
|
+ }
|
|
+ void StartMissingProperties() {
|
|
+ currentError_.SetArray();
|
|
+ }
|
|
+ void AddMissingProperty(const SValue& name) {
|
|
+ currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator());
|
|
+ }
|
|
+ bool EndMissingProperties() {
|
|
+ if (currentError_.Empty())
|
|
+ return false;
|
|
+ ValueType error(kObjectType);
|
|
+ error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
|
|
+ currentError_ = error;
|
|
+ AddCurrentError(kValidateErrorRequired);
|
|
+ return true;
|
|
+ }
|
|
+ void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
|
|
+ for (SizeType i = 0; i < count; ++i)
|
|
+ MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
|
|
+ }
|
|
+ void DisallowedProperty(const Ch* name, SizeType length) {
|
|
+ currentError_.SetObject();
|
|
+ currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
|
|
+ AddCurrentError(kValidateErrorAdditionalProperties, true);
|
|
+ }
|
|
+
|
|
+ void StartDependencyErrors() {
|
|
+ currentError_.SetObject();
|
|
+ }
|
|
+ void StartMissingDependentProperties() {
|
|
+ missingDependents_.SetArray();
|
|
+ }
|
|
+ void AddMissingDependentProperty(const SValue& targetName) {
|
|
+ missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
|
|
+ }
|
|
+ void EndMissingDependentProperties(const SValue& sourceName) {
|
|
+ if (!missingDependents_.Empty()) {
|
|
+ // Create equivalent 'required' error
|
|
+ ValueType error(kObjectType);
|
|
+ ValidateErrorCode code = kValidateErrorRequired;
|
|
+ error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator());
|
|
+ AddErrorCode(error, code);
|
|
+ AddErrorInstanceLocation(error, false);
|
|
+ // When appending to a pointer ensure its allocator is used
|
|
+ PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator());
|
|
+ AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator()));
|
|
+ ValueType wrapper(kObjectType);
|
|
+ wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator());
|
|
+ currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator());
|
|
+ }
|
|
+ }
|
|
+ void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
|
|
+ currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
|
|
+ static_cast<GenericSchemaValidator*>(subvalidator)->GetError(), GetStateAllocator());
|
|
+ }
|
|
+ bool EndDependencyErrors() {
|
|
+ if (currentError_.ObjectEmpty())
|
|
+ return false;
|
|
+ ValueType error(kObjectType);
|
|
+ error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
|
|
+ currentError_ = error;
|
|
+ AddCurrentError(kValidateErrorDependencies);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) {
|
|
+ currentError_.SetObject();
|
|
+ AddCurrentError(code);
|
|
+ }
|
|
+ void StartDisallowedType() {
|
|
+ currentError_.SetArray();
|
|
+ }
|
|
+ void AddExpectedType(const typename SchemaType::ValueType& expectedType) {
|
|
+ currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator());
|
|
+ }
|
|
+ void EndDisallowedType(const typename SchemaType::ValueType& actualType) {
|
|
+ ValueType error(kObjectType);
|
|
+ error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
|
|
+ error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
|
|
+ currentError_ = error;
|
|
+ AddCurrentError(kValidateErrorType);
|
|
+ }
|
|
+ void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
|
|
+ // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf
|
|
+ AddErrorArray(kValidateErrorAllOf, subvalidators, count);
|
|
+ //for (SizeType i = 0; i < count; ++i) {
|
|
+ // MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
|
|
+ //}
|
|
+ }
|
|
+ void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
|
|
+ AddErrorArray(kValidateErrorAnyOf, subvalidators, count);
|
|
+ }
|
|
+ void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
|
|
+ AddErrorArray(kValidateErrorOneOf, subvalidators, count);
|
|
+ }
|
|
+ void MultipleOneOf(SizeType index1, SizeType index2) {
|
|
+ ValueType matches(kArrayType);
|
|
+ matches.PushBack(index1, GetStateAllocator());
|
|
+ matches.PushBack(index2, GetStateAllocator());
|
|
+ currentError_.SetObject();
|
|
+ currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator());
|
|
+ AddCurrentError(kValidateErrorOneOfMatch);
|
|
+ }
|
|
+ void Disallowed() {
|
|
+ currentError_.SetObject();
|
|
+ AddCurrentError(kValidateErrorNot);
|
|
+ }
|
|
+ void DisallowedWhenWriting() {
|
|
+ currentError_.SetObject();
|
|
+ AddCurrentError(kValidateErrorReadOnly);
|
|
+ }
|
|
+ void DisallowedWhenReading() {
|
|
+ currentError_.SetObject();
|
|
+ AddCurrentError(kValidateErrorWriteOnly);
|
|
+ }
|
|
+
|
|
+#define RAPIDJSON_STRING_(name, ...) \
|
|
+ static const StringRefType& Get##name##String() {\
|
|
+ static const Ch s[] = { __VA_ARGS__, '\0' };\
|
|
+ static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
|
|
+ return v;\
|
|
+ }
|
|
+
|
|
+ RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
|
|
+ RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f')
|
|
+ RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd')
|
|
+ RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l')
|
|
+ RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
|
|
+ RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
|
|
+ RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
|
|
+ RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
|
|
+ RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e')
|
|
+ RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
|
|
+ RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's')
|
|
+
|
|
+#undef RAPIDJSON_STRING_
|
|
|
|
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
|
|
if (!valid_) return false; \
|
|
- if (!BeginValue() || !CurrentSchema().method arg1) {\
|
|
- RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
|
|
- return valid_ = false;\
|
|
+ if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\
|
|
+ *documentStack_.template Push<Ch>() = '\0';\
|
|
+ documentStack_.template Pop<Ch>(1);\
|
|
+ RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom<Ch>());\
|
|
+ valid_ = false;\
|
|
+ return valid_;\
|
|
}
|
|
|
|
#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
|
|
@@ -1679,14 +2820,15 @@ RAPIDJSON_MULTILINEMACRO_END
|
|
}
|
|
|
|
#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
|
|
- return valid_ = EndValue() && outputHandler_.method arg2
|
|
+ valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\
|
|
+ return valid_;
|
|
|
|
#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
|
|
RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
|
|
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
|
|
RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
|
|
|
|
- bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); }
|
|
+ bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); }
|
|
bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
|
|
bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
|
|
bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
|
|
@@ -1699,51 +2841,69 @@ RAPIDJSON_MULTILINEMACRO_END
|
|
{ RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
|
|
|
|
bool StartObject() {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject");
|
|
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
|
|
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
|
|
- return valid_ = outputHandler_.StartObject();
|
|
+ valid_ = !outputHandler_ || outputHandler_->StartObject();
|
|
+ return valid_;
|
|
}
|
|
|
|
bool Key(const Ch* str, SizeType len, bool copy) {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str);
|
|
if (!valid_) return false;
|
|
AppendToken(str, len);
|
|
- if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
|
|
+ if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) {
|
|
+ valid_ = false;
|
|
+ return valid_;
|
|
+ }
|
|
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
|
|
- return valid_ = outputHandler_.Key(str, len, copy);
|
|
+ valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
|
|
+ return valid_;
|
|
}
|
|
|
|
- bool EndObject(SizeType memberCount) {
|
|
+ bool EndObject(SizeType memberCount) {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject");
|
|
if (!valid_) return false;
|
|
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
|
|
- if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
|
|
+ if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) {
|
|
+ valid_ = false;
|
|
+ return valid_;
|
|
+ }
|
|
RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
|
|
}
|
|
|
|
bool StartArray() {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray");
|
|
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
|
|
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
|
|
- return valid_ = outputHandler_.StartArray();
|
|
+ valid_ = !outputHandler_ || outputHandler_->StartArray();
|
|
+ return valid_;
|
|
}
|
|
|
|
bool EndArray(SizeType elementCount) {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray");
|
|
if (!valid_) return false;
|
|
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
|
|
- if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
|
|
+ if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) {
|
|
+ valid_ = false;
|
|
+ return valid_;
|
|
+ }
|
|
RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
|
|
}
|
|
|
|
-#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
|
|
#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
|
|
#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
|
|
#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
|
|
|
|
// Implementation of ISchemaStateFactory<SchemaType>
|
|
- virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
|
|
- return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
|
|
-#if RAPIDJSON_SCHEMA_VERBOSE
|
|
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) {
|
|
+ *documentStack_.template Push<Ch>() = '\0';
|
|
+ documentStack_.template Pop<Ch>(1);
|
|
+ ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
|
|
depth_ + 1,
|
|
-#endif
|
|
&GetStateAllocator());
|
|
+ sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag);
|
|
+ return sv;
|
|
}
|
|
|
|
virtual void DestroySchemaValidator(ISchemaValidator* validator) {
|
|
@@ -1771,8 +2931,9 @@ RAPIDJSON_MULTILINEMACRO_END
|
|
}
|
|
|
|
virtual void FreeState(void* p) {
|
|
- return StateAllocator::Free(p);
|
|
+ StateAllocator::Free(p);
|
|
}
|
|
+ // End of implementation of ISchemaStateFactory<SchemaType>
|
|
|
|
private:
|
|
typedef typename SchemaType::Context Context;
|
|
@@ -1782,57 +2943,67 @@ private:
|
|
GenericSchemaValidator(
|
|
const SchemaDocumentType& schemaDocument,
|
|
const SchemaType& root,
|
|
-#if RAPIDJSON_SCHEMA_VERBOSE
|
|
+ const char* basePath, size_t basePathSize,
|
|
unsigned depth,
|
|
-#endif
|
|
StateAllocator* allocator = 0,
|
|
size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
|
|
size_t documentStackCapacity = kDefaultDocumentStackCapacity)
|
|
:
|
|
schemaDocument_(&schemaDocument),
|
|
root_(root),
|
|
- outputHandler_(GetNullHandler()),
|
|
stateAllocator_(allocator),
|
|
ownStateAllocator_(0),
|
|
schemaStack_(allocator, schemaStackCapacity),
|
|
documentStack_(allocator, documentStackCapacity),
|
|
- valid_(true)
|
|
-#if RAPIDJSON_SCHEMA_VERBOSE
|
|
- , depth_(depth)
|
|
-#endif
|
|
+ outputHandler_(0),
|
|
+ error_(kObjectType),
|
|
+ currentError_(),
|
|
+ missingDependents_(),
|
|
+ valid_(true),
|
|
+ flags_(kValidateDefaultFlags),
|
|
+ depth_(depth)
|
|
{
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : "");
|
|
+ if (basePath && basePathSize)
|
|
+ memcpy(documentStack_.template Push<char>(basePathSize), basePath, basePathSize);
|
|
}
|
|
|
|
StateAllocator& GetStateAllocator() {
|
|
if (!stateAllocator_)
|
|
- stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator());
|
|
+ stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
|
|
return *stateAllocator_;
|
|
}
|
|
|
|
+ bool GetContinueOnErrors() const {
|
|
+ return flags_ & kValidateContinueOnErrorFlag;
|
|
+ }
|
|
+
|
|
bool BeginValue() {
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue");
|
|
if (schemaStack_.Empty())
|
|
PushSchema(root_);
|
|
else {
|
|
if (CurrentContext().inArray)
|
|
internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
|
|
|
|
- if (!CurrentSchema().BeginValue(CurrentContext()))
|
|
+ if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors())
|
|
return false;
|
|
|
|
SizeType count = CurrentContext().patternPropertiesSchemaCount;
|
|
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;
|
|
ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
|
|
SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
|
|
va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
|
|
+ std::memset(va, 0, sizeof(ISchemaValidator*) * count);
|
|
for (SizeType i = 0; i < count; i++)
|
|
- va[validatorCount++] = CreateSchemaValidator(*sa[i]);
|
|
+ va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError
|
|
}
|
|
|
|
CurrentContext().arrayUniqueness = valueUniqueness;
|
|
@@ -1841,31 +3012,37 @@ private:
|
|
}
|
|
|
|
bool EndValue() {
|
|
- if (!CurrentSchema().EndValue(CurrentContext()))
|
|
+ RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue");
|
|
+ if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors())
|
|
return false;
|
|
|
|
-#if RAPIDJSON_SCHEMA_VERBOSE
|
|
GenericStringBuffer<EncodingType> sb;
|
|
- schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
|
|
-
|
|
+ schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb);
|
|
*documentStack_.template Push<Ch>() = '\0';
|
|
documentStack_.template Pop<Ch>(1);
|
|
- internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
|
|
-#endif
|
|
-
|
|
- uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
|
|
+ RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom<Ch>(), depth_);
|
|
+ void* hasher = CurrentContext().hasher;
|
|
+ uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast<HasherType*>(hasher)->GetHashCode() : 0;
|
|
|
|
PopSchema();
|
|
|
|
if (!schemaStack_.Empty()) {
|
|
Context& context = CurrentContext();
|
|
- if (context.valueUniqueness) {
|
|
+ // Only check uniqueness if there is a hasher
|
|
+ if (hasher && context.valueUniqueness) {
|
|
HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
|
|
if (!a)
|
|
CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
|
|
for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
|
|
- if (itr->GetUint64() == h)
|
|
- RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
|
|
+ if (itr->GetUint64() == h) {
|
|
+ DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
|
|
+ // Cleanup before returning if continuing
|
|
+ if (GetContinueOnErrors()) {
|
|
+ a->PushBack(h, GetStateAllocator());
|
|
+ while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/');
|
|
+ }
|
|
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems);
|
|
+ }
|
|
a->PushBack(h, GetStateAllocator());
|
|
}
|
|
}
|
|
@@ -1894,7 +3071,7 @@ private:
|
|
}
|
|
}
|
|
|
|
- RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
|
|
+ RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, *this, &schema, flags_); }
|
|
|
|
RAPIDJSON_FORCEINLINE void PopSchema() {
|
|
Context* c = schemaStack_.template Pop<Context>(1);
|
|
@@ -1905,28 +3082,98 @@ private:
|
|
c->~Context();
|
|
}
|
|
|
|
+ void AddErrorInstanceLocation(ValueType& result, bool parent) {
|
|
+ GenericStringBuffer<EncodingType> sb;
|
|
+ PointerType instancePointer = GetInvalidDocumentPointer();
|
|
+ ((parent && instancePointer.GetTokenCount() > 0)
|
|
+ ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
|
|
+ : instancePointer).StringifyUriFragment(sb);
|
|
+ ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
|
|
+ GetStateAllocator());
|
|
+ result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
|
|
+ }
|
|
+
|
|
+ void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) {
|
|
+ GenericStringBuffer<EncodingType> sb;
|
|
+ SizeType len = CurrentSchema().GetURI().GetStringLength();
|
|
+ if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch));
|
|
+ if (schema.GetTokenCount()) schema.StringifyUriFragment(sb);
|
|
+ else GetInvalidSchemaPointer().StringifyUriFragment(sb);
|
|
+ ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
|
|
+ GetStateAllocator());
|
|
+ result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
|
|
+ }
|
|
+
|
|
+ void AddErrorCode(ValueType& result, const ValidateErrorCode code) {
|
|
+ result.AddMember(GetErrorCodeString(), code, GetStateAllocator());
|
|
+ }
|
|
+
|
|
+ void AddError(ValueType& keyword, ValueType& error) {
|
|
+ typename ValueType::MemberIterator member = error_.FindMember(keyword);
|
|
+ if (member == error_.MemberEnd())
|
|
+ error_.AddMember(keyword, error, GetStateAllocator());
|
|
+ else {
|
|
+ if (member->value.IsObject()) {
|
|
+ ValueType errors(kArrayType);
|
|
+ errors.PushBack(member->value, GetStateAllocator());
|
|
+ member->value = errors;
|
|
+ }
|
|
+ member->value.PushBack(error, GetStateAllocator());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void AddCurrentError(const ValidateErrorCode code, bool parent = false) {
|
|
+ AddErrorCode(currentError_, code);
|
|
+ AddErrorInstanceLocation(currentError_, parent);
|
|
+ AddErrorSchemaLocation(currentError_);
|
|
+ AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_);
|
|
+ }
|
|
+
|
|
+ void MergeError(ValueType& other) {
|
|
+ for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) {
|
|
+ AddError(it->name, it->value);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected,
|
|
+ const typename SchemaType::ValueType& (*exclusive)() = 0) {
|
|
+ currentError_.SetObject();
|
|
+ currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
|
|
+ currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
|
|
+ if (exclusive)
|
|
+ currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
|
|
+ AddCurrentError(code);
|
|
+ }
|
|
+
|
|
+ void AddErrorArray(const ValidateErrorCode code,
|
|
+ ISchemaValidator** subvalidators, SizeType count) {
|
|
+ ValueType errors(kArrayType);
|
|
+ for (SizeType i = 0; i < count; ++i)
|
|
+ errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
|
|
+ currentError_.SetObject();
|
|
+ currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
|
|
+ AddCurrentError(code);
|
|
+ }
|
|
+
|
|
const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
|
|
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
|
|
const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
|
|
|
|
- static OutputHandler& GetNullHandler() {
|
|
- static OutputHandler nullHandler;
|
|
- return nullHandler;
|
|
- }
|
|
-
|
|
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<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
|
|
internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
|
|
+ OutputHandler* outputHandler_;
|
|
+ ValueType error_;
|
|
+ ValueType currentError_;
|
|
+ ValueType missingDependents_;
|
|
bool valid_;
|
|
-#if RAPIDJSON_SCHEMA_VERBOSE
|
|
+ unsigned flags_;
|
|
unsigned depth_;
|
|
-#endif
|
|
};
|
|
|
|
typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
|
|
@@ -1954,13 +3201,14 @@ class SchemaValidatingReader {
|
|
public:
|
|
typedef typename SchemaDocumentType::PointerType PointerType;
|
|
typedef typename InputStream::Ch Ch;
|
|
+ typedef GenericValue<SourceEncoding, StackAllocator> ValueType;
|
|
|
|
//! Constructor
|
|
/*!
|
|
\param is Input stream.
|
|
\param sd Schema document.
|
|
*/
|
|
- SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
|
|
+ SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {}
|
|
|
|
template <typename Handler>
|
|
bool operator()(Handler& handler) {
|
|
@@ -1973,11 +3221,14 @@ public:
|
|
invalidSchemaPointer_ = PointerType();
|
|
invalidSchemaKeyword_ = 0;
|
|
invalidDocumentPointer_ = PointerType();
|
|
+ error_.SetObject();
|
|
}
|
|
else {
|
|
invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
|
|
invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
|
|
+ invalidSchemaCode_ = validator.GetInvalidSchemaCode();
|
|
invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
|
|
+ error_.CopyFrom(validator.GetError(), allocator_);
|
|
}
|
|
|
|
return parseResult_;
|
|
@@ -1988,6 +3239,8 @@ public:
|
|
const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
|
|
const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
|
|
const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
|
|
+ const ValueType& GetError() const { return error_; }
|
|
+ ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; }
|
|
|
|
private:
|
|
InputStream& is_;
|
|
@@ -1997,6 +3250,9 @@ private:
|
|
PointerType invalidSchemaPointer_;
|
|
const Ch* invalidSchemaKeyword_;
|
|
PointerType invalidDocumentPointer_;
|
|
+ ValidateErrorCode invalidSchemaCode_;
|
|
+ StackAllocator allocator_;
|
|
+ ValueType error_;
|
|
bool isValid_;
|
|
};
|
|
|
|
diff --git a/src/common/thirdparty/rapidjson/stream.h b/src/common/thirdparty/rapidjson/stream.h
|
|
index fef82c252..1fd70915c 100644
|
|
--- a/src/common/thirdparty/rapidjson/stream.h
|
|
+++ b/src/common/thirdparty/rapidjson/stream.h
|
|
@@ -1,15 +1,15 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
-//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+//
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://opensource.org/licenses/MIT
|
|
//
|
|
-// Unless required by applicable law or agreed to in writing, software distributed
|
|
-// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
-// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
#include "rapidjson.h"
|
|
@@ -100,6 +100,50 @@ inline void PutN(Stream& stream, Ch c, size_t n) {
|
|
PutUnsafe(stream, c);
|
|
}
|
|
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// GenericStreamWrapper
|
|
+
|
|
+//! A Stream Wrapper
|
|
+/*! \tThis string stream is a wrapper for any stream by just forwarding any
|
|
+ \treceived message to the origin stream.
|
|
+ \note implements Stream concept
|
|
+*/
|
|
+
|
|
+#if defined(_MSC_VER) && _MSC_VER <= 1800
|
|
+RAPIDJSON_DIAG_PUSH
|
|
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
|
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
|
+#endif
|
|
+
|
|
+template <typename InputStream, typename Encoding = UTF8<> >
|
|
+class GenericStreamWrapper {
|
|
+public:
|
|
+ typedef typename Encoding::Ch Ch;
|
|
+ GenericStreamWrapper(InputStream& is): is_(is) {}
|
|
+
|
|
+ Ch Peek() const { return is_.Peek(); }
|
|
+ Ch Take() { return is_.Take(); }
|
|
+ size_t Tell() { return is_.Tell(); }
|
|
+ Ch* PutBegin() { return is_.PutBegin(); }
|
|
+ void Put(Ch ch) { is_.Put(ch); }
|
|
+ void Flush() { is_.Flush(); }
|
|
+ size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); }
|
|
+
|
|
+ // wrapper for MemoryStream
|
|
+ const Ch* Peek4() const { return is_.Peek4(); }
|
|
+
|
|
+ // wrapper for AutoUTFInputStream
|
|
+ UTFType GetType() const { return is_.GetType(); }
|
|
+ bool HasBOM() const { return is_.HasBOM(); }
|
|
+
|
|
+protected:
|
|
+ InputStream& is_;
|
|
+};
|
|
+
|
|
+#if defined(_MSC_VER) && _MSC_VER <= 1800
|
|
+RAPIDJSON_DIAG_POP
|
|
+#endif
|
|
+
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StringStream
|
|
|
|
diff --git a/src/common/thirdparty/rapidjson/stringbuffer.h b/src/common/thirdparty/rapidjson/stringbuffer.h
|
|
index 78f34d209..82ad3ca6b 100644
|
|
--- a/src/common/thirdparty/rapidjson/stringbuffer.h
|
|
+++ b/src/common/thirdparty/rapidjson/stringbuffer.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -78,8 +78,12 @@ public:
|
|
return stack_.template Bottom<Ch>();
|
|
}
|
|
|
|
+ //! Get the size of string in bytes in the string buffer.
|
|
size_t GetSize() const { return stack_.GetSize(); }
|
|
|
|
+ //! Get the length of string in Ch in the string buffer.
|
|
+ size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); }
|
|
+
|
|
static const size_t kDefaultCapacity = 256;
|
|
mutable internal::Stack<Allocator> stack_;
|
|
|
|
diff --git a/src/common/thirdparty/rapidjson/uri.h b/src/common/thirdparty/rapidjson/uri.h
|
|
new file mode 100644
|
|
index 000000000..f93e508a4
|
|
--- /dev/null
|
|
+++ b/src/common/thirdparty/rapidjson/uri.h
|
|
@@ -0,0 +1,481 @@
|
|
+// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
+//
|
|
+// (C) Copyright IBM Corporation 2021
|
|
+//
|
|
+// Licensed under the MIT License (the "License"); you may not use this file except
|
|
+// in compliance with the License. You may obtain a copy of the License at
|
|
+//
|
|
+// http://opensource.org/licenses/MIT
|
|
+//
|
|
+// Unless required by applicable law or agreed to in writing, software distributed
|
|
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+// specific language governing permissions and limitations under the License.
|
|
+
|
|
+#ifndef RAPIDJSON_URI_H_
|
|
+#define RAPIDJSON_URI_H_
|
|
+
|
|
+#include "internal/strfunc.h"
|
|
+
|
|
+#if defined(__clang__)
|
|
+RAPIDJSON_DIAG_PUSH
|
|
+RAPIDJSON_DIAG_OFF(c++98-compat)
|
|
+#elif defined(_MSC_VER)
|
|
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
|
+#endif
|
|
+
|
|
+RAPIDJSON_NAMESPACE_BEGIN
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+// GenericUri
|
|
+
|
|
+template <typename ValueType, typename Allocator=CrtAllocator>
|
|
+class GenericUri {
|
|
+public:
|
|
+ typedef typename ValueType::Ch Ch;
|
|
+#if RAPIDJSON_HAS_STDSTRING
|
|
+ typedef std::basic_string<Ch> String;
|
|
+#endif
|
|
+
|
|
+ //! Constructors
|
|
+ GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
|
+ }
|
|
+
|
|
+ GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
|
+ Parse(uri, len);
|
|
+ }
|
|
+
|
|
+ GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
|
+ Parse(uri, internal::StrLen<Ch>(uri));
|
|
+ }
|
|
+
|
|
+ // Use with specializations of GenericValue
|
|
+ template<typename T> GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
|
+ const Ch* u = uri.template Get<const Ch*>(); // TypeHelper from document.h
|
|
+ Parse(u, internal::StrLen<Ch>(u));
|
|
+ }
|
|
+
|
|
+#if RAPIDJSON_HAS_STDSTRING
|
|
+ GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
|
+ Parse(uri.c_str(), internal::StrLen<Ch>(uri.c_str()));
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ //! Copy constructor
|
|
+ GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() {
|
|
+ *this = rhs;
|
|
+ }
|
|
+
|
|
+ //! Copy constructor
|
|
+ GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
|
+ *this = rhs;
|
|
+ }
|
|
+
|
|
+ //! Destructor.
|
|
+ ~GenericUri() {
|
|
+ Free();
|
|
+ RAPIDJSON_DELETE(ownAllocator_);
|
|
+ }
|
|
+
|
|
+ //! Assignment operator
|
|
+ GenericUri& operator=(const GenericUri& rhs) {
|
|
+ if (this != &rhs) {
|
|
+ // Do not delete ownAllocator
|
|
+ Free();
|
|
+ Allocate(rhs.GetStringLength());
|
|
+ auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength());
|
|
+ path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength());
|
|
+ query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength());
|
|
+ frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength());
|
|
+ base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength());
|
|
+ uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength());
|
|
+ CopyPart(uri_, rhs.uri_, rhs.GetStringLength());
|
|
+ }
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ //! Getters
|
|
+ // Use with specializations of GenericValue
|
|
+ template<typename T> void Get(T& uri, Allocator& allocator) {
|
|
+ uri.template Set<const Ch*>(this->GetString(), allocator); // TypeHelper from document.h
|
|
+ }
|
|
+
|
|
+ const Ch* GetString() const { return uri_; }
|
|
+ SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen<Ch>(uri_); }
|
|
+ const Ch* GetBaseString() const { return base_; }
|
|
+ SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen<Ch>(base_); }
|
|
+ const Ch* GetSchemeString() const { return scheme_; }
|
|
+ SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen<Ch>(scheme_); }
|
|
+ const Ch* GetAuthString() const { return auth_; }
|
|
+ SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen<Ch>(auth_); }
|
|
+ const Ch* GetPathString() const { return path_; }
|
|
+ SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen<Ch>(path_); }
|
|
+ const Ch* GetQueryString() const { return query_; }
|
|
+ SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen<Ch>(query_); }
|
|
+ const Ch* GetFragString() const { return frag_; }
|
|
+ SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen<Ch>(frag_); }
|
|
+
|
|
+#if RAPIDJSON_HAS_STDSTRING
|
|
+ static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); }
|
|
+ static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); }
|
|
+ static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); }
|
|
+ static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); }
|
|
+ static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); }
|
|
+ static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); }
|
|
+ static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); }
|
|
+#endif
|
|
+
|
|
+ //! Equality operators
|
|
+ bool operator==(const GenericUri& rhs) const {
|
|
+ return Match(rhs, true);
|
|
+ }
|
|
+
|
|
+ bool operator!=(const GenericUri& rhs) const {
|
|
+ return !Match(rhs, true);
|
|
+ }
|
|
+
|
|
+ bool Match(const GenericUri& uri, bool full = true) const {
|
|
+ Ch* s1;
|
|
+ Ch* s2;
|
|
+ if (full) {
|
|
+ s1 = uri_;
|
|
+ s2 = uri.uri_;
|
|
+ } else {
|
|
+ s1 = base_;
|
|
+ s2 = uri.base_;
|
|
+ }
|
|
+ if (s1 == s2) return true;
|
|
+ if (s1 == 0 || s2 == 0) return false;
|
|
+ return internal::StrCmp<Ch>(s1, s2) == 0;
|
|
+ }
|
|
+
|
|
+ //! Resolve this URI against another (base) URI in accordance with URI resolution rules.
|
|
+ // See https://tools.ietf.org/html/rfc3986
|
|
+ // Use for resolving an id or $ref with an in-scope id.
|
|
+ // Returns a new GenericUri for the resolved URI.
|
|
+ GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) {
|
|
+ GenericUri resuri;
|
|
+ resuri.allocator_ = allocator;
|
|
+ // Ensure enough space for combining paths
|
|
+ resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash
|
|
+
|
|
+ if (!(GetSchemeStringLength() == 0)) {
|
|
+ // Use all of this URI
|
|
+ resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength());
|
|
+ resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
|
|
+ resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
|
|
+ resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
|
+ resuri.RemoveDotSegments();
|
|
+ } else {
|
|
+ // Use the base scheme
|
|
+ resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength());
|
|
+ if (!(GetAuthStringLength() == 0)) {
|
|
+ // Use this auth, path, query
|
|
+ resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
|
|
+ resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
|
|
+ resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
|
+ resuri.RemoveDotSegments();
|
|
+ } else {
|
|
+ // Use the base auth
|
|
+ resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength());
|
|
+ if (GetPathStringLength() == 0) {
|
|
+ // Use the base path
|
|
+ resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength());
|
|
+ if (GetQueryStringLength() == 0) {
|
|
+ // Use the base query
|
|
+ resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength());
|
|
+ } else {
|
|
+ // Use this query
|
|
+ resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
|
+ }
|
|
+ } else {
|
|
+ if (path_[0] == '/') {
|
|
+ // Absolute path - use all of this path
|
|
+ resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
|
|
+ resuri.RemoveDotSegments();
|
|
+ } else {
|
|
+ // Relative path - append this path to base path after base path's last slash
|
|
+ size_t pos = 0;
|
|
+ if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) {
|
|
+ resuri.path_[pos] = '/';
|
|
+ pos++;
|
|
+ }
|
|
+ size_t lastslashpos = baseuri.GetPathStringLength();
|
|
+ while (lastslashpos > 0) {
|
|
+ if (baseuri.path_[lastslashpos - 1] == '/') break;
|
|
+ lastslashpos--;
|
|
+ }
|
|
+ std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch));
|
|
+ pos += lastslashpos;
|
|
+ resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength());
|
|
+ resuri.RemoveDotSegments();
|
|
+ }
|
|
+ // Use this query
|
|
+ resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Always use this frag
|
|
+ resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength());
|
|
+
|
|
+ // Re-constitute base_ and uri_
|
|
+ resuri.SetBase();
|
|
+ resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1;
|
|
+ resuri.SetUri();
|
|
+ return resuri;
|
|
+ }
|
|
+
|
|
+ //! Get the allocator of this GenericUri.
|
|
+ Allocator& GetAllocator() { return *allocator_; }
|
|
+
|
|
+private:
|
|
+ // Allocate memory for a URI
|
|
+ // Returns total amount allocated
|
|
+ std::size_t Allocate(std::size_t len) {
|
|
+ // Create own allocator if user did not supply.
|
|
+ if (!allocator_)
|
|
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
|
+
|
|
+ // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated.
|
|
+ // Order: scheme, auth, path, query, frag, base, uri
|
|
+ // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
|
+ size_t total = (3 * len + 7) * sizeof(Ch);
|
|
+ scheme_ = static_cast<Ch*>(allocator_->Malloc(total));
|
|
+ *scheme_ = '\0';
|
|
+ auth_ = scheme_;
|
|
+ auth_++;
|
|
+ *auth_ = '\0';
|
|
+ path_ = auth_;
|
|
+ path_++;
|
|
+ *path_ = '\0';
|
|
+ query_ = path_;
|
|
+ query_++;
|
|
+ *query_ = '\0';
|
|
+ frag_ = query_;
|
|
+ frag_++;
|
|
+ *frag_ = '\0';
|
|
+ base_ = frag_;
|
|
+ base_++;
|
|
+ *base_ = '\0';
|
|
+ uri_ = base_;
|
|
+ uri_++;
|
|
+ *uri_ = '\0';
|
|
+ return total;
|
|
+ }
|
|
+
|
|
+ // Free memory for a URI
|
|
+ void Free() {
|
|
+ if (scheme_) {
|
|
+ Allocator::Free(scheme_);
|
|
+ scheme_ = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Parse a URI into constituent scheme, authority, path, query, & fragment parts
|
|
+ // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per
|
|
+ // https://tools.ietf.org/html/rfc3986
|
|
+ void Parse(const Ch* uri, std::size_t len) {
|
|
+ std::size_t start = 0, pos1 = 0, pos2 = 0;
|
|
+ Allocate(len);
|
|
+
|
|
+ // Look for scheme ([^:/?#]+):)?
|
|
+ if (start < len) {
|
|
+ while (pos1 < len) {
|
|
+ if (uri[pos1] == ':') break;
|
|
+ pos1++;
|
|
+ }
|
|
+ if (pos1 != len) {
|
|
+ while (pos2 < len) {
|
|
+ if (uri[pos2] == '/') break;
|
|
+ if (uri[pos2] == '?') break;
|
|
+ if (uri[pos2] == '#') break;
|
|
+ pos2++;
|
|
+ }
|
|
+ if (pos1 < pos2) {
|
|
+ pos1++;
|
|
+ std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch));
|
|
+ scheme_[pos1] = '\0';
|
|
+ start = pos1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Look for auth (//([^/?#]*))?
|
|
+ // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
|
+ auth_ = scheme_ + GetSchemeStringLength();
|
|
+ auth_++;
|
|
+ *auth_ = '\0';
|
|
+ if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') {
|
|
+ pos2 = start + 2;
|
|
+ while (pos2 < len) {
|
|
+ if (uri[pos2] == '/') break;
|
|
+ if (uri[pos2] == '?') break;
|
|
+ if (uri[pos2] == '#') break;
|
|
+ pos2++;
|
|
+ }
|
|
+ std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch));
|
|
+ auth_[pos2 - start] = '\0';
|
|
+ start = pos2;
|
|
+ }
|
|
+ // Look for path ([^?#]*)
|
|
+ // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
|
+ path_ = auth_ + GetAuthStringLength();
|
|
+ path_++;
|
|
+ *path_ = '\0';
|
|
+ if (start < len) {
|
|
+ pos2 = start;
|
|
+ while (pos2 < len) {
|
|
+ if (uri[pos2] == '?') break;
|
|
+ if (uri[pos2] == '#') break;
|
|
+ pos2++;
|
|
+ }
|
|
+ if (start != pos2) {
|
|
+ std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch));
|
|
+ path_[pos2 - start] = '\0';
|
|
+ if (path_[0] == '/')
|
|
+ RemoveDotSegments(); // absolute path - normalize
|
|
+ start = pos2;
|
|
+ }
|
|
+ }
|
|
+ // Look for query (\?([^#]*))?
|
|
+ // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
|
+ query_ = path_ + GetPathStringLength();
|
|
+ query_++;
|
|
+ *query_ = '\0';
|
|
+ if (start < len && uri[start] == '?') {
|
|
+ pos2 = start + 1;
|
|
+ while (pos2 < len) {
|
|
+ if (uri[pos2] == '#') break;
|
|
+ pos2++;
|
|
+ }
|
|
+ if (start != pos2) {
|
|
+ std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch));
|
|
+ query_[pos2 - start] = '\0';
|
|
+ start = pos2;
|
|
+ }
|
|
+ }
|
|
+ // Look for fragment (#(.*))?
|
|
+ // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
|
+ frag_ = query_ + GetQueryStringLength();
|
|
+ frag_++;
|
|
+ *frag_ = '\0';
|
|
+ if (start < len && uri[start] == '#') {
|
|
+ std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch));
|
|
+ frag_[len - start] = '\0';
|
|
+ }
|
|
+
|
|
+ // Re-constitute base_ and uri_
|
|
+ base_ = frag_ + GetFragStringLength() + 1;
|
|
+ SetBase();
|
|
+ uri_ = base_ + GetBaseStringLength() + 1;
|
|
+ SetUri();
|
|
+ }
|
|
+
|
|
+ // Reconstitute base
|
|
+ void SetBase() {
|
|
+ Ch* next = base_;
|
|
+ std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch));
|
|
+ next+= GetSchemeStringLength();
|
|
+ std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch));
|
|
+ next+= GetAuthStringLength();
|
|
+ std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch));
|
|
+ next+= GetPathStringLength();
|
|
+ std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch));
|
|
+ next+= GetQueryStringLength();
|
|
+ *next = '\0';
|
|
+ }
|
|
+
|
|
+ // Reconstitute uri
|
|
+ void SetUri() {
|
|
+ Ch* next = uri_;
|
|
+ std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch));
|
|
+ next+= GetBaseStringLength();
|
|
+ std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch));
|
|
+ next+= GetFragStringLength();
|
|
+ *next = '\0';
|
|
+ }
|
|
+
|
|
+ // Copy a part from one GenericUri to another
|
|
+ // Return the pointer to the next part to be copied to
|
|
+ Ch* CopyPart(Ch* to, Ch* from, std::size_t len) {
|
|
+ RAPIDJSON_ASSERT(to != 0);
|
|
+ RAPIDJSON_ASSERT(from != 0);
|
|
+ std::memcpy(to, from, len * sizeof(Ch));
|
|
+ to[len] = '\0';
|
|
+ Ch* next = to + len + 1;
|
|
+ return next;
|
|
+ }
|
|
+
|
|
+ // Remove . and .. segments from the path_ member.
|
|
+ // https://tools.ietf.org/html/rfc3986
|
|
+ // This is done in place as we are only removing segments.
|
|
+ void RemoveDotSegments() {
|
|
+ std::size_t pathlen = GetPathStringLength();
|
|
+ std::size_t pathpos = 0; // Position in path_
|
|
+ std::size_t newpos = 0; // Position in new path_
|
|
+
|
|
+ // Loop through each segment in original path_
|
|
+ while (pathpos < pathlen) {
|
|
+ // Get next segment, bounded by '/' or end
|
|
+ size_t slashpos = 0;
|
|
+ while ((pathpos + slashpos) < pathlen) {
|
|
+ if (path_[pathpos + slashpos] == '/') break;
|
|
+ slashpos++;
|
|
+ }
|
|
+ // Check for .. and . segments
|
|
+ if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') {
|
|
+ // Backup a .. segment in the new path_
|
|
+ // We expect to find a previously added slash at the end or nothing
|
|
+ RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/');
|
|
+ size_t lastslashpos = newpos;
|
|
+ // Make sure we don't go beyond the start segment
|
|
+ if (lastslashpos > 1) {
|
|
+ // Find the next to last slash and back up to it
|
|
+ lastslashpos--;
|
|
+ while (lastslashpos > 0) {
|
|
+ if (path_[lastslashpos - 1] == '/') break;
|
|
+ lastslashpos--;
|
|
+ }
|
|
+ // Set the new path_ position
|
|
+ newpos = lastslashpos;
|
|
+ }
|
|
+ } else if (slashpos == 1 && path_[pathpos] == '.') {
|
|
+ // Discard . segment, leaves new path_ unchanged
|
|
+ } else {
|
|
+ // Move any other kind of segment to the new path_
|
|
+ RAPIDJSON_ASSERT(newpos <= pathpos);
|
|
+ std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch));
|
|
+ newpos += slashpos;
|
|
+ // Add slash if not at end
|
|
+ if ((pathpos + slashpos) < pathlen) {
|
|
+ path_[newpos] = '/';
|
|
+ newpos++;
|
|
+ }
|
|
+ }
|
|
+ // Move to next segment
|
|
+ pathpos += slashpos + 1;
|
|
+ }
|
|
+ path_[newpos] = '\0';
|
|
+ }
|
|
+
|
|
+ Ch* uri_; // Everything
|
|
+ Ch* base_; // Everything except fragment
|
|
+ Ch* scheme_; // Includes the :
|
|
+ Ch* auth_; // Includes the //
|
|
+ Ch* path_; // Absolute if starts with /
|
|
+ Ch* query_; // Includes the ?
|
|
+ Ch* frag_; // Includes the #
|
|
+
|
|
+ Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_.
|
|
+ Allocator* ownAllocator_; //!< Allocator owned by this Uri.
|
|
+};
|
|
+
|
|
+//! GenericUri for Value (UTF-8, default allocator).
|
|
+typedef GenericUri<Value> Uri;
|
|
+
|
|
+RAPIDJSON_NAMESPACE_END
|
|
+
|
|
+#if defined(__clang__)
|
|
+RAPIDJSON_DIAG_POP
|
|
+#endif
|
|
+
|
|
+#endif // RAPIDJSON_URI_H_
|
|
diff --git a/src/common/thirdparty/rapidjson/writer.h b/src/common/thirdparty/rapidjson/writer.h
|
|
index 200abadae..50f6af989 100644
|
|
--- a/src/common/thirdparty/rapidjson/writer.h
|
|
+++ b/src/common/thirdparty/rapidjson/writer.h
|
|
@@ -1,6 +1,6 @@
|
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
|
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
@@ -16,6 +16,8 @@
|
|
#define RAPIDJSON_WRITER_H_
|
|
|
|
#include "stream.h"
|
|
+#include "internal/clzll.h"
|
|
+#include "internal/meta.h"
|
|
#include "internal/stack.h"
|
|
#include "internal/strfunc.h"
|
|
#include "internal/dtoa.h"
|
|
@@ -31,17 +33,18 @@
|
|
#include <nmmintrin.h>
|
|
#elif defined(RAPIDJSON_SSE2)
|
|
#include <emmintrin.h>
|
|
-#endif
|
|
-
|
|
-#ifdef _MSC_VER
|
|
-RAPIDJSON_DIAG_PUSH
|
|
-RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
|
+#elif defined(RAPIDJSON_NEON)
|
|
+#include <arm_neon.h>
|
|
#endif
|
|
|
|
#ifdef __clang__
|
|
RAPIDJSON_DIAG_PUSH
|
|
RAPIDJSON_DIAG_OFF(padded)
|
|
RAPIDJSON_DIAG_OFF(unreachable-code)
|
|
+RAPIDJSON_DIAG_OFF(c++98-compat)
|
|
+#elif defined(_MSC_VER)
|
|
+RAPIDJSON_DIAG_PUSH
|
|
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
|
#endif
|
|
|
|
RAPIDJSON_NAMESPACE_BEGIN
|
|
@@ -103,6 +106,13 @@ public:
|
|
Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
|
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
|
|
|
|
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
|
+ Writer(Writer&& rhs) :
|
|
+ os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) {
|
|
+ rhs.os_ = 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
//! Reset the writer with a new stream.
|
|
/*!
|
|
This function reset the writer with a new stream and default settings,
|
|
@@ -211,10 +221,18 @@ public:
|
|
|
|
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<Ch>& str)
|
|
+ {
|
|
+ return Key(str.data(), SizeType(str.size()));
|
|
+ }
|
|
+#endif
|
|
+
|
|
bool EndObject(SizeType memberCount = 0) {
|
|
(void)memberCount;
|
|
- RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
|
- RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
|
|
+ RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object
|
|
+ RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray); // currently inside an Array, not Object
|
|
+ RAPIDJSON_ASSERT(0 == level_stack_.template Top<Level>()->valueCount % 2); // Object has a Key without a Value
|
|
level_stack_.template Pop<Level>(1);
|
|
return EndValue(WriteEndObject());
|
|
}
|
|
@@ -238,9 +256,9 @@ public:
|
|
//@{
|
|
|
|
//! 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)); }
|
|
-
|
|
+ bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); }
|
|
+ bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); }
|
|
+
|
|
//@}
|
|
|
|
//! Write a raw JSON value.
|
|
@@ -257,6 +275,16 @@ public:
|
|
return EndValue(WriteRawValue(json, length));
|
|
}
|
|
|
|
+ //! Flush the output stream.
|
|
+ /*!
|
|
+ Allows the user to flush the output stream immediately.
|
|
+ */
|
|
+ void Flush() {
|
|
+ os_->Flush();
|
|
+ }
|
|
+
|
|
+ static const size_t kDefaultLevelDepth = 32;
|
|
+
|
|
protected:
|
|
//! Information for each nested level
|
|
struct Level {
|
|
@@ -265,8 +293,6 @@ protected:
|
|
bool inArray; //!< true if in array, otherwise in object
|
|
};
|
|
|
|
- static const size_t kDefaultLevelDepth = 32;
|
|
-
|
|
bool WriteNull() {
|
|
PutReserve(*os_, 4);
|
|
PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
|
|
@@ -289,7 +315,7 @@ protected:
|
|
const char* end = internal::i32toa(i, buffer);
|
|
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
|
for (const char* p = buffer; p != end; ++p)
|
|
- PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
|
|
+ PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
|
return true;
|
|
}
|
|
|
|
@@ -298,7 +324,7 @@ protected:
|
|
const char* end = internal::u32toa(u, buffer);
|
|
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
|
for (const char* p = buffer; p != end; ++p)
|
|
- PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
|
|
+ PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
|
return true;
|
|
}
|
|
|
|
@@ -307,7 +333,7 @@ protected:
|
|
const char* end = internal::i64toa(i64, buffer);
|
|
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
|
for (const char* p = buffer; p != end; ++p)
|
|
- PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
|
|
+ PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
|
return true;
|
|
}
|
|
|
|
@@ -316,7 +342,7 @@ protected:
|
|
char* end = internal::u64toa(u64, buffer);
|
|
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
|
for (char* p = buffer; p != end; ++p)
|
|
- PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
|
|
+ PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
|
return true;
|
|
}
|
|
|
|
@@ -352,12 +378,12 @@ protected:
|
|
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
|
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
|
for (char* p = buffer; p != end; ++p)
|
|
- PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
|
|
+ PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
|
return ret;
|
|
}
|
|
|
|
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 +439,7 @@ protected:
|
|
else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)])) {
|
|
is.Take();
|
|
PutUnsafe(*os_, '\\');
|
|
- PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(escape[static_cast<unsigned char>(c)]));
|
|
+ PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(escape[static_cast<unsigned char>(c)]));
|
|
if (escape[static_cast<unsigned char>(c)] == 'u') {
|
|
PutUnsafe(*os_, '0');
|
|
PutUnsafe(*os_, '0');
|
|
@@ -441,9 +467,13 @@ protected:
|
|
|
|
bool WriteRawValue(const Ch* json, size_t length) {
|
|
PutReserve(*os_, length);
|
|
- for (size_t i = 0; i < length; i++) {
|
|
- RAPIDJSON_ASSERT(json[i] != '\0');
|
|
- PutUnsafe(*os_, json[i]);
|
|
+ GenericStringStream<SourceEncoding> is(json);
|
|
+ while (RAPIDJSON_LIKELY(is.Tell() < length)) {
|
|
+ RAPIDJSON_ASSERT(is.Peek() != '\0');
|
|
+ if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
|
|
+ Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
|
|
+ Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
|
|
+ return false;
|
|
}
|
|
return true;
|
|
}
|
|
@@ -471,7 +501,7 @@ protected:
|
|
// 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;
|
|
}
|
|
|
|
@@ -524,15 +554,8 @@ template<>
|
|
inline bool Writer<StringBuffer>::WriteDouble(double d) {
|
|
if (internal::Double(d).IsNanOrInf()) {
|
|
// Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag).
|
|
- if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
|
|
- {
|
|
- // At least ensure that the output does not get broken.
|
|
- PutReserve(*os_, 3);
|
|
- PutUnsafe(*os_, '0');
|
|
- PutUnsafe(*os_, '.');
|
|
- PutUnsafe(*os_, '0');
|
|
- return false;
|
|
- }
|
|
+ if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
|
|
+ return false;
|
|
if (internal::Double(d).IsNan()) {
|
|
PutReserve(*os_, 3);
|
|
PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
|
|
@@ -582,7 +605,7 @@ inline bool Writer<StringBuffer>::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<const __m128i *>(&dquote[0]));
|
|
const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
|
|
const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
|
|
@@ -591,7 +614,7 @@ inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, siz
|
|
const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(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<unsigned short>(_mm_movemask_epi8(x));
|
|
if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
|
|
@@ -616,15 +639,79 @@ inline bool Writer<StringBuffer>::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<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
|
|
+ if (length < 16)
|
|
+ return RAPIDJSON_LIKELY(is.Tell() < length);
|
|
|
|
-RAPIDJSON_NAMESPACE_END
|
|
+ if (!RAPIDJSON_LIKELY(is.Tell() < length))
|
|
+ return false;
|
|
|
|
-#ifdef _MSC_VER
|
|
-RAPIDJSON_DIAG_POP
|
|
-#endif
|
|
+ const char* p = is.src_;
|
|
+ const char* end = is.head_ + length;
|
|
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
|
|
+ const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
|
|
+ if (nextAligned > end)
|
|
+ return true;
|
|
|
|
-#ifdef __clang__
|
|
+ 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<const uint8_t *>(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(vreinterpretq_u64_u8(x), 0); // extract
|
|
+ uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract
|
|
+
|
|
+ SizeType len = 0;
|
|
+ bool escaped = false;
|
|
+ if (low == 0) {
|
|
+ if (high != 0) {
|
|
+ uint32_t lz = internal::clzll(high);
|
|
+ len = 8 + (lz >> 3);
|
|
+ escaped = true;
|
|
+ }
|
|
+ } else {
|
|
+ uint32_t lz = internal::clzll(low);
|
|
+ len = lz >> 3;
|
|
+ escaped = true;
|
|
+ }
|
|
+ if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped
|
|
+ char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
|
|
+ for (size_t i = 0; i < len; i++)
|
|
+ q[i] = p[i];
|
|
+
|
|
+ p += len;
|
|
+ break;
|
|
+ }
|
|
+ vst1q_u8(reinterpret_cast<uint8_t *>(os_->PushUnsafe(16)), s);
|
|
+ }
|
|
+
|
|
+ is.src_ = p;
|
|
+ return RAPIDJSON_LIKELY(is.Tell() < length);
|
|
+}
|
|
+#endif // RAPIDJSON_NEON
|
|
+
|
|
+RAPIDJSON_NAMESPACE_END
|
|
+
|
|
+#if defined(_MSC_VER) || defined(__clang__)
|
|
RAPIDJSON_DIAG_POP
|
|
#endif
|
|
|
|
--
|
|
2.39.3
|
|
|