修改进程间接口,统一为foo(int, string)
| | |
| | | test_items/driving_curve.cpp |
| | | test_items/turn_a90.cpp |
| | | rtk_module/rtk.cpp |
| | | master/comm_if.cpp |
| | | |
| | | utils/crc16.cpp |
| | | utils/num.cpp) |
| | | utils/num.cpp |
| | | utils/xconvert.cpp) |
| | | |
| | | # Searches for a specified prebuilt library and stores the path as a |
| | | # variable. Because CMake includes system libraries in the search path by |
New file |
| | |
| | | // |
| | | // Created by YY on 2020/1/7. |
| | | // |
| | | |
| | | #include "comm_if.h" |
| | | #include "../native-lib.h" |
| | | |
| | | #include "../rapidjson/document.h" |
| | | #include "../rapidjson/writer.h" |
| | | #include "../rapidjson/stringbuffer.h" |
| | | #include "../rtk_platform/platform.h" |
| | | #include "../utils/xconvert.h" |
| | | |
| | | #include <string> |
| | | #include <iostream> |
| | | |
| | | using namespace rapidjson; |
| | | |
| | | #define ID_SM_NDK_START 0x0001 |
| | | #define ID_SM_REQ_RTK_PLAT_CFG 0x0002 |
| | | #define ID_MS_RTK_PLAT_CFG 0x8002 |
| | | #define ID_SM_RTK_PLAT_CONN 0x0003 |
| | | #define ID_SM_RTK_PLAT_REG 0x0004 |
| | | #define ID_SM_RTK_PLAT_LOGIN 0x0005 |
| | | |
| | | void MA_NdkStart(void) |
| | | { |
| | | SendMsgToMainProc(ID_SM_NDK_START, NULL); |
| | | } |
| | | |
| | | void MA_ReqRtkPlatformConfig(void) |
| | | { |
| | | SendMsgToMainProc(ID_SM_REQ_RTK_PLAT_CFG, NULL); |
| | | } |
| | | |
| | | void MA_RtkPlatformConnect(int conn, const char *ip, int port) |
| | | { |
| | | StringBuffer sb; |
| | | Writer<StringBuffer> writer(sb); |
| | | |
| | | writer.StartObject(); |
| | | writer.Key("connected"); |
| | | writer.Int(conn); |
| | | writer.Key("ip"); |
| | | writer.String(ip); |
| | | writer.Key("port"); |
| | | writer.Int(port); |
| | | |
| | | writer.EndObject(); |
| | | |
| | | SendMsgToMainProc(ID_SM_RTK_PLAT_CONN, sb.GetString()); |
| | | } |
| | | |
| | | void MA_RtkPlatformRegister(int reg, const uint8_t *pwd, int length) |
| | | { |
| | | StringBuffer sb; |
| | | Writer<StringBuffer> writer(sb); |
| | | |
| | | writer.StartObject(); |
| | | writer.Key("register_code"); |
| | | writer.Int(reg); |
| | | |
| | | if (reg == 0 && length > 0) { |
| | | writer.Key("password"); |
| | | char *str = new char[length*2 + 1]; |
| | | ConvertHex2String(str, pwd, length); |
| | | writer.String(str); |
| | | delete[]str; |
| | | } |
| | | |
| | | writer.EndObject(); |
| | | |
| | | SendMsgToMainProc(ID_SM_RTK_PLAT_REG, sb.GetString()); |
| | | } |
| | | |
| | | void MA_RtkPlatformLogin(int login) |
| | | { |
| | | StringBuffer sb; |
| | | Writer<StringBuffer> writer(sb); |
| | | |
| | | writer.StartObject(); |
| | | |
| | | writer.Key("password"); |
| | | writer.Int(login); |
| | | |
| | | writer.EndObject(); |
| | | |
| | | SendMsgToMainProc(ID_SM_RTK_PLAT_REG, sb.GetString()); |
| | | } |
| | | |
| | | void MA_MainProcMsgEntry(int cmd, const char *value) |
| | | { |
| | | switch (cmd) { |
| | | case ID_MS_RTK_PLAT_CFG: { |
| | | rtk_platform_cfg_t cfg; |
| | | memset(&cfg, 0, sizeof(cfg)); |
| | | |
| | | Document d; |
| | | d.Parse(value); |
| | | |
| | | if (d.HasMember("ip")) { |
| | | Value& s = d["ip"]; |
| | | strcpy(cfg.domain_name, s.GetString()); |
| | | } |
| | | if (d.HasMember("port")) { |
| | | Value& s = d["port"]; |
| | | cfg.port = s.GetInt(); |
| | | } |
| | | if (d.HasMember("province")) { |
| | | Value& s = d["province"]; |
| | | cfg.province = s.GetInt(); |
| | | } |
| | | if (d.HasMember("city")) { |
| | | Value& s = d["city"]; |
| | | cfg.city = s.GetInt(); |
| | | } |
| | | if (d.HasMember("model")) { |
| | | Value& s = d["model"]; |
| | | strcpy(cfg.device_model, s.GetString()); |
| | | } |
| | | if (d.HasMember("sn")) { |
| | | Value& s = d["sn"]; |
| | | strcpy(cfg.device_sn, s.GetString()); |
| | | } |
| | | if (d.HasMember("imei")) { |
| | | Value& s = d["imei"]; |
| | | strcpy(cfg.imei, s.GetString()); |
| | | } |
| | | if (d.HasMember("phone")) { |
| | | Value& s = d["phone"]; |
| | | strcpy(cfg.phone, s.GetString()); |
| | | } |
| | | if (d.HasMember("password")) { |
| | | Value& s = d["password"]; |
| | | strcpy(cfg.password, s.GetString()); |
| | | } |
| | | if (d.HasMember("registered")) { |
| | | Value& s = d["registered"]; |
| | | cfg.registered = s.GetInt(); |
| | | } |
| | | if (d.HasMember("interval")) { |
| | | Value& s = d["interval"]; |
| | | cfg.rtk_interval = s.GetInt(); |
| | | } |
| | | |
| | | ConfigPlatform(&cfg); |
| | | break; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | // |
| | | // Created by YY on 2020/1/7. |
| | | // |
| | | |
| | | #ifndef MYAPPLICATION2_COMM_IF_H |
| | | #define MYAPPLICATION2_COMM_IF_H |
| | | |
| | | #include <cstdint> |
| | | |
| | | void MA_MainProcMsgEntry(int cmd, const char *value); |
| | | |
| | | void MA_NdkStart(void); |
| | | void MA_ReqRtkPlatformConfig(void); |
| | | void MA_RtkPlatformConnect(int conn, const char *ip, int port); |
| | | void MA_RtkPlatformRegister(int reg, const uint8_t *pwd, int length); |
| | | void MA_RtkPlatformLogin(int login); |
| | | |
| | | #endif //MYAPPLICATION2_COMM_IF_H |
| | |
| | | #include "rtk_module/rtk.h" |
| | | #include "mcu/mcu_if.h" |
| | | #include "driver_test.h" |
| | | #include "master/comm_if.h" |
| | | |
| | | static JavaVM *sg_jvm = NULL; |
| | | static jobject sg_obj = NULL; |
| | |
| | | } |
| | | } |
| | | |
| | | void SendMsgToMainProc(int cmd, const char *value) |
| | | { |
| | | JNIEnv *env; |
| | | bool ready_in_java_env = false; |
| | | |
| | | if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) { |
| | | // Attach主线程 |
| | | if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) { |
| | | LOGE("%s: AttachCurrentThread() failed", __FUNCTION__); |
| | | return; |
| | | } |
| | | } else { |
| | | ready_in_java_env = true; |
| | | } |
| | | |
| | | jclass cls = env->GetObjectClass(sg_obj); |
| | | jmethodID fun = env->GetMethodID(cls, "SendMsgToMainProc", "(ILjava/lang/String;)V"); |
| | | |
| | | env->CallVoidMethod(sg_obj, fun, cmd, value != NULL ? env->NewStringUTF(value) : NULL); |
| | | |
| | | env->DeleteLocalRef(cls); |
| | | |
| | | if (!ready_in_java_env) { |
| | | //Detach主线程 |
| | | if (sg_jvm->DetachCurrentThread() != JNI_OK) { |
| | | LOGE("%s: DetachCurrentThread() failed", __FUNCTION__); |
| | | } |
| | | } |
| | | } |
| | | |
| | | void DisplayText(const char *string) |
| | | { |
| | | DEBUG("DisplayText: %s", string); |
| | |
| | | // 不能直接赋值(g_obj = ojb) |
| | | sg_obj = env->NewGlobalRef(thiz); |
| | | |
| | | MA_NdkStart(); |
| | | AppTimer_Init(); |
| | | |
| | | ConfigMCU(); |
| | | DriverTestInit(); |
| | | ConfigRTKModule(); |
| | | |
| | | InitPlatform(phone, RTK_PLATFORM_IP, RTK_PLATFORM_PORT); |
| | | } |
| | | } |
| | | |
| | | extern "C" |
| | | JNIEXPORT void JNICALL |
| | | Java_com_anyun_exam_lib_RemoteService_MainProcMsgEntry(JNIEnv *env, jobject thiz, jint cmd, |
| | | jstring value) { |
| | | // TODO: implement MainProcMsgEntry() |
| | | if (value != NULL) { |
| | | const char *str = env->GetStringUTFChars(value, 0); |
| | | MA_MainProcMsgEntry(cmd, str); |
| | | env->ReleaseStringUTFChars(value, str); |
| | | } else { |
| | | MA_MainProcMsgEntry(cmd, NULL); |
| | | } |
| | | } |
| | |
| | | void TextSpeak(const char *text); |
| | | void TextOsd(int type, const char *text); |
| | | void DrawScreen(const Polygon *map, const Polygon *car); |
| | | |
| | | void SendMsgToMainProc(int cmd, const char *value); |
| | | void DisplayText(const char *string); |
| | | |
| | | #endif //RTKBASESTATION_NATIVE_LIB_H |
New file |
| | |
| | | // 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 |
| | | // 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_ALLOCATORS_H_ |
| | | #define RAPIDJSON_ALLOCATORS_H_ |
| | | |
| | | #include "rapidjson.h" |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Allocator |
| | | |
| | | /*! \class rapidjson::Allocator |
| | | \brief Concept for allocating, resizing and freeing memory block. |
| | | |
| | | Note that Malloc() and Realloc() are non-static but Free() is static. |
| | | |
| | | So if an allocator need to support Free(), it needs to put its pointer in |
| | | the header of memory block. |
| | | |
| | | \code |
| | | concept Allocator { |
| | | static const bool kNeedFree; //!< Whether this allocator needs to call Free(). |
| | | |
| | | // Allocate a memory block. |
| | | // \param size of the memory block in bytes. |
| | | // \returns pointer to the memory block. |
| | | void* Malloc(size_t size); |
| | | |
| | | // Resize a memory block. |
| | | // \param originalPtr The pointer to current memory block. Null pointer is permitted. |
| | | // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) |
| | | // \param newSize the new size in bytes. |
| | | void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); |
| | | |
| | | // Free a memory block. |
| | | // \param pointer to the memory block. Null pointer is permitted. |
| | | static void Free(void *ptr); |
| | | }; |
| | | \endcode |
| | | */ |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // CrtAllocator |
| | | |
| | | //! C-runtime library allocator. |
| | | /*! This class is just wrapper for standard C library memory routines. |
| | | \note implements Allocator concept |
| | | */ |
| | | class CrtAllocator { |
| | | public: |
| | | static const bool kNeedFree = true; |
| | | void* Malloc(size_t size) { |
| | | if (size) // behavior of malloc(0) is implementation defined. |
| | | return std::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); |
| | | return NULL; |
| | | } |
| | | return std::realloc(originalPtr, newSize); |
| | | } |
| | | static void Free(void *ptr) { std::free(ptr); } |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // MemoryPoolAllocator |
| | | |
| | | //! Default memory allocator used by the parser and DOM. |
| | | /*! This allocator allocate memory blocks from pre-allocated memory chunks. |
| | | |
| | | It does not free memory blocks. And Realloc() only allocate new memory. |
| | | |
| | | The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. |
| | | |
| | | User may also supply a buffer as the first chunk. |
| | | |
| | | If the user-buffer is full then additional chunks are allocated by BaseAllocator. |
| | | |
| | | The user-buffer is not deallocated by this allocator. |
| | | |
| | | \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. |
| | | \note implements Allocator concept |
| | | */ |
| | | template <typename BaseAllocator = CrtAllocator> |
| | | class MemoryPoolAllocator { |
| | | public: |
| | | static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) |
| | | |
| | | //! Constructor with chunkSize. |
| | | /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. |
| | | \param baseAllocator The allocator for allocating memory chunks. |
| | | */ |
| | | MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : |
| | | chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) |
| | | { |
| | | } |
| | | |
| | | //! Constructor with user-supplied buffer. |
| | | /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. |
| | | |
| | | The user buffer will not be deallocated when this allocator is destructed. |
| | | |
| | | \param buffer User supplied buffer. |
| | | \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). |
| | | \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. |
| | | \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) |
| | | { |
| | | 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; |
| | | } |
| | | |
| | | //! Destructor. |
| | | /*! This deallocates all memory chunks, excluding the user-supplied buffer. |
| | | */ |
| | | ~MemoryPoolAllocator() { |
| | | Clear(); |
| | | RAPIDJSON_DELETE(ownBaseAllocator_); |
| | | } |
| | | |
| | | //! Deallocates all memory chunks, excluding the user-supplied buffer. |
| | | void Clear() { |
| | | while (chunkHead_ && chunkHead_ != userBuffer_) { |
| | | ChunkHeader* next = chunkHead_->next; |
| | | baseAllocator_->Free(chunkHead_); |
| | | chunkHead_ = next; |
| | | } |
| | | if (chunkHead_ && chunkHead_ == userBuffer_) |
| | | chunkHead_->size = 0; // Clear user buffer |
| | | } |
| | | |
| | | //! Computes the total capacity of allocated memory chunks. |
| | | /*! \return total capacity in bytes. |
| | | */ |
| | | size_t Capacity() const { |
| | | size_t capacity = 0; |
| | | for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) |
| | | capacity += c->capacity; |
| | | return capacity; |
| | | } |
| | | |
| | | //! Computes the memory blocks allocated. |
| | | /*! \return total used bytes. |
| | | */ |
| | | size_t Size() const { |
| | | size_t size = 0; |
| | | for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) |
| | | size += c->size; |
| | | return size; |
| | | } |
| | | |
| | | //! Allocates a memory block. (concept Allocator) |
| | | void* Malloc(size_t size) { |
| | | if (!size) |
| | | return NULL; |
| | | |
| | | size = RAPIDJSON_ALIGN(size); |
| | | if (chunkHead_ == 0 || chunkHead_->size + size > 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; |
| | | return buffer; |
| | | } |
| | | |
| | | //! Resizes a memory block (concept Allocator) |
| | | void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { |
| | | if (originalPtr == 0) |
| | | return Malloc(newSize); |
| | | |
| | | if (newSize == 0) |
| | | return NULL; |
| | | |
| | | originalSize = RAPIDJSON_ALIGN(originalSize); |
| | | newSize = RAPIDJSON_ALIGN(newSize); |
| | | |
| | | // Do not shrink if new size is smaller than original |
| | | if (originalSize >= newSize) |
| | | 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) { |
| | | size_t increment = static_cast<size_t>(newSize - originalSize); |
| | | if (chunkHead_->size + increment <= chunkHead_->capacity) { |
| | | chunkHead_->size += increment; |
| | | return originalPtr; |
| | | } |
| | | } |
| | | |
| | | // Realloc process: allocate and copy memory, do not free original buffer. |
| | | if (void* newBuffer = Malloc(newSize)) { |
| | | if (originalSize) |
| | | std::memcpy(newBuffer, originalPtr, originalSize); |
| | | return newBuffer; |
| | | } |
| | | else |
| | | return NULL; |
| | | } |
| | | |
| | | //! Frees a memory block (concept Allocator) |
| | | static void Free(void *ptr) { (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 */; |
| | | |
| | | //! 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))) { |
| | | chunk->capacity = capacity; |
| | | chunk->size = 0; |
| | | chunk->next = chunkHead_; |
| | | 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. |
| | | }; |
| | | |
| | | 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. |
| | | }; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_ENCODINGS_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_ |
New file |
| | |
| | | // 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 |
| | | // 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_DOCUMENT_H_ |
| | | #define RAPIDJSON_DOCUMENT_H_ |
| | | |
| | | /*! \file document.h */ |
| | | |
| | | #include "reader.h" |
| | | #include "internal/meta.h" |
| | | #include "internal/strfunc.h" |
| | | #include "memorystream.h" |
| | | #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 |
| | | #endif |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | RAPIDJSON_DIAG_OFF(switch-enum) |
| | | RAPIDJSON_DIAG_OFF(c++98-compat) |
| | | #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__ |
| | | |
| | | #ifndef RAPIDJSON_NOMEMBERITERATORCLASS |
| | | #include <iterator> // std::random_access_iterator_tag |
| | | #endif |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | #include <utility> // std::move |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | // Forward declaration. |
| | | template <typename Encoding, typename Allocator> |
| | | class GenericValue; |
| | | |
| | | template <typename Encoding, typename Allocator, typename StackAllocator> |
| | | class GenericDocument; |
| | | |
| | | //! Name-value pair in a JSON object value. |
| | | /*! |
| | | This class was internal to GenericValue. It used to be a inner struct. |
| | | But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. |
| | | https://code.google.com/p/rapidjson/issues/detail?id=64 |
| | | */ |
| | | template <typename Encoding, typename Allocator> |
| | | struct GenericMember { |
| | | GenericValue<Encoding, Allocator> name; //!< name of member (must be a string) |
| | | GenericValue<Encoding, Allocator> value; //!< value of member. |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // GenericMemberIterator |
| | | |
| | | #ifndef RAPIDJSON_NOMEMBERITERATORCLASS |
| | | |
| | | //! (Constant) member iterator for a JSON object value |
| | | /*! |
| | | \tparam Const Is this a constant iterator? |
| | | \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. |
| | | |
| | | This class implements a Random Access Iterator for GenericMember elements |
| | | of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. |
| | | |
| | | \note This iterator implementation is mainly intended to avoid implicit |
| | | conversions from iterator values to \c NULL, |
| | | e.g. from GenericValue::FindMember. |
| | | |
| | | \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a |
| | | pointer-based implementation, if your platform doesn't provide |
| | | the C++ <iterator> header. |
| | | |
| | | \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator |
| | | */ |
| | | template <bool Const, typename Encoding, typename Allocator> |
| | | 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; |
| | | |
| | | public: |
| | | //! Iterator type itself |
| | | typedef GenericMemberIterator Iterator; |
| | | //! Constant iterator type |
| | | typedef GenericMemberIterator<true,Encoding,Allocator> ConstIterator; |
| | | //! 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 pointer Pointer; |
| | | //! Reference to (const) GenericMember |
| | | typedef reference Reference; |
| | | //! Signed integer type (e.g. \c ptrdiff_t) |
| | | typedef difference_type DifferenceType; |
| | | |
| | | //! Default constructor (singular value) |
| | | /*! Creates an iterator pointing to no element. |
| | | \note All operations, except for comparisons, are undefined on such values. |
| | | */ |
| | | GenericMemberIterator() : ptr_() {} |
| | | |
| | | //! Iterator conversions to more const |
| | | /*! |
| | | \param it (Non-const) iterator to copy from |
| | | |
| | | Allows the creation of an iterator from another GenericMemberIterator |
| | | that is "less const". Especially, creating a non-constant iterator |
| | | from a constant iterator are disabled: |
| | | \li const -> non-const (not ok) |
| | | \li const -> const (ok) |
| | | \li non-const -> const (ok) |
| | | \li non-const -> non-const (ok) |
| | | |
| | | \note If the \c Const template parameter is already \c false, this |
| | | constructor effectively defines a regular copy-constructor. |
| | | Otherwise, the copy constructor is implicitly defined. |
| | | */ |
| | | GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} |
| | | Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } |
| | | |
| | | //! @name stepping |
| | | //@{ |
| | | Iterator& operator++(){ ++ptr_; return *this; } |
| | | Iterator& operator--(){ --ptr_; return *this; } |
| | | Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } |
| | | Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } |
| | | //@} |
| | | |
| | | //! @name increment/decrement |
| | | //@{ |
| | | Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } |
| | | Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } |
| | | |
| | | Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } |
| | | Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } |
| | | //@} |
| | | |
| | | //! @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_; } |
| | | //@} |
| | | |
| | | //! @name dereference |
| | | //@{ |
| | | Reference operator*() const { return *ptr_; } |
| | | Pointer operator->() const { return ptr_; } |
| | | Reference operator[](DifferenceType n) const { return ptr_[n]; } |
| | | //@} |
| | | |
| | | //! Distance |
| | | DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } |
| | | |
| | | private: |
| | | //! Internal constructor from plain pointer |
| | | explicit GenericMemberIterator(Pointer p) : ptr_(p) {} |
| | | |
| | | Pointer ptr_; //!< raw pointer |
| | | }; |
| | | |
| | | #else // RAPIDJSON_NOMEMBERITERATORCLASS |
| | | |
| | | // class-based member iterator implementation disabled, use plain pointers |
| | | |
| | | template <bool Const, typename Encoding, typename Allocator> |
| | | struct GenericMemberIterator; |
| | | |
| | | //! non-const GenericMemberIterator |
| | | template <typename Encoding, typename Allocator> |
| | | struct GenericMemberIterator<false,Encoding,Allocator> { |
| | | //! use plain pointer as iterator type |
| | | typedef GenericMember<Encoding,Allocator>* Iterator; |
| | | }; |
| | | //! const GenericMemberIterator |
| | | template <typename Encoding, typename Allocator> |
| | | struct GenericMemberIterator<true,Encoding,Allocator> { |
| | | //! use plain const pointer as iterator type |
| | | typedef const GenericMember<Encoding,Allocator>* Iterator; |
| | | }; |
| | | |
| | | #endif // RAPIDJSON_NOMEMBERITERATORCLASS |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // GenericStringRef |
| | | |
| | | //! Reference to a constant string (not taking a copy) |
| | | /*! |
| | | \tparam CharType character type of the string |
| | | |
| | | This helper class is used to automatically infer constant string |
| | | references for string literals, especially from \c const \b (!) |
| | | character arrays. |
| | | |
| | | The main use is for creating JSON string values without copying the |
| | | source string via an \ref Allocator. This requires that the referenced |
| | | string pointers have a sufficient lifetime, which exceeds the lifetime |
| | | of the associated GenericValue. |
| | | |
| | | \b Example |
| | | \code |
| | | Value v("foo"); // ok, no need to copy & calculate length |
| | | const char foo[] = "foo"; |
| | | v.SetString(foo); // ok |
| | | |
| | | const char* bar = foo; |
| | | // Value x(bar); // not ok, can't rely on bar's lifetime |
| | | Value x(StringRef(bar)); // lifetime explicitly guaranteed by user |
| | | Value y(StringRef(bar, 3)); // ok, explicitly pass length |
| | | \endcode |
| | | |
| | | \see StringRef, GenericValue::SetString |
| | | */ |
| | | template<typename CharType> |
| | | struct GenericStringRef { |
| | | typedef CharType Ch; //!< character type of the string |
| | | |
| | | //! Create string reference from \c const character array |
| | | #ifndef __clang__ // -Wdocumentation |
| | | /*! |
| | | This constructor implicitly creates a constant string reference from |
| | | a \c const character array. It has better performance than |
| | | \ref StringRef(const CharType*) by inferring the string \ref length |
| | | from the array length, and also supports strings containing null |
| | | characters. |
| | | |
| | | \tparam N length of the string, automatically inferred |
| | | |
| | | \param str Constant character array, lifetime assumed to be longer |
| | | than the use of the string in e.g. a GenericValue |
| | | |
| | | \post \ref s == str |
| | | |
| | | \note Constant complexity. |
| | | \note There is a hidden, private overload to disallow references to |
| | | non-const character arrays to be created via this constructor. |
| | | By this, e.g. function-scope arrays used to be filled via |
| | | \c snprintf are excluded from consideration. |
| | | In such cases, the referenced string should be \b copied to the |
| | | GenericValue instead. |
| | | */ |
| | | #endif |
| | | template<SizeType N> |
| | | GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT |
| | | : s(str), length(N-1) {} |
| | | |
| | | //! Explicitly create string reference from \c const character pointer |
| | | #ifndef __clang__ // -Wdocumentation |
| | | /*! |
| | | This constructor can be used to \b explicitly create a reference to |
| | | a constant string pointer. |
| | | |
| | | \see StringRef(const CharType*) |
| | | |
| | | \param str Constant character pointer, lifetime assumed to be longer |
| | | than the use of the string in e.g. a GenericValue |
| | | |
| | | \post \ref s == str |
| | | |
| | | \note There is a hidden, private overload to disallow references to |
| | | non-const character arrays to be created via this constructor. |
| | | By this, e.g. function-scope arrays used to be filled via |
| | | \c snprintf are excluded from consideration. |
| | | In such cases, the referenced string should be \b copied to the |
| | | GenericValue instead. |
| | | */ |
| | | #endif |
| | | explicit GenericStringRef(const CharType* str) |
| | | : s(str), length(NotNullStrLen(str)) {} |
| | | |
| | | //! Create constant string reference from pointer and length |
| | | #ifndef __clang__ // -Wdocumentation |
| | | /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue |
| | | \param len length of the string, excluding the trailing NULL terminator |
| | | |
| | | \post \ref s == str && \ref length == len |
| | | \note Constant complexity. |
| | | */ |
| | | #endif |
| | | GenericStringRef(const CharType* str, SizeType len) |
| | | : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } |
| | | |
| | | GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} |
| | | |
| | | //! implicit conversion to plain CharType pointer |
| | | operator const Ch *() const { return s; } |
| | | |
| | | const Ch* const s; //!< plain CharType pointer |
| | | 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 */; |
| | | //! Copy assignment operator not permitted - immutable type |
| | | 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 |
| | | value in a JSON GenericValue object, if the string's lifetime is known |
| | | to be valid long enough. |
| | | \tparam CharType Character type of the string |
| | | \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue |
| | | \return GenericStringRef string reference object |
| | | \relatesalso GenericStringRef |
| | | |
| | | \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember |
| | | */ |
| | | template<typename CharType> |
| | | inline GenericStringRef<CharType> StringRef(const CharType* str) { |
| | | return GenericStringRef<CharType>(str); |
| | | } |
| | | |
| | | //! 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 |
| | | value in a JSON GenericValue object, if the string's lifetime is known |
| | | to be valid long enough. |
| | | |
| | | This version has better performance with supplied length, and also |
| | | supports string containing null characters. |
| | | |
| | | \tparam CharType character type of the string |
| | | \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue |
| | | \param length The length of source string. |
| | | \return GenericStringRef string reference object |
| | | \relatesalso GenericStringRef |
| | | */ |
| | | template<typename CharType> |
| | | inline GenericStringRef<CharType> StringRef(const CharType* str, size_t length) { |
| | | return GenericStringRef<CharType>(str, SizeType(length)); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Mark a string object as constant string |
| | | /*! Mark a string object (e.g. \c std::string) as a "string literal". |
| | | This function can be used to avoid copying a string to be referenced as a |
| | | value in a JSON GenericValue object, if the string's lifetime is known |
| | | to be valid long enough. |
| | | |
| | | \tparam CharType character type of the string |
| | | \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue |
| | | \return GenericStringRef string reference object |
| | | \relatesalso GenericStringRef |
| | | \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. |
| | | */ |
| | | template<typename CharType> |
| | | inline GenericStringRef<CharType> StringRef(const std::basic_string<CharType>& str) { |
| | | return GenericStringRef<CharType>(str.data(), SizeType(str.size())); |
| | | } |
| | | #endif |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // GenericValue type traits |
| | | namespace internal { |
| | | |
| | | template <typename T, typename Encoding = void, typename Allocator = void> |
| | | struct IsGenericValueImpl : FalseType {}; |
| | | |
| | | // select candidates according to nested encoding and allocator types |
| | | template <typename T> struct IsGenericValueImpl<T, typename Void<typename T::EncodingType>::Type, typename Void<typename T::AllocatorType>::Type> |
| | | : IsBaseOf<GenericValue<typename T::EncodingType, typename T::AllocatorType>, T>::Type {}; |
| | | |
| | | // helper to match arbitrary GenericValue instantiations, including derived classes |
| | | template <typename T> struct IsGenericValue : IsGenericValueImpl<T>::Type {}; |
| | | |
| | | } // namespace internal |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // TypeHelper |
| | | |
| | | namespace internal { |
| | | |
| | | template <typename ValueType, typename T> |
| | | struct TypeHelper {}; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, bool> { |
| | | static bool Is(const ValueType& v) { return v.IsBool(); } |
| | | static bool Get(const ValueType& v) { return v.GetBool(); } |
| | | static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } |
| | | static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, int> { |
| | | static bool Is(const ValueType& v) { return v.IsInt(); } |
| | | static int Get(const ValueType& v) { return v.GetInt(); } |
| | | static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } |
| | | static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, unsigned> { |
| | | static bool Is(const ValueType& v) { return v.IsUint(); } |
| | | static unsigned Get(const ValueType& v) { return v.GetUint(); } |
| | | static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } |
| | | static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, int64_t> { |
| | | static bool Is(const ValueType& v) { return v.IsInt64(); } |
| | | static int64_t Get(const ValueType& v) { return v.GetInt64(); } |
| | | static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } |
| | | static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, uint64_t> { |
| | | static bool Is(const ValueType& v) { return v.IsUint64(); } |
| | | static uint64_t Get(const ValueType& v) { return v.GetUint64(); } |
| | | static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } |
| | | static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, double> { |
| | | static bool Is(const ValueType& v) { return v.IsDouble(); } |
| | | static double Get(const ValueType& v) { return v.GetDouble(); } |
| | | static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } |
| | | static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, float> { |
| | | static bool Is(const ValueType& v) { return v.IsFloat(); } |
| | | static float Get(const ValueType& v) { return v.GetFloat(); } |
| | | static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } |
| | | static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, const typename ValueType::Ch*> { |
| | | typedef const typename ValueType::Ch* StringType; |
| | | static bool Is(const ValueType& v) { return v.IsString(); } |
| | | static StringType Get(const ValueType& v) { return v.GetString(); } |
| | | static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } |
| | | static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } |
| | | }; |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, std::basic_string<typename ValueType::Ch> > { |
| | | typedef std::basic_string<typename ValueType::Ch> StringType; |
| | | static bool Is(const ValueType& v) { return v.IsString(); } |
| | | static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } |
| | | static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } |
| | | }; |
| | | #endif |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, typename ValueType::Array> { |
| | | typedef typename ValueType::Array ArrayType; |
| | | static bool Is(const ValueType& v) { return v.IsArray(); } |
| | | static ArrayType Get(ValueType& v) { return v.GetArray(); } |
| | | static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } |
| | | static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, typename ValueType::ConstArray> { |
| | | typedef typename ValueType::ConstArray ArrayType; |
| | | static bool Is(const ValueType& v) { return v.IsArray(); } |
| | | static ArrayType Get(const ValueType& v) { return v.GetArray(); } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, typename ValueType::Object> { |
| | | typedef typename ValueType::Object ObjectType; |
| | | 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&) { return v = data; } |
| | | }; |
| | | |
| | | template<typename ValueType> |
| | | struct TypeHelper<ValueType, typename ValueType::ConstObject> { |
| | | typedef typename ValueType::ConstObject ObjectType; |
| | | static bool Is(const ValueType& v) { return v.IsObject(); } |
| | | static ObjectType Get(const ValueType& v) { return v.GetObject(); } |
| | | }; |
| | | |
| | | } // namespace internal |
| | | |
| | | // Forward declarations |
| | | template <bool, typename> class GenericArray; |
| | | template <bool, typename> class GenericObject; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // GenericValue |
| | | |
| | | //! Represents a JSON value. Use Value for UTF8 encoding and default allocator. |
| | | /*! |
| | | A JSON value can be one of 7 types. This class is a variant type supporting |
| | | these types. |
| | | |
| | | Use the Value if UTF8 and default allocator |
| | | |
| | | \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<> > |
| | | class GenericValue { |
| | | public: |
| | | //! Name-value pair in an object. |
| | | typedef GenericMember<Encoding, Allocator> Member; |
| | | typedef Encoding EncodingType; //!< Encoding type from template parameter. |
| | | typedef Allocator AllocatorType; //!< Allocator type from template parameter. |
| | | typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. |
| | | typedef GenericStringRef<Ch> StringRefType; //!< Reference to a constant string |
| | | typedef typename GenericMemberIterator<false,Encoding,Allocator>::Iterator MemberIterator; //!< Member iterator for iterating in object. |
| | | typedef typename GenericMemberIterator<true,Encoding,Allocator>::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. |
| | | typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. |
| | | typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. |
| | | typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of itself. |
| | | typedef GenericArray<false, ValueType> Array; |
| | | typedef GenericArray<true, ValueType> ConstArray; |
| | | typedef GenericObject<false, ValueType> Object; |
| | | typedef GenericObject<true, ValueType> ConstObject; |
| | | |
| | | //!@name Constructors and destructor. |
| | | //@{ |
| | | |
| | | //! Default constructor creates a null value. |
| | | GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | //! Move constructor in C++11 |
| | | GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { |
| | | rhs.data_.f.flags = kNullFlag; // give up contents |
| | | } |
| | | #endif |
| | | |
| | | private: |
| | | //! Copy constructor is not permitted. |
| | | GenericValue(const GenericValue& rhs); |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | //! Moving from a GenericDocument is not permitted. |
| | | template <typename StackAllocator> |
| | | GenericValue(GenericDocument<Encoding,Allocator,StackAllocator>&& rhs); |
| | | |
| | | //! Move assignment from a GenericDocument is not permitted. |
| | | template <typename StackAllocator> |
| | | GenericValue& operator=(GenericDocument<Encoding,Allocator,StackAllocator>&& rhs); |
| | | #endif |
| | | |
| | | public: |
| | | |
| | | //! Constructor with JSON value type. |
| | | /*! This creates a Value of specified type with default content. |
| | | \param type Type of the value. |
| | | \note Default content for number is zero. |
| | | */ |
| | | explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { |
| | | static const uint16_t defaultFlags[7] = { |
| | | kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, |
| | | kNumberAnyFlag |
| | | }; |
| | | RAPIDJSON_ASSERT(type >= kNullType && type <= kNumberType); |
| | | data_.f.flags = defaultFlags[type]; |
| | | |
| | | // Use ShortString to store empty string. |
| | | if (type == kStringType) |
| | | data_.ss.SetLength(0); |
| | | } |
| | | |
| | | //! Explicit copy constructor (with allocator) |
| | | /*! Creates a copy of a Value by using the given Allocator |
| | | \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, bool copyConstStrings = false) { |
| | | switch (rhs.GetType()) { |
| | | case kObjectType: { |
| | | SizeType count = rhs.data_.o.size; |
| | | Member* lm = reinterpret_cast<Member*>(allocator.Malloc(count * sizeof(Member))); |
| | | const typename GenericValue<Encoding,SourceAllocator>::Member* rm = rhs.GetMembersPointer(); |
| | | 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); |
| | | } |
| | | data_.f.flags = kObjectFlag; |
| | | data_.o.size = data_.o.capacity = count; |
| | | SetMembersPointer(lm); |
| | | } |
| | | break; |
| | | case kArrayType: { |
| | | SizeType count = rhs.data_.a.size; |
| | | GenericValue* le = reinterpret_cast<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 |
| | | \note This constructor is limited to \em real boolean values and rejects |
| | | implicitly converted types like arbitrary pointers. Use an explicit cast |
| | | to \c bool, if you want to construct a boolean JSON value in such cases. |
| | | */ |
| | | #ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen |
| | | template <typename T> |
| | | explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<bool, T>))) RAPIDJSON_NOEXCEPT // See #472 |
| | | #else |
| | | explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT |
| | | #endif |
| | | : data_() { |
| | | // safe-guard against failing SFINAE |
| | | RAPIDJSON_STATIC_ASSERT((internal::IsSame<bool,T>::Value)); |
| | | data_.f.flags = b ? kTrueFlag : kFalseFlag; |
| | | } |
| | | |
| | | //! Constructor for int value. |
| | | explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { |
| | | data_.n.i64 = i; |
| | | data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; |
| | | } |
| | | |
| | | //! Constructor for unsigned value. |
| | | explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { |
| | | data_.n.u64 = u; |
| | | data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); |
| | | } |
| | | |
| | | //! Constructor for int64_t value. |
| | | explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { |
| | | data_.n.i64 = i64; |
| | | data_.f.flags = kNumberInt64Flag; |
| | | if (i64 >= 0) { |
| | | data_.f.flags |= kNumberUint64Flag; |
| | | if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) |
| | | data_.f.flags |= kUintFlag; |
| | | if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) |
| | | data_.f.flags |= kIntFlag; |
| | | } |
| | | else if (i64 >= static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) |
| | | data_.f.flags |= kIntFlag; |
| | | } |
| | | |
| | | //! Constructor for uint64_t value. |
| | | explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { |
| | | data_.n.u64 = u64; |
| | | data_.f.flags = kNumberUint64Flag; |
| | | if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) |
| | | data_.f.flags |= kInt64Flag; |
| | | if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) |
| | | data_.f.flags |= kUintFlag; |
| | | if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) |
| | | data_.f.flags |= kIntFlag; |
| | | } |
| | | |
| | | //! 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)); } |
| | | |
| | | //! Constructor for constant string (i.e. do not make a copy of string) |
| | | explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } |
| | | |
| | | //! Constructor for copy-string (i.e. do make a copy of string) |
| | | GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } |
| | | |
| | | //! Constructor for copy-string (i.e. do make a copy of string) |
| | | GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Constructor for copy-string from a string object (i.e. do make a copy of string) |
| | | /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. |
| | | */ |
| | | GenericValue(const std::basic_string<Ch>& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } |
| | | #endif |
| | | |
| | | //! Constructor for Array. |
| | | /*! |
| | | \param a An array obtained by \c GetArray(). |
| | | \note \c Array is always pass-by-value. |
| | | \note the source array is moved into this value and the sourec array becomes empty. |
| | | */ |
| | | GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { |
| | | a.value_.data_ = Data(); |
| | | a.value_.data_.f.flags = kArrayFlag; |
| | | } |
| | | |
| | | //! Constructor for Object. |
| | | /*! |
| | | \param o An object obtained by \c GetObject(). |
| | | \note \c Object is always pass-by-value. |
| | | \note the source object is moved into this value and the sourec object becomes empty. |
| | | */ |
| | | GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { |
| | | o.value_.data_ = Data(); |
| | | o.value_.data_.f.flags = kObjectFlag; |
| | | } |
| | | |
| | | //! Destructor. |
| | | /*! Need to destruct elements of array, members of object, or copy-string. |
| | | */ |
| | | ~GenericValue() { |
| | | if (Allocator::kNeedFree) { // Shortcut by Allocator's trait |
| | | switch(data_.f.flags) { |
| | | case kArrayFlag: |
| | | { |
| | | GenericValue* e = GetElementsPointer(); |
| | | for (GenericValue* v = e; v != e + data_.a.size; ++v) |
| | | v->~GenericValue(); |
| | | Allocator::Free(e); |
| | | } |
| | | break; |
| | | |
| | | case kObjectFlag: |
| | | for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) |
| | | m->~Member(); |
| | | Allocator::Free(GetMembersPointer()); |
| | | break; |
| | | |
| | | case kCopyStringFlag: |
| | | Allocator::Free(const_cast<Ch*>(GetStringPointer())); |
| | | break; |
| | | |
| | | default: |
| | | break; // Do nothing for other types. |
| | | } |
| | | } |
| | | } |
| | | |
| | | //@} |
| | | |
| | | //!@name Assignment operators |
| | | //@{ |
| | | |
| | | //! Assignment with move semantics. |
| | | /*! \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); |
| | | return *this; |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | //! Move assignment in C++11 |
| | | GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { |
| | | return *this = rhs.Move(); |
| | | } |
| | | #endif |
| | | |
| | | //! Assignment of constant string reference (no copy) |
| | | /*! \param str Constant string reference to be assigned |
| | | \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. |
| | | \see GenericStringRef, operator=(T) |
| | | */ |
| | | GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { |
| | | GenericValue s(str); |
| | | return *this = s; |
| | | } |
| | | |
| | | //! Assignment with primitive types. |
| | | /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t |
| | | \param value The value to be assigned. |
| | | |
| | | \note The source type \c T explicitly disallows all pointer types, |
| | | especially (\c const) \ref Ch*. This helps avoiding implicitly |
| | | referencing character strings with insufficient lifetime, use |
| | | \ref SetString(const Ch*, Allocator&) (for copying) or |
| | | \ref StringRef() (to explicitly mark the pointer as constant) instead. |
| | | All other pointer types would implicitly convert to \c bool, |
| | | use \ref SetBool() instead. |
| | | */ |
| | | template <typename T> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer<T>), (GenericValue&)) |
| | | operator=(T value) { |
| | | GenericValue v(value); |
| | | return *this = v; |
| | | } |
| | | |
| | | //! Deep-copy assignment from Value |
| | | /*! Assigns a \b copy of the Value to the current Value object |
| | | \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, bool copyConstStrings = false) { |
| | | RAPIDJSON_ASSERT(static_cast<void*>(this) != static_cast<void const*>(&rhs)); |
| | | this->~GenericValue(); |
| | | new (this) GenericValue(rhs, allocator, copyConstStrings); |
| | | return *this; |
| | | } |
| | | |
| | | //! Exchange the contents of this value with those of other. |
| | | /*! |
| | | \param other Another value. |
| | | \note Constant complexity. |
| | | */ |
| | | GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { |
| | | GenericValue temp; |
| | | temp.RawAssign(*this); |
| | | RawAssign(other); |
| | | other.RawAssign(temp); |
| | | 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.value, b.value); |
| | | // ... |
| | | } |
| | | \endcode |
| | | \see Swap() |
| | | */ |
| | | friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } |
| | | |
| | | //! Prepare Value for move semantics |
| | | /*! \return *this */ |
| | | GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } |
| | | //@} |
| | | |
| | | //!@name Equal-to and not-equal-to operators |
| | | //@{ |
| | | //! 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). |
| | | */ |
| | | template <typename SourceAllocator> |
| | | bool operator==(const GenericValue<Encoding, SourceAllocator>& rhs) const { |
| | | typedef GenericValue<Encoding, SourceAllocator> RhsType; |
| | | if (GetType() != rhs.GetType()) |
| | | return false; |
| | | |
| | | switch (GetType()) { |
| | | case kObjectType: // Warning: O(n^2) inner-loop |
| | | if (data_.o.size != rhs.data_.o.size) |
| | | return false; |
| | | for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { |
| | | typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); |
| | | if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) |
| | | return false; |
| | | } |
| | | return true; |
| | | |
| | | case kArrayType: |
| | | if (data_.a.size != rhs.data_.a.size) |
| | | return false; |
| | | for (SizeType i = 0; i < data_.a.size; i++) |
| | | if ((*this)[i] != rhs[i]) |
| | | return false; |
| | | return true; |
| | | |
| | | case kStringType: |
| | | return StringEqual(rhs); |
| | | |
| | | case kNumberType: |
| | | if (IsDouble() || rhs.IsDouble()) { |
| | | double a = GetDouble(); // May convert from integer to double. |
| | | double b = rhs.GetDouble(); // Ditto |
| | | return a >= b && a <= b; // Prevent -Wfloat-equal |
| | | } |
| | | else |
| | | return data_.n.u64 == rhs.data_.n.u64; |
| | | |
| | | default: |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | //! Equal-to operator with const C-string pointer |
| | | bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Equal-to operator with string object |
| | | /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. |
| | | */ |
| | | bool operator==(const std::basic_string<Ch>& rhs) const { return *this == GenericValue(StringRef(rhs)); } |
| | | #endif |
| | | |
| | | //! Equal-to operator with primitive types |
| | | /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false |
| | | */ |
| | | template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>,internal::IsGenericValue<T> >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } |
| | | |
| | | //! Not-equal-to operator |
| | | /*! \return !(*this == rhs) |
| | | */ |
| | | template <typename SourceAllocator> |
| | | bool operator!=(const GenericValue<Encoding, SourceAllocator>& rhs) const { return !(*this == rhs); } |
| | | |
| | | //! Not-equal-to operator with const C-string pointer |
| | | bool operator!=(const Ch* rhs) const { return !(*this == rhs); } |
| | | |
| | | //! Not-equal-to operator with arbitrary types |
| | | /*! \return !(*this == rhs) |
| | | */ |
| | | template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } |
| | | |
| | | //! Equal-to operator with arbitrary types (symmetric version) |
| | | /*! \return (rhs == lhs) |
| | | */ |
| | | template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } |
| | | |
| | | //! Not-Equal-to operator with arbitrary types (symmetric version) |
| | | /*! \return !(rhs == lhs) |
| | | */ |
| | | template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } |
| | | //@} |
| | | |
| | | //!@name Type |
| | | //@{ |
| | | |
| | | Type GetType() const { return static_cast<Type>(data_.f.flags & kTypeMask); } |
| | | bool IsNull() const { return data_.f.flags == kNullFlag; } |
| | | bool IsFalse() const { return data_.f.flags == kFalseFlag; } |
| | | bool IsTrue() const { return data_.f.flags == kTrueFlag; } |
| | | bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } |
| | | bool IsObject() const { return data_.f.flags == kObjectFlag; } |
| | | bool IsArray() const { return data_.f.flags == kArrayFlag; } |
| | | bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } |
| | | bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } |
| | | bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } |
| | | bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } |
| | | bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } |
| | | bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } |
| | | bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } |
| | | |
| | | // Checks whether a number can be losslessly converted to a double. |
| | | bool IsLosslessDouble() const { |
| | | if (!IsNumber()) return false; |
| | | if (IsUint64()) { |
| | | 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)())) |
| | | && (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)())) |
| | | && (i == static_cast<int64_t>(d)); |
| | | } |
| | | return true; // double, int, uint are always lossless |
| | | } |
| | | |
| | | // Checks whether a number is a float (possible lossy). |
| | | bool IsFloat() const { |
| | | if ((data_.f.flags & kDoubleFlag) == 0) |
| | | return false; |
| | | double d = GetDouble(); |
| | | return d >= -3.4028234e38 && d <= 3.4028234e38; |
| | | } |
| | | // Checks whether a number can be losslessly converted to a float. |
| | | 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)())) |
| | | return false; |
| | | double b = static_cast<double>(static_cast<float>(a)); |
| | | return a >= b && a <= b; // Prevent -Wfloat-equal |
| | | } |
| | | |
| | | //@} |
| | | |
| | | //!@name Null |
| | | //@{ |
| | | |
| | | GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } |
| | | |
| | | //@} |
| | | |
| | | //!@name Bool |
| | | //@{ |
| | | |
| | | bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } |
| | | //!< Set boolean value |
| | | /*! \post IsBool() == true */ |
| | | GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } |
| | | |
| | | //@} |
| | | |
| | | //!@name Object |
| | | //@{ |
| | | |
| | | //! Set this value as an empty object. |
| | | /*! \post IsObject() == true */ |
| | | GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } |
| | | |
| | | //! 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; } |
| | | |
| | | //! Get a value from an object associated with the name. |
| | | /*! \pre IsObject() == true |
| | | \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) |
| | | \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. |
| | | Since 0.2, if the name is not correct, it will assert. |
| | | If user is unsure whether a member exists, user should use HasMember() first. |
| | | A better approach is to use FindMember(). |
| | | \note Linear time complexity. |
| | | */ |
| | | template <typename T> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(GenericValue&)) operator[](T* name) { |
| | | GenericValue n(StringRef(name)); |
| | | return (*this)[n]; |
| | | } |
| | | template <typename T> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast<GenericValue&>(*this)[name]; } |
| | | |
| | | //! Get a value from an object associated with the name. |
| | | /*! \pre IsObject() == true |
| | | \tparam SourceAllocator Allocator of the \c name value |
| | | |
| | | \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). |
| | | And it can also handle strings with embedded null characters. |
| | | |
| | | \note Linear time complexity. |
| | | */ |
| | | template <typename SourceAllocator> |
| | | GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) { |
| | | MemberIterator member = FindMember(name); |
| | | if (member != MemberEnd()) |
| | | return member->value; |
| | | 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)]; |
| | | return *new (buffer) GenericValue(); |
| | | } |
| | | } |
| | | template <typename SourceAllocator> |
| | | const GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this)[name]; } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Get a value from an object associated with name (string object). |
| | | GenericValue& operator[](const std::basic_string<Ch>& name) { return (*this)[GenericValue(StringRef(name))]; } |
| | | const GenericValue& operator[](const std::basic_string<Ch>& name) const { return (*this)[GenericValue(StringRef(name))]; } |
| | | #endif |
| | | |
| | | //! Const member iterator |
| | | /*! \pre IsObject() == true */ |
| | | ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } |
| | | //! Const \em past-the-end member iterator |
| | | /*! \pre IsObject() == true */ |
| | | ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } |
| | | //! Member iterator |
| | | /*! \pre IsObject() == true */ |
| | | MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } |
| | | //! \em Past-the-end member iterator |
| | | /*! \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()); |
| | | if (newCapacity > data_.o.capacity) { |
| | | SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member)))); |
| | | data_.o.capacity = newCapacity; |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | //! Check whether a member exists in the object. |
| | | /*! |
| | | \param name Member name to be searched. |
| | | \pre IsObject() == true |
| | | \return Whether a member with that name exists. |
| | | \note It is better to use FindMember() directly if you need the obtain the value as well. |
| | | \note Linear time complexity. |
| | | */ |
| | | bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Check whether a member exists in the object with string object. |
| | | /*! |
| | | \param name Member name to be searched. |
| | | \pre IsObject() == true |
| | | \return Whether a member with that name exists. |
| | | \note It is better to use FindMember() directly if you need the obtain the value as well. |
| | | \note Linear time complexity. |
| | | */ |
| | | bool HasMember(const std::basic_string<Ch>& name) const { return FindMember(name) != MemberEnd(); } |
| | | #endif |
| | | |
| | | //! Check whether a member exists in the object with GenericValue name. |
| | | /*! |
| | | This version is faster because it does not need a StrLen(). It can also handle string with null character. |
| | | \param name Member name to be searched. |
| | | \pre IsObject() == true |
| | | \return Whether a member with that name exists. |
| | | \note It is better to use FindMember() directly if you need the obtain the value as well. |
| | | \note Linear time complexity. |
| | | */ |
| | | template <typename SourceAllocator> |
| | | bool HasMember(const GenericValue<Encoding, SourceAllocator>& name) const { return FindMember(name) != MemberEnd(); } |
| | | |
| | | //! Find member by name. |
| | | /*! |
| | | \param name Member name to be searched. |
| | | \pre IsObject() == true |
| | | \return Iterator to member, if it exists. |
| | | Otherwise returns \ref MemberEnd(). |
| | | |
| | | \note Earlier versions of Rapidjson returned a \c NULL pointer, in case |
| | | the requested member doesn't exist. For consistency with e.g. |
| | | \c std::map, this has been changed to MemberEnd() now. |
| | | \note Linear time complexity. |
| | | */ |
| | | MemberIterator FindMember(const Ch* name) { |
| | | GenericValue n(StringRef(name)); |
| | | return FindMember(n); |
| | | } |
| | | |
| | | ConstMemberIterator FindMember(const Ch* name) const { return const_cast<GenericValue&>(*this).FindMember(name); } |
| | | |
| | | //! Find member by name. |
| | | /*! |
| | | This version is faster because it does not need a StrLen(). It can also handle string with null character. |
| | | \param name Member name to be searched. |
| | | \pre IsObject() == true |
| | | \return Iterator to member, if it exists. |
| | | Otherwise returns \ref MemberEnd(). |
| | | |
| | | \note Earlier versions of Rapidjson returned a \c NULL pointer, in case |
| | | the requested member doesn't exist. For consistency with e.g. |
| | | \c std::map, this has been changed to MemberEnd() now. |
| | | \note Linear time complexity. |
| | | */ |
| | | template <typename SourceAllocator> |
| | | 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; |
| | | } |
| | | template <typename SourceAllocator> ConstMemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this).FindMember(name); } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Find member by string object name. |
| | | /*! |
| | | \param name Member name to be searched. |
| | | \pre IsObject() == true |
| | | \return Iterator to member, if it exists. |
| | | Otherwise returns \ref MemberEnd(). |
| | | */ |
| | | MemberIterator FindMember(const std::basic_string<Ch>& name) { return FindMember(GenericValue(StringRef(name))); } |
| | | ConstMemberIterator FindMember(const std::basic_string<Ch>& name) const { return FindMember(GenericValue(StringRef(name))); } |
| | | #endif |
| | | |
| | | //! Add a member (name-value pair) to the object. |
| | | /*! \param name A string value as name of member. |
| | | \param value Value of any type. |
| | | \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 The ownership of \c name and \c value will be transferred to this object on success. |
| | | \pre IsObject() && name.IsString() |
| | | \post name.IsNull() && value.IsNull() |
| | | \note Amortized Constant time complexity. |
| | | */ |
| | | GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { |
| | | RAPIDJSON_ASSERT(IsObject()); |
| | | RAPIDJSON_ASSERT(name.IsString()); |
| | | |
| | | ObjectData& o = data_.o; |
| | | if (o.size >= o.capacity) |
| | | MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator); |
| | | Member* members = GetMembersPointer(); |
| | | members[o.size].name.RawAssign(name); |
| | | members[o.size].value.RawAssign(value); |
| | | o.size++; |
| | | return *this; |
| | | } |
| | | |
| | | //! Add a constant string value as member (name-value pair) to the object. |
| | | /*! \param name A string value as name of member. |
| | | \param value constant string reference as value of member. |
| | | \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. |
| | | \pre IsObject() |
| | | \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. |
| | | \note Amortized Constant time complexity. |
| | | */ |
| | | GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { |
| | | GenericValue v(value); |
| | | return AddMember(name, v, allocator); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Add a string object as member (name-value pair) to the object. |
| | | /*! \param name A string value as name of member. |
| | | \param value constant string reference as value of member. |
| | | \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. |
| | | \pre IsObject() |
| | | \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. |
| | | \note Amortized Constant time complexity. |
| | | */ |
| | | GenericValue& AddMember(GenericValue& name, std::basic_string<Ch>& value, Allocator& allocator) { |
| | | GenericValue v(value, allocator); |
| | | return AddMember(name, v, allocator); |
| | | } |
| | | #endif |
| | | |
| | | //! Add any primitive value as member (name-value pair) to the object. |
| | | /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t |
| | | \param name A string value as name of member. |
| | | \param value Value of primitive type \c T as value of member |
| | | \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). |
| | | \return The value itself for fluent API. |
| | | \pre IsObject() |
| | | |
| | | \note The source type \c T explicitly disallows all pointer types, |
| | | especially (\c const) \ref Ch*. This helps avoiding implicitly |
| | | referencing character strings with insufficient lifetime, use |
| | | \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref |
| | | AddMember(StringRefType, StringRefType, Allocator&). |
| | | All other pointer types would implicitly convert to \c bool, |
| | | use an explicit cast instead, if needed. |
| | | \note Amortized Constant time complexity. |
| | | */ |
| | | template <typename T> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&)) |
| | | AddMember(GenericValue& name, T value, Allocator& allocator) { |
| | | GenericValue v(value); |
| | | return AddMember(name, v, allocator); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { |
| | | return AddMember(name, value, allocator); |
| | | } |
| | | GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { |
| | | return AddMember(name, value, allocator); |
| | | } |
| | | GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { |
| | | return AddMember(name, value, allocator); |
| | | } |
| | | GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { |
| | | GenericValue n(name); |
| | | return AddMember(n, value, allocator); |
| | | } |
| | | #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | |
| | | |
| | | //! Add a member (name-value pair) to the object. |
| | | /*! \param name A constant string reference as name of member. |
| | | \param value Value of any type. |
| | | \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 The ownership of \c value will be transferred to this object on success. |
| | | \pre IsObject() |
| | | \post value.IsNull() |
| | | \note Amortized Constant time complexity. |
| | | */ |
| | | GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { |
| | | GenericValue n(name); |
| | | return AddMember(n, value, allocator); |
| | | } |
| | | |
| | | //! Add a constant string value as member (name-value pair) to the object. |
| | | /*! \param name A constant string reference as name of member. |
| | | \param value constant string reference as value of member. |
| | | \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. |
| | | \pre IsObject() |
| | | \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. |
| | | \note Amortized Constant time complexity. |
| | | */ |
| | | GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { |
| | | GenericValue v(value); |
| | | return AddMember(name, v, allocator); |
| | | } |
| | | |
| | | //! Add any primitive value as member (name-value pair) to the object. |
| | | /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t |
| | | \param name A constant string reference as name of member. |
| | | \param value Value of primitive type \c T as value of member |
| | | \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). |
| | | \return The value itself for fluent API. |
| | | \pre IsObject() |
| | | |
| | | \note The source type \c T explicitly disallows all pointer types, |
| | | especially (\c const) \ref Ch*. This helps avoiding implicitly |
| | | referencing character strings with insufficient lifetime, use |
| | | \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref |
| | | AddMember(StringRefType, StringRefType, Allocator&). |
| | | All other pointer types would implicitly convert to \c bool, |
| | | use an explicit cast instead, if needed. |
| | | \note Amortized Constant time complexity. |
| | | */ |
| | | template <typename T> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&)) |
| | | AddMember(StringRefType name, T value, Allocator& allocator) { |
| | | GenericValue n(name); |
| | | return AddMember(n, value, allocator); |
| | | } |
| | | |
| | | //! Remove all members in the object. |
| | | /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. |
| | | \note Linear time complexity. |
| | | */ |
| | | void RemoveAllMembers() { |
| | | RAPIDJSON_ASSERT(IsObject()); |
| | | for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) |
| | | m->~Member(); |
| | | data_.o.size = 0; |
| | | } |
| | | |
| | | //! Remove a member in object by its name. |
| | | /*! \param name Name of member to be removed. |
| | | \return Whether the member existed. |
| | | \note This function may reorder the object members. Use \ref |
| | | EraseMember(ConstMemberIterator) if you need to preserve the |
| | | relative order of the remaining members. |
| | | \note Linear time complexity. |
| | | */ |
| | | bool RemoveMember(const Ch* name) { |
| | | GenericValue n(StringRef(name)); |
| | | return RemoveMember(n); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | bool RemoveMember(const std::basic_string<Ch>& name) { return RemoveMember(GenericValue(StringRef(name))); } |
| | | #endif |
| | | |
| | | template <typename SourceAllocator> |
| | | bool RemoveMember(const GenericValue<Encoding, SourceAllocator>& name) { |
| | | MemberIterator m = FindMember(name); |
| | | if (m != MemberEnd()) { |
| | | RemoveMember(m); |
| | | return true; |
| | | } |
| | | else |
| | | return false; |
| | | } |
| | | |
| | | //! Remove a member in object by iterator. |
| | | /*! \param m member iterator (obtained by FindMember() or MemberBegin()). |
| | | \return the new iterator after removal. |
| | | \note This function may reorder the object members. Use \ref |
| | | EraseMember(ConstMemberIterator) if you need to preserve the |
| | | relative order of the remaining members. |
| | | \note Constant time complexity. |
| | | */ |
| | | MemberIterator RemoveMember(MemberIterator m) { |
| | | RAPIDJSON_ASSERT(IsObject()); |
| | | 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; |
| | | } |
| | | |
| | | //! Remove a member from an object by iterator. |
| | | /*! \param pos iterator to the member to remove |
| | | \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() |
| | | \return Iterator following the removed element. |
| | | If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. |
| | | \note This function preserves the relative order of the remaining object |
| | | members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). |
| | | \note Linear time complexity. |
| | | */ |
| | | MemberIterator EraseMember(ConstMemberIterator pos) { |
| | | return EraseMember(pos, pos +1); |
| | | } |
| | | |
| | | //! Remove members in the range [first, last) from an object. |
| | | /*! \param first iterator to the first member to remove |
| | | \param last iterator following the last member to remove |
| | | \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() |
| | | \return Iterator following the last removed element. |
| | | \note This function preserves the relative order of the remaining object |
| | | members. |
| | | \note Linear time complexity. |
| | | */ |
| | | MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { |
| | | RAPIDJSON_ASSERT(IsObject()); |
| | | RAPIDJSON_ASSERT(data_.o.size > 0); |
| | | RAPIDJSON_ASSERT(GetMembersPointer() != 0); |
| | | 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; |
| | | } |
| | | |
| | | //! Erase a member in object by its name. |
| | | /*! \param name Name of member to be removed. |
| | | \return Whether the member existed. |
| | | \note Linear time complexity. |
| | | */ |
| | | bool EraseMember(const Ch* name) { |
| | | GenericValue n(StringRef(name)); |
| | | return EraseMember(n); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | bool EraseMember(const std::basic_string<Ch>& name) { return EraseMember(GenericValue(StringRef(name))); } |
| | | #endif |
| | | |
| | | template <typename SourceAllocator> |
| | | bool EraseMember(const GenericValue<Encoding, SourceAllocator>& name) { |
| | | MemberIterator m = FindMember(name); |
| | | if (m != MemberEnd()) { |
| | | EraseMember(m); |
| | | return true; |
| | | } |
| | | else |
| | | return false; |
| | | } |
| | | |
| | | Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } |
| | | ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } |
| | | |
| | | //@} |
| | | |
| | | //!@name Array |
| | | //@{ |
| | | |
| | | //! Set this value as an empty array. |
| | | /*! \post IsArray == true */ |
| | | GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } |
| | | |
| | | //! Get the number of elements in array. |
| | | SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } |
| | | |
| | | //! Get the capacity of array. |
| | | SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } |
| | | |
| | | //! Check whether the array is empty. |
| | | bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } |
| | | |
| | | //! Remove all elements in the array. |
| | | /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. |
| | | \note Linear time complexity. |
| | | */ |
| | | void Clear() { |
| | | RAPIDJSON_ASSERT(IsArray()); |
| | | GenericValue* e = GetElementsPointer(); |
| | | for (GenericValue* v = e; v != e + data_.a.size; ++v) |
| | | v->~GenericValue(); |
| | | data_.a.size = 0; |
| | | } |
| | | |
| | | //! Get an element from array by index. |
| | | /*! \pre IsArray() == true |
| | | \param index Zero-based index of element. |
| | | \see operator[](T*) |
| | | */ |
| | | GenericValue& operator[](SizeType index) { |
| | | RAPIDJSON_ASSERT(IsArray()); |
| | | RAPIDJSON_ASSERT(index < data_.a.size); |
| | | return GetElementsPointer()[index]; |
| | | } |
| | | const GenericValue& operator[](SizeType index) const { return const_cast<GenericValue&>(*this)[index]; } |
| | | |
| | | //! Element iterator |
| | | /*! \pre IsArray() == true */ |
| | | ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } |
| | | //! \em Past-the-end element iterator |
| | | /*! \pre IsArray() == true */ |
| | | ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } |
| | | //! Constant element iterator |
| | | /*! \pre IsArray() == true */ |
| | | ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); } |
| | | //! Constant \em past-the-end element iterator |
| | | /*! \pre IsArray() == true */ |
| | | ConstValueIterator End() const { return const_cast<GenericValue&>(*this).End(); } |
| | | |
| | | //! Request the array to have enough capacity to store elements. |
| | | /*! \param newCapacity The capacity that the array 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& Reserve(SizeType newCapacity, Allocator &allocator) { |
| | | RAPIDJSON_ASSERT(IsArray()); |
| | | if (newCapacity > data_.a.capacity) { |
| | | SetElementsPointer(reinterpret_cast<GenericValue*>(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); |
| | | data_.a.capacity = newCapacity; |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | //! Append a GenericValue at the end of the array. |
| | | /*! \param value Value to be appended. |
| | | \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). |
| | | \pre IsArray() == true |
| | | \post value.IsNull() == true |
| | | \return The value itself for fluent API. |
| | | \note The ownership of \c value will be transferred to this array on success. |
| | | \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. |
| | | \note Amortized constant time complexity. |
| | | */ |
| | | GenericValue& PushBack(GenericValue& value, Allocator& allocator) { |
| | | RAPIDJSON_ASSERT(IsArray()); |
| | | if (data_.a.size >= data_.a.capacity) |
| | | Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); |
| | | GetElementsPointer()[data_.a.size++].RawAssign(value); |
| | | return *this; |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { |
| | | return PushBack(value, allocator); |
| | | } |
| | | #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | |
| | | //! Append a constant string reference at the end of the array. |
| | | /*! \param value Constant string reference to be appended. |
| | | \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). |
| | | \pre IsArray() == true |
| | | \return The value itself for fluent API. |
| | | \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. |
| | | \note Amortized constant time complexity. |
| | | \see GenericStringRef |
| | | */ |
| | | GenericValue& PushBack(StringRefType value, Allocator& allocator) { |
| | | return (*this).template PushBack<StringRefType>(value, allocator); |
| | | } |
| | | |
| | | //! Append a primitive value at the end of the array. |
| | | /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t |
| | | \param value Value of primitive type T to be appended. |
| | | \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). |
| | | \pre IsArray() == true |
| | | \return The value itself for fluent API. |
| | | \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. |
| | | |
| | | \note The source type \c T explicitly disallows all pointer types, |
| | | especially (\c const) \ref Ch*. This helps avoiding implicitly |
| | | referencing character strings with insufficient lifetime, use |
| | | \ref PushBack(GenericValue&, Allocator&) or \ref |
| | | PushBack(StringRefType, Allocator&). |
| | | All other pointer types would implicitly convert to \c bool, |
| | | use an explicit cast instead, if needed. |
| | | \note Amortized constant time complexity. |
| | | */ |
| | | template <typename T> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&)) |
| | | PushBack(T value, Allocator& allocator) { |
| | | GenericValue v(value); |
| | | return PushBack(v, allocator); |
| | | } |
| | | |
| | | //! Remove the last element in the array. |
| | | /*! |
| | | \note Constant time complexity. |
| | | */ |
| | | GenericValue& PopBack() { |
| | | RAPIDJSON_ASSERT(IsArray()); |
| | | RAPIDJSON_ASSERT(!Empty()); |
| | | GetElementsPointer()[--data_.a.size].~GenericValue(); |
| | | return *this; |
| | | } |
| | | |
| | | //! Remove an element of array by iterator. |
| | | /*! |
| | | \param pos iterator to the element to remove |
| | | \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() |
| | | \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. |
| | | \note Linear time complexity. |
| | | */ |
| | | ValueIterator Erase(ConstValueIterator pos) { |
| | | return Erase(pos, pos + 1); |
| | | } |
| | | |
| | | //! Remove elements in the range [first, last) of the array. |
| | | /*! |
| | | \param first iterator to the first element to remove |
| | | \param last iterator following the last element to remove |
| | | \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() |
| | | \return Iterator following the last removed element. |
| | | \note Linear time complexity. |
| | | */ |
| | | ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { |
| | | RAPIDJSON_ASSERT(IsArray()); |
| | | RAPIDJSON_ASSERT(data_.a.size > 0); |
| | | RAPIDJSON_ASSERT(GetElementsPointer() != 0); |
| | | RAPIDJSON_ASSERT(first >= Begin()); |
| | | RAPIDJSON_ASSERT(first <= last); |
| | | 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)); |
| | | data_.a.size -= static_cast<SizeType>(last - first); |
| | | return pos; |
| | | } |
| | | |
| | | Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } |
| | | ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } |
| | | |
| | | //@} |
| | | |
| | | //!@name Number |
| | | //@{ |
| | | |
| | | int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } |
| | | unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } |
| | | int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } |
| | | uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } |
| | | |
| | | //! Get the value as double type. |
| | | /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. |
| | | */ |
| | | double GetDouble() const { |
| | | RAPIDJSON_ASSERT(IsNumber()); |
| | | if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. |
| | | if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double |
| | | if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double |
| | | if ((data_.f.flags & kInt64Flag) != 0) return static_cast<double>(data_.n.i64); // int64_t -> double (may lose precision) |
| | | RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast<double>(data_.n.u64); // uint64_t -> double (may lose precision) |
| | | } |
| | | |
| | | //! Get the value as float type. |
| | | /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. |
| | | */ |
| | | float GetFloat() const { |
| | | return static_cast<float>(GetDouble()); |
| | | } |
| | | |
| | | GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } |
| | | GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } |
| | | 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(static_cast<double>(f)); return *this; } |
| | | |
| | | //@} |
| | | |
| | | //!@name String |
| | | //@{ |
| | | |
| | | const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } |
| | | |
| | | //! 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); } |
| | | |
| | | //! 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. |
| | | \param s source string pointer. |
| | | \param length The length of source string, excluding the trailing null terminator. |
| | | \return The value itself for fluent API. |
| | | \post IsString() == true && GetString() == s && GetStringLength() == length |
| | | \see SetString(StringRefType) |
| | | */ |
| | | GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } |
| | | |
| | | //! Set this value as a string without copying source string. |
| | | /*! \param s source string reference |
| | | \return The value itself for fluent API. |
| | | \post IsString() == true && GetString() == s && GetStringLength() == s.length |
| | | */ |
| | | GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } |
| | | |
| | | //! Set this value as a string by copying from source string. |
| | | /*! This version has better performance with supplied length, and also support string containing null character. |
| | | \param s source string. |
| | | \param length The length of source string, excluding the trailing null terminator. |
| | | \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). |
| | | \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) { return SetString(StringRef(s, length), allocator); } |
| | | |
| | | //! Set this value as a string by copying from source string. |
| | | /*! \param s source string. |
| | | \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). |
| | | \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(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. |
| | | /*! \param s source string. |
| | | \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). |
| | | \return The value itself for fluent API. |
| | | \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(StringRef(s), allocator); } |
| | | #endif |
| | | |
| | | //@} |
| | | |
| | | //!@name Array |
| | | //@{ |
| | | |
| | | //! Templated version for checking whether this value is type T. |
| | | /*! |
| | | \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string<Ch> |
| | | */ |
| | | template <typename T> |
| | | bool Is() const { return internal::TypeHelper<ValueType, T>::Is(*this); } |
| | | |
| | | template <typename T> |
| | | T Get() const { return internal::TypeHelper<ValueType, T>::Get(*this); } |
| | | |
| | | template <typename T> |
| | | T Get() { return internal::TypeHelper<ValueType, T>::Get(*this); } |
| | | |
| | | template<typename T> |
| | | ValueType& Set(const T& data) { return internal::TypeHelper<ValueType, T>::Set(*this, data); } |
| | | |
| | | template<typename T> |
| | | ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper<ValueType, T>::Set(*this, data, allocator); } |
| | | |
| | | //@} |
| | | |
| | | //! Generate events of this value to a Handler. |
| | | /*! This function adopts the GoF visitor pattern. |
| | | Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. |
| | | It can also be used to deep clone this value via GenericDocument, which is also a Handler. |
| | | \tparam Handler type of handler. |
| | | \param handler An object implementing concept Handler. |
| | | */ |
| | | template <typename Handler> |
| | | bool Accept(Handler& handler) const { |
| | | switch(GetType()) { |
| | | case kNullType: return handler.Null(); |
| | | case kFalseType: return handler.Bool(false); |
| | | case kTrueType: return handler.Bool(true); |
| | | |
| | | case kObjectType: |
| | | if (RAPIDJSON_UNLIKELY(!handler.StartObject())) |
| | | return false; |
| | | for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { |
| | | RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. |
| | | if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) |
| | | return false; |
| | | if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) |
| | | return false; |
| | | } |
| | | return handler.EndObject(data_.o.size); |
| | | |
| | | case kArrayType: |
| | | if (RAPIDJSON_UNLIKELY(!handler.StartArray())) |
| | | return false; |
| | | for (const GenericValue* v = Begin(); v != End(); ++v) |
| | | if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) |
| | | return false; |
| | | return handler.EndArray(data_.a.size); |
| | | |
| | | case kStringType: |
| | | return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); |
| | | |
| | | default: |
| | | RAPIDJSON_ASSERT(GetType() == kNumberType); |
| | | if (IsDouble()) return handler.Double(data_.n.d); |
| | | else if (IsInt()) return handler.Int(data_.n.i.i); |
| | | else if (IsUint()) return handler.Uint(data_.n.u.u); |
| | | else if (IsInt64()) return handler.Int64(data_.n.i64); |
| | | else return handler.Uint64(data_.n.u64); |
| | | } |
| | | } |
| | | |
| | | private: |
| | | template <typename, typename> friend class GenericValue; |
| | | template <typename, typename, typename> friend class GenericDocument; |
| | | |
| | | enum { |
| | | kBoolFlag = 0x0008, |
| | | kNumberFlag = 0x0010, |
| | | kIntFlag = 0x0020, |
| | | kUintFlag = 0x0040, |
| | | kInt64Flag = 0x0080, |
| | | kUint64Flag = 0x0100, |
| | | kDoubleFlag = 0x0200, |
| | | kStringFlag = 0x0400, |
| | | kCopyFlag = 0x0800, |
| | | kInlineStrFlag = 0x1000, |
| | | |
| | | // 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, |
| | | kObjectFlag = kObjectType, |
| | | kArrayFlag = kArrayType, |
| | | |
| | | kTypeMask = 0x07 |
| | | }; |
| | | |
| | | static const SizeType kDefaultArrayCapacity = 16; |
| | | static const SizeType kDefaultObjectCapacity = 16; |
| | | |
| | | struct Flag { |
| | | #if RAPIDJSON_48BITPOINTER_OPTIMIZATION |
| | | char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer |
| | | #elif RAPIDJSON_64BIT |
| | | char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes |
| | | #else |
| | | char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes |
| | | #endif |
| | | uint16_t flags; |
| | | }; |
| | | |
| | | struct String { |
| | | SizeType length; |
| | | SizeType hashcode; //!< reserved |
| | | const Ch* str; |
| | | }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode |
| | | |
| | | // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars |
| | | // (excluding the terminating zero) and store a value to determine the length of the contained |
| | | // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string |
| | | // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as |
| | | // the string terminator as well. For getting the string length back from that value just use |
| | | // "MaxSize - str[LenPos]". |
| | | // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, |
| | | // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). |
| | | struct ShortString { |
| | | enum { MaxChars = sizeof(static_cast<Flag*>(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; |
| | | Ch str[MaxChars]; |
| | | |
| | | inline static bool Usable(SizeType len) { return (MaxSize >= len); } |
| | | inline void SetLength(SizeType len) { str[LenPos] = static_cast<Ch>(MaxSize - len); } |
| | | inline SizeType GetLength() const { return static_cast<SizeType>(MaxSize - str[LenPos]); } |
| | | }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode |
| | | |
| | | // By using proper binary layout, retrieval of different integer types do not need conversions. |
| | | union Number { |
| | | #if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN |
| | | struct I { |
| | | int i; |
| | | char padding[4]; |
| | | }i; |
| | | struct U { |
| | | unsigned u; |
| | | char padding2[4]; |
| | | }u; |
| | | #else |
| | | struct I { |
| | | char padding[4]; |
| | | int i; |
| | | }i; |
| | | struct U { |
| | | char padding2[4]; |
| | | unsigned u; |
| | | }u; |
| | | #endif |
| | | int64_t i64; |
| | | uint64_t u64; |
| | | double d; |
| | | }; // 8 bytes |
| | | |
| | | struct ObjectData { |
| | | SizeType size; |
| | | SizeType capacity; |
| | | Member* members; |
| | | }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode |
| | | |
| | | struct ArrayData { |
| | | SizeType size; |
| | | SizeType capacity; |
| | | GenericValue* elements; |
| | | }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode |
| | | |
| | | union Data { |
| | | String s; |
| | | ShortString ss; |
| | | Number n; |
| | | ObjectData o; |
| | | ArrayData a; |
| | | Flag f; |
| | | }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION |
| | | |
| | | 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); } |
| | | RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } |
| | | 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); } |
| | | |
| | | // 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(e, values, count * sizeof(GenericValue)); |
| | | } |
| | | else |
| | | SetElementsPointer(0); |
| | | data_.a.size = data_.a.capacity = count; |
| | | } |
| | | |
| | | //! Initialize this value as object with initial data, without calling destructor. |
| | | void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { |
| | | data_.f.flags = kObjectFlag; |
| | | if (count) { |
| | | Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member))); |
| | | SetMembersPointer(m); |
| | | std::memcpy(m, members, count * sizeof(Member)); |
| | | } |
| | | else |
| | | SetMembersPointer(0); |
| | | data_.o.size = data_.o.capacity = count; |
| | | } |
| | | |
| | | //! Initialize this value as constant string, without calling destructor. |
| | | void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { |
| | | data_.f.flags = kConstStringFlag; |
| | | SetStringPointer(s); |
| | | data_.s.length = s.length; |
| | | } |
| | | |
| | | //! Initialize this value as copy string with initial data, without calling destructor. |
| | | void SetStringRaw(StringRefType s, Allocator& allocator) { |
| | | Ch* str = 0; |
| | | if (ShortString::Usable(s.length)) { |
| | | data_.f.flags = kShortStringFlag; |
| | | data_.ss.SetLength(s.length); |
| | | str = data_.ss.str; |
| | | } else { |
| | | data_.f.flags = kCopyStringFlag; |
| | | data_.s.length = s.length; |
| | | str = static_cast<Ch *>(allocator.Malloc((s.length + 1) * sizeof(Ch))); |
| | | SetStringPointer(str); |
| | | } |
| | | std::memcpy(str, s, s.length * sizeof(Ch)); |
| | | str[s.length] = '\0'; |
| | | } |
| | | |
| | | //! Assignment without calling destructor |
| | | void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { |
| | | data_ = rhs.data_; |
| | | // data_.f.flags = rhs.data_.f.flags; |
| | | rhs.data_.f.flags = kNullFlag; |
| | | } |
| | | |
| | | template <typename SourceAllocator> |
| | | bool StringEqual(const GenericValue<Encoding, SourceAllocator>& rhs) const { |
| | | RAPIDJSON_ASSERT(IsString()); |
| | | RAPIDJSON_ASSERT(rhs.IsString()); |
| | | |
| | | const SizeType len1 = GetStringLength(); |
| | | const SizeType len2 = rhs.GetStringLength(); |
| | | if(len1 != len2) { return false; } |
| | | |
| | | const Ch* const str1 = GetString(); |
| | | const Ch* const str2 = rhs.GetString(); |
| | | if(str1 == str2) { return true; } // fast path for constant string |
| | | |
| | | return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); |
| | | } |
| | | |
| | | Data data_; |
| | | }; |
| | | |
| | | //! GenericValue with UTF8 encoding |
| | | typedef GenericValue<UTF8<> > Value; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // GenericDocument |
| | | |
| | | //! A document for parsing JSON text as DOM. |
| | | /*! |
| | | \note implements Handler concept |
| | | \tparam Encoding Encoding for both parsing and string storage. |
| | | \tparam Allocator Allocator for allocating memory for the DOM |
| | | \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> |
| | | 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. |
| | | |
| | | //! Constructor |
| | | /*! Creates an empty document of specified type. |
| | | \param type Mandatory type of object to create. |
| | | \param allocator Optional allocator for allocating memory. |
| | | \param stackCapacity Optional initial capacity of stack in bytes. |
| | | \param stackAllocator Optional allocator for allocating memory for stack. |
| | | */ |
| | | explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : |
| | | GenericValue<Encoding, Allocator>(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() |
| | | { |
| | | if (!allocator_) |
| | | ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); |
| | | } |
| | | |
| | | //! Constructor |
| | | /*! Creates an empty document which type is Null. |
| | | \param allocator Optional allocator for allocating memory. |
| | | \param stackCapacity Optional initial capacity of stack in bytes. |
| | | \param stackAllocator Optional allocator for allocating memory for stack. |
| | | */ |
| | | GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : |
| | | allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() |
| | | { |
| | | if (!allocator_) |
| | | ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | //! Move constructor in C++11 |
| | | GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT |
| | | : ValueType(std::forward<ValueType>(rhs)), // explicit cast to avoid prohibited move from Document |
| | | allocator_(rhs.allocator_), |
| | | ownAllocator_(rhs.ownAllocator_), |
| | | stack_(std::move(rhs.stack_)), |
| | | parseResult_(rhs.parseResult_) |
| | | { |
| | | rhs.allocator_ = 0; |
| | | rhs.ownAllocator_ = 0; |
| | | rhs.parseResult_ = ParseResult(); |
| | | } |
| | | #endif |
| | | |
| | | ~GenericDocument() { |
| | | Destroy(); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | //! Move assignment in C++11 |
| | | GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT |
| | | { |
| | | // The cast to ValueType is necessary here, because otherwise it would |
| | | // attempt to call GenericValue's templated assignment operator. |
| | | ValueType::operator=(std::forward<ValueType>(rhs)); |
| | | |
| | | // Calling the destructor here would prematurely call stack_'s destructor |
| | | Destroy(); |
| | | |
| | | allocator_ = rhs.allocator_; |
| | | ownAllocator_ = rhs.ownAllocator_; |
| | | stack_ = std::move(rhs.stack_); |
| | | parseResult_ = rhs.parseResult_; |
| | | |
| | | rhs.allocator_ = 0; |
| | | rhs.ownAllocator_ = 0; |
| | | rhs.parseResult_ = ParseResult(); |
| | | |
| | | return *this; |
| | | } |
| | | #endif |
| | | |
| | | //! Exchange the contents of this document with those of another. |
| | | /*! |
| | | \param rhs Another document. |
| | | \note Constant complexity. |
| | | \see GenericValue::Swap |
| | | */ |
| | | GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { |
| | | ValueType::Swap(rhs); |
| | | stack_.Swap(rhs.stack_); |
| | | internal::Swap(allocator_, rhs.allocator_); |
| | | internal::Swap(ownAllocator_, rhs.ownAllocator_); |
| | | internal::Swap(parseResult_, rhs.parseResult_); |
| | | 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: |
| | | \code |
| | | void swap(MyClass& a, MyClass& b) { |
| | | using std::swap; |
| | | swap(a.doc, b.doc); |
| | | // ... |
| | | } |
| | | \endcode |
| | | \see Swap() |
| | | */ |
| | | friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } |
| | | |
| | | //! Populate this document by a generator which produces SAX events. |
| | | /*! \tparam Generator A functor with <tt>bool f(Handler)</tt> prototype. |
| | | \param g Generator functor which sends SAX events to the parameter. |
| | | \return The document itself for fluent API. |
| | | */ |
| | | template <typename Generator> |
| | | GenericDocument& Populate(Generator& g) { |
| | | ClearStackOnExit scope(*this); |
| | | if (g(*this)) { |
| | | RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object |
| | | ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | //!@name Parse from stream |
| | | //!@{ |
| | | |
| | | //! Parse JSON text from an input stream (with Encoding conversion) |
| | | /*! \tparam parseFlags Combination of \ref ParseFlag. |
| | | \tparam SourceEncoding Encoding of input stream |
| | | \tparam InputStream Type of input stream, implementing Stream concept |
| | | \param is Input stream to be parsed. |
| | | \return The document itself for fluent API. |
| | | */ |
| | | template <unsigned parseFlags, typename SourceEncoding, typename InputStream> |
| | | GenericDocument& ParseStream(InputStream& is) { |
| | | GenericReader<SourceEncoding, Encoding, StackAllocator> reader( |
| | | stack_.HasAllocator() ? &stack_.GetAllocator() : 0); |
| | | ClearStackOnExit scope(*this); |
| | | parseResult_ = reader.template Parse<parseFlags>(is, *this); |
| | | if (parseResult_) { |
| | | RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object |
| | | ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | //! Parse JSON text from an input stream |
| | | /*! \tparam parseFlags Combination of \ref ParseFlag. |
| | | \tparam InputStream Type of input stream, implementing Stream concept |
| | | \param is Input stream to be parsed. |
| | | \return The document itself for fluent API. |
| | | */ |
| | | template <unsigned parseFlags, typename InputStream> |
| | | GenericDocument& ParseStream(InputStream& is) { |
| | | return ParseStream<parseFlags, Encoding, InputStream>(is); |
| | | } |
| | | |
| | | //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) |
| | | /*! \tparam InputStream Type of input stream, implementing Stream concept |
| | | \param is Input stream to be parsed. |
| | | \return The document itself for fluent API. |
| | | */ |
| | | template <typename InputStream> |
| | | GenericDocument& ParseStream(InputStream& is) { |
| | | return ParseStream<kParseDefaultFlags, Encoding, InputStream>(is); |
| | | } |
| | | //!@} |
| | | |
| | | //!@name Parse in-place from mutable string |
| | | //!@{ |
| | | |
| | | //! Parse JSON text from a mutable string |
| | | /*! \tparam parseFlags Combination of \ref ParseFlag. |
| | | \param str Mutable zero-terminated string to be parsed. |
| | | \return The document itself for fluent API. |
| | | */ |
| | | template <unsigned parseFlags> |
| | | GenericDocument& ParseInsitu(Ch* str) { |
| | | GenericInsituStringStream<Encoding> s(str); |
| | | return ParseStream<parseFlags | kParseInsituFlag>(s); |
| | | } |
| | | |
| | | //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) |
| | | /*! \param str Mutable zero-terminated string to be parsed. |
| | | \return The document itself for fluent API. |
| | | */ |
| | | GenericDocument& ParseInsitu(Ch* str) { |
| | | return ParseInsitu<kParseDefaultFlags>(str); |
| | | } |
| | | //!@} |
| | | |
| | | //!@name Parse from read-only string |
| | | //!@{ |
| | | |
| | | //! Parse JSON text from a read-only string (with Encoding conversion) |
| | | /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). |
| | | \tparam SourceEncoding Transcoding from input Encoding |
| | | \param str Read-only zero-terminated string to be parsed. |
| | | */ |
| | | template <unsigned parseFlags, typename SourceEncoding> |
| | | GenericDocument& Parse(const typename SourceEncoding::Ch* str) { |
| | | RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); |
| | | GenericStringStream<SourceEncoding> s(str); |
| | | return ParseStream<parseFlags, SourceEncoding>(s); |
| | | } |
| | | |
| | | //! Parse JSON text from a read-only string |
| | | /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). |
| | | \param str Read-only zero-terminated string to be parsed. |
| | | */ |
| | | template <unsigned parseFlags> |
| | | GenericDocument& Parse(const Ch* str) { |
| | | return Parse<parseFlags, Encoding>(str); |
| | | } |
| | | |
| | | //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) |
| | | /*! \param str Read-only zero-terminated string to be parsed. |
| | | */ |
| | | GenericDocument& Parse(const Ch* str) { |
| | | return Parse<kParseDefaultFlags>(str); |
| | | } |
| | | |
| | | template <unsigned parseFlags, typename SourceEncoding> |
| | | GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { |
| | | RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); |
| | | MemoryStream ms(reinterpret_cast<const char*>(str), length * sizeof(typename SourceEncoding::Ch)); |
| | | EncodedInputStream<SourceEncoding, MemoryStream> is(ms); |
| | | ParseStream<parseFlags, SourceEncoding>(is); |
| | | return *this; |
| | | } |
| | | |
| | | template <unsigned parseFlags> |
| | | GenericDocument& Parse(const Ch* str, size_t length) { |
| | | return Parse<parseFlags, Encoding>(str, length); |
| | | } |
| | | |
| | | GenericDocument& Parse(const Ch* str, size_t length) { |
| | | return Parse<kParseDefaultFlags>(str, length); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template <unsigned parseFlags, typename SourceEncoding> |
| | | GenericDocument& Parse(const std::basic_string<typename SourceEncoding::Ch>& str) { |
| | | // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) |
| | | return Parse<parseFlags, SourceEncoding>(str.c_str()); |
| | | } |
| | | |
| | | template <unsigned parseFlags> |
| | | GenericDocument& Parse(const std::basic_string<Ch>& str) { |
| | | return Parse<parseFlags, Encoding>(str.c_str()); |
| | | } |
| | | |
| | | GenericDocument& Parse(const std::basic_string<Ch>& str) { |
| | | return Parse<kParseDefaultFlags>(str); |
| | | } |
| | | #endif // RAPIDJSON_HAS_STDSTRING |
| | | |
| | | //!@} |
| | | |
| | | //!@name Handling parse errors |
| | | //!@{ |
| | | |
| | | //! Whether a parse error has occured in the last parsing. |
| | | bool HasParseError() const { return parseResult_.IsError(); } |
| | | |
| | | //! Get the \ref ParseErrorCode of last parsing. |
| | | ParseErrorCode GetParseError() const { return parseResult_.Code(); } |
| | | |
| | | //! Get the position of last parsing error in input, 0 otherwise. |
| | | size_t GetErrorOffset() const { return parseResult_.Offset(); } |
| | | |
| | | //! Implicit conversion to get the last parse result |
| | | #ifndef __clang // -Wdocumentation |
| | | /*! \return \ref ParseResult of the last parse operation |
| | | |
| | | \code |
| | | Document doc; |
| | | ParseResult ok = doc.Parse(json); |
| | | if (!ok) |
| | | printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); |
| | | \endcode |
| | | */ |
| | | #endif |
| | | operator ParseResult() const { return parseResult_; } |
| | | //!@} |
| | | |
| | | //! Get the allocator of this document. |
| | | Allocator& GetAllocator() { |
| | | RAPIDJSON_ASSERT(allocator_); |
| | | return *allocator_; |
| | | } |
| | | |
| | | //! Get the capacity of stack in bytes. |
| | | size_t GetStackCapacity() const { return stack_.GetCapacity(); } |
| | | |
| | | private: |
| | | // clear stack on any exit from ParseStream, e.g. due to exception |
| | | struct ClearStackOnExit { |
| | | explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} |
| | | ~ClearStackOnExit() { d_.ClearStack(); } |
| | | private: |
| | | ClearStackOnExit(const ClearStackOnExit&); |
| | | ClearStackOnExit& operator=(const ClearStackOnExit&); |
| | | GenericDocument& d_; |
| | | }; |
| | | |
| | | // callers of the following private Handler functions |
| | | // template <typename,typename,typename> friend class GenericReader; // for parsing |
| | | template <typename, typename> friend class GenericValue; // for deep copying |
| | | |
| | | public: |
| | | // Implementation of Handler |
| | | bool Null() { new (stack_.template Push<ValueType>()) ValueType(); return true; } |
| | | bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); return true; } |
| | | bool Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; } |
| | | bool Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; } |
| | | bool Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; } |
| | | bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; } |
| | | bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; } |
| | | |
| | | bool RawNumber(const Ch* str, SizeType length, bool copy) { |
| | | if (copy) |
| | | new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator()); |
| | | else |
| | | new (stack_.template Push<ValueType>()) ValueType(str, length); |
| | | return true; |
| | | } |
| | | |
| | | bool String(const Ch* str, SizeType length, bool copy) { |
| | | if (copy) |
| | | new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator()); |
| | | else |
| | | new (stack_.template Push<ValueType>()) ValueType(str, length); |
| | | return true; |
| | | } |
| | | |
| | | bool StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); return true; } |
| | | |
| | | bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } |
| | | |
| | | bool EndObject(SizeType memberCount) { |
| | | typename ValueType::Member* members = stack_.template Pop<typename ValueType::Member>(memberCount); |
| | | stack_.template Top<ValueType>()->SetObjectRaw(members, memberCount, GetAllocator()); |
| | | return true; |
| | | } |
| | | |
| | | bool StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); return true; } |
| | | |
| | | bool EndArray(SizeType elementCount) { |
| | | ValueType* elements = stack_.template Pop<ValueType>(elementCount); |
| | | stack_.template Top<ValueType>()->SetArrayRaw(elements, elementCount, GetAllocator()); |
| | | return true; |
| | | } |
| | | |
| | | private: |
| | | //! Prohibit copying |
| | | GenericDocument(const GenericDocument&); |
| | | //! Prohibit assignment |
| | | GenericDocument& operator=(const GenericDocument&); |
| | | |
| | | void ClearStack() { |
| | | if (Allocator::kNeedFree) |
| | | while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) |
| | | (stack_.template Pop<ValueType>(1))->~ValueType(); |
| | | else |
| | | stack_.Clear(); |
| | | stack_.ShrinkToFit(); |
| | | } |
| | | |
| | | void Destroy() { |
| | | RAPIDJSON_DELETE(ownAllocator_); |
| | | } |
| | | |
| | | static const size_t kDefaultStackCapacity = 1024; |
| | | Allocator* allocator_; |
| | | Allocator* ownAllocator_; |
| | | internal::Stack<StackAllocator> stack_; |
| | | ParseResult parseResult_; |
| | | }; |
| | | |
| | | //! GenericDocument with UTF8 encoding |
| | | typedef GenericDocument<UTF8<> > Document; |
| | | |
| | | //! Helper class for accessing Value of array type. |
| | | /*! |
| | | Instance of this helper class is obtained by \c GenericValue::GetArray(). |
| | | In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. |
| | | */ |
| | | template <bool Const, typename ValueT> |
| | | class GenericArray { |
| | | public: |
| | | typedef GenericArray<true, ValueT> ConstArray; |
| | | typedef GenericArray<false, ValueT> Array; |
| | | typedef ValueT PlainType; |
| | | typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType; |
| | | typedef ValueType* ValueIterator; // This may be const or non-const iterator |
| | | typedef const ValueT* ConstValueIterator; |
| | | typedef typename ValueType::AllocatorType AllocatorType; |
| | | typedef typename ValueType::StringRefType StringRefType; |
| | | |
| | | template <typename, typename> |
| | | friend class GenericValue; |
| | | |
| | | GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} |
| | | GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } |
| | | ~GenericArray() {} |
| | | |
| | | SizeType Size() const { return value_.Size(); } |
| | | SizeType Capacity() const { return value_.Capacity(); } |
| | | bool Empty() const { return value_.Empty(); } |
| | | void Clear() const { value_.Clear(); } |
| | | ValueType& operator[](SizeType index) const { return value_[index]; } |
| | | ValueIterator Begin() const { return value_.Begin(); } |
| | | ValueIterator End() const { return value_.End(); } |
| | | GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } |
| | | GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } |
| | | #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } |
| | | template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } |
| | | GenericArray PopBack() const { value_.PopBack(); return *this; } |
| | | ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } |
| | | ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RANGE_FOR |
| | | ValueIterator begin() const { return value_.Begin(); } |
| | | ValueIterator end() const { return value_.End(); } |
| | | #endif |
| | | |
| | | private: |
| | | GenericArray(); |
| | | GenericArray(ValueType& value) : value_(value) {} |
| | | ValueType& value_; |
| | | }; |
| | | |
| | | //! Helper class for accessing Value of object type. |
| | | /*! |
| | | Instance of this helper class is obtained by \c GenericValue::GetObject(). |
| | | In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. |
| | | */ |
| | | template <bool Const, typename ValueT> |
| | | class GenericObject { |
| | | public: |
| | | typedef GenericObject<true, ValueT> ConstObject; |
| | | typedef GenericObject<false, ValueT> Object; |
| | | typedef ValueT PlainType; |
| | | typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType; |
| | | typedef GenericMemberIterator<Const, typename ValueT::EncodingType, typename ValueT::AllocatorType> MemberIterator; // This may be const or non-const iterator |
| | | typedef GenericMemberIterator<true, typename ValueT::EncodingType, typename ValueT::AllocatorType> ConstMemberIterator; |
| | | typedef typename ValueType::AllocatorType AllocatorType; |
| | | typedef typename ValueType::StringRefType StringRefType; |
| | | typedef typename ValueType::EncodingType EncodingType; |
| | | typedef typename ValueType::Ch Ch; |
| | | |
| | | template <typename, typename> |
| | | friend class GenericValue; |
| | | |
| | | GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} |
| | | GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } |
| | | ~GenericObject() {} |
| | | |
| | | 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]; } |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | ValueType& operator[](const std::basic_string<Ch>& name) const { return value_[name]; } |
| | | #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); } |
| | | #endif |
| | | template <typename SourceAllocator> bool HasMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.HasMember(name); } |
| | | MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } |
| | | template <typename SourceAllocator> MemberIterator FindMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.FindMember(name); } |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | MemberIterator FindMember(const std::basic_string<Ch>& name) const { return value_.FindMember(name); } |
| | | #endif |
| | | GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } |
| | | GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | GenericObject AddMember(ValueType& name, std::basic_string<Ch>& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } |
| | | #endif |
| | | template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } |
| | | GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } |
| | | GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } |
| | | GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } |
| | | #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | 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() { 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); } |
| | | #endif |
| | | template <typename SourceAllocator> bool RemoveMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.RemoveMember(name); } |
| | | MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } |
| | | MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } |
| | | MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } |
| | | bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | bool EraseMember(const std::basic_string<Ch>& name) const { return EraseMember(ValueType(StringRef(name))); } |
| | | #endif |
| | | template <typename SourceAllocator> bool EraseMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.EraseMember(name); } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RANGE_FOR |
| | | MemberIterator begin() const { return value_.MemberBegin(); } |
| | | MemberIterator end() const { return value_.MemberEnd(); } |
| | | #endif |
| | | |
| | | private: |
| | | GenericObject(); |
| | | GenericObject(ValueType& value) : value_(value) {} |
| | | ValueType& value_; |
| | | }; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | RAPIDJSON_DIAG_POP |
| | | |
| | | #endif // RAPIDJSON_DOCUMENT_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_ENCODEDSTREAM_H_ |
| | | #define RAPIDJSON_ENCODEDSTREAM_H_ |
| | | |
| | | #include "stream.h" |
| | | #include "memorystream.h" |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(effc++) |
| | | #endif |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! Input byte stream wrapper with a statically bound encoding. |
| | | /*! |
| | | \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. |
| | | \tparam InputByteStream Type of input byte stream. For example, FileReadStream. |
| | | */ |
| | | template <typename Encoding, typename InputByteStream> |
| | | class EncodedInputStream { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | public: |
| | | typedef typename Encoding::Ch Ch; |
| | | |
| | | EncodedInputStream(InputByteStream& is) : is_(is) { |
| | | current_ = Encoding::TakeBOM(is_); |
| | | } |
| | | |
| | | Ch Peek() const { return current_; } |
| | | Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } |
| | | size_t Tell() const { return is_.Tell(); } |
| | | |
| | | // Not implemented |
| | | void Put(Ch) { RAPIDJSON_ASSERT(false); } |
| | | void Flush() { RAPIDJSON_ASSERT(false); } |
| | | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } |
| | | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } |
| | | |
| | | private: |
| | | EncodedInputStream(const EncodedInputStream&); |
| | | EncodedInputStream& operator=(const EncodedInputStream&); |
| | | |
| | | InputByteStream& is_; |
| | | Ch current_; |
| | | }; |
| | | |
| | | //! Specialized for UTF8 MemoryStream. |
| | | template <> |
| | | class EncodedInputStream<UTF8<>, MemoryStream> { |
| | | public: |
| | | typedef UTF8<>::Ch Ch; |
| | | |
| | | EncodedInputStream(MemoryStream& is) : is_(is) { |
| | | if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take(); |
| | | if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take(); |
| | | if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take(); |
| | | } |
| | | Ch Peek() const { return is_.Peek(); } |
| | | Ch Take() { return is_.Take(); } |
| | | size_t Tell() const { return is_.Tell(); } |
| | | |
| | | // Not implemented |
| | | void Put(Ch) {} |
| | | void Flush() {} |
| | | Ch* PutBegin() { return 0; } |
| | | size_t PutEnd(Ch*) { return 0; } |
| | | |
| | | MemoryStream& is_; |
| | | |
| | | private: |
| | | EncodedInputStream(const EncodedInputStream&); |
| | | EncodedInputStream& operator=(const EncodedInputStream&); |
| | | }; |
| | | |
| | | //! Output byte stream wrapper with statically bound encoding. |
| | | /*! |
| | | \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. |
| | | \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. |
| | | */ |
| | | template <typename Encoding, typename OutputByteStream> |
| | | class EncodedOutputStream { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | public: |
| | | typedef typename Encoding::Ch Ch; |
| | | |
| | | EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { |
| | | if (putBOM) |
| | | Encoding::PutBOM(os_); |
| | | } |
| | | |
| | | void Put(Ch c) { Encoding::Put(os_, c); } |
| | | void Flush() { os_.Flush(); } |
| | | |
| | | // Not implemented |
| | | Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} |
| | | Ch Take() { RAPIDJSON_ASSERT(false); return 0;} |
| | | size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } |
| | | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } |
| | | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } |
| | | |
| | | private: |
| | | EncodedOutputStream(const EncodedOutputStream&); |
| | | EncodedOutputStream& operator=(const EncodedOutputStream&); |
| | | |
| | | OutputByteStream& os_; |
| | | }; |
| | | |
| | | #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x |
| | | |
| | | //! Input stream wrapper with dynamically bound encoding and automatic encoding detection. |
| | | /*! |
| | | \tparam CharType Type of character for reading. |
| | | \tparam InputByteStream type of input byte stream to be wrapped. |
| | | */ |
| | | template <typename CharType, typename InputByteStream> |
| | | class AutoUTFInputStream { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | public: |
| | | typedef CharType Ch; |
| | | |
| | | //! Constructor. |
| | | /*! |
| | | \param is input stream to be wrapped. |
| | | \param type UTF encoding type if it is not detected from the stream. |
| | | */ |
| | | AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { |
| | | RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); |
| | | DetectType(); |
| | | static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; |
| | | takeFunc_ = f[type_]; |
| | | current_ = takeFunc_(*is_); |
| | | } |
| | | |
| | | UTFType GetType() const { return type_; } |
| | | bool HasBOM() const { return hasBOM_; } |
| | | |
| | | Ch Peek() const { return current_; } |
| | | Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } |
| | | size_t Tell() const { return is_->Tell(); } |
| | | |
| | | // Not implemented |
| | | void Put(Ch) { RAPIDJSON_ASSERT(false); } |
| | | void Flush() { RAPIDJSON_ASSERT(false); } |
| | | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } |
| | | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } |
| | | |
| | | private: |
| | | AutoUTFInputStream(const AutoUTFInputStream&); |
| | | AutoUTFInputStream& operator=(const AutoUTFInputStream&); |
| | | |
| | | // Detect encoding type with BOM or RFC 4627 |
| | | void DetectType() { |
| | | // BOM (Byte Order Mark): |
| | | // 00 00 FE FF UTF-32BE |
| | | // FF FE 00 00 UTF-32LE |
| | | // FE FF UTF-16BE |
| | | // FF FE UTF-16LE |
| | | // EF BB BF UTF-8 |
| | | |
| | | const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4()); |
| | | if (!c) |
| | | return; |
| | | |
| | | unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); |
| | | hasBOM_ = false; |
| | | if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } |
| | | else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } |
| | | else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } |
| | | else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } |
| | | else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } |
| | | |
| | | // RFC 4627: Section 3 |
| | | // "Since the first two characters of a JSON text will always be ASCII |
| | | // characters [RFC0020], it is possible to determine whether an octet |
| | | // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking |
| | | // at the pattern of nulls in the first four octets." |
| | | // 00 00 00 xx UTF-32BE |
| | | // 00 xx 00 xx UTF-16BE |
| | | // xx 00 00 00 UTF-32LE |
| | | // xx 00 xx 00 UTF-16LE |
| | | // xx xx xx xx UTF-8 |
| | | |
| | | if (!hasBOM_) { |
| | | 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; |
| | | case 0x01: type_ = kUTF32LE; break; |
| | | case 0x05: type_ = kUTF16LE; break; |
| | | case 0x0F: type_ = kUTF8; break; |
| | | default: break; // Use type defined by user. |
| | | } |
| | | } |
| | | |
| | | // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. |
| | | if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); |
| | | if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); |
| | | } |
| | | |
| | | typedef Ch (*TakeFunc)(InputByteStream& is); |
| | | InputByteStream* is_; |
| | | UTFType type_; |
| | | Ch current_; |
| | | TakeFunc takeFunc_; |
| | | bool hasBOM_; |
| | | }; |
| | | |
| | | //! Output stream wrapper with dynamically bound encoding and automatic encoding detection. |
| | | /*! |
| | | \tparam CharType Type of character for writing. |
| | | \tparam OutputByteStream type of output byte stream to be wrapped. |
| | | */ |
| | | template <typename CharType, typename OutputByteStream> |
| | | class AutoUTFOutputStream { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | public: |
| | | typedef CharType Ch; |
| | | |
| | | //! Constructor. |
| | | /*! |
| | | \param os output stream to be wrapped. |
| | | \param type UTF encoding type. |
| | | \param putBOM Whether to write BOM at the beginning of the stream. |
| | | */ |
| | | AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { |
| | | RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); |
| | | |
| | | // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. |
| | | if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); |
| | | if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); |
| | | |
| | | static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; |
| | | putFunc_ = f[type_]; |
| | | |
| | | if (putBOM) |
| | | PutBOM(); |
| | | } |
| | | |
| | | UTFType GetType() const { return type_; } |
| | | |
| | | void Put(Ch c) { putFunc_(*os_, c); } |
| | | void Flush() { os_->Flush(); } |
| | | |
| | | // Not implemented |
| | | Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} |
| | | Ch Take() { RAPIDJSON_ASSERT(false); return 0;} |
| | | size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } |
| | | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } |
| | | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } |
| | | |
| | | private: |
| | | AutoUTFOutputStream(const AutoUTFOutputStream&); |
| | | AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); |
| | | |
| | | void PutBOM() { |
| | | typedef void (*PutBOMFunc)(OutputByteStream&); |
| | | static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; |
| | | f[type_](*os_); |
| | | } |
| | | |
| | | typedef void (*PutFunc)(OutputByteStream&, Ch); |
| | | |
| | | OutputByteStream* os_; |
| | | UTFType type_; |
| | | PutFunc putFunc_; |
| | | }; |
| | | |
| | | #undef RAPIDJSON_ENCODINGS_FUNC |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_FILESTREAM_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_ENCODINGS_H_ |
| | | #define RAPIDJSON_ENCODINGS_H_ |
| | | |
| | | #include "rapidjson.h" |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data |
| | | RAPIDJSON_DIAG_OFF(4702) // unreachable code |
| | | #elif defined(__GNUC__) |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(effc++) |
| | | RAPIDJSON_DIAG_OFF(overflow) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Encoding |
| | | |
| | | /*! \class rapidjson::Encoding |
| | | \brief Concept for encoding of Unicode characters. |
| | | |
| | | \code |
| | | concept Encoding { |
| | | typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. |
| | | |
| | | enum { supportUnicode = 1 }; // or 0 if not supporting unicode |
| | | |
| | | //! \brief Encode a Unicode codepoint to an output stream. |
| | | //! \param os Output stream. |
| | | //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. |
| | | template<typename OutputStream> |
| | | static void Encode(OutputStream& os, unsigned codepoint); |
| | | |
| | | //! \brief Decode a Unicode codepoint from an input stream. |
| | | //! \param is Input stream. |
| | | //! \param codepoint Output of the unicode codepoint. |
| | | //! \return true if a valid codepoint can be decoded from the stream. |
| | | template <typename InputStream> |
| | | static bool Decode(InputStream& is, unsigned* codepoint); |
| | | |
| | | //! \brief Validate one Unicode codepoint from an encoded stream. |
| | | //! \param is Input stream to obtain codepoint. |
| | | //! \param os Output for copying one codepoint. |
| | | //! \return true if it is valid. |
| | | //! \note This function just validating and copying the codepoint without actually decode it. |
| | | template <typename InputStream, typename OutputStream> |
| | | static bool Validate(InputStream& is, OutputStream& os); |
| | | |
| | | // The following functions are deal with byte streams. |
| | | |
| | | //! Take a character from input byte stream, skip BOM if exist. |
| | | template <typename InputByteStream> |
| | | static CharType TakeBOM(InputByteStream& is); |
| | | |
| | | //! Take a character from input byte stream. |
| | | template <typename InputByteStream> |
| | | static Ch Take(InputByteStream& is); |
| | | |
| | | //! Put BOM to output byte stream. |
| | | template <typename OutputByteStream> |
| | | static void PutBOM(OutputByteStream& os); |
| | | |
| | | //! Put a character to output byte stream. |
| | | template <typename OutputByteStream> |
| | | static void Put(OutputByteStream& os, Ch c); |
| | | }; |
| | | \endcode |
| | | */ |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // UTF8 |
| | | |
| | | //! UTF-8 encoding. |
| | | /*! http://en.wikipedia.org/wiki/UTF-8 |
| | | http://tools.ietf.org/html/rfc3629 |
| | | \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. |
| | | \note implements Encoding concept |
| | | */ |
| | | template<typename CharType = char> |
| | | struct UTF8 { |
| | | typedef CharType Ch; |
| | | |
| | | enum { supportUnicode = 1 }; |
| | | |
| | | template<typename OutputStream> |
| | | static void Encode(OutputStream& os, unsigned codepoint) { |
| | | if (codepoint <= 0x7F) |
| | | os.Put(static_cast<Ch>(codepoint & 0xFF)); |
| | | else if (codepoint <= 0x7FF) { |
| | | os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF))); |
| | | os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F)))); |
| | | } |
| | | else if (codepoint <= 0xFFFF) { |
| | | os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF))); |
| | | os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); |
| | | os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F))); |
| | | } |
| | | else { |
| | | RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); |
| | | os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF))); |
| | | os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F))); |
| | | os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); |
| | | os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F))); |
| | | } |
| | | } |
| | | |
| | | template<typename OutputStream> |
| | | static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { |
| | | if (codepoint <= 0x7F) |
| | | PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF)); |
| | | else if (codepoint <= 0x7FF) { |
| | | PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF))); |
| | | PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F)))); |
| | | } |
| | | else if (codepoint <= 0xFFFF) { |
| | | PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF))); |
| | | PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); |
| | | PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F))); |
| | | } |
| | | else { |
| | | RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); |
| | | PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF))); |
| | | PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F))); |
| | | PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); |
| | | PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F))); |
| | | } |
| | | } |
| | | |
| | | 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) |
| | | typename InputStream::Ch c = is.Take(); |
| | | if (!(c & 0x80)) { |
| | | *codepoint = static_cast<unsigned char>(c); |
| | | return true; |
| | | } |
| | | |
| | | unsigned char type = GetRange(static_cast<unsigned char>(c)); |
| | | if (type >= 32) { |
| | | *codepoint = 0; |
| | | } else { |
| | | *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; |
| | | default: return false; |
| | | } |
| | | #undef COPY |
| | | #undef TRANS |
| | | #undef 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) |
| | | Ch c; |
| | | 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; |
| | | default: return false; |
| | | } |
| | | #undef COPY |
| | | #undef TRANS |
| | | #undef TAIL |
| | | } |
| | | |
| | | static unsigned char GetRange(unsigned char c) { |
| | | // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ |
| | | // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. |
| | | static const unsigned char type[] = { |
| | | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| | | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| | | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| | | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| | | 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, |
| | | 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, |
| | | 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, |
| | | 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, |
| | | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, |
| | | 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, |
| | | }; |
| | | return type[c]; |
| | | } |
| | | |
| | | template <typename InputByteStream> |
| | | static CharType TakeBOM(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | typename InputByteStream::Ch c = Take(is); |
| | | if (static_cast<unsigned char>(c) != 0xEFu) return c; |
| | | c = is.Take(); |
| | | if (static_cast<unsigned char>(c) != 0xBBu) return c; |
| | | c = is.Take(); |
| | | if (static_cast<unsigned char>(c) != 0xBFu) return c; |
| | | c = is.Take(); |
| | | return c; |
| | | } |
| | | |
| | | template <typename InputByteStream> |
| | | static Ch Take(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | return static_cast<Ch>(is.Take()); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void PutBOM(OutputByteStream& os) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu)); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void Put(OutputByteStream& os, Ch c) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(c)); |
| | | } |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // UTF16 |
| | | |
| | | //! UTF-16 encoding. |
| | | /*! http://en.wikipedia.org/wiki/UTF-16 |
| | | http://tools.ietf.org/html/rfc2781 |
| | | \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. |
| | | \note implements Encoding concept |
| | | |
| | | \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. |
| | | For streaming, use UTF16LE and UTF16BE, which handle endianness. |
| | | */ |
| | | template<typename CharType = wchar_t> |
| | | struct UTF16 { |
| | | typedef CharType Ch; |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); |
| | | |
| | | enum { supportUnicode = 1 }; |
| | | |
| | | template<typename OutputStream> |
| | | static void Encode(OutputStream& os, unsigned codepoint) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); |
| | | if (codepoint <= 0xFFFF) { |
| | | RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair |
| | | os.Put(static_cast<typename OutputStream::Ch>(codepoint)); |
| | | } |
| | | else { |
| | | RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); |
| | | unsigned v = codepoint - 0x10000; |
| | | os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800)); |
| | | os.Put(static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00)); |
| | | } |
| | | } |
| | | |
| | | |
| | | template<typename OutputStream> |
| | | static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); |
| | | if (codepoint <= 0xFFFF) { |
| | | RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair |
| | | PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint)); |
| | | } |
| | | else { |
| | | RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); |
| | | unsigned v = codepoint - 0x10000; |
| | | PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800)); |
| | | PutUnsafe(os, static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00)); |
| | | } |
| | | } |
| | | |
| | | template <typename InputStream> |
| | | static bool Decode(InputStream& is, unsigned* codepoint) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); |
| | | typename InputStream::Ch c = is.Take(); |
| | | if (c < 0xD800 || c > 0xDFFF) { |
| | | *codepoint = static_cast<unsigned>(c); |
| | | return true; |
| | | } |
| | | else if (c <= 0xDBFF) { |
| | | *codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10; |
| | | c = is.Take(); |
| | | *codepoint |= (static_cast<unsigned>(c) & 0x3FF); |
| | | *codepoint += 0x10000; |
| | | return c >= 0xDC00 && c <= 0xDFFF; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | template <typename InputStream, typename OutputStream> |
| | | static bool Validate(InputStream& is, OutputStream& os) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); |
| | | typename InputStream::Ch c; |
| | | os.Put(static_cast<typename OutputStream::Ch>(c = is.Take())); |
| | | if (c < 0xD800 || c > 0xDFFF) |
| | | return true; |
| | | else if (c <= 0xDBFF) { |
| | | os.Put(c = is.Take()); |
| | | return c >= 0xDC00 && c <= 0xDFFF; |
| | | } |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | //! UTF-16 little endian encoding. |
| | | template<typename CharType = wchar_t> |
| | | struct UTF16LE : UTF16<CharType> { |
| | | template <typename InputByteStream> |
| | | static CharType TakeBOM(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | CharType c = Take(is); |
| | | return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c; |
| | | } |
| | | |
| | | template <typename InputByteStream> |
| | | static CharType Take(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | unsigned c = static_cast<uint8_t>(is.Take()); |
| | | c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8; |
| | | return static_cast<CharType>(c); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void PutBOM(OutputByteStream& os) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void Put(OutputByteStream& os, CharType c) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu)); |
| | | } |
| | | }; |
| | | |
| | | //! UTF-16 big endian encoding. |
| | | template<typename CharType = wchar_t> |
| | | struct UTF16BE : UTF16<CharType> { |
| | | template <typename InputByteStream> |
| | | static CharType TakeBOM(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | CharType c = Take(is); |
| | | return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c; |
| | | } |
| | | |
| | | template <typename InputByteStream> |
| | | 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<unsigned>(static_cast<uint8_t>(is.Take())); |
| | | return static_cast<CharType>(c); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void PutBOM(OutputByteStream& os) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void Put(OutputByteStream& os, CharType c) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu)); |
| | | } |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // UTF32 |
| | | |
| | | //! UTF-32 encoding. |
| | | /*! http://en.wikipedia.org/wiki/UTF-32 |
| | | \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. |
| | | \note implements Encoding concept |
| | | |
| | | \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. |
| | | For streaming, use UTF32LE and UTF32BE, which handle endianness. |
| | | */ |
| | | template<typename CharType = unsigned> |
| | | struct UTF32 { |
| | | typedef CharType Ch; |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); |
| | | |
| | | enum { supportUnicode = 1 }; |
| | | |
| | | template<typename OutputStream> |
| | | static void Encode(OutputStream& os, unsigned codepoint) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); |
| | | RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); |
| | | os.Put(codepoint); |
| | | } |
| | | |
| | | template<typename OutputStream> |
| | | static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); |
| | | RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); |
| | | PutUnsafe(os, codepoint); |
| | | } |
| | | |
| | | template <typename InputStream> |
| | | static bool Decode(InputStream& is, unsigned* codepoint) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); |
| | | Ch c = is.Take(); |
| | | *codepoint = c; |
| | | return c <= 0x10FFFF; |
| | | } |
| | | |
| | | template <typename InputStream, typename OutputStream> |
| | | static bool Validate(InputStream& is, OutputStream& os) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); |
| | | Ch c; |
| | | os.Put(c = is.Take()); |
| | | return c <= 0x10FFFF; |
| | | } |
| | | }; |
| | | |
| | | //! UTF-32 little endian enocoding. |
| | | template<typename CharType = unsigned> |
| | | struct UTF32LE : UTF32<CharType> { |
| | | template <typename InputByteStream> |
| | | static CharType TakeBOM(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | CharType c = Take(is); |
| | | return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c; |
| | | } |
| | | |
| | | template <typename InputByteStream> |
| | | static CharType Take(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | unsigned c = static_cast<uint8_t>(is.Take()); |
| | | c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8; |
| | | c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16; |
| | | c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24; |
| | | return static_cast<CharType>(c); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void PutBOM(OutputByteStream& os) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0x00u)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0x00u)); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void Put(OutputByteStream& os, CharType c) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu)); |
| | | } |
| | | }; |
| | | |
| | | //! UTF-32 big endian encoding. |
| | | template<typename CharType = unsigned> |
| | | struct UTF32BE : UTF32<CharType> { |
| | | template <typename InputByteStream> |
| | | static CharType TakeBOM(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | CharType c = Take(is); |
| | | return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c; |
| | | } |
| | | |
| | | template <typename InputByteStream> |
| | | static CharType Take(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24; |
| | | c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16; |
| | | c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8; |
| | | c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())); |
| | | return static_cast<CharType>(c); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void PutBOM(OutputByteStream& os) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0x00u)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0x00u)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void Put(OutputByteStream& os, CharType c) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu)); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu)); |
| | | } |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // ASCII |
| | | |
| | | //! ASCII encoding. |
| | | /*! http://en.wikipedia.org/wiki/ASCII |
| | | \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. |
| | | \note implements Encoding concept |
| | | */ |
| | | template<typename CharType = char> |
| | | struct ASCII { |
| | | typedef CharType Ch; |
| | | |
| | | enum { supportUnicode = 0 }; |
| | | |
| | | template<typename OutputStream> |
| | | static void Encode(OutputStream& os, unsigned codepoint) { |
| | | RAPIDJSON_ASSERT(codepoint <= 0x7F); |
| | | os.Put(static_cast<Ch>(codepoint & 0xFF)); |
| | | } |
| | | |
| | | template<typename OutputStream> |
| | | static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { |
| | | RAPIDJSON_ASSERT(codepoint <= 0x7F); |
| | | PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF)); |
| | | } |
| | | |
| | | template <typename InputStream> |
| | | static bool Decode(InputStream& is, unsigned* codepoint) { |
| | | uint8_t c = static_cast<uint8_t>(is.Take()); |
| | | *codepoint = c; |
| | | return c <= 0X7F; |
| | | } |
| | | |
| | | template <typename InputStream, typename OutputStream> |
| | | static bool Validate(InputStream& is, OutputStream& os) { |
| | | uint8_t c = static_cast<uint8_t>(is.Take()); |
| | | os.Put(static_cast<typename OutputStream::Ch>(c)); |
| | | return c <= 0x7F; |
| | | } |
| | | |
| | | template <typename InputByteStream> |
| | | static CharType TakeBOM(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | uint8_t c = static_cast<uint8_t>(Take(is)); |
| | | return static_cast<Ch>(c); |
| | | } |
| | | |
| | | template <typename InputByteStream> |
| | | static Ch Take(InputByteStream& is) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); |
| | | return static_cast<Ch>(is.Take()); |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void PutBOM(OutputByteStream& os) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | (void)os; |
| | | } |
| | | |
| | | template <typename OutputByteStream> |
| | | static void Put(OutputByteStream& os, Ch c) { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); |
| | | os.Put(static_cast<typename OutputByteStream::Ch>(c)); |
| | | } |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // AutoUTF |
| | | |
| | | //! Runtime-specified UTF encoding type of a stream. |
| | | enum UTFType { |
| | | kUTF8 = 0, //!< UTF-8. |
| | | kUTF16LE = 1, //!< UTF-16 little endian. |
| | | kUTF16BE = 2, //!< UTF-16 big endian. |
| | | kUTF32LE = 3, //!< UTF-32 little endian. |
| | | kUTF32BE = 4 //!< UTF-32 big endian. |
| | | }; |
| | | |
| | | //! Dynamically select encoding according to stream's runtime-specified UTF encoding type. |
| | | /*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). |
| | | */ |
| | | template<typename CharType> |
| | | struct AutoUTF { |
| | | typedef CharType Ch; |
| | | |
| | | enum { supportUnicode = 1 }; |
| | | |
| | | #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x |
| | | |
| | | template<typename OutputStream> |
| | | 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> |
| | | 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> |
| | | 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> |
| | | 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); |
| | | } |
| | | |
| | | #undef RAPIDJSON_ENCODINGS_FUNC |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Transcoder |
| | | |
| | | //! Encoding conversion. |
| | | 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> |
| | | static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { |
| | | unsigned codepoint; |
| | | if (!SourceEncoding::Decode(is, &codepoint)) |
| | | return false; |
| | | TargetEncoding::Encode(os, codepoint); |
| | | return true; |
| | | } |
| | | |
| | | template<typename InputStream, typename OutputStream> |
| | | static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { |
| | | unsigned codepoint; |
| | | if (!SourceEncoding::Decode(is, &codepoint)) |
| | | return false; |
| | | TargetEncoding::EncodeUnsafe(os, codepoint); |
| | | return true; |
| | | } |
| | | |
| | | //! Validate one Unicode codepoint from an encoded stream. |
| | | template<typename InputStream, typename OutputStream> |
| | | static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { |
| | | return Transcode(is, os); // Since source/target encoding is different, must transcode. |
| | | } |
| | | }; |
| | | |
| | | // Forward declaration. |
| | | template<typename Stream> |
| | | inline void PutUnsafe(Stream& stream, typename Stream::Ch c); |
| | | |
| | | //! Specialization of Transcoder with same source and target encoding. |
| | | template<typename Encoding> |
| | | struct Transcoder<Encoding, Encoding> { |
| | | template<typename InputStream, typename OutputStream> |
| | | 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> |
| | | 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> |
| | | 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) |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_ENCODINGS_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_ERROR_EN_H_ |
| | | #define RAPIDJSON_ERROR_EN_H_ |
| | | |
| | | #include "error.h" |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(switch-enum) |
| | | RAPIDJSON_DIAG_OFF(covered-switch-default) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! Maps error code of parsing into error message. |
| | | /*! |
| | | \ingroup RAPIDJSON_ERRORS |
| | | \param parseErrorCode Error code obtained in parsing. |
| | | \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* GetParseError_En(ParseErrorCode parseErrorCode) { |
| | | switch (parseErrorCode) { |
| | | case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); |
| | | |
| | | 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."); |
| | | case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); |
| | | case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); |
| | | case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); |
| | | case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); |
| | | |
| | | case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); |
| | | case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); |
| | | case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); |
| | | |
| | | case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); |
| | | case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); |
| | | |
| | | default: return RAPIDJSON_ERROR_STRING("Unknown error."); |
| | | } |
| | | } |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_ERROR_EN_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_ERROR_ERROR_H_ |
| | | #define RAPIDJSON_ERROR_ERROR_H_ |
| | | |
| | | #include "../rapidjson.h" |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | #endif |
| | | |
| | | /*! \file error.h */ |
| | | |
| | | /*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_ERROR_CHARTYPE |
| | | |
| | | //! Character type of error messages. |
| | | /*! \ingroup RAPIDJSON_ERRORS |
| | | The default character type is \c char. |
| | | On Windows, user can define this macro as \c TCHAR for supporting both |
| | | unicode/non-unicode settings. |
| | | */ |
| | | #ifndef RAPIDJSON_ERROR_CHARTYPE |
| | | #define RAPIDJSON_ERROR_CHARTYPE char |
| | | #endif |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_ERROR_STRING |
| | | |
| | | //! Macro for converting string literial 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 |
| | | unicode/non-unicode settings. |
| | | */ |
| | | #ifndef RAPIDJSON_ERROR_STRING |
| | | #define RAPIDJSON_ERROR_STRING(x) x |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // ParseErrorCode |
| | | |
| | | //! Error code of parsing. |
| | | /*! \ingroup RAPIDJSON_ERRORS |
| | | \see GenericReader::Parse, GenericReader::GetParseErrorCode |
| | | */ |
| | | enum ParseErrorCode { |
| | | kParseErrorNone = 0, //!< No error. |
| | | |
| | | kParseErrorDocumentEmpty, //!< The document is empty. |
| | | kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. |
| | | |
| | | kParseErrorValueInvalid, //!< Invalid value. |
| | | |
| | | kParseErrorObjectMissName, //!< Missing a name for object member. |
| | | kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. |
| | | kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. |
| | | |
| | | kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. |
| | | |
| | | kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. |
| | | kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. |
| | | kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. |
| | | kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. |
| | | kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. |
| | | |
| | | kParseErrorNumberTooBig, //!< Number too big to be stored in double. |
| | | kParseErrorNumberMissFraction, //!< Miss fraction part in number. |
| | | kParseErrorNumberMissExponent, //!< Miss exponent in number. |
| | | |
| | | kParseErrorTermination, //!< Parsing was terminated. |
| | | kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. |
| | | }; |
| | | |
| | | //! Result of parsing (wraps ParseErrorCode) |
| | | /*! |
| | | \ingroup RAPIDJSON_ERRORS |
| | | \code |
| | | Document doc; |
| | | ParseResult ok = doc.Parse("[42]"); |
| | | if (!ok) { |
| | | fprintf(stderr, "JSON parse error: %s (%u)", |
| | | GetParseError_En(ok.Code()), ok.Offset()); |
| | | exit(EXIT_FAILURE); |
| | | } |
| | | \endcode |
| | | \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) {} |
| | | //! Constructor to set an error. |
| | | ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} |
| | | |
| | | //! Get the error code. |
| | | ParseErrorCode Code() const { return code_; } |
| | | //! Get the error offset, if \ref IsError(), 0 otherwise. |
| | | size_t Offset() const { return offset_; } |
| | | |
| | | //! 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; } |
| | | |
| | | bool operator==(const ParseResult& that) const { return code_ == that.code_; } |
| | | 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. |
| | | void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } |
| | | |
| | | private: |
| | | ParseErrorCode code_; |
| | | size_t offset_; |
| | | }; |
| | | |
| | | //! Function pointer type of GetParseError(). |
| | | /*! \ingroup RAPIDJSON_ERRORS |
| | | |
| | | This is the prototype for \c GetParseError_X(), where \c X is a locale. |
| | | User can dynamically change locale in runtime, e.g.: |
| | | \code |
| | | GetParseErrorFunc GetParseError = GetParseError_En; // or whatever |
| | | const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); |
| | | \endcode |
| | | */ |
| | | typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_ERROR_ERROR_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_FILEREADSTREAM_H_ |
| | | #define RAPIDJSON_FILEREADSTREAM_H_ |
| | | |
| | | #include "stream.h" |
| | | #include <cstdio> |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | RAPIDJSON_DIAG_OFF(unreachable-code) |
| | | RAPIDJSON_DIAG_OFF(missing-noreturn) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! File byte stream for input using fread(). |
| | | /*! |
| | | \note implements Stream concept |
| | | */ |
| | | class FileReadStream { |
| | | public: |
| | | typedef char Ch; //!< Character type (byte). |
| | | |
| | | //! Constructor. |
| | | /*! |
| | | \param fp File pointer opened for read. |
| | | \param buffer user-supplied buffer. |
| | | \param bufferSize size of buffer in bytes. Must >=4 bytes. |
| | | */ |
| | | FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { |
| | | RAPIDJSON_ASSERT(fp_ != 0); |
| | | RAPIDJSON_ASSERT(bufferSize >= 4); |
| | | Read(); |
| | | } |
| | | |
| | | 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_); } |
| | | |
| | | // Not implemented |
| | | void Put(Ch) { 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 { |
| | | return (current_ + 4 <= bufferLast_) ? current_ : 0; |
| | | } |
| | | |
| | | private: |
| | | void Read() { |
| | | if (current_ < bufferLast_) |
| | | ++current_; |
| | | else if (!eof_) { |
| | | count_ += readCount_; |
| | | readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); |
| | | bufferLast_ = buffer_ + readCount_ - 1; |
| | | current_ = buffer_; |
| | | |
| | | if (readCount_ < bufferSize_) { |
| | | buffer_[readCount_] = '\0'; |
| | | ++bufferLast_; |
| | | eof_ = true; |
| | | } |
| | | } |
| | | } |
| | | |
| | | std::FILE* fp_; |
| | | Ch *buffer_; |
| | | size_t bufferSize_; |
| | | Ch *bufferLast_; |
| | | Ch *current_; |
| | | size_t readCount_; |
| | | size_t count_; //!< Number of characters read |
| | | bool eof_; |
| | | }; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_FILESTREAM_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_FILEWRITESTREAM_H_ |
| | | #define RAPIDJSON_FILEWRITESTREAM_H_ |
| | | |
| | | #include "stream.h" |
| | | #include <cstdio> |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(unreachable-code) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! Wrapper of C file stream for output using fwrite(). |
| | | /*! |
| | | \note implements Stream concept |
| | | */ |
| | | class FileWriteStream { |
| | | public: |
| | | typedef char Ch; //!< Character type. Only support char. |
| | | |
| | | FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { |
| | | RAPIDJSON_ASSERT(fp_ != 0); |
| | | } |
| | | |
| | | void Put(char c) { |
| | | if (current_ >= bufferEnd_) |
| | | Flush(); |
| | | |
| | | *current_++ = c; |
| | | } |
| | | |
| | | void PutN(char c, size_t n) { |
| | | size_t avail = static_cast<size_t>(bufferEnd_ - current_); |
| | | while (n > avail) { |
| | | std::memset(current_, c, avail); |
| | | current_ += avail; |
| | | Flush(); |
| | | n -= avail; |
| | | avail = static_cast<size_t>(bufferEnd_ - current_); |
| | | } |
| | | |
| | | if (n > 0) { |
| | | std::memset(current_, c, n); |
| | | current_ += n; |
| | | } |
| | | } |
| | | |
| | | void Flush() { |
| | | if (current_ != buffer_) { |
| | | 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 |
| | | } |
| | | current_ = buffer_; |
| | | } |
| | | } |
| | | |
| | | // Not implemented |
| | | char Peek() const { RAPIDJSON_ASSERT(false); return 0; } |
| | | char Take() { RAPIDJSON_ASSERT(false); return 0; } |
| | | size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } |
| | | char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } |
| | | size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } |
| | | |
| | | private: |
| | | // Prohibit copy constructor & assignment operator. |
| | | FileWriteStream(const FileWriteStream&); |
| | | FileWriteStream& operator=(const FileWriteStream&); |
| | | |
| | | std::FILE* fp_; |
| | | char *buffer_; |
| | | char *bufferEnd_; |
| | | char *current_; |
| | | }; |
| | | |
| | | //! Implement specialized version of PutN() with memset() for better performance. |
| | | template<> |
| | | inline void PutN(FileWriteStream& stream, char c, size_t n) { |
| | | stream.PutN(c, n); |
| | | } |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_FILESTREAM_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_FWD_H_ |
| | | #define RAPIDJSON_FWD_H_ |
| | | |
| | | #include "rapidjson.h" |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | // encodings.h |
| | | |
| | | template<typename CharType> struct UTF8; |
| | | template<typename CharType> struct UTF16; |
| | | template<typename CharType> struct UTF16BE; |
| | | template<typename CharType> struct UTF16LE; |
| | | template<typename CharType> struct UTF32; |
| | | template<typename CharType> struct UTF32BE; |
| | | template<typename CharType> struct UTF32LE; |
| | | template<typename CharType> struct ASCII; |
| | | template<typename CharType> struct AutoUTF; |
| | | |
| | | template<typename SourceEncoding, typename TargetEncoding> |
| | | struct Transcoder; |
| | | |
| | | // allocators.h |
| | | |
| | | class CrtAllocator; |
| | | |
| | | template <typename BaseAllocator> |
| | | class MemoryPoolAllocator; |
| | | |
| | | // stream.h |
| | | |
| | | template <typename Encoding> |
| | | struct GenericStringStream; |
| | | |
| | | typedef GenericStringStream<UTF8<char> > StringStream; |
| | | |
| | | template <typename Encoding> |
| | | struct GenericInsituStringStream; |
| | | |
| | | typedef GenericInsituStringStream<UTF8<char> > InsituStringStream; |
| | | |
| | | // stringbuffer.h |
| | | |
| | | template <typename Encoding, typename Allocator> |
| | | class GenericStringBuffer; |
| | | |
| | | typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer; |
| | | |
| | | // filereadstream.h |
| | | |
| | | class FileReadStream; |
| | | |
| | | // filewritestream.h |
| | | |
| | | class FileWriteStream; |
| | | |
| | | // memorybuffer.h |
| | | |
| | | template <typename Allocator> |
| | | struct GenericMemoryBuffer; |
| | | |
| | | typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer; |
| | | |
| | | // memorystream.h |
| | | |
| | | struct MemoryStream; |
| | | |
| | | // reader.h |
| | | |
| | | template<typename Encoding, typename Derived> |
| | | struct BaseReaderHandler; |
| | | |
| | | template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator> |
| | | class GenericReader; |
| | | |
| | | typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader; |
| | | |
| | | // writer.h |
| | | |
| | | template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags> |
| | | class Writer; |
| | | |
| | | // prettywriter.h |
| | | |
| | | template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags> |
| | | class PrettyWriter; |
| | | |
| | | // document.h |
| | | |
| | | template <typename Encoding, typename Allocator> |
| | | struct GenericMember; |
| | | |
| | | template <bool Const, typename Encoding, typename Allocator> |
| | | class GenericMemberIterator; |
| | | |
| | | template<typename CharType> |
| | | struct GenericStringRef; |
| | | |
| | | template <typename Encoding, typename Allocator> |
| | | class GenericValue; |
| | | |
| | | typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value; |
| | | |
| | | template <typename Encoding, typename Allocator, typename StackAllocator> |
| | | class GenericDocument; |
| | | |
| | | typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document; |
| | | |
| | | // pointer.h |
| | | |
| | | template <typename ValueType, typename Allocator> |
| | | class GenericPointer; |
| | | |
| | | typedef GenericPointer<Value, CrtAllocator> Pointer; |
| | | |
| | | // schema.h |
| | | |
| | | template <typename SchemaDocumentType> |
| | | class IGenericRemoteSchemaDocumentProvider; |
| | | |
| | | template <typename ValueT, typename Allocator> |
| | | class GenericSchemaDocument; |
| | | |
| | | typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument; |
| | | typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider; |
| | | |
| | | template < |
| | | typename SchemaDocumentType, |
| | | typename OutputHandler, |
| | | typename StateAllocator> |
| | | class GenericSchemaValidator; |
| | | |
| | | typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_RAPIDJSONFWD_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_BIGINTEGER_H_ |
| | | #define RAPIDJSON_BIGINTEGER_H_ |
| | | |
| | | #include "../rapidjson.h" |
| | | |
| | | #if defined(_MSC_VER) && defined(_M_AMD64) |
| | | #include <intrin.h> // for _umul128 |
| | | #pragma intrinsic(_umul128) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | class BigInteger { |
| | | public: |
| | | typedef uint64_t Type; |
| | | |
| | | BigInteger(const BigInteger& rhs) : count_(rhs.count_) { |
| | | std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); |
| | | } |
| | | |
| | | explicit BigInteger(uint64_t u) : count_(1) { |
| | | digits_[0] = u; |
| | | } |
| | | |
| | | BigInteger(const char* decimals, size_t length) : count_(1) { |
| | | RAPIDJSON_ASSERT(length > 0); |
| | | digits_[0] = 0; |
| | | size_t i = 0; |
| | | const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 |
| | | while (length >= kMaxDigitPerIteration) { |
| | | AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); |
| | | length -= kMaxDigitPerIteration; |
| | | i += kMaxDigitPerIteration; |
| | | } |
| | | |
| | | if (length > 0) |
| | | AppendDecimal64(decimals + i, decimals + i + length); |
| | | } |
| | | |
| | | BigInteger& operator=(const BigInteger &rhs) |
| | | { |
| | | if (this != &rhs) { |
| | | count_ = rhs.count_; |
| | | std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | BigInteger& operator=(uint64_t u) { |
| | | digits_[0] = u; |
| | | count_ = 1; |
| | | return *this; |
| | | } |
| | | |
| | | BigInteger& operator+=(uint64_t u) { |
| | | Type backup = digits_[0]; |
| | | digits_[0] += u; |
| | | for (size_t i = 0; i < count_ - 1; i++) { |
| | | if (digits_[i] >= backup) |
| | | return *this; // no carry |
| | | backup = digits_[i + 1]; |
| | | digits_[i + 1] += 1; |
| | | } |
| | | |
| | | // Last carry |
| | | if (digits_[count_ - 1] < backup) |
| | | PushBack(1); |
| | | |
| | | return *this; |
| | | } |
| | | |
| | | BigInteger& operator*=(uint64_t u) { |
| | | if (u == 0) return *this = 0; |
| | | if (u == 1) return *this; |
| | | if (*this == 1) return *this = u; |
| | | |
| | | uint64_t k = 0; |
| | | for (size_t i = 0; i < count_; i++) { |
| | | uint64_t hi; |
| | | digits_[i] = MulAdd64(digits_[i], u, k, &hi); |
| | | k = hi; |
| | | } |
| | | |
| | | if (k > 0) |
| | | PushBack(k); |
| | | |
| | | return *this; |
| | | } |
| | | |
| | | BigInteger& operator*=(uint32_t u) { |
| | | if (u == 0) return *this = 0; |
| | | if (u == 1) return *this; |
| | | if (*this == 1) return *this = u; |
| | | |
| | | uint64_t k = 0; |
| | | for (size_t i = 0; i < count_; i++) { |
| | | const uint64_t c = digits_[i] >> 32; |
| | | const uint64_t d = digits_[i] & 0xFFFFFFFF; |
| | | const uint64_t uc = u * c; |
| | | const uint64_t ud = u * d; |
| | | const uint64_t p0 = ud + k; |
| | | const uint64_t p1 = uc + (p0 >> 32); |
| | | digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); |
| | | k = p1 >> 32; |
| | | } |
| | | |
| | | if (k > 0) |
| | | PushBack(k); |
| | | |
| | | return *this; |
| | | } |
| | | |
| | | BigInteger& operator<<=(size_t shift) { |
| | | if (IsZero() || shift == 0) return *this; |
| | | |
| | | size_t offset = shift / kTypeBit; |
| | | size_t interShift = shift % kTypeBit; |
| | | RAPIDJSON_ASSERT(count_ + offset <= kCapacity); |
| | | |
| | | if (interShift == 0) { |
| | | std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); |
| | | count_ += offset; |
| | | } |
| | | else { |
| | | digits_[count_] = 0; |
| | | for (size_t i = count_; i > 0; i--) |
| | | digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); |
| | | digits_[offset] = digits_[0] << interShift; |
| | | count_ += offset; |
| | | if (digits_[count_]) |
| | | count_++; |
| | | } |
| | | |
| | | std::memset(digits_, 0, offset * sizeof(Type)); |
| | | |
| | | return *this; |
| | | } |
| | | |
| | | bool operator==(const BigInteger& rhs) const { |
| | | return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; |
| | | } |
| | | |
| | | bool operator==(const Type rhs) const { |
| | | return count_ == 1 && digits_[0] == rhs; |
| | | } |
| | | |
| | | BigInteger& MultiplyPow5(unsigned exp) { |
| | | static const uint32_t kPow5[12] = { |
| | | 5, |
| | | 5 * 5, |
| | | 5 * 5 * 5, |
| | | 5 * 5 * 5 * 5, |
| | | 5 * 5 * 5 * 5 * 5, |
| | | 5 * 5 * 5 * 5 * 5 * 5, |
| | | 5 * 5 * 5 * 5 * 5 * 5 * 5, |
| | | 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, |
| | | 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, |
| | | 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, |
| | | 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, |
| | | 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 |
| | | }; |
| | | if (exp == 0) return *this; |
| | | for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 |
| | | for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13 |
| | | if (exp > 0) *this *= kPow5[exp - 1]; |
| | | return *this; |
| | | } |
| | | |
| | | // Compute absolute difference of this and rhs. |
| | | // Assume this != rhs |
| | | bool Difference(const BigInteger& rhs, BigInteger* out) const { |
| | | int cmp = Compare(rhs); |
| | | RAPIDJSON_ASSERT(cmp != 0); |
| | | const BigInteger *a, *b; // Makes a > b |
| | | bool ret; |
| | | if (cmp < 0) { a = &rhs; b = this; ret = true; } |
| | | else { a = this; b = &rhs; ret = false; } |
| | | |
| | | Type borrow = 0; |
| | | for (size_t i = 0; i < a->count_; i++) { |
| | | Type d = a->digits_[i] - borrow; |
| | | if (i < b->count_) |
| | | d -= b->digits_[i]; |
| | | borrow = (d > a->digits_[i]) ? 1 : 0; |
| | | out->digits_[i] = d; |
| | | if (d != 0) |
| | | out->count_ = i + 1; |
| | | } |
| | | |
| | | return ret; |
| | | } |
| | | |
| | | int Compare(const BigInteger& rhs) const { |
| | | if (count_ != rhs.count_) |
| | | return count_ < rhs.count_ ? -1 : 1; |
| | | |
| | | for (size_t i = count_; i-- > 0;) |
| | | if (digits_[i] != rhs.digits_[i]) |
| | | return digits_[i] < rhs.digits_[i] ? -1 : 1; |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | size_t GetCount() const { return count_; } |
| | | Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } |
| | | bool IsZero() const { return count_ == 1 && digits_[0] == 0; } |
| | | |
| | | private: |
| | | void AppendDecimal64(const char* begin, const char* end) { |
| | | uint64_t u = ParseUint64(begin, end); |
| | | if (IsZero()) |
| | | *this = u; |
| | | else { |
| | | unsigned exp = static_cast<unsigned>(end - begin); |
| | | (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u |
| | | } |
| | | } |
| | | |
| | | void PushBack(Type digit) { |
| | | RAPIDJSON_ASSERT(count_ < kCapacity); |
| | | digits_[count_++] = digit; |
| | | } |
| | | |
| | | static uint64_t ParseUint64(const char* begin, const char* 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'); |
| | | } |
| | | return r; |
| | | } |
| | | |
| | | // Assume a * b + k < 2^128 |
| | | static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { |
| | | #if defined(_MSC_VER) && defined(_M_AMD64) |
| | | uint64_t low = _umul128(a, b, outHigh) + k; |
| | | if (low < k) |
| | | (*outHigh)++; |
| | | return low; |
| | | #elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) |
| | | __extension__ typedef unsigned __int128 uint128; |
| | | uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b); |
| | | p += k; |
| | | *outHigh = static_cast<uint64_t>(p >> 64); |
| | | return static_cast<uint64_t>(p); |
| | | #else |
| | | const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; |
| | | uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; |
| | | x1 += (x0 >> 32); // can't give carry |
| | | x1 += x2; |
| | | if (x1 < x2) |
| | | x3 += (static_cast<uint64_t>(1) << 32); |
| | | uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); |
| | | uint64_t hi = x3 + (x1 >> 32); |
| | | |
| | | lo += k; |
| | | if (lo < k) |
| | | hi++; |
| | | *outHigh = hi; |
| | | return lo; |
| | | #endif |
| | | } |
| | | |
| | | static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 |
| | | static const size_t kCapacity = kBitCount / sizeof(Type); |
| | | static const size_t kTypeBit = sizeof(Type) * 8; |
| | | |
| | | Type digits_[kCapacity]; |
| | | size_t count_; |
| | | }; |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_BIGINTEGER_H_ |
New file |
| | |
| | | // 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 |
| | | // 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. |
| | | |
| | | // This is a C++ header-only implementation of Grisu2 algorithm from the publication: |
| | | // Loitsch, Florian. "Printing floating-point numbers quickly and accurately with |
| | | // integers." ACM Sigplan Notices 45.6 (2010): 233-243. |
| | | |
| | | #ifndef RAPIDJSON_DIYFP_H_ |
| | | #define RAPIDJSON_DIYFP_H_ |
| | | |
| | | #include "../rapidjson.h" |
| | | |
| | | #if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) |
| | | #include <intrin.h> |
| | | #pragma intrinsic(_BitScanReverse64) |
| | | #pragma intrinsic(_umul128) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(effc++) |
| | | #endif |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | #endif |
| | | |
| | | struct DiyFp { |
| | | DiyFp() : f(), e() {} |
| | | |
| | | DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} |
| | | |
| | | explicit DiyFp(double d) { |
| | | union { |
| | | double d; |
| | | uint64_t u64; |
| | | } u = { d }; |
| | | |
| | | int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize); |
| | | uint64_t significand = (u.u64 & kDpSignificandMask); |
| | | if (biased_e != 0) { |
| | | f = significand + kDpHiddenBit; |
| | | e = biased_e - kDpExponentBias; |
| | | } |
| | | else { |
| | | f = significand; |
| | | e = kDpMinExponent + 1; |
| | | } |
| | | } |
| | | |
| | | DiyFp operator-(const DiyFp& rhs) const { |
| | | return DiyFp(f - rhs.f, e); |
| | | } |
| | | |
| | | DiyFp operator*(const DiyFp& rhs) const { |
| | | #if defined(_MSC_VER) && defined(_M_AMD64) |
| | | uint64_t h; |
| | | uint64_t l = _umul128(f, rhs.f, &h); |
| | | if (l & (uint64_t(1) << 63)) // rounding |
| | | h++; |
| | | return DiyFp(h, e + rhs.e + 64); |
| | | #elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) |
| | | __extension__ typedef unsigned __int128 uint128; |
| | | uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f); |
| | | uint64_t h = static_cast<uint64_t>(p >> 64); |
| | | uint64_t l = static_cast<uint64_t>(p); |
| | | if (l & (uint64_t(1) << 63)) // rounding |
| | | h++; |
| | | return DiyFp(h, e + rhs.e + 64); |
| | | #else |
| | | const uint64_t M32 = 0xFFFFFFFF; |
| | | const uint64_t a = f >> 32; |
| | | const uint64_t b = f & M32; |
| | | const uint64_t c = rhs.f >> 32; |
| | | const uint64_t d = rhs.f & M32; |
| | | const uint64_t ac = a * c; |
| | | const uint64_t bc = b * c; |
| | | const uint64_t ad = a * d; |
| | | const uint64_t bd = b * d; |
| | | uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); |
| | | tmp += 1U << 31; /// mult_round |
| | | return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); |
| | | #endif |
| | | } |
| | | |
| | | 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); |
| | | 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 { |
| | | DiyFp res = *this; |
| | | while (!(res.f & (kDpHiddenBit << 1))) { |
| | | res.f <<= 1; |
| | | res.e--; |
| | | } |
| | | res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); |
| | | res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); |
| | | return res; |
| | | } |
| | | |
| | | void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { |
| | | DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); |
| | | DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); |
| | | mi.f <<= mi.e - pl.e; |
| | | mi.e = pl.e; |
| | | *plus = pl; |
| | | *minus = mi; |
| | | } |
| | | |
| | | double ToDouble() const { |
| | | union { |
| | | double d; |
| | | uint64_t u64; |
| | | }u; |
| | | 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; |
| | | } |
| | | |
| | | static const int kDiySignificandSize = 64; |
| | | static const int kDpSignificandSize = 52; |
| | | static const int kDpExponentBias = 0x3FF + kDpSignificandSize; |
| | | static const int kDpMaxExponent = 0x7FF - kDpExponentBias; |
| | | static const int kDpMinExponent = -kDpExponentBias; |
| | | static const int kDpDenormalExponent = -kDpExponentBias + 1; |
| | | static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); |
| | | static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); |
| | | static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); |
| | | |
| | | uint64_t f; |
| | | int e; |
| | | }; |
| | | |
| | | inline DiyFp GetCachedPowerByIndex(size_t index) { |
| | | // 10^-348, 10^-340, ..., 10^340 |
| | | static const uint64_t kCachedPowers_F[] = { |
| | | RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), |
| | | RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), |
| | | RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), |
| | | RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), |
| | | RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), |
| | | RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), |
| | | RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), |
| | | RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), |
| | | RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), |
| | | RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), |
| | | RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), |
| | | RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), |
| | | RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), |
| | | RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), |
| | | RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), |
| | | RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), |
| | | RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), |
| | | RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), |
| | | RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), |
| | | RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), |
| | | RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), |
| | | RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), |
| | | RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), |
| | | RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), |
| | | RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), |
| | | RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), |
| | | RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), |
| | | RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), |
| | | RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), |
| | | RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), |
| | | RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), |
| | | RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), |
| | | RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), |
| | | RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), |
| | | RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), |
| | | RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), |
| | | RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), |
| | | RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), |
| | | RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), |
| | | RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), |
| | | RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), |
| | | RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), |
| | | RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), |
| | | RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) |
| | | }; |
| | | static const int16_t kCachedPowers_E[] = { |
| | | -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, |
| | | -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, |
| | | -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, |
| | | -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, |
| | | -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, |
| | | 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, |
| | | 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, |
| | | 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, |
| | | 907, 933, 960, 986, 1013, 1039, 1066 |
| | | }; |
| | | 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; |
| | | double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive |
| | | int k = static_cast<int>(dk); |
| | | if (dk - k > 0.0) |
| | | k++; |
| | | |
| | | unsigned index = static_cast<unsigned>((k >> 3) + 1); |
| | | *K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table |
| | | |
| | | return GetCachedPowerByIndex(index); |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | #endif |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_DIYFP_H_ |
New file |
| | |
| | | // 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 |
| | | // 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. |
| | | |
| | | // This is a C++ header-only implementation of Grisu2 algorithm from the publication: |
| | | // Loitsch, Florian. "Printing floating-point numbers quickly and accurately with |
| | | // integers." ACM Sigplan Notices 45.6 (2010): 233-243. |
| | | |
| | | #ifndef RAPIDJSON_DTOA_ |
| | | #define RAPIDJSON_DTOA_ |
| | | |
| | | #include "itoa.h" // GetDigitsLut() |
| | | #include "diyfp.h" |
| | | #include "ieee754.h" |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(effc++) |
| | | RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 |
| | | #endif |
| | | |
| | | inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { |
| | | while (rest < wp_w && delta - rest >= ten_kappa && |
| | | (rest + ten_kappa < wp_w || /// closer |
| | | wp_w - rest > rest + ten_kappa - wp_w)) { |
| | | buffer[len - 1]--; |
| | | rest += ten_kappa; |
| | | } |
| | | } |
| | | |
| | | 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; |
| | | if (n < 1000) return 3; |
| | | if (n < 10000) return 4; |
| | | if (n < 100000) return 5; |
| | | if (n < 1000000) return 6; |
| | | if (n < 10000000) return 7; |
| | | if (n < 100000000) return 8; |
| | | // Will not reach 10 digits in DigitGen() |
| | | //if (n < 1000000000) return 9; |
| | | //return 10; |
| | | return 9; |
| | | } |
| | | |
| | | 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 }; |
| | | 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); |
| | | int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] |
| | | *len = 0; |
| | | |
| | | while (kappa > 0) { |
| | | uint32_t d = 0; |
| | | switch (kappa) { |
| | | case 9: d = p1 / 100000000; p1 %= 100000000; break; |
| | | case 8: d = p1 / 10000000; p1 %= 10000000; break; |
| | | case 7: d = p1 / 1000000; p1 %= 1000000; break; |
| | | case 6: d = p1 / 100000; p1 %= 100000; break; |
| | | case 5: d = p1 / 10000; p1 %= 10000; break; |
| | | case 4: d = p1 / 1000; p1 %= 1000; break; |
| | | case 3: d = p1 / 100; p1 %= 100; break; |
| | | case 2: d = p1 / 10; p1 %= 10; break; |
| | | case 1: d = p1; p1 = 0; break; |
| | | default:; |
| | | } |
| | | if (d || *len) |
| | | buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d)); |
| | | kappa--; |
| | | 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); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // kappa = 0 |
| | | for (;;) { |
| | | p2 *= 10; |
| | | delta *= 10; |
| | | char d = static_cast<char>(p2 >> -one.e); |
| | | if (d || *len) |
| | | buffer[(*len)++] = static_cast<char>('0' + d); |
| | | p2 &= one.f - 1; |
| | | kappa--; |
| | | if (p2 < delta) { |
| | | *K += kappa; |
| | | int index = -kappa; |
| | | GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | |
| | | inline void Grisu2(double value, char* buffer, int* length, int* K) { |
| | | const DiyFp v(value); |
| | | DiyFp w_m, w_p; |
| | | v.NormalizedBoundaries(&w_m, &w_p); |
| | | |
| | | const DiyFp c_mk = GetCachedPower(w_p.e, K); |
| | | const DiyFp W = v.Normalize() * c_mk; |
| | | DiyFp Wp = w_p * c_mk; |
| | | DiyFp Wm = w_m * c_mk; |
| | | Wm.f++; |
| | | Wp.f--; |
| | | DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); |
| | | } |
| | | |
| | | inline char* WriteExponent(int K, char* buffer) { |
| | | if (K < 0) { |
| | | *buffer++ = '-'; |
| | | K = -K; |
| | | } |
| | | |
| | | if (K >= 100) { |
| | | *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100)); |
| | | K %= 100; |
| | | const char* d = GetDigitsLut() + K * 2; |
| | | *buffer++ = d[0]; |
| | | *buffer++ = d[1]; |
| | | } |
| | | else if (K >= 10) { |
| | | const char* d = GetDigitsLut() + K * 2; |
| | | *buffer++ = d[0]; |
| | | *buffer++ = d[1]; |
| | | } |
| | | else |
| | | *buffer++ = static_cast<char>('0' + static_cast<char>(K)); |
| | | |
| | | return buffer; |
| | | } |
| | | |
| | | inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { |
| | | const int kk = length + k; // 10^(kk-1) <= v < 10^kk |
| | | |
| | | if (0 <= k && kk <= 21) { |
| | | // 1234e7 -> 12340000000 |
| | | for (int i = length; i < kk; i++) |
| | | buffer[i] = '0'; |
| | | buffer[kk] = '.'; |
| | | buffer[kk + 1] = '0'; |
| | | return &buffer[kk + 2]; |
| | | } |
| | | else if (0 < kk && kk <= 21) { |
| | | // 1234e-2 -> 12.34 |
| | | std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk)); |
| | | buffer[kk] = '.'; |
| | | if (0 > k + maxDecimalPlaces) { |
| | | // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 |
| | | // Remove extra trailing zeros (at least one) after truncation. |
| | | for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) |
| | | if (buffer[i] != '0') |
| | | return &buffer[i + 1]; |
| | | return &buffer[kk + 2]; // Reserve one zero |
| | | } |
| | | else |
| | | return &buffer[length + 1]; |
| | | } |
| | | else if (-6 < kk && kk <= 0) { |
| | | // 1234e-6 -> 0.001234 |
| | | const int offset = 2 - kk; |
| | | std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length)); |
| | | buffer[0] = '0'; |
| | | buffer[1] = '.'; |
| | | for (int i = 2; i < offset; i++) |
| | | buffer[i] = '0'; |
| | | if (length - kk > maxDecimalPlaces) { |
| | | // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 |
| | | // Remove extra trailing zeros (at least one) after truncation. |
| | | for (int i = maxDecimalPlaces + 1; i > 2; i--) |
| | | if (buffer[i] != '0') |
| | | return &buffer[i + 1]; |
| | | return &buffer[3]; // Reserve one zero |
| | | } |
| | | else |
| | | return &buffer[length + offset]; |
| | | } |
| | | else if (kk < -maxDecimalPlaces) { |
| | | // Truncate to zero |
| | | buffer[0] = '0'; |
| | | buffer[1] = '.'; |
| | | buffer[2] = '0'; |
| | | return &buffer[3]; |
| | | } |
| | | else if (length == 1) { |
| | | // 1e30 |
| | | buffer[1] = 'e'; |
| | | return WriteExponent(kk - 1, &buffer[2]); |
| | | } |
| | | else { |
| | | // 1234e30 -> 1.234e33 |
| | | std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1)); |
| | | buffer[1] = '.'; |
| | | buffer[length + 1] = 'e'; |
| | | return WriteExponent(kk - 1, &buffer[0 + length + 2]); |
| | | } |
| | | } |
| | | |
| | | inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { |
| | | RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); |
| | | Double d(value); |
| | | if (d.IsZero()) { |
| | | if (d.Sign()) |
| | | *buffer++ = '-'; // -0.0, Issue #289 |
| | | buffer[0] = '0'; |
| | | buffer[1] = '.'; |
| | | buffer[2] = '0'; |
| | | return &buffer[3]; |
| | | } |
| | | else { |
| | | if (value < 0) { |
| | | *buffer++ = '-'; |
| | | value = -value; |
| | | } |
| | | int length, K; |
| | | Grisu2(value, buffer, &length, &K); |
| | | return Prettify(buffer, length, K, maxDecimalPlaces); |
| | | } |
| | | } |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_DTOA_ |
New file |
| | |
| | | // 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 |
| | | // 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_IEEE754_ |
| | | #define RAPIDJSON_IEEE754_ |
| | | |
| | | #include "../rapidjson.h" |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | class Double { |
| | | public: |
| | | Double() {} |
| | | Double(double d) : d_(d) {} |
| | | Double(uint64_t u) : u_(u) {} |
| | | |
| | | double Value() const { return d_; } |
| | | uint64_t Uint64Value() const { return u_; } |
| | | |
| | | double NextPositiveDouble() const { |
| | | RAPIDJSON_ASSERT(!Sign()); |
| | | return Double(u_ + 1).Value(); |
| | | } |
| | | |
| | | bool Sign() const { return (u_ & kSignMask) != 0; } |
| | | uint64_t Significand() const { return u_ & kSignificandMask; } |
| | | int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } |
| | | |
| | | bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } |
| | | bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } |
| | | bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } |
| | | bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } |
| | | bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } |
| | | |
| | | uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } |
| | | int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } |
| | | uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } |
| | | |
| | | static int EffectiveSignificandSize(int order) { |
| | | if (order >= -1021) |
| | | return 53; |
| | | else if (order <= -1074) |
| | | return 0; |
| | | else |
| | | return order + 1074; |
| | | } |
| | | |
| | | private: |
| | | static const int kSignificandSize = 52; |
| | | static const int kExponentBias = 0x3FF; |
| | | static const int kDenormalExponent = 1 - kExponentBias; |
| | | static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); |
| | | static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); |
| | | static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); |
| | | static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); |
| | | |
| | | union { |
| | | double d_; |
| | | uint64_t u_; |
| | | }; |
| | | }; |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_IEEE754_ |
New file |
| | |
| | | // 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 |
| | | // 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_ITOA_ |
| | | #define RAPIDJSON_ITOA_ |
| | | |
| | | #include "../rapidjson.h" |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | inline const char* GetDigitsLut() { |
| | | static const char cDigitsLut[200] = { |
| | | '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', |
| | | '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', |
| | | '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', |
| | | '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', |
| | | '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', |
| | | '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', |
| | | '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', |
| | | '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', |
| | | '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', |
| | | '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' |
| | | }; |
| | | return cDigitsLut; |
| | | } |
| | | |
| | | inline char* u32toa(uint32_t value, char* buffer) { |
| | | 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) |
| | | *buffer++ = cDigitsLut[d1 + 1]; |
| | | if (value >= 10) |
| | | *buffer++ = cDigitsLut[d2]; |
| | | *buffer++ = cDigitsLut[d2 + 1]; |
| | | } |
| | | else if (value < 100000000) { |
| | | // 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) |
| | | *buffer++ = cDigitsLut[d1 + 1]; |
| | | if (value >= 100000) |
| | | *buffer++ = cDigitsLut[d2]; |
| | | *buffer++ = cDigitsLut[d2 + 1]; |
| | | |
| | | *buffer++ = cDigitsLut[d3]; |
| | | *buffer++ = cDigitsLut[d3 + 1]; |
| | | *buffer++ = cDigitsLut[d4]; |
| | | *buffer++ = cDigitsLut[d4 + 1]; |
| | | } |
| | | 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]; |
| | | *buffer++ = cDigitsLut[i + 1]; |
| | | } |
| | | else |
| | | *buffer++ = static_cast<char>('0' + static_cast<char>(a)); |
| | | |
| | | 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]; |
| | | *buffer++ = cDigitsLut[d2 + 1]; |
| | | *buffer++ = cDigitsLut[d3]; |
| | | *buffer++ = cDigitsLut[d3 + 1]; |
| | | *buffer++ = cDigitsLut[d4]; |
| | | *buffer++ = cDigitsLut[d4 + 1]; |
| | | } |
| | | return buffer; |
| | | } |
| | | |
| | | inline char* i32toa(int32_t value, char* buffer) { |
| | | uint32_t u = static_cast<uint32_t>(value); |
| | | if (value < 0) { |
| | | *buffer++ = '-'; |
| | | u = ~u + 1; |
| | | } |
| | | |
| | | return u32toa(u, buffer); |
| | | } |
| | | |
| | | inline char* u64toa(uint64_t value, char* buffer) { |
| | | const char* cDigitsLut = GetDigitsLut(); |
| | | const uint64_t kTen8 = 100000000; |
| | | const uint64_t kTen9 = kTen8 * 10; |
| | | const uint64_t kTen10 = kTen8 * 100; |
| | | const uint64_t kTen11 = kTen8 * 1000; |
| | | const uint64_t kTen12 = kTen8 * 10000; |
| | | const uint64_t kTen13 = kTen8 * 100000; |
| | | 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) |
| | | *buffer++ = cDigitsLut[d1 + 1]; |
| | | if (v >= 10) |
| | | *buffer++ = cDigitsLut[d2]; |
| | | *buffer++ = cDigitsLut[d2 + 1]; |
| | | } |
| | | else { |
| | | // 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) |
| | | *buffer++ = cDigitsLut[d1 + 1]; |
| | | if (value >= 100000) |
| | | *buffer++ = cDigitsLut[d2]; |
| | | *buffer++ = cDigitsLut[d2 + 1]; |
| | | |
| | | *buffer++ = cDigitsLut[d3]; |
| | | *buffer++ = cDigitsLut[d3 + 1]; |
| | | *buffer++ = cDigitsLut[d4]; |
| | | *buffer++ = cDigitsLut[d4 + 1]; |
| | | } |
| | | } |
| | | 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; |
| | | |
| | | if (value >= kTen15) |
| | | *buffer++ = cDigitsLut[d1]; |
| | | if (value >= kTen14) |
| | | *buffer++ = cDigitsLut[d1 + 1]; |
| | | if (value >= kTen13) |
| | | *buffer++ = cDigitsLut[d2]; |
| | | if (value >= kTen12) |
| | | *buffer++ = cDigitsLut[d2 + 1]; |
| | | if (value >= kTen11) |
| | | *buffer++ = cDigitsLut[d3]; |
| | | if (value >= kTen10) |
| | | *buffer++ = cDigitsLut[d3 + 1]; |
| | | if (value >= kTen9) |
| | | *buffer++ = cDigitsLut[d4]; |
| | | if (value >= kTen8) |
| | | *buffer++ = cDigitsLut[d4 + 1]; |
| | | |
| | | *buffer++ = cDigitsLut[d5]; |
| | | *buffer++ = cDigitsLut[d5 + 1]; |
| | | *buffer++ = cDigitsLut[d6]; |
| | | *buffer++ = cDigitsLut[d6 + 1]; |
| | | *buffer++ = cDigitsLut[d7]; |
| | | *buffer++ = cDigitsLut[d7 + 1]; |
| | | *buffer++ = cDigitsLut[d8]; |
| | | *buffer++ = cDigitsLut[d8 + 1]; |
| | | } |
| | | 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) { |
| | | const uint32_t i = a << 1; |
| | | *buffer++ = cDigitsLut[i]; |
| | | *buffer++ = cDigitsLut[i + 1]; |
| | | } |
| | | 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]; |
| | | } |
| | | else { |
| | | const uint32_t i = (a / 100) << 1; |
| | | const uint32_t j = (a % 100) << 1; |
| | | *buffer++ = cDigitsLut[i]; |
| | | *buffer++ = cDigitsLut[i + 1]; |
| | | *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]; |
| | | *buffer++ = cDigitsLut[d2 + 1]; |
| | | *buffer++ = cDigitsLut[d3]; |
| | | *buffer++ = cDigitsLut[d3 + 1]; |
| | | *buffer++ = cDigitsLut[d4]; |
| | | *buffer++ = cDigitsLut[d4 + 1]; |
| | | *buffer++ = cDigitsLut[d5]; |
| | | *buffer++ = cDigitsLut[d5 + 1]; |
| | | *buffer++ = cDigitsLut[d6]; |
| | | *buffer++ = cDigitsLut[d6 + 1]; |
| | | *buffer++ = cDigitsLut[d7]; |
| | | *buffer++ = cDigitsLut[d7 + 1]; |
| | | *buffer++ = cDigitsLut[d8]; |
| | | *buffer++ = cDigitsLut[d8 + 1]; |
| | | } |
| | | |
| | | return buffer; |
| | | } |
| | | |
| | | inline char* i64toa(int64_t value, char* buffer) { |
| | | uint64_t u = static_cast<uint64_t>(value); |
| | | if (value < 0) { |
| | | *buffer++ = '-'; |
| | | u = ~u + 1; |
| | | } |
| | | |
| | | return u64toa(u, buffer); |
| | | } |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_ITOA_ |
New file |
| | |
| | | // 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 |
| | | // 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_INTERNAL_META_H_ |
| | | #define RAPIDJSON_INTERNAL_META_H_ |
| | | |
| | | #include "../rapidjson.h" |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(effc++) |
| | | #endif |
| | | #if defined(_MSC_VER) |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(6334) |
| | | #endif |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_TYPETRAITS |
| | | #include <type_traits> |
| | | #endif |
| | | |
| | | //@cond RAPIDJSON_INTERNAL |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | // Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching |
| | | template <typename T> struct Void { typedef void Type; }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // BoolType, TrueType, FalseType |
| | | // |
| | | template <bool Cond> struct BoolType { |
| | | static const bool Value = Cond; |
| | | typedef BoolType Type; |
| | | }; |
| | | typedef BoolType<true> TrueType; |
| | | typedef BoolType<false> FalseType; |
| | | |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr |
| | | // |
| | | |
| | | template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; }; |
| | | template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; }; |
| | | template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {}; |
| | | template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {}; |
| | | |
| | | template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {}; |
| | | template <> struct AndExprCond<true, true> : TrueType {}; |
| | | template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {}; |
| | | template <> struct OrExprCond<false, false> : FalseType {}; |
| | | |
| | | template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {}; |
| | | template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {}; |
| | | template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {}; |
| | | template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {}; |
| | | |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // AddConst, MaybeAddConst, RemoveConst |
| | | template <typename T> struct AddConst { typedef const T Type; }; |
| | | template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {}; |
| | | template <typename T> struct RemoveConst { typedef T Type; }; |
| | | template <typename T> struct RemoveConst<const T> { typedef T Type; }; |
| | | |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // IsSame, IsConst, IsMoreConst, IsPointer |
| | | // |
| | | template <typename T, typename U> struct IsSame : FalseType {}; |
| | | template <typename T> struct IsSame<T, T> : TrueType {}; |
| | | |
| | | template <typename T> struct IsConst : FalseType {}; |
| | | template <typename T> struct IsConst<const T> : TrueType {}; |
| | | |
| | | template <typename CT, typename T> |
| | | struct IsMoreConst |
| | | : AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>, |
| | | BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {}; |
| | | |
| | | template <typename T> struct IsPointer : FalseType {}; |
| | | template <typename T> struct IsPointer<T*> : TrueType {}; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // IsBaseOf |
| | | // |
| | | #if RAPIDJSON_HAS_CXX11_TYPETRAITS |
| | | |
| | | template <typename B, typename D> struct IsBaseOf |
| | | : BoolType< ::std::is_base_of<B,D>::value> {}; |
| | | |
| | | #else // simplified version adopted from Boost |
| | | |
| | | template<typename B, typename D> struct IsBaseOfImpl { |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); |
| | | RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); |
| | | |
| | | typedef char (&Yes)[1]; |
| | | typedef char (&No) [2]; |
| | | |
| | | template <typename T> |
| | | static Yes Check(const D*, T); |
| | | static No Check(const B*, int); |
| | | |
| | | struct Host { |
| | | operator const B*() const; |
| | | operator const D*(); |
| | | }; |
| | | |
| | | enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; |
| | | }; |
| | | |
| | | template <typename B, typename D> struct IsBaseOf |
| | | : OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {}; |
| | | |
| | | #endif // RAPIDJSON_HAS_CXX11_TYPETRAITS |
| | | |
| | | |
| | | ////////////////////////////////////////////////////////////////////////// |
| | | // EnableIf / DisableIf |
| | | // |
| | | template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; }; |
| | | template <typename T> struct EnableIfCond<false, T> { /* empty */ }; |
| | | |
| | | template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; }; |
| | | template <typename T> struct DisableIfCond<true, T> { /* empty */ }; |
| | | |
| | | template <typename Condition, typename T = void> |
| | | struct EnableIf : EnableIfCond<Condition::Value, T> {}; |
| | | |
| | | template <typename Condition, typename T = void> |
| | | struct DisableIf : DisableIfCond<Condition::Value, T> {}; |
| | | |
| | | // SFINAE helpers |
| | | struct SfinaeTag {}; |
| | | template <typename T> struct RemoveSfinaeTag; |
| | | template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; }; |
| | | |
| | | #define RAPIDJSON_REMOVEFPTR_(type) \ |
| | | typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ |
| | | < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type |
| | | |
| | | #define RAPIDJSON_ENABLEIF(cond) \ |
| | | typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ |
| | | <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL |
| | | |
| | | #define RAPIDJSON_DISABLEIF(cond) \ |
| | | typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ |
| | | <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL |
| | | |
| | | #define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ |
| | | typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ |
| | | <RAPIDJSON_REMOVEFPTR_(cond), \ |
| | | RAPIDJSON_REMOVEFPTR_(returntype)>::Type |
| | | |
| | | #define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ |
| | | typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ |
| | | <RAPIDJSON_REMOVEFPTR_(cond), \ |
| | | RAPIDJSON_REMOVEFPTR_(returntype)>::Type |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | //@endcond |
| | | |
| | | #if defined(__GNUC__) || defined(_MSC_VER) |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_INTERNAL_META_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_POW10_ |
| | | #define RAPIDJSON_POW10_ |
| | | |
| | | #include "../rapidjson.h" |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | //! Computes integer powers of 10 in double (10.0^n). |
| | | /*! This function uses lookup table for fast and accurate results. |
| | | \param n non-negative exponent. Must <= 308. |
| | | \return 10.0^n |
| | | */ |
| | | inline double Pow10(int n) { |
| | | static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes |
| | | 1e+0, |
| | | 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, |
| | | 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, |
| | | 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, |
| | | 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, |
| | | 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, |
| | | 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, |
| | | 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, |
| | | 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, |
| | | 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, |
| | | 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, |
| | | 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, |
| | | 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, |
| | | 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, |
| | | 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, |
| | | 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, |
| | | 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 |
| | | }; |
| | | RAPIDJSON_ASSERT(n >= 0 && n <= 308); |
| | | return e[n]; |
| | | } |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_POW10_ |
New file |
| | |
| | | // 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 |
| | | // 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_INTERNAL_REGEX_H_ |
| | | #define RAPIDJSON_INTERNAL_REGEX_H_ |
| | | |
| | | #include "../allocators.h" |
| | | #include "../stream.h" |
| | | #include "stack.h" |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | RAPIDJSON_DIAG_OFF(switch-enum) |
| | | RAPIDJSON_DIAG_OFF(implicit-fallthrough) |
| | | #endif |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(effc++) |
| | | #if __GNUC__ >= 7 |
| | | RAPIDJSON_DIAG_OFF(implicit-fallthrough) |
| | | #endif |
| | | #endif |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated |
| | | #endif |
| | | |
| | | #ifndef RAPIDJSON_REGEX_VERBOSE |
| | | #define RAPIDJSON_REGEX_VERBOSE 0 |
| | | #endif |
| | | |
| | | 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: |
| | | - \c ab Concatenation |
| | | - \c a|b Alternation |
| | | - \c a? Zero or one |
| | | - \c a* Zero or more |
| | | - \c a+ One or more |
| | | - \c a{3} Exactly 3 times |
| | | - \c a{3,} At least 3 times |
| | | - \c a{3,5} 3 to 5 times |
| | | - \c (ab) Grouping |
| | | - \c ^a At the beginning |
| | | - \c a$ At the end |
| | | - \c . Any character |
| | | - \c [abc] Character classes |
| | | - \c [a-c] Character class range |
| | | - \c [a-z0-9_] Character class combination |
| | | - \c [^abc] Negated character classes |
| | | - \c [^a-c] Negated character class range |
| | | - \c [\b] Backspace (U+0008) |
| | | - \c \\| \\\\ ... Escape characters |
| | | - \c \\f Form feed (U+000C) |
| | | - \c \\n Line feed (U+000A) |
| | | - \c \\r Carriage return (U+000D) |
| | | - \c \\t Tab (U+0009) |
| | | - \c \\v Vertical tab (U+000B) |
| | | |
| | | \note This is a Thompson NFA engine, implemented with reference to |
| | | Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", |
| | | https://swtch.com/~rsc/regexp/regexp1.html |
| | | */ |
| | | 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_(), |
| | | anchorBegin_(), anchorEnd_() |
| | | { |
| | | GenericStringStream<Encoding> ss(source); |
| | | DecodedStream<GenericStringStream<Encoding>, Encoding> ds(ss); |
| | | Parse(ds); |
| | | } |
| | | |
| | | ~GenericRegex() {} |
| | | |
| | | bool IsValid() const { |
| | | return root_ != kRegexInvalidState; |
| | | } |
| | | |
| | | private: |
| | | enum Operator { |
| | | kZeroOrOne, |
| | | kZeroOrMore, |
| | | kOneOrMore, |
| | | kConcatenation, |
| | | kAlternation, |
| | | kLeftParenthesis |
| | | }; |
| | | |
| | | static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' |
| | | static const unsigned kRangeCharacterClass = 0xFFFFFFFE; |
| | | static const unsigned kRangeNegationFlag = 0x80000000; |
| | | |
| | | struct Range { |
| | | unsigned start; // |
| | | unsigned end; |
| | | SizeType next; |
| | | }; |
| | | |
| | | struct State { |
| | | SizeType out; //!< Equals to kInvalid for matching state |
| | | SizeType out1; //!< Equals to non-kInvalid for split |
| | | SizeType rangeStart; |
| | | unsigned codepoint; |
| | | }; |
| | | |
| | | struct Frag { |
| | | Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} |
| | | SizeType start; |
| | | SizeType out; //!< link-list of all output states |
| | | SizeType minIndex; |
| | | }; |
| | | |
| | | State& GetState(SizeType index) { |
| | | RAPIDJSON_ASSERT(index < stateCount_); |
| | | return states_.template Bottom<State>()[index]; |
| | | } |
| | | |
| | | const State& GetState(SizeType index) const { |
| | | RAPIDJSON_ASSERT(index < stateCount_); |
| | | return states_.template Bottom<State>()[index]; |
| | | } |
| | | |
| | | Range& GetRange(SizeType index) { |
| | | RAPIDJSON_ASSERT(index < rangeCount_); |
| | | return ranges_.template Bottom<Range>()[index]; |
| | | } |
| | | |
| | | const Range& GetRange(SizeType index) const { |
| | | RAPIDJSON_ASSERT(index < rangeCount_); |
| | | return ranges_.template Bottom<Range>()[index]; |
| | | } |
| | | |
| | | template <typename InputStream> |
| | | void Parse(DecodedStream<InputStream, Encoding>& ds) { |
| | | Allocator allocator; |
| | | 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; |
| | | |
| | | unsigned codepoint; |
| | | while (ds.Peek() != 0) { |
| | | switch (codepoint = ds.Take()) { |
| | | case '^': |
| | | anchorBegin_ = true; |
| | | break; |
| | | |
| | | case '$': |
| | | anchorEnd_ = true; |
| | | break; |
| | | |
| | | case '|': |
| | | while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation) |
| | | if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1))) |
| | | return; |
| | | *operatorStack.template Push<Operator>() = kAlternation; |
| | | *atomCountStack.template Top<unsigned>() = 0; |
| | | break; |
| | | |
| | | case '(': |
| | | *operatorStack.template Push<Operator>() = kLeftParenthesis; |
| | | *atomCountStack.template Push<unsigned>() = 0; |
| | | break; |
| | | |
| | | case ')': |
| | | while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis) |
| | | if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1))) |
| | | return; |
| | | if (operatorStack.Empty()) |
| | | return; |
| | | operatorStack.template Pop<Operator>(1); |
| | | atomCountStack.template Pop<unsigned>(1); |
| | | ImplicitConcatenation(atomCountStack, operatorStack); |
| | | break; |
| | | |
| | | case '?': |
| | | if (!Eval(operandStack, kZeroOrOne)) |
| | | return; |
| | | break; |
| | | |
| | | case '*': |
| | | if (!Eval(operandStack, kZeroOrMore)) |
| | | return; |
| | | break; |
| | | |
| | | case '+': |
| | | if (!Eval(operandStack, kOneOrMore)) |
| | | return; |
| | | break; |
| | | |
| | | case '{': |
| | | { |
| | | unsigned n, m; |
| | | if (!ParseUnsigned(ds, &n)) |
| | | return; |
| | | |
| | | if (ds.Peek() == ',') { |
| | | ds.Take(); |
| | | if (ds.Peek() == '}') |
| | | m = kInfinityQuantifier; |
| | | else if (!ParseUnsigned(ds, &m) || m < n) |
| | | return; |
| | | } |
| | | else |
| | | m = n; |
| | | |
| | | if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') |
| | | return; |
| | | ds.Take(); |
| | | } |
| | | break; |
| | | |
| | | case '.': |
| | | PushOperand(operandStack, kAnyCharacterClass); |
| | | ImplicitConcatenation(atomCountStack, operatorStack); |
| | | break; |
| | | |
| | | case '[': |
| | | { |
| | | SizeType range; |
| | | if (!ParseRange(ds, &range)) |
| | | return; |
| | | SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); |
| | | GetState(s).rangeStart = range; |
| | | *operandStack.template Push<Frag>() = Frag(s, s, s); |
| | | } |
| | | ImplicitConcatenation(atomCountStack, operatorStack); |
| | | break; |
| | | |
| | | case '\\': // Escape character |
| | | if (!CharacterEscape(ds, &codepoint)) |
| | | return; // Unsupported escape character |
| | | // fall through to default |
| | | |
| | | default: // Pattern character |
| | | PushOperand(operandStack, codepoint); |
| | | ImplicitConcatenation(atomCountStack, operatorStack); |
| | | } |
| | | } |
| | | |
| | | while (!operatorStack.Empty()) |
| | | if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1))) |
| | | return; |
| | | |
| | | // Link the operand to matching state. |
| | | if (operandStack.GetSize() == sizeof(Frag)) { |
| | | Frag* e = operandStack.template Pop<Frag>(1); |
| | | Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); |
| | | root_ = e->start; |
| | | |
| | | #if RAPIDJSON_REGEX_VERBOSE |
| | | printf("root: %d\n", root_); |
| | | for (SizeType i = 0; i < stateCount_ ; i++) { |
| | | State& s = GetState(i); |
| | | printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); |
| | | } |
| | | printf("\n"); |
| | | #endif |
| | | } |
| | | } |
| | | |
| | | SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { |
| | | State* s = states_.template Push<State>(); |
| | | s->out = out; |
| | | s->out1 = out1; |
| | | s->codepoint = codepoint; |
| | | s->rangeStart = kRegexInvalidRange; |
| | | return stateCount_++; |
| | | } |
| | | |
| | | void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) { |
| | | SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); |
| | | *operandStack.template Push<Frag>() = Frag(s, s, s); |
| | | } |
| | | |
| | | void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) { |
| | | if (*atomCountStack.template Top<unsigned>()) |
| | | *operatorStack.template Push<Operator>() = kConcatenation; |
| | | (*atomCountStack.template Top<unsigned>())++; |
| | | } |
| | | |
| | | SizeType Append(SizeType l1, SizeType l2) { |
| | | SizeType old = l1; |
| | | while (GetState(l1).out != kRegexInvalidState) |
| | | l1 = GetState(l1).out; |
| | | GetState(l1).out = l2; |
| | | return old; |
| | | } |
| | | |
| | | void Patch(SizeType l, SizeType s) { |
| | | for (SizeType next; l != kRegexInvalidState; l = next) { |
| | | next = GetState(l).out; |
| | | GetState(l).out = s; |
| | | } |
| | | } |
| | | |
| | | bool Eval(Stack<Allocator>& operandStack, Operator op) { |
| | | switch (op) { |
| | | case kConcatenation: |
| | | RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); |
| | | { |
| | | Frag e2 = *operandStack.template Pop<Frag>(1); |
| | | Frag e1 = *operandStack.template Pop<Frag>(1); |
| | | Patch(e1.out, e2.start); |
| | | *operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); |
| | | } |
| | | return true; |
| | | |
| | | case kAlternation: |
| | | if (operandStack.GetSize() >= sizeof(Frag) * 2) { |
| | | Frag e2 = *operandStack.template Pop<Frag>(1); |
| | | Frag e1 = *operandStack.template Pop<Frag>(1); |
| | | SizeType s = NewState(e1.start, e2.start, 0); |
| | | *operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); |
| | | return true; |
| | | } |
| | | return false; |
| | | |
| | | case kZeroOrOne: |
| | | if (operandStack.GetSize() >= sizeof(Frag)) { |
| | | Frag e = *operandStack.template Pop<Frag>(1); |
| | | SizeType s = NewState(kRegexInvalidState, e.start, 0); |
| | | *operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex); |
| | | return true; |
| | | } |
| | | return false; |
| | | |
| | | case kZeroOrMore: |
| | | if (operandStack.GetSize() >= sizeof(Frag)) { |
| | | Frag e = *operandStack.template Pop<Frag>(1); |
| | | SizeType s = NewState(kRegexInvalidState, e.start, 0); |
| | | Patch(e.out, s); |
| | | *operandStack.template Push<Frag>() = Frag(s, s, e.minIndex); |
| | | return true; |
| | | } |
| | | return false; |
| | | |
| | | default: |
| | | RAPIDJSON_ASSERT(op == kOneOrMore); |
| | | if (operandStack.GetSize() >= sizeof(Frag)) { |
| | | Frag e = *operandStack.template Pop<Frag>(1); |
| | | SizeType s = NewState(kRegexInvalidState, e.start, 0); |
| | | Patch(e.out, s); |
| | | *operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex); |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) { |
| | | RAPIDJSON_ASSERT(n <= m); |
| | | RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); |
| | | |
| | | if (n == 0) { |
| | | if (m == 0) // a{0} not support |
| | | return false; |
| | | else if (m == kInfinityQuantifier) |
| | | Eval(operandStack, kZeroOrMore); // a{0,} -> a* |
| | | else { |
| | | Eval(operandStack, kZeroOrOne); // a{0,5} -> a? |
| | | for (unsigned i = 0; i < m - 1; i++) |
| | | CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? |
| | | for (unsigned i = 0; i < m - 1; i++) |
| | | Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a |
| | | CloneTopOperand(operandStack); |
| | | |
| | | if (m == kInfinityQuantifier) |
| | | Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ |
| | | else if (m > n) { |
| | | CloneTopOperand(operandStack); // a{3,5} -> a a a a |
| | | Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? |
| | | for (unsigned i = n; i < m - 1; i++) |
| | | CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? |
| | | for (unsigned i = n; i < m; i++) |
| | | Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? |
| | | } |
| | | |
| | | for (unsigned i = 0; i < n - 1; i++) |
| | | Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? |
| | | |
| | | return true; |
| | | } |
| | | |
| | | static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } |
| | | |
| | | void CloneTopOperand(Stack<Allocator>& operandStack) { |
| | | const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation |
| | | SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) |
| | | State* s = states_.template Push<State>(count); |
| | | memcpy(s, &GetState(src.minIndex), count * sizeof(State)); |
| | | for (SizeType j = 0; j < count; j++) { |
| | | if (s[j].out != kRegexInvalidState) |
| | | s[j].out += count; |
| | | if (s[j].out1 != kRegexInvalidState) |
| | | s[j].out1 += count; |
| | | } |
| | | *operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count); |
| | | stateCount_ += count; |
| | | } |
| | | |
| | | template <typename InputStream> |
| | | bool ParseUnsigned(DecodedStream<InputStream, Encoding>& ds, unsigned* u) { |
| | | unsigned r = 0; |
| | | if (ds.Peek() < '0' || ds.Peek() > '9') |
| | | return false; |
| | | while (ds.Peek() >= '0' && ds.Peek() <= '9') { |
| | | if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 |
| | | return false; // overflow |
| | | r = r * 10 + (ds.Take() - '0'); |
| | | } |
| | | *u = r; |
| | | return true; |
| | | } |
| | | |
| | | template <typename InputStream> |
| | | bool ParseRange(DecodedStream<InputStream, Encoding>& ds, SizeType* range) { |
| | | bool isBegin = true; |
| | | bool negate = false; |
| | | int step = 0; |
| | | SizeType start = kRegexInvalidRange; |
| | | SizeType current = kRegexInvalidRange; |
| | | unsigned codepoint; |
| | | while ((codepoint = ds.Take()) != 0) { |
| | | if (isBegin) { |
| | | isBegin = false; |
| | | if (codepoint == '^') { |
| | | negate = true; |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | switch (codepoint) { |
| | | case ']': |
| | | if (start == kRegexInvalidRange) |
| | | return false; // Error: nothing inside [] |
| | | if (step == 2) { // Add trailing '-' |
| | | SizeType r = NewRange('-'); |
| | | RAPIDJSON_ASSERT(current != kRegexInvalidRange); |
| | | GetRange(current).next = r; |
| | | } |
| | | if (negate) |
| | | GetRange(start).start |= kRangeNegationFlag; |
| | | *range = start; |
| | | return true; |
| | | |
| | | case '\\': |
| | | if (ds.Peek() == 'b') { |
| | | ds.Take(); |
| | | codepoint = 0x0008; // Escape backspace character |
| | | } |
| | | else if (!CharacterEscape(ds, &codepoint)) |
| | | return false; |
| | | // fall through to default |
| | | |
| | | default: |
| | | switch (step) { |
| | | case 1: |
| | | if (codepoint == '-') { |
| | | step++; |
| | | break; |
| | | } |
| | | // fall through to step 0 for other characters |
| | | |
| | | case 0: |
| | | { |
| | | SizeType r = NewRange(codepoint); |
| | | if (current != kRegexInvalidRange) |
| | | GetRange(current).next = r; |
| | | if (start == kRegexInvalidRange) |
| | | start = r; |
| | | current = r; |
| | | } |
| | | step = 1; |
| | | break; |
| | | |
| | | default: |
| | | RAPIDJSON_ASSERT(step == 2); |
| | | GetRange(current).end = codepoint; |
| | | step = 0; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | SizeType NewRange(unsigned codepoint) { |
| | | Range* r = ranges_.template Push<Range>(); |
| | | r->start = r->end = codepoint; |
| | | r->next = kRegexInvalidRange; |
| | | return rangeCount_++; |
| | | } |
| | | |
| | | template <typename InputStream> |
| | | bool CharacterEscape(DecodedStream<InputStream, Encoding>& ds, unsigned* escapedCodepoint) { |
| | | unsigned codepoint; |
| | | switch (codepoint = ds.Take()) { |
| | | case '^': |
| | | case '$': |
| | | case '|': |
| | | case '(': |
| | | case ')': |
| | | case '?': |
| | | case '*': |
| | | case '+': |
| | | case '.': |
| | | case '[': |
| | | case ']': |
| | | case '{': |
| | | case '}': |
| | | case '\\': |
| | | *escapedCodepoint = codepoint; return true; |
| | | case 'f': *escapedCodepoint = 0x000C; return true; |
| | | case 'n': *escapedCodepoint = 0x000A; return true; |
| | | case 'r': *escapedCodepoint = 0x000D; return true; |
| | | case 't': *escapedCodepoint = 0x0009; return true; |
| | | case 'v': *escapedCodepoint = 0x000B; return true; |
| | | default: |
| | | return false; // Unsupported escape character |
| | | } |
| | | } |
| | | |
| | | 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 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, 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 = regex_.GetState(*s); |
| | | if (sr.codepoint == 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, regex_.root_); |
| | | } |
| | | internal::Swap(current, next); |
| | | } |
| | | |
| | | return matched; |
| | | } |
| | | |
| | | size_t GetStateSetSize() const { |
| | | return (regex_.stateCount_ + 31) / 32 * 4; |
| | | } |
| | | |
| | | // Return whether the added states is a match state |
| | | bool AddState(Stack<Allocator>& l, SizeType index) { |
| | | RAPIDJSON_ASSERT(index != kRegexInvalidState); |
| | | |
| | | 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] & (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 = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; |
| | | while (rangeIndex != kRegexInvalidRange) { |
| | | const Range& r = regex_.GetRange(rangeIndex); |
| | | if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) |
| | | return yes; |
| | | rangeIndex = r.next; |
| | | } |
| | | return !yes; |
| | | } |
| | | |
| | | 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__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_INTERNAL_REGEX_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_INTERNAL_STACK_H_ |
| | | #define RAPIDJSON_INTERNAL_STACK_H_ |
| | | |
| | | #include "../allocators.h" |
| | | #include "swap.h" |
| | | |
| | | #if defined(__clang__) |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(c++98-compat) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Stack |
| | | |
| | | //! A type-unsafe stack for storing different types of data. |
| | | /*! \tparam Allocator Allocator for allocating stack memory. |
| | | */ |
| | | template <typename Allocator> |
| | | class Stack { |
| | | public: |
| | | // Optimization note: Do not allocate memory for stack_ in constructor. |
| | | // Do it lazily when first Push() -> Expand() -> Resize(). |
| | | Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | Stack(Stack&& rhs) |
| | | : allocator_(rhs.allocator_), |
| | | ownAllocator_(rhs.ownAllocator_), |
| | | stack_(rhs.stack_), |
| | | stackTop_(rhs.stackTop_), |
| | | stackEnd_(rhs.stackEnd_), |
| | | initialCapacity_(rhs.initialCapacity_) |
| | | { |
| | | rhs.allocator_ = 0; |
| | | rhs.ownAllocator_ = 0; |
| | | rhs.stack_ = 0; |
| | | rhs.stackTop_ = 0; |
| | | rhs.stackEnd_ = 0; |
| | | rhs.initialCapacity_ = 0; |
| | | } |
| | | #endif |
| | | |
| | | ~Stack() { |
| | | Destroy(); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | Stack& operator=(Stack&& rhs) { |
| | | if (&rhs != this) |
| | | { |
| | | Destroy(); |
| | | |
| | | allocator_ = rhs.allocator_; |
| | | ownAllocator_ = rhs.ownAllocator_; |
| | | stack_ = rhs.stack_; |
| | | stackTop_ = rhs.stackTop_; |
| | | stackEnd_ = rhs.stackEnd_; |
| | | initialCapacity_ = rhs.initialCapacity_; |
| | | |
| | | rhs.allocator_ = 0; |
| | | rhs.ownAllocator_ = 0; |
| | | rhs.stack_ = 0; |
| | | rhs.stackTop_ = 0; |
| | | rhs.stackEnd_ = 0; |
| | | rhs.initialCapacity_ = 0; |
| | | } |
| | | return *this; |
| | | } |
| | | #endif |
| | | |
| | | void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { |
| | | internal::Swap(allocator_, rhs.allocator_); |
| | | internal::Swap(ownAllocator_, rhs.ownAllocator_); |
| | | internal::Swap(stack_, rhs.stack_); |
| | | internal::Swap(stackTop_, rhs.stackTop_); |
| | | internal::Swap(stackEnd_, rhs.stackEnd_); |
| | | internal::Swap(initialCapacity_, rhs.initialCapacity_); |
| | | } |
| | | |
| | | void Clear() { stackTop_ = stack_; } |
| | | |
| | | void ShrinkToFit() { |
| | | if (Empty()) { |
| | | // If the stack is empty, completely deallocate the memory. |
| | | Allocator::Free(stack_); |
| | | stack_ = 0; |
| | | stackTop_ = 0; |
| | | stackEnd_ = 0; |
| | | } |
| | | else |
| | | Resize(GetSize()); |
| | | } |
| | | |
| | | // Optimization note: try to minimize the size of this function for force inline. |
| | | // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. |
| | | template<typename T> |
| | | RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { |
| | | // Expand the stack if needed |
| | | if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) |
| | | Expand<T>(count); |
| | | } |
| | | |
| | | template<typename T> |
| | | RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { |
| | | Reserve<T>(count); |
| | | return PushUnsafe<T>(count); |
| | | } |
| | | |
| | | template<typename T> |
| | | RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { |
| | | RAPIDJSON_ASSERT(stackTop_); |
| | | RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); |
| | | T* ret = reinterpret_cast<T*>(stackTop_); |
| | | stackTop_ += sizeof(T) * count; |
| | | return ret; |
| | | } |
| | | |
| | | template<typename T> |
| | | T* Pop(size_t count) { |
| | | RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); |
| | | stackTop_ -= count * sizeof(T); |
| | | return reinterpret_cast<T*>(stackTop_); |
| | | } |
| | | |
| | | template<typename T> |
| | | T* Top() { |
| | | RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); |
| | | return reinterpret_cast<T*>(stackTop_ - sizeof(T)); |
| | | } |
| | | |
| | | template<typename T> |
| | | const T* Top() const { |
| | | RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); |
| | | return reinterpret_cast<T*>(stackTop_ - sizeof(T)); |
| | | } |
| | | |
| | | template<typename T> |
| | | T* End() { return reinterpret_cast<T*>(stackTop_); } |
| | | |
| | | template<typename T> |
| | | const T* End() const { return reinterpret_cast<T*>(stackTop_); } |
| | | |
| | | template<typename T> |
| | | T* Bottom() { return reinterpret_cast<T*>(stack_); } |
| | | |
| | | template<typename T> |
| | | const T* Bottom() const { return reinterpret_cast<T*>(stack_); } |
| | | |
| | | bool HasAllocator() const { |
| | | return allocator_ != 0; |
| | | } |
| | | |
| | | Allocator& GetAllocator() { |
| | | RAPIDJSON_ASSERT(allocator_); |
| | | return *allocator_; |
| | | } |
| | | |
| | | bool Empty() const { return stackTop_ == stack_; } |
| | | size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); } |
| | | size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); } |
| | | |
| | | private: |
| | | template<typename T> |
| | | void Expand(size_t count) { |
| | | // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. |
| | | size_t newCapacity; |
| | | if (stack_ == 0) { |
| | | if (!allocator_) |
| | | ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); |
| | | newCapacity = initialCapacity_; |
| | | } else { |
| | | newCapacity = GetCapacity(); |
| | | newCapacity += (newCapacity + 1) / 2; |
| | | } |
| | | size_t newSize = GetSize() + sizeof(T) * count; |
| | | if (newCapacity < newSize) |
| | | newCapacity = newSize; |
| | | |
| | | Resize(newCapacity); |
| | | } |
| | | |
| | | void Resize(size_t newCapacity) { |
| | | const size_t size = GetSize(); // Backup the current size |
| | | stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); |
| | | stackTop_ = stack_ + size; |
| | | stackEnd_ = stack_ + newCapacity; |
| | | } |
| | | |
| | | void Destroy() { |
| | | Allocator::Free(stack_); |
| | | RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack |
| | | } |
| | | |
| | | // Prohibit copy constructor & assignment operator. |
| | | Stack(const Stack&); |
| | | Stack& operator=(const Stack&); |
| | | |
| | | Allocator* allocator_; |
| | | Allocator* ownAllocator_; |
| | | char *stack_; |
| | | char *stackTop_; |
| | | char *stackEnd_; |
| | | size_t initialCapacity_; |
| | | }; |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #if defined(__clang__) |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_STACK_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_INTERNAL_STRFUNC_H_ |
| | | #define RAPIDJSON_INTERNAL_STRFUNC_H_ |
| | | |
| | | #include "../stream.h" |
| | | #include <cwchar> |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | //! Custom strlen() which works on different character types. |
| | | /*! \tparam Ch Character type (e.g. char, wchar_t, short) |
| | | \param s Null-terminated input string. |
| | | \return Number of characters in the string. |
| | | \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. |
| | | */ |
| | | template <typename Ch> |
| | | inline SizeType StrLen(const Ch* s) { |
| | | RAPIDJSON_ASSERT(s != 0); |
| | | const Ch* p = s; |
| | | while (*p) ++p; |
| | | return SizeType(p - s); |
| | | } |
| | | |
| | | template <> |
| | | inline SizeType StrLen(const char* s) { |
| | | return SizeType(std::strlen(s)); |
| | | } |
| | | |
| | | template <> |
| | | inline SizeType StrLen(const wchar_t* s) { |
| | | return SizeType(std::wcslen(s)); |
| | | } |
| | | |
| | | //! Returns number of code points in a encoded string. |
| | | template<typename Encoding> |
| | | bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { |
| | | RAPIDJSON_ASSERT(s != 0); |
| | | RAPIDJSON_ASSERT(outCount != 0); |
| | | GenericStringStream<Encoding> is(s); |
| | | const typename Encoding::Ch* end = s + length; |
| | | SizeType count = 0; |
| | | while (is.src_ < end) { |
| | | unsigned codepoint; |
| | | if (!Encoding::Decode(is, &codepoint)) |
| | | return false; |
| | | count++; |
| | | } |
| | | *outCount = count; |
| | | return true; |
| | | } |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_INTERNAL_STRFUNC_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_STRTOD_ |
| | | #define RAPIDJSON_STRTOD_ |
| | | |
| | | #include "ieee754.h" |
| | | #include "biginteger.h" |
| | | #include "diyfp.h" |
| | | #include "pow10.h" |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | inline double FastPath(double significand, int exp) { |
| | | if (exp < -308) |
| | | return 0.0; |
| | | else if (exp >= 0) |
| | | return significand * internal::Pow10(exp); |
| | | else |
| | | return significand / internal::Pow10(-exp); |
| | | } |
| | | |
| | | inline double StrtodNormalPrecision(double d, int p) { |
| | | if (p < -308) { |
| | | // Prevent expSum < -308, making Pow10(p) = 0 |
| | | d = FastPath(d, -308); |
| | | d = FastPath(d, p + 308); |
| | | } |
| | | else |
| | | d = FastPath(d, p); |
| | | return d; |
| | | } |
| | | |
| | | template <typename T> |
| | | inline T Min3(T a, T b, T c) { |
| | | T m = a; |
| | | if (m > b) m = b; |
| | | if (m > c) m = c; |
| | | return m; |
| | | } |
| | | |
| | | inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { |
| | | const Double db(b); |
| | | const uint64_t bInt = db.IntegerSignificand(); |
| | | const int bExp = db.IntegerExponent(); |
| | | const int hExp = bExp - 1; |
| | | |
| | | int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; |
| | | |
| | | // Adjust for decimal exponent |
| | | if (dExp >= 0) { |
| | | dS_Exp2 += dExp; |
| | | dS_Exp5 += dExp; |
| | | } |
| | | else { |
| | | bS_Exp2 -= dExp; |
| | | bS_Exp5 -= dExp; |
| | | hS_Exp2 -= dExp; |
| | | hS_Exp5 -= dExp; |
| | | } |
| | | |
| | | // Adjust for binary exponent |
| | | if (bExp >= 0) |
| | | bS_Exp2 += bExp; |
| | | else { |
| | | dS_Exp2 -= bExp; |
| | | hS_Exp2 -= bExp; |
| | | } |
| | | |
| | | // Adjust for half ulp exponent |
| | | if (hExp >= 0) |
| | | hS_Exp2 += hExp; |
| | | else { |
| | | dS_Exp2 -= hExp; |
| | | bS_Exp2 -= hExp; |
| | | } |
| | | |
| | | // Remove common power of two factor from all three scaled values |
| | | int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); |
| | | dS_Exp2 -= common_Exp2; |
| | | bS_Exp2 -= common_Exp2; |
| | | hS_Exp2 -= common_Exp2; |
| | | |
| | | BigInteger dS = d; |
| | | dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2); |
| | | |
| | | BigInteger bS(bInt); |
| | | bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2); |
| | | |
| | | BigInteger hS(1); |
| | | hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2); |
| | | |
| | | BigInteger delta(0); |
| | | dS.Difference(bS, &delta); |
| | | |
| | | return delta.Compare(hS); |
| | | } |
| | | |
| | | inline bool StrtodFast(double d, int p, double* result) { |
| | | // Use fast path for string-to-double conversion if possible |
| | | // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ |
| | | if (p > 22 && p < 22 + 16) { |
| | | // Fast Path Cases In Disguise |
| | | d *= internal::Pow10(p - 22); |
| | | p = 22; |
| | | } |
| | | |
| | | if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 |
| | | *result = FastPath(d, p); |
| | | return true; |
| | | } |
| | | else |
| | | return false; |
| | | } |
| | | |
| | | // 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) { |
| | | uint64_t significand = 0; |
| | | size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 |
| | | for (; i < length; i++) { |
| | | if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || |
| | | (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) |
| | | break; |
| | | significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0'); |
| | | } |
| | | |
| | | if (i < length && decimals[i] >= '5') // Rounding |
| | | significand++; |
| | | |
| | | size_t remaining = length - 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; |
| | | |
| | | 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 |
| | | }; |
| | | 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 |
| | | error += kUlp / 2; |
| | | } |
| | | |
| | | v = v * cachedPower; |
| | | |
| | | error += kUlp + (error == 0 ? 0 : 1); |
| | | |
| | | const int oldExp = v.e; |
| | | v = v.Normalize(); |
| | | error <<= oldExp - v.e; |
| | | |
| | | const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); |
| | | int precisionSize = 64 - effectiveSignificandSize; |
| | | if (precisionSize + kUlpShift >= 64) { |
| | | int scaleExp = (precisionSize + kUlpShift) - 63; |
| | | v.f >>= scaleExp; |
| | | v.e += scaleExp; |
| | | error = (error >> scaleExp) + 1 + kUlp; |
| | | precisionSize -= scaleExp; |
| | | } |
| | | |
| | | 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)) { |
| | | rounded.f++; |
| | | if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) |
| | | rounded.f >>= 1; |
| | | rounded.e++; |
| | | } |
| | | } |
| | | |
| | | *result = rounded.ToDouble(); |
| | | |
| | | 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; |
| | | Double a(approx); |
| | | int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); |
| | | if (cmp < 0) |
| | | return a.Value(); // within half ULP |
| | | else if (cmp == 0) { |
| | | // Round towards even |
| | | if (a.Significand() & 1) |
| | | return a.NextPositiveDouble(); |
| | | else |
| | | return a.Value(); |
| | | } |
| | | else // adjustment |
| | | return a.NextPositiveDouble(); |
| | | } |
| | | |
| | | inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { |
| | | RAPIDJSON_ASSERT(d >= 0.0); |
| | | RAPIDJSON_ASSERT(length >= 1); |
| | | |
| | | double result; |
| | | if (StrtodFast(d, p, &result)) |
| | | return result; |
| | | |
| | | // Trim leading zeros |
| | | while (*decimals == '0' && length > 1) { |
| | | length--; |
| | | decimals++; |
| | | decimalPosition--; |
| | | } |
| | | |
| | | // Trim trailing zeros |
| | | while (decimals[length - 1] == '0' && length > 1) { |
| | | length--; |
| | | decimalPosition--; |
| | | exp++; |
| | | } |
| | | |
| | | // 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; |
| | | } |
| | | |
| | | // If too small, underflow to zero |
| | | if (int(length) + exp < -324) |
| | | return 0.0; |
| | | |
| | | if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) |
| | | return result; |
| | | |
| | | // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison |
| | | return StrtodBigInteger(result, decimals, length, decimalPosition, exp); |
| | | } |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_STRTOD_ |
New file |
| | |
| | | // 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 |
| | | // 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_INTERNAL_SWAP_H_ |
| | | #define RAPIDJSON_INTERNAL_SWAP_H_ |
| | | |
| | | #include "../rapidjson.h" |
| | | |
| | | #if defined(__clang__) |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(c++98-compat) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | namespace internal { |
| | | |
| | | //! Custom swap() to avoid dependency on C++ <algorithm> header |
| | | /*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. |
| | | \note This has the same semantics as std::swap(). |
| | | */ |
| | | template <typename T> |
| | | inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { |
| | | T tmp = a; |
| | | a = b; |
| | | b = tmp; |
| | | } |
| | | |
| | | } // namespace internal |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #if defined(__clang__) |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_INTERNAL_SWAP_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_ISTREAMWRAPPER_H_ |
| | | #define RAPIDJSON_ISTREAMWRAPPER_H_ |
| | | |
| | | #include "stream.h" |
| | | #include <iosfwd> |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | #endif |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. |
| | | /*! |
| | | The classes can be wrapped including but not limited to: |
| | | |
| | | - \c std::istringstream |
| | | - \c std::stringstream |
| | | - \c std::wistringstream |
| | | - \c std::wstringstream |
| | | - \c std::ifstream |
| | | - \c std::fstream |
| | | - \c std::wifstream |
| | | - \c std::wfstream |
| | | |
| | | \tparam StreamType Class derived from \c std::basic_istream. |
| | | */ |
| | | |
| | | 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) : static_cast<Ch>('\0'); |
| | | } |
| | | |
| | | 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'; |
| | | } |
| | | |
| | | // tellg() may return -1 when failed. So we count by ourself. |
| | | size_t Tell() const { return count_; } |
| | | |
| | | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } |
| | | void Put(Ch) { RAPIDJSON_ASSERT(false); } |
| | | void Flush() { RAPIDJSON_ASSERT(false); } |
| | | 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; |
| | | } |
| | | |
| | | private: |
| | | BasicIStreamWrapper(const BasicIStreamWrapper&); |
| | | BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); |
| | | |
| | | StreamType& stream_; |
| | | size_t count_; //!< Number of characters read. Note: |
| | | mutable Ch peekBuffer_[4]; |
| | | }; |
| | | |
| | | typedef BasicIStreamWrapper<std::istream> IStreamWrapper; |
| | | typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper; |
| | | |
| | | #if defined(__clang__) || defined(_MSC_VER) |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_ISTREAMWRAPPER_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_MEMORYBUFFER_H_ |
| | | #define RAPIDJSON_MEMORYBUFFER_H_ |
| | | |
| | | #include "stream.h" |
| | | #include "internal/stack.h" |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! Represents an in-memory output byte stream. |
| | | /*! |
| | | This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. |
| | | |
| | | It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. |
| | | |
| | | Differences between MemoryBuffer and StringBuffer: |
| | | 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. |
| | | 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. |
| | | |
| | | \tparam Allocator type for allocating memory buffer. |
| | | \note implements Stream concept |
| | | */ |
| | | template <typename Allocator = CrtAllocator> |
| | | struct GenericMemoryBuffer { |
| | | typedef char Ch; // byte |
| | | |
| | | GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} |
| | | |
| | | void Put(Ch c) { *stack_.template Push<Ch>() = c; } |
| | | void Flush() {} |
| | | |
| | | void Clear() { stack_.Clear(); } |
| | | void ShrinkToFit() { stack_.ShrinkToFit(); } |
| | | Ch* Push(size_t count) { return stack_.template Push<Ch>(count); } |
| | | void Pop(size_t count) { stack_.template Pop<Ch>(count); } |
| | | |
| | | const Ch* GetBuffer() const { |
| | | return stack_.template Bottom<Ch>(); |
| | | } |
| | | |
| | | size_t GetSize() const { return stack_.GetSize(); } |
| | | |
| | | static const size_t kDefaultCapacity = 256; |
| | | mutable internal::Stack<Allocator> stack_; |
| | | }; |
| | | |
| | | typedef GenericMemoryBuffer<> MemoryBuffer; |
| | | |
| | | //! Implement specialized version of PutN() with memset() for better performance. |
| | | template<> |
| | | inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { |
| | | std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c)); |
| | | } |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_MEMORYBUFFER_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_MEMORYSTREAM_H_ |
| | | #define RAPIDJSON_MEMORYSTREAM_H_ |
| | | |
| | | #include "stream.h" |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(unreachable-code) |
| | | RAPIDJSON_DIAG_OFF(missing-noreturn) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! Represents an in-memory input byte stream. |
| | | /*! |
| | | This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. |
| | | |
| | | It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. |
| | | |
| | | Differences between MemoryStream and StringStream: |
| | | 1. StringStream has encoding but MemoryStream is a byte stream. |
| | | 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. |
| | | 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). |
| | | \note implements Stream concept |
| | | */ |
| | | struct MemoryStream { |
| | | typedef char Ch; // byte |
| | | |
| | | MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} |
| | | |
| | | Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } |
| | | Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } |
| | | size_t Tell() const { return static_cast<size_t>(src_ - begin_); } |
| | | |
| | | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } |
| | | void Put(Ch) { RAPIDJSON_ASSERT(false); } |
| | | void Flush() { RAPIDJSON_ASSERT(false); } |
| | | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } |
| | | |
| | | // For encoding detection only. |
| | | const Ch* Peek4() const { |
| | | return Tell() + 4 <= size_ ? src_ : 0; |
| | | } |
| | | |
| | | const Ch* src_; //!< Current read position. |
| | | const Ch* begin_; //!< Original head of the string. |
| | | const Ch* end_; //!< End of stream. |
| | | size_t size_; //!< Size of the stream. |
| | | }; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_MEMORYBUFFER_H_ |
New file |
| | |
| | | // ISO C9x compliant inttypes.h for Microsoft Visual Studio |
| | | // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 |
| | | // |
| | | // Copyright (c) 2006-2013 Alexander Chemeris |
| | | // |
| | | // Redistribution and use in source and binary forms, with or without |
| | | // modification, are permitted provided that the following conditions are met: |
| | | // |
| | | // 1. Redistributions of source code must retain the above copyright notice, |
| | | // this list of conditions and the following disclaimer. |
| | | // |
| | | // 2. Redistributions in binary form must reproduce the above copyright |
| | | // notice, this list of conditions and the following disclaimer in the |
| | | // documentation and/or other materials provided with the distribution. |
| | | // |
| | | // 3. Neither the name of the product nor the names of its contributors may |
| | | // be used to endorse or promote products derived from this software |
| | | // without specific prior written permission. |
| | | // |
| | | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| | | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| | | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| | | // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| | | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| | | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| | | // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| | | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| | | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| | | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| | | // |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | |
| | | // The above software in this distribution may have been modified by |
| | | // THL A29 Limited ("Tencent Modifications"). |
| | | // All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. |
| | | |
| | | #ifndef _MSC_VER // [ |
| | | #error "Use this header only with Microsoft Visual C++ compilers!" |
| | | #endif // _MSC_VER ] |
| | | |
| | | #ifndef _MSC_INTTYPES_H_ // [ |
| | | #define _MSC_INTTYPES_H_ |
| | | |
| | | #if _MSC_VER > 1000 |
| | | #pragma once |
| | | #endif |
| | | |
| | | #include "stdint.h" |
| | | |
| | | // miloyip: VC supports inttypes.h since VC2013 |
| | | #if _MSC_VER >= 1800 |
| | | #include <inttypes.h> |
| | | #else |
| | | |
| | | // 7.8 Format conversion of integer types |
| | | |
| | | typedef struct { |
| | | intmax_t quot; |
| | | intmax_t rem; |
| | | } imaxdiv_t; |
| | | |
| | | // 7.8.1 Macros for format specifiers |
| | | |
| | | #if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 |
| | | |
| | | // The fprintf macros for signed integers are: |
| | | #define PRId8 "d" |
| | | #define PRIi8 "i" |
| | | #define PRIdLEAST8 "d" |
| | | #define PRIiLEAST8 "i" |
| | | #define PRIdFAST8 "d" |
| | | #define PRIiFAST8 "i" |
| | | |
| | | #define PRId16 "hd" |
| | | #define PRIi16 "hi" |
| | | #define PRIdLEAST16 "hd" |
| | | #define PRIiLEAST16 "hi" |
| | | #define PRIdFAST16 "hd" |
| | | #define PRIiFAST16 "hi" |
| | | |
| | | #define PRId32 "I32d" |
| | | #define PRIi32 "I32i" |
| | | #define PRIdLEAST32 "I32d" |
| | | #define PRIiLEAST32 "I32i" |
| | | #define PRIdFAST32 "I32d" |
| | | #define PRIiFAST32 "I32i" |
| | | |
| | | #define PRId64 "I64d" |
| | | #define PRIi64 "I64i" |
| | | #define PRIdLEAST64 "I64d" |
| | | #define PRIiLEAST64 "I64i" |
| | | #define PRIdFAST64 "I64d" |
| | | #define PRIiFAST64 "I64i" |
| | | |
| | | #define PRIdMAX "I64d" |
| | | #define PRIiMAX "I64i" |
| | | |
| | | #define PRIdPTR "Id" |
| | | #define PRIiPTR "Ii" |
| | | |
| | | // The fprintf macros for unsigned integers are: |
| | | #define PRIo8 "o" |
| | | #define PRIu8 "u" |
| | | #define PRIx8 "x" |
| | | #define PRIX8 "X" |
| | | #define PRIoLEAST8 "o" |
| | | #define PRIuLEAST8 "u" |
| | | #define PRIxLEAST8 "x" |
| | | #define PRIXLEAST8 "X" |
| | | #define PRIoFAST8 "o" |
| | | #define PRIuFAST8 "u" |
| | | #define PRIxFAST8 "x" |
| | | #define PRIXFAST8 "X" |
| | | |
| | | #define PRIo16 "ho" |
| | | #define PRIu16 "hu" |
| | | #define PRIx16 "hx" |
| | | #define PRIX16 "hX" |
| | | #define PRIoLEAST16 "ho" |
| | | #define PRIuLEAST16 "hu" |
| | | #define PRIxLEAST16 "hx" |
| | | #define PRIXLEAST16 "hX" |
| | | #define PRIoFAST16 "ho" |
| | | #define PRIuFAST16 "hu" |
| | | #define PRIxFAST16 "hx" |
| | | #define PRIXFAST16 "hX" |
| | | |
| | | #define PRIo32 "I32o" |
| | | #define PRIu32 "I32u" |
| | | #define PRIx32 "I32x" |
| | | #define PRIX32 "I32X" |
| | | #define PRIoLEAST32 "I32o" |
| | | #define PRIuLEAST32 "I32u" |
| | | #define PRIxLEAST32 "I32x" |
| | | #define PRIXLEAST32 "I32X" |
| | | #define PRIoFAST32 "I32o" |
| | | #define PRIuFAST32 "I32u" |
| | | #define PRIxFAST32 "I32x" |
| | | #define PRIXFAST32 "I32X" |
| | | |
| | | #define PRIo64 "I64o" |
| | | #define PRIu64 "I64u" |
| | | #define PRIx64 "I64x" |
| | | #define PRIX64 "I64X" |
| | | #define PRIoLEAST64 "I64o" |
| | | #define PRIuLEAST64 "I64u" |
| | | #define PRIxLEAST64 "I64x" |
| | | #define PRIXLEAST64 "I64X" |
| | | #define PRIoFAST64 "I64o" |
| | | #define PRIuFAST64 "I64u" |
| | | #define PRIxFAST64 "I64x" |
| | | #define PRIXFAST64 "I64X" |
| | | |
| | | #define PRIoMAX "I64o" |
| | | #define PRIuMAX "I64u" |
| | | #define PRIxMAX "I64x" |
| | | #define PRIXMAX "I64X" |
| | | |
| | | #define PRIoPTR "Io" |
| | | #define PRIuPTR "Iu" |
| | | #define PRIxPTR "Ix" |
| | | #define PRIXPTR "IX" |
| | | |
| | | // The fscanf macros for signed integers are: |
| | | #define SCNd8 "d" |
| | | #define SCNi8 "i" |
| | | #define SCNdLEAST8 "d" |
| | | #define SCNiLEAST8 "i" |
| | | #define SCNdFAST8 "d" |
| | | #define SCNiFAST8 "i" |
| | | |
| | | #define SCNd16 "hd" |
| | | #define SCNi16 "hi" |
| | | #define SCNdLEAST16 "hd" |
| | | #define SCNiLEAST16 "hi" |
| | | #define SCNdFAST16 "hd" |
| | | #define SCNiFAST16 "hi" |
| | | |
| | | #define SCNd32 "ld" |
| | | #define SCNi32 "li" |
| | | #define SCNdLEAST32 "ld" |
| | | #define SCNiLEAST32 "li" |
| | | #define SCNdFAST32 "ld" |
| | | #define SCNiFAST32 "li" |
| | | |
| | | #define SCNd64 "I64d" |
| | | #define SCNi64 "I64i" |
| | | #define SCNdLEAST64 "I64d" |
| | | #define SCNiLEAST64 "I64i" |
| | | #define SCNdFAST64 "I64d" |
| | | #define SCNiFAST64 "I64i" |
| | | |
| | | #define SCNdMAX "I64d" |
| | | #define SCNiMAX "I64i" |
| | | |
| | | #ifdef _WIN64 // [ |
| | | # define SCNdPTR "I64d" |
| | | # define SCNiPTR "I64i" |
| | | #else // _WIN64 ][ |
| | | # define SCNdPTR "ld" |
| | | # define SCNiPTR "li" |
| | | #endif // _WIN64 ] |
| | | |
| | | // The fscanf macros for unsigned integers are: |
| | | #define SCNo8 "o" |
| | | #define SCNu8 "u" |
| | | #define SCNx8 "x" |
| | | #define SCNX8 "X" |
| | | #define SCNoLEAST8 "o" |
| | | #define SCNuLEAST8 "u" |
| | | #define SCNxLEAST8 "x" |
| | | #define SCNXLEAST8 "X" |
| | | #define SCNoFAST8 "o" |
| | | #define SCNuFAST8 "u" |
| | | #define SCNxFAST8 "x" |
| | | #define SCNXFAST8 "X" |
| | | |
| | | #define SCNo16 "ho" |
| | | #define SCNu16 "hu" |
| | | #define SCNx16 "hx" |
| | | #define SCNX16 "hX" |
| | | #define SCNoLEAST16 "ho" |
| | | #define SCNuLEAST16 "hu" |
| | | #define SCNxLEAST16 "hx" |
| | | #define SCNXLEAST16 "hX" |
| | | #define SCNoFAST16 "ho" |
| | | #define SCNuFAST16 "hu" |
| | | #define SCNxFAST16 "hx" |
| | | #define SCNXFAST16 "hX" |
| | | |
| | | #define SCNo32 "lo" |
| | | #define SCNu32 "lu" |
| | | #define SCNx32 "lx" |
| | | #define SCNX32 "lX" |
| | | #define SCNoLEAST32 "lo" |
| | | #define SCNuLEAST32 "lu" |
| | | #define SCNxLEAST32 "lx" |
| | | #define SCNXLEAST32 "lX" |
| | | #define SCNoFAST32 "lo" |
| | | #define SCNuFAST32 "lu" |
| | | #define SCNxFAST32 "lx" |
| | | #define SCNXFAST32 "lX" |
| | | |
| | | #define SCNo64 "I64o" |
| | | #define SCNu64 "I64u" |
| | | #define SCNx64 "I64x" |
| | | #define SCNX64 "I64X" |
| | | #define SCNoLEAST64 "I64o" |
| | | #define SCNuLEAST64 "I64u" |
| | | #define SCNxLEAST64 "I64x" |
| | | #define SCNXLEAST64 "I64X" |
| | | #define SCNoFAST64 "I64o" |
| | | #define SCNuFAST64 "I64u" |
| | | #define SCNxFAST64 "I64x" |
| | | #define SCNXFAST64 "I64X" |
| | | |
| | | #define SCNoMAX "I64o" |
| | | #define SCNuMAX "I64u" |
| | | #define SCNxMAX "I64x" |
| | | #define SCNXMAX "I64X" |
| | | |
| | | #ifdef _WIN64 // [ |
| | | # define SCNoPTR "I64o" |
| | | # define SCNuPTR "I64u" |
| | | # define SCNxPTR "I64x" |
| | | # define SCNXPTR "I64X" |
| | | #else // _WIN64 ][ |
| | | # define SCNoPTR "lo" |
| | | # define SCNuPTR "lu" |
| | | # define SCNxPTR "lx" |
| | | # define SCNXPTR "lX" |
| | | #endif // _WIN64 ] |
| | | |
| | | #endif // __STDC_FORMAT_MACROS ] |
| | | |
| | | // 7.8.2 Functions for greatest-width integer types |
| | | |
| | | // 7.8.2.1 The imaxabs function |
| | | #define imaxabs _abs64 |
| | | |
| | | // 7.8.2.2 The imaxdiv function |
| | | |
| | | // This is modified version of div() function from Microsoft's div.c found |
| | | // in %MSVC.NET%\crt\src\div.c |
| | | #ifdef STATIC_IMAXDIV // [ |
| | | static |
| | | #else // STATIC_IMAXDIV ][ |
| | | _inline |
| | | #endif // STATIC_IMAXDIV ] |
| | | imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) |
| | | { |
| | | imaxdiv_t result; |
| | | |
| | | result.quot = numer / denom; |
| | | result.rem = numer % denom; |
| | | |
| | | if (numer < 0 && result.rem > 0) { |
| | | // did division wrong; must fix up |
| | | ++result.quot; |
| | | result.rem -= denom; |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | // 7.8.2.3 The strtoimax and strtoumax functions |
| | | #define strtoimax _strtoi64 |
| | | #define strtoumax _strtoui64 |
| | | |
| | | // 7.8.2.4 The wcstoimax and wcstoumax functions |
| | | #define wcstoimax _wcstoi64 |
| | | #define wcstoumax _wcstoui64 |
| | | |
| | | #endif // _MSC_VER >= 1800 |
| | | |
| | | #endif // _MSC_INTTYPES_H_ ] |
New file |
| | |
| | | // ISO C9x compliant stdint.h for Microsoft Visual Studio |
| | | // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 |
| | | // |
| | | // Copyright (c) 2006-2013 Alexander Chemeris |
| | | // |
| | | // Redistribution and use in source and binary forms, with or without |
| | | // modification, are permitted provided that the following conditions are met: |
| | | // |
| | | // 1. Redistributions of source code must retain the above copyright notice, |
| | | // this list of conditions and the following disclaimer. |
| | | // |
| | | // 2. Redistributions in binary form must reproduce the above copyright |
| | | // notice, this list of conditions and the following disclaimer in the |
| | | // documentation and/or other materials provided with the distribution. |
| | | // |
| | | // 3. Neither the name of the product nor the names of its contributors may |
| | | // be used to endorse or promote products derived from this software |
| | | // without specific prior written permission. |
| | | // |
| | | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| | | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| | | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| | | // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| | | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| | | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| | | // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| | | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| | | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| | | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| | | // |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | |
| | | // The above software in this distribution may have been modified by |
| | | // THL A29 Limited ("Tencent Modifications"). |
| | | // All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. |
| | | |
| | | #ifndef _MSC_VER // [ |
| | | #error "Use this header only with Microsoft Visual C++ compilers!" |
| | | #endif // _MSC_VER ] |
| | | |
| | | #ifndef _MSC_STDINT_H_ // [ |
| | | #define _MSC_STDINT_H_ |
| | | |
| | | #if _MSC_VER > 1000 |
| | | #pragma once |
| | | #endif |
| | | |
| | | // miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. |
| | | #if _MSC_VER >= 1600 // [ |
| | | #include <stdint.h> |
| | | |
| | | #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 |
| | | |
| | | #undef INT8_C |
| | | #undef INT16_C |
| | | #undef INT32_C |
| | | #undef INT64_C |
| | | #undef UINT8_C |
| | | #undef UINT16_C |
| | | #undef UINT32_C |
| | | #undef UINT64_C |
| | | |
| | | // 7.18.4.1 Macros for minimum-width integer constants |
| | | |
| | | #define INT8_C(val) val##i8 |
| | | #define INT16_C(val) val##i16 |
| | | #define INT32_C(val) val##i32 |
| | | #define INT64_C(val) val##i64 |
| | | |
| | | #define UINT8_C(val) val##ui8 |
| | | #define UINT16_C(val) val##ui16 |
| | | #define UINT32_C(val) val##ui32 |
| | | #define UINT64_C(val) val##ui64 |
| | | |
| | | // 7.18.4.2 Macros for greatest-width integer constants |
| | | // These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>. |
| | | // Check out Issue 9 for the details. |
| | | #ifndef INTMAX_C // [ |
| | | # define INTMAX_C INT64_C |
| | | #endif // INTMAX_C ] |
| | | #ifndef UINTMAX_C // [ |
| | | # define UINTMAX_C UINT64_C |
| | | #endif // UINTMAX_C ] |
| | | |
| | | #endif // __STDC_CONSTANT_MACROS ] |
| | | |
| | | #else // ] _MSC_VER >= 1700 [ |
| | | |
| | | #include <limits.h> |
| | | |
| | | // For Visual Studio 6 in C++ mode and for many Visual Studio versions when |
| | | // compiling for ARM we have to wrap <wchar.h> include with 'extern "C++" {}' |
| | | // or compiler would give many errors like this: |
| | | // error C2733: second C linkage of overloaded function 'wmemchr' not allowed |
| | | #if defined(__cplusplus) && !defined(_M_ARM) |
| | | extern "C" { |
| | | #endif |
| | | # include <wchar.h> |
| | | #if defined(__cplusplus) && !defined(_M_ARM) |
| | | } |
| | | #endif |
| | | |
| | | // Define _W64 macros to mark types changing their size, like intptr_t. |
| | | #ifndef _W64 |
| | | # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 |
| | | # define _W64 __w64 |
| | | # else |
| | | # define _W64 |
| | | # endif |
| | | #endif |
| | | |
| | | |
| | | // 7.18.1 Integer types |
| | | |
| | | // 7.18.1.1 Exact-width integer types |
| | | |
| | | // Visual Studio 6 and Embedded Visual C++ 4 doesn't |
| | | // realize that, e.g. char has the same size as __int8 |
| | | // so we give up on __intX for them. |
| | | #if (_MSC_VER < 1300) |
| | | typedef signed char int8_t; |
| | | typedef signed short int16_t; |
| | | typedef signed int int32_t; |
| | | typedef unsigned char uint8_t; |
| | | typedef unsigned short uint16_t; |
| | | typedef unsigned int uint32_t; |
| | | #else |
| | | typedef signed __int8 int8_t; |
| | | typedef signed __int16 int16_t; |
| | | typedef signed __int32 int32_t; |
| | | typedef unsigned __int8 uint8_t; |
| | | typedef unsigned __int16 uint16_t; |
| | | typedef unsigned __int32 uint32_t; |
| | | #endif |
| | | typedef signed __int64 int64_t; |
| | | typedef unsigned __int64 uint64_t; |
| | | |
| | | |
| | | // 7.18.1.2 Minimum-width integer types |
| | | typedef int8_t int_least8_t; |
| | | typedef int16_t int_least16_t; |
| | | typedef int32_t int_least32_t; |
| | | typedef int64_t int_least64_t; |
| | | typedef uint8_t uint_least8_t; |
| | | typedef uint16_t uint_least16_t; |
| | | typedef uint32_t uint_least32_t; |
| | | typedef uint64_t uint_least64_t; |
| | | |
| | | // 7.18.1.3 Fastest minimum-width integer types |
| | | typedef int8_t int_fast8_t; |
| | | typedef int16_t int_fast16_t; |
| | | typedef int32_t int_fast32_t; |
| | | typedef int64_t int_fast64_t; |
| | | typedef uint8_t uint_fast8_t; |
| | | typedef uint16_t uint_fast16_t; |
| | | typedef uint32_t uint_fast32_t; |
| | | typedef uint64_t uint_fast64_t; |
| | | |
| | | // 7.18.1.4 Integer types capable of holding object pointers |
| | | #ifdef _WIN64 // [ |
| | | typedef signed __int64 intptr_t; |
| | | typedef unsigned __int64 uintptr_t; |
| | | #else // _WIN64 ][ |
| | | typedef _W64 signed int intptr_t; |
| | | typedef _W64 unsigned int uintptr_t; |
| | | #endif // _WIN64 ] |
| | | |
| | | // 7.18.1.5 Greatest-width integer types |
| | | typedef int64_t intmax_t; |
| | | typedef uint64_t uintmax_t; |
| | | |
| | | |
| | | // 7.18.2 Limits of specified-width integer types |
| | | |
| | | #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 |
| | | |
| | | // 7.18.2.1 Limits of exact-width integer types |
| | | #define INT8_MIN ((int8_t)_I8_MIN) |
| | | #define INT8_MAX _I8_MAX |
| | | #define INT16_MIN ((int16_t)_I16_MIN) |
| | | #define INT16_MAX _I16_MAX |
| | | #define INT32_MIN ((int32_t)_I32_MIN) |
| | | #define INT32_MAX _I32_MAX |
| | | #define INT64_MIN ((int64_t)_I64_MIN) |
| | | #define INT64_MAX _I64_MAX |
| | | #define UINT8_MAX _UI8_MAX |
| | | #define UINT16_MAX _UI16_MAX |
| | | #define UINT32_MAX _UI32_MAX |
| | | #define UINT64_MAX _UI64_MAX |
| | | |
| | | // 7.18.2.2 Limits of minimum-width integer types |
| | | #define INT_LEAST8_MIN INT8_MIN |
| | | #define INT_LEAST8_MAX INT8_MAX |
| | | #define INT_LEAST16_MIN INT16_MIN |
| | | #define INT_LEAST16_MAX INT16_MAX |
| | | #define INT_LEAST32_MIN INT32_MIN |
| | | #define INT_LEAST32_MAX INT32_MAX |
| | | #define INT_LEAST64_MIN INT64_MIN |
| | | #define INT_LEAST64_MAX INT64_MAX |
| | | #define UINT_LEAST8_MAX UINT8_MAX |
| | | #define UINT_LEAST16_MAX UINT16_MAX |
| | | #define UINT_LEAST32_MAX UINT32_MAX |
| | | #define UINT_LEAST64_MAX UINT64_MAX |
| | | |
| | | // 7.18.2.3 Limits of fastest minimum-width integer types |
| | | #define INT_FAST8_MIN INT8_MIN |
| | | #define INT_FAST8_MAX INT8_MAX |
| | | #define INT_FAST16_MIN INT16_MIN |
| | | #define INT_FAST16_MAX INT16_MAX |
| | | #define INT_FAST32_MIN INT32_MIN |
| | | #define INT_FAST32_MAX INT32_MAX |
| | | #define INT_FAST64_MIN INT64_MIN |
| | | #define INT_FAST64_MAX INT64_MAX |
| | | #define UINT_FAST8_MAX UINT8_MAX |
| | | #define UINT_FAST16_MAX UINT16_MAX |
| | | #define UINT_FAST32_MAX UINT32_MAX |
| | | #define UINT_FAST64_MAX UINT64_MAX |
| | | |
| | | // 7.18.2.4 Limits of integer types capable of holding object pointers |
| | | #ifdef _WIN64 // [ |
| | | # define INTPTR_MIN INT64_MIN |
| | | # define INTPTR_MAX INT64_MAX |
| | | # define UINTPTR_MAX UINT64_MAX |
| | | #else // _WIN64 ][ |
| | | # define INTPTR_MIN INT32_MIN |
| | | # define INTPTR_MAX INT32_MAX |
| | | # define UINTPTR_MAX UINT32_MAX |
| | | #endif // _WIN64 ] |
| | | |
| | | // 7.18.2.5 Limits of greatest-width integer types |
| | | #define INTMAX_MIN INT64_MIN |
| | | #define INTMAX_MAX INT64_MAX |
| | | #define UINTMAX_MAX UINT64_MAX |
| | | |
| | | // 7.18.3 Limits of other integer types |
| | | |
| | | #ifdef _WIN64 // [ |
| | | # define PTRDIFF_MIN _I64_MIN |
| | | # define PTRDIFF_MAX _I64_MAX |
| | | #else // _WIN64 ][ |
| | | # define PTRDIFF_MIN _I32_MIN |
| | | # define PTRDIFF_MAX _I32_MAX |
| | | #endif // _WIN64 ] |
| | | |
| | | #define SIG_ATOMIC_MIN INT_MIN |
| | | #define SIG_ATOMIC_MAX INT_MAX |
| | | |
| | | #ifndef SIZE_MAX // [ |
| | | # ifdef _WIN64 // [ |
| | | # define SIZE_MAX _UI64_MAX |
| | | # else // _WIN64 ][ |
| | | # define SIZE_MAX _UI32_MAX |
| | | # endif // _WIN64 ] |
| | | #endif // SIZE_MAX ] |
| | | |
| | | // WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> |
| | | #ifndef WCHAR_MIN // [ |
| | | # define WCHAR_MIN 0 |
| | | #endif // WCHAR_MIN ] |
| | | #ifndef WCHAR_MAX // [ |
| | | # define WCHAR_MAX _UI16_MAX |
| | | #endif // WCHAR_MAX ] |
| | | |
| | | #define WINT_MIN 0 |
| | | #define WINT_MAX _UI16_MAX |
| | | |
| | | #endif // __STDC_LIMIT_MACROS ] |
| | | |
| | | |
| | | // 7.18.4 Limits of other integer types |
| | | |
| | | #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 |
| | | |
| | | // 7.18.4.1 Macros for minimum-width integer constants |
| | | |
| | | #define INT8_C(val) val##i8 |
| | | #define INT16_C(val) val##i16 |
| | | #define INT32_C(val) val##i32 |
| | | #define INT64_C(val) val##i64 |
| | | |
| | | #define UINT8_C(val) val##ui8 |
| | | #define UINT16_C(val) val##ui16 |
| | | #define UINT32_C(val) val##ui32 |
| | | #define UINT64_C(val) val##ui64 |
| | | |
| | | // 7.18.4.2 Macros for greatest-width integer constants |
| | | // These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>. |
| | | // Check out Issue 9 for the details. |
| | | #ifndef INTMAX_C // [ |
| | | # define INTMAX_C INT64_C |
| | | #endif // INTMAX_C ] |
| | | #ifndef UINTMAX_C // [ |
| | | # define UINTMAX_C UINT64_C |
| | | #endif // UINTMAX_C ] |
| | | |
| | | #endif // __STDC_CONSTANT_MACROS ] |
| | | |
| | | #endif // _MSC_VER >= 1600 ] |
| | | |
| | | #endif // _MSC_STDINT_H_ ] |
New file |
| | |
| | | // 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 |
| | | // 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_OSTREAMWRAPPER_H_ |
| | | #define RAPIDJSON_OSTREAMWRAPPER_H_ |
| | | |
| | | #include "stream.h" |
| | | #include <iosfwd> |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. |
| | | /*! |
| | | The classes can be wrapped including but not limited to: |
| | | |
| | | - \c std::ostringstream |
| | | - \c std::stringstream |
| | | - \c std::wpstringstream |
| | | - \c std::wstringstream |
| | | - \c std::ifstream |
| | | - \c std::fstream |
| | | - \c std::wofstream |
| | | - \c std::wfstream |
| | | |
| | | \tparam StreamType Class derived from \c std::basic_ostream. |
| | | */ |
| | | |
| | | template <typename StreamType> |
| | | class BasicOStreamWrapper { |
| | | public: |
| | | typedef typename StreamType::char_type Ch; |
| | | BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} |
| | | |
| | | void Put(Ch c) { |
| | | stream_.put(c); |
| | | } |
| | | |
| | | void Flush() { |
| | | stream_.flush(); |
| | | } |
| | | |
| | | // Not implemented |
| | | char Peek() const { RAPIDJSON_ASSERT(false); return 0; } |
| | | char Take() { RAPIDJSON_ASSERT(false); return 0; } |
| | | size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } |
| | | char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } |
| | | size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } |
| | | |
| | | private: |
| | | BasicOStreamWrapper(const BasicOStreamWrapper&); |
| | | BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); |
| | | |
| | | StreamType& stream_; |
| | | }; |
| | | |
| | | typedef BasicOStreamWrapper<std::ostream> OStreamWrapper; |
| | | typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper; |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_OSTREAMWRAPPER_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_POINTER_H_ |
| | | #define RAPIDJSON_POINTER_H_ |
| | | |
| | | #include "document.h" |
| | | #include "internal/itoa.h" |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(switch-enum) |
| | | #endif |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated |
| | | #endif |
| | | |
| | | 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 |
| | | |
| | | //! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. |
| | | /*! |
| | | This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" |
| | | (https://tools.ietf.org/html/rfc6901). |
| | | |
| | | A JSON pointer is for identifying a specific value in a JSON document |
| | | (GenericDocument). It can simplify coding of DOM tree manipulation, because it |
| | | can access multiple-level depth of DOM tree with single API call. |
| | | |
| | | After it parses a string representation (e.g. "/foo/0" or URI fragment |
| | | representation (e.g. "#/foo/0") into its internal representation (tokens), |
| | | it can be used to resolve a specific value in multiple documents, or sub-tree |
| | | of documents. |
| | | |
| | | Contrary to GenericValue, Pointer can be copy constructed and copy assigned. |
| | | Apart from assignment, a Pointer cannot be modified after construction. |
| | | |
| | | Although Pointer is very convenient, please aware that constructing Pointer |
| | | involves parsing and dynamic memory allocation. A special constructor with user- |
| | | 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. |
| | | */ |
| | | template <typename ValueType, typename Allocator = CrtAllocator> |
| | | class GenericPointer { |
| | | public: |
| | | typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value |
| | | typedef typename ValueType::Ch Ch; //!< Character type from Value |
| | | |
| | | //! 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. |
| | | They are resolved according to the actual value type (object or array). |
| | | |
| | | For token that are not numbers, or the numeric value is out of bound |
| | | (greater than limits of SizeType), they are only treated as string form |
| | | (i.e. the token's index will be equal to kPointerInvalidIndex). |
| | | |
| | | This struct is public so that user can create a Pointer without parsing and |
| | | allocation, using a special constructor. |
| | | */ |
| | | struct Token { |
| | | const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. |
| | | SizeType length; //!< Length of the name. |
| | | SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. |
| | | }; |
| | | |
| | | //!@name Constructors and destructor. |
| | | //@{ |
| | | |
| | | //! Default constructor. |
| | | GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} |
| | | |
| | | //! Constructor that parses a string or URI fragment representation. |
| | | /*! |
| | | \param source A null-terminated, string or URI fragment representation of JSON pointer. |
| | | \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. |
| | | */ |
| | | explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { |
| | | Parse(source, internal::StrLen(source)); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Constructor that parses a string or URI fragment representation. |
| | | /*! |
| | | \param source A string or URI fragment representation of JSON pointer. |
| | | \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. |
| | | \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. |
| | | */ |
| | | explicit GenericPointer(const std::basic_string<Ch>& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { |
| | | Parse(source.c_str(), source.size()); |
| | | } |
| | | #endif |
| | | |
| | | //! Constructor that parses a string or URI fragment representation, with length of the source string. |
| | | /*! |
| | | \param source A string or URI fragment representation of JSON pointer. |
| | | \param length Length of source. |
| | | \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. |
| | | \note Slightly faster than the overload without length. |
| | | */ |
| | | GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { |
| | | Parse(source, length); |
| | | } |
| | | |
| | | //! Constructor with user-supplied tokens. |
| | | /*! |
| | | This constructor let user supplies const array of tokens. |
| | | This prevents the parsing process and eliminates allocation. |
| | | This is preferred for memory constrained environments. |
| | | |
| | | \param tokens An constant array of tokens representing the JSON pointer. |
| | | \param tokenCount Number of tokens. |
| | | |
| | | \b Example |
| | | \code |
| | | #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } |
| | | #define INDEX(i) { #i, sizeof(#i) - 1, i } |
| | | |
| | | static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; |
| | | static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); |
| | | // Equivalent to static const Pointer p("/foo/123"); |
| | | |
| | | #undef NAME |
| | | #undef INDEX |
| | | \endcode |
| | | */ |
| | | 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) { |
| | | *this = rhs; |
| | | } |
| | | |
| | | //! Destructor. |
| | | ~GenericPointer() { |
| | | if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. |
| | | Allocator::Free(tokens_); |
| | | RAPIDJSON_DELETE(ownAllocator_); |
| | | } |
| | | |
| | | //! Assignment operator. |
| | | GenericPointer& operator=(const GenericPointer& rhs) { |
| | | if (this != &rhs) { |
| | | // Do not delete ownAllcator |
| | | if (nameBuffer_) |
| | | Allocator::Free(tokens_); |
| | | |
| | | tokenCount_ = rhs.tokenCount_; |
| | | parseErrorOffset_ = rhs.parseErrorOffset_; |
| | | parseErrorCode_ = rhs.parseErrorCode_; |
| | | |
| | | if (rhs.nameBuffer_) |
| | | CopyFromRaw(rhs); // Normally parsed tokens. |
| | | else { |
| | | tokens_ = rhs.tokens_; // User supplied const tokens. |
| | | nameBuffer_ = 0; |
| | | } |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | //@} |
| | | |
| | | //!@name Append token |
| | | //@{ |
| | | |
| | | //! Append a token and return a new Pointer |
| | | /*! |
| | | \param token Token to be appended. |
| | | \param allocator Allocator for the newly return Pointer. |
| | | \return A new Pointer with appended token. |
| | | */ |
| | | GenericPointer Append(const Token& token, Allocator* allocator = 0) const { |
| | | GenericPointer r; |
| | | r.allocator_ = allocator; |
| | | Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); |
| | | std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); |
| | | r.tokens_[tokenCount_].name = p; |
| | | r.tokens_[tokenCount_].length = token.length; |
| | | r.tokens_[tokenCount_].index = token.index; |
| | | return r; |
| | | } |
| | | |
| | | //! Append a name token with length, and return a new Pointer |
| | | /*! |
| | | \param name Name to be appended. |
| | | \param length Length of name. |
| | | \param allocator Allocator for the newly return Pointer. |
| | | \return A new Pointer with appended token. |
| | | */ |
| | | GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { |
| | | Token token = { name, length, kPointerInvalidIndex }; |
| | | return Append(token, allocator); |
| | | } |
| | | |
| | | //! Append a name token without length, and return a new Pointer |
| | | /*! |
| | | \param name Name (const Ch*) to be appended. |
| | | \param allocator Allocator for the newly return Pointer. |
| | | \return A new Pointer with appended token. |
| | | */ |
| | | 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, internal::StrLen(name), allocator); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Append a name token, and return a new Pointer |
| | | /*! |
| | | \param name Name to be appended. |
| | | \param allocator Allocator for the newly return Pointer. |
| | | \return A new Pointer with appended token. |
| | | */ |
| | | GenericPointer Append(const std::basic_string<Ch>& name, Allocator* allocator = 0) const { |
| | | return Append(name.c_str(), static_cast<SizeType>(name.size()), allocator); |
| | | } |
| | | #endif |
| | | |
| | | //! Append a index token, and return a new Pointer |
| | | /*! |
| | | \param index Index to be appended. |
| | | \param allocator Allocator for the newly return Pointer. |
| | | \return A new Pointer with appended token. |
| | | */ |
| | | GenericPointer Append(SizeType index, Allocator* allocator = 0) const { |
| | | char buffer[21]; |
| | | char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); |
| | | SizeType length = static_cast<SizeType>(end - buffer); |
| | | buffer[length] = '\0'; |
| | | |
| | | if (sizeof(Ch) == 1) { |
| | | Token token = { reinterpret_cast<Ch*>(buffer), length, index }; |
| | | return Append(token, allocator); |
| | | } |
| | | else { |
| | | Ch name[21]; |
| | | for (size_t i = 0; i <= length; i++) |
| | | name[i] = static_cast<Ch>(buffer[i]); |
| | | Token token = { name, length, index }; |
| | | return Append(token, allocator); |
| | | } |
| | | } |
| | | |
| | | //! Append a token by value, and return a new Pointer |
| | | /*! |
| | | \param token token to be appended. |
| | | \param allocator Allocator for the newly return Pointer. |
| | | \return A new Pointer with appended token. |
| | | */ |
| | | GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { |
| | | if (token.IsString()) |
| | | return Append(token.GetString(), token.GetStringLength(), allocator); |
| | | else { |
| | | RAPIDJSON_ASSERT(token.IsUint64()); |
| | | RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); |
| | | return Append(static_cast<SizeType>(token.GetUint64()), allocator); |
| | | } |
| | | } |
| | | |
| | | //!@name Handling Parse Error |
| | | //@{ |
| | | |
| | | //! Check whether this is a valid pointer. |
| | | bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } |
| | | |
| | | //! Get the parsing error offset in code unit. |
| | | size_t GetParseErrorOffset() const { return parseErrorOffset_; } |
| | | |
| | | //! Get the parsing error code. |
| | | PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } |
| | | |
| | | //@} |
| | | |
| | | //! Get the allocator of this pointer. |
| | | Allocator& GetAllocator() { return *allocator_; } |
| | | |
| | | //!@name Tokens |
| | | //@{ |
| | | |
| | | //! Get the token array (const version only). |
| | | const Token* GetTokens() const { return tokens_; } |
| | | |
| | | //! Get the number of tokens. |
| | | size_t GetTokenCount() const { return tokenCount_; } |
| | | |
| | | //@} |
| | | |
| | | //!@name Equality/inequality operators |
| | | //@{ |
| | | |
| | | //! Equality operator. |
| | | /*! |
| | | \note When any pointers are invalid, always returns false. |
| | | */ |
| | | bool operator==(const GenericPointer& rhs) const { |
| | | if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) |
| | | return false; |
| | | |
| | | for (size_t i = 0; i < tokenCount_; i++) { |
| | | if (tokens_[i].index != rhs.tokens_[i].index || |
| | | tokens_[i].length != rhs.tokens_[i].length || |
| | | (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | //! Inequality operator. |
| | | /*! |
| | | \note When any pointers are invalid, always returns true. |
| | | */ |
| | | bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } |
| | | |
| | | //@} |
| | | |
| | | //!@name Stringify |
| | | //@{ |
| | | |
| | | //! Stringify the pointer into string representation. |
| | | /*! |
| | | \tparam OutputStream Type of output stream. |
| | | \param os The output stream. |
| | | */ |
| | | template<typename OutputStream> |
| | | bool Stringify(OutputStream& os) const { |
| | | return Stringify<false, OutputStream>(os); |
| | | } |
| | | |
| | | //! Stringify the pointer into URI fragment representation. |
| | | /*! |
| | | \tparam OutputStream Type of output stream. |
| | | \param os The output stream. |
| | | */ |
| | | template<typename OutputStream> |
| | | bool StringifyUriFragment(OutputStream& os) const { |
| | | return Stringify<true, OutputStream>(os); |
| | | } |
| | | |
| | | //@} |
| | | |
| | | //!@name Create value |
| | | //@{ |
| | | |
| | | //! Create a value in a subtree. |
| | | /*! |
| | | If the value is not exist, it creates all parent values and a JSON Null value. |
| | | So it always succeed and return the newly created or existing value. |
| | | |
| | | Remind that it may change types of parents according to tokens, so it |
| | | potentially removes previously stored values. For example, if a document |
| | | was an array, and "/foo" is used to create a value, then the document |
| | | will be changed to an object, and all existing array elements are lost. |
| | | |
| | | \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. |
| | | \param allocator Allocator for creating the values if the specified value or its parents are not exist. |
| | | \param alreadyExist If non-null, it stores whether the resolved value is already exist. |
| | | \return The resolved newly created (a JSON Null value), or already exists value. |
| | | */ |
| | | ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { |
| | | RAPIDJSON_ASSERT(IsValid()); |
| | | ValueType* v = &root; |
| | | bool exist = true; |
| | | for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { |
| | | if (v->IsArray() && t->name[0] == '-' && t->length == 1) { |
| | | v->PushBack(ValueType().Move(), allocator); |
| | | v = &((*v)[v->Size() - 1]); |
| | | exist = false; |
| | | } |
| | | else { |
| | | if (t->index == kPointerInvalidIndex) { // must be object name |
| | | if (!v->IsObject()) |
| | | v->SetObject(); // Change to Object |
| | | } |
| | | else { // object name or array index |
| | | if (!v->IsArray() && !v->IsObject()) |
| | | v->SetArray(); // Change to Array |
| | | } |
| | | |
| | | if (v->IsArray()) { |
| | | if (t->index >= v->Size()) { |
| | | v->Reserve(t->index + 1, allocator); |
| | | while (t->index >= v->Size()) |
| | | v->PushBack(ValueType().Move(), allocator); |
| | | exist = false; |
| | | } |
| | | v = &((*v)[t->index]); |
| | | } |
| | | else { |
| | | typename ValueType::MemberIterator m = v->FindMember(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 |
| | | exist = false; |
| | | } |
| | | else |
| | | v = &m->value; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (alreadyExist) |
| | | *alreadyExist = exist; |
| | | |
| | | return *v; |
| | | } |
| | | |
| | | //! Creates a value in a document. |
| | | /*! |
| | | \param document A document to be resolved. |
| | | \param alreadyExist If non-null, it stores whether the resolved value is already exist. |
| | | \return The resolved newly created, or already exists value. |
| | | */ |
| | | template <typename stackAllocator> |
| | | ValueType& Create(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, bool* alreadyExist = 0) const { |
| | | return Create(document, document.GetAllocator(), alreadyExist); |
| | | } |
| | | |
| | | //@} |
| | | |
| | | //!@name Query value |
| | | //@{ |
| | | |
| | | //! Query a value in a subtree. |
| | | /*! |
| | | \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. |
| | | \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. |
| | | \return Pointer to the value if it can be resolved. Otherwise null. |
| | | |
| | | \note |
| | | There are only 3 situations when a value 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. |
| | | */ |
| | | ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { |
| | | RAPIDJSON_ASSERT(IsValid()); |
| | | ValueType* v = &root; |
| | | for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { |
| | | switch (v->GetType()) { |
| | | case kObjectType: |
| | | { |
| | | typename ValueType::MemberIterator m = v->FindMember(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 0; |
| | | } |
| | | return v; |
| | | } |
| | | |
| | | //! Query a const value in a const subtree. |
| | | /*! |
| | | \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. |
| | | \return Pointer to the value if it can be resolved. Otherwise null. |
| | | */ |
| | | const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { |
| | | return Get(const_cast<ValueType&>(root), unresolvedTokenIndex); |
| | | } |
| | | |
| | | //@} |
| | | |
| | | //!@name Query a value with default |
| | | //@{ |
| | | |
| | | //! Query a value in a subtree with default value. |
| | | /*! |
| | | Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. |
| | | So that this function always succeed. |
| | | |
| | | \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. |
| | | \param defaultValue Default value to be cloned if the value was not exists. |
| | | \param allocator Allocator for creating the values if the specified value or its parents are not exist. |
| | | \see Create() |
| | | */ |
| | | ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { |
| | | bool alreadyExist; |
| | | Value& 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); |
| | | return alreadyExist ? v : v.SetString(defaultValue, allocator); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! 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); |
| | | return alreadyExist ? v : v.SetString(defaultValue, allocator); |
| | | } |
| | | #endif |
| | | |
| | | //! Query a value in a subtree with default primitive value. |
| | | /*! |
| | | \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool |
| | | */ |
| | | template <typename T> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) |
| | | GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { |
| | | return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); |
| | | } |
| | | |
| | | //! Query a value in a document with default value. |
| | | template <typename stackAllocator> |
| | | ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& defaultValue) const { |
| | | return GetWithDefault(document, defaultValue, document.GetAllocator()); |
| | | } |
| | | |
| | | //! Query a value in a document with default null-terminated string. |
| | | template <typename stackAllocator> |
| | | 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> |
| | | ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& defaultValue) const { |
| | | return GetWithDefault(document, defaultValue, document.GetAllocator()); |
| | | } |
| | | #endif |
| | | |
| | | //! Query a value in a document with default primitive value. |
| | | /*! |
| | | \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool |
| | | */ |
| | | template <typename T, typename stackAllocator> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) |
| | | GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T defaultValue) const { |
| | | return GetWithDefault(document, defaultValue, document.GetAllocator()); |
| | | } |
| | | |
| | | //@} |
| | | |
| | | //!@name Set a value |
| | | //@{ |
| | | |
| | | //! Set a value in a subtree, with move semantics. |
| | | /*! |
| | | It creates all parents if they are not exist or types are different to the tokens. |
| | | So this function always succeeds but potentially remove existing values. |
| | | |
| | | \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. |
| | | \param value Value to be set. |
| | | \param allocator Allocator for creating the values if the specified value or its parents are not exist. |
| | | \see Create() |
| | | */ |
| | | ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { |
| | | return Create(root, allocator) = value; |
| | | } |
| | | |
| | | //! Set a value in a subtree, with copy semantics. |
| | | ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { |
| | | return Create(root, allocator).CopyFrom(value, allocator); |
| | | } |
| | | |
| | | //! Set a null-terminated string in a subtree. |
| | | ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { |
| | | return Create(root, allocator) = ValueType(value, allocator).Move(); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Set a std::basic_string in a subtree. |
| | | ValueType& Set(ValueType& root, const std::basic_string<Ch>& value, typename ValueType::AllocatorType& allocator) const { |
| | | return Create(root, allocator) = ValueType(value, allocator).Move(); |
| | | } |
| | | #endif |
| | | |
| | | //! Set a primitive value in a subtree. |
| | | /*! |
| | | \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool |
| | | */ |
| | | template <typename T> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) |
| | | Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { |
| | | return Create(root, allocator) = ValueType(value).Move(); |
| | | } |
| | | |
| | | //! Set a value in a document, with move semantics. |
| | | template <typename stackAllocator> |
| | | ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const { |
| | | return Create(document) = value; |
| | | } |
| | | |
| | | //! Set a value in a document, with copy semantics. |
| | | template <typename stackAllocator> |
| | | ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& value) const { |
| | | return Create(document).CopyFrom(value, document.GetAllocator()); |
| | | } |
| | | |
| | | //! Set a null-terminated string in a document. |
| | | template <typename stackAllocator> |
| | | ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* value) const { |
| | | return Create(document) = ValueType(value, document.GetAllocator()).Move(); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | //! Sets a std::basic_string in a document. |
| | | template <typename stackAllocator> |
| | | ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& value) const { |
| | | return Create(document) = ValueType(value, document.GetAllocator()).Move(); |
| | | } |
| | | #endif |
| | | |
| | | //! Set a primitive value in a document. |
| | | /*! |
| | | \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool |
| | | */ |
| | | template <typename T, typename stackAllocator> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) |
| | | Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T value) const { |
| | | return Create(document) = value; |
| | | } |
| | | |
| | | //@} |
| | | |
| | | //!@name Swap a value |
| | | //@{ |
| | | |
| | | //! Swap a value with a value in a subtree. |
| | | /*! |
| | | It creates all parents if they are not exist or types are different to the tokens. |
| | | So this function always succeeds but potentially remove existing values. |
| | | |
| | | \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. |
| | | \param value Value to be swapped. |
| | | \param allocator Allocator for creating the values if the specified value or its parents are not exist. |
| | | \see Create() |
| | | */ |
| | | ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { |
| | | return Create(root, allocator).Swap(value); |
| | | } |
| | | |
| | | //! Swap a value with a value in a document. |
| | | template <typename stackAllocator> |
| | | ValueType& Swap(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const { |
| | | return Create(document).Swap(value); |
| | | } |
| | | |
| | | //@} |
| | | |
| | | //! Erase a value in a subtree. |
| | | /*! |
| | | \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. |
| | | \return Whether the resolved value is found and erased. |
| | | |
| | | \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. |
| | | */ |
| | | bool Erase(ValueType& root) const { |
| | | RAPIDJSON_ASSERT(IsValid()); |
| | | if (tokenCount_ == 0) // Cannot erase the root |
| | | return false; |
| | | |
| | | ValueType* v = &root; |
| | | const Token* last = tokens_ + (tokenCount_ - 1); |
| | | for (const Token *t = tokens_; t != last; ++t) { |
| | | switch (v->GetType()) { |
| | | case kObjectType: |
| | | { |
| | | typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length)); |
| | | if (m == v->MemberEnd()) |
| | | return false; |
| | | v = &m->value; |
| | | } |
| | | break; |
| | | case kArrayType: |
| | | if (t->index == kPointerInvalidIndex || t->index >= v->Size()) |
| | | return false; |
| | | v = &((*v)[t->index]); |
| | | break; |
| | | default: |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | switch (v->GetType()) { |
| | | case kObjectType: |
| | | return v->EraseMember(GenericStringRef<Ch>(last->name, last->length)); |
| | | case kArrayType: |
| | | if (last->index == kPointerInvalidIndex || last->index >= v->Size()) |
| | | return false; |
| | | v->Erase(v->Begin() + last->index); |
| | | return true; |
| | | default: |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private: |
| | | //! Clone the content from rhs to this. |
| | | /*! |
| | | \param rhs Source pointer. |
| | | \param extraToken Extra tokens to be allocated. |
| | | \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. |
| | | \return Start of non-occupied name buffer, for storing extra names. |
| | | */ |
| | | Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { |
| | | if (!allocator_) // allocator is independently owned. |
| | | 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) |
| | | nameBufferSize += t->length; |
| | | |
| | | tokenCount_ = rhs.tokenCount_ + extraToken; |
| | | tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); |
| | | nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_); |
| | | if (rhs.tokenCount_ > 0) { |
| | | std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); |
| | | } |
| | | if (nameBufferSize > 0) { |
| | | std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); |
| | | } |
| | | |
| | | // Adjust pointers to name buffer |
| | | std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; |
| | | for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) |
| | | t->name += diff; |
| | | |
| | | return nameBuffer_ + nameBufferSize; |
| | | } |
| | | |
| | | //! Check whether a character should be percent-encoded. |
| | | /*! |
| | | According to RFC 3986 2.3 Unreserved Characters. |
| | | \param c The character (code unit) to be tested. |
| | | */ |
| | | bool NeedPercentEncode(Ch c) const { |
| | | return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); |
| | | } |
| | | |
| | | //! Parse a JSON String or its URI fragment representation into tokens. |
| | | #ifndef __clang__ // -Wdocumentation |
| | | /*! |
| | | \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. |
| | | \param length Length of the source string. |
| | | \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. |
| | | */ |
| | | #endif |
| | | void Parse(const Ch* source, size_t length) { |
| | | RAPIDJSON_ASSERT(source != NULL); |
| | | RAPIDJSON_ASSERT(nameBuffer_ == 0); |
| | | RAPIDJSON_ASSERT(tokens_ == 0); |
| | | |
| | | // Create own allocator if user did not supply. |
| | | if (!allocator_) |
| | | ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); |
| | | |
| | | // Count number of '/' as tokenCount |
| | | tokenCount_ = 0; |
| | | for (const Ch* s = source; s != source + length; s++) |
| | | if (*s == '/') |
| | | tokenCount_++; |
| | | |
| | | Token* token = tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); |
| | | Ch* name = nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_); |
| | | size_t i = 0; |
| | | |
| | | // Detect if it is a URI fragment |
| | | bool uriFragment = false; |
| | | if (source[i] == '#') { |
| | | uriFragment = true; |
| | | i++; |
| | | } |
| | | |
| | | if (i != length && source[i] != '/') { |
| | | parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; |
| | | goto error; |
| | | } |
| | | |
| | | while (i < length) { |
| | | RAPIDJSON_ASSERT(source[i] == '/'); |
| | | i++; // consumes '/' |
| | | |
| | | token->name = name; |
| | | bool isNumber = true; |
| | | |
| | | while (i < length && source[i] != '/') { |
| | | Ch c = source[i]; |
| | | if (uriFragment) { |
| | | // Decoding percent-encoding for URI fragment |
| | | if (c == '%') { |
| | | PercentDecodeStream is(&source[i], source + length); |
| | | GenericInsituStringStream<EncodingType> os(name); |
| | | Ch* begin = os.PutBegin(); |
| | | if (!Transcoder<UTF8<>, EncodingType>().Validate(is, os) || !is.IsValid()) { |
| | | parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; |
| | | goto error; |
| | | } |
| | | size_t len = os.PutEnd(begin); |
| | | i += is.Tell() - 1; |
| | | if (len == 1) |
| | | c = *name; |
| | | else { |
| | | name += len; |
| | | isNumber = false; |
| | | i++; |
| | | continue; |
| | | } |
| | | } |
| | | else if (NeedPercentEncode(c)) { |
| | | parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; |
| | | goto error; |
| | | } |
| | | } |
| | | |
| | | i++; |
| | | |
| | | // Escaping "~0" -> '~', "~1" -> '/' |
| | | if (c == '~') { |
| | | if (i < length) { |
| | | c = source[i]; |
| | | if (c == '0') c = '~'; |
| | | else if (c == '1') c = '/'; |
| | | else { |
| | | parseErrorCode_ = kPointerParseErrorInvalidEscape; |
| | | goto error; |
| | | } |
| | | i++; |
| | | } |
| | | else { |
| | | parseErrorCode_ = kPointerParseErrorInvalidEscape; |
| | | goto error; |
| | | } |
| | | } |
| | | |
| | | // First check for index: all of characters are digit |
| | | if (c < '0' || c > '9') |
| | | isNumber = false; |
| | | |
| | | *name++ = c; |
| | | } |
| | | token->length = static_cast<SizeType>(name - token->name); |
| | | if (token->length == 0) |
| | | isNumber = false; |
| | | *name++ = '\0'; // Null terminator |
| | | |
| | | // Second check for index: more than one digit cannot have leading zero |
| | | if (isNumber && token->length > 1 && token->name[0] == '0') |
| | | isNumber = false; |
| | | |
| | | // String to SizeType conversion |
| | | SizeType n = 0; |
| | | if (isNumber) { |
| | | for (size_t j = 0; j < token->length; j++) { |
| | | SizeType m = n * 10 + static_cast<SizeType>(token->name[j] - '0'); |
| | | if (m < n) { // overflow detection |
| | | isNumber = false; |
| | | break; |
| | | } |
| | | n = m; |
| | | } |
| | | } |
| | | |
| | | token->index = isNumber ? n : kPointerInvalidIndex; |
| | | token++; |
| | | } |
| | | |
| | | RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer |
| | | parseErrorCode_ = kPointerParseErrorNone; |
| | | return; |
| | | |
| | | error: |
| | | Allocator::Free(tokens_); |
| | | nameBuffer_ = 0; |
| | | tokens_ = 0; |
| | | tokenCount_ = 0; |
| | | parseErrorOffset_ = i; |
| | | return; |
| | | } |
| | | |
| | | //! Stringify to string or URI fragment representation. |
| | | /*! |
| | | \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. |
| | | \tparam OutputStream type of output stream. |
| | | \param os The output stream. |
| | | */ |
| | | template<bool uriFragment, typename OutputStream> |
| | | bool Stringify(OutputStream& os) const { |
| | | RAPIDJSON_ASSERT(IsValid()); |
| | | |
| | | if (uriFragment) |
| | | os.Put('#'); |
| | | |
| | | for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { |
| | | os.Put('/'); |
| | | for (size_t j = 0; j < t->length; j++) { |
| | | Ch c = t->name[j]; |
| | | if (c == '~') { |
| | | os.Put('~'); |
| | | os.Put('0'); |
| | | } |
| | | else if (c == '/') { |
| | | os.Put('~'); |
| | | os.Put('1'); |
| | | } |
| | | else if (uriFragment && NeedPercentEncode(c)) { |
| | | // Transcode to UTF8 sequence |
| | | GenericStringStream<typename ValueType::EncodingType> source(&t->name[j]); |
| | | PercentEncodeStream<OutputStream> target(os); |
| | | if (!Transcoder<EncodingType, UTF8<> >().Validate(source, target)) |
| | | return false; |
| | | j += source.Tell() - 1; |
| | | } |
| | | else |
| | | os.Put(c); |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | //! A helper stream for decoding a percent-encoded sequence into code unit. |
| | | /*! |
| | | This stream decodes %XY triplet into code unit (0-255). |
| | | If it encounters invalid characters, it sets output code unit as 0 and |
| | | mark invalid, and to be checked by IsValid(). |
| | | */ |
| | | class PercentDecodeStream { |
| | | public: |
| | | typedef typename ValueType::Ch Ch; |
| | | |
| | | //! Constructor |
| | | /*! |
| | | \param source Start of the stream |
| | | \param end Past-the-end of the stream. |
| | | */ |
| | | PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} |
| | | |
| | | Ch Take() { |
| | | if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet |
| | | valid_ = false; |
| | | return 0; |
| | | } |
| | | src_++; |
| | | Ch c = 0; |
| | | for (int j = 0; j < 2; j++) { |
| | | c = static_cast<Ch>(c << 4); |
| | | Ch h = *src_; |
| | | if (h >= '0' && h <= '9') c = static_cast<Ch>(c + h - '0'); |
| | | else if (h >= 'A' && h <= 'F') c = static_cast<Ch>(c + h - 'A' + 10); |
| | | else if (h >= 'a' && h <= 'f') c = static_cast<Ch>(c + h - 'a' + 10); |
| | | else { |
| | | valid_ = false; |
| | | return 0; |
| | | } |
| | | src_++; |
| | | } |
| | | return c; |
| | | } |
| | | |
| | | size_t Tell() const { return static_cast<size_t>(src_ - head_); } |
| | | bool IsValid() const { return valid_; } |
| | | |
| | | private: |
| | | const Ch* src_; //!< Current read position. |
| | | const Ch* head_; //!< Original head of the string. |
| | | const Ch* end_; //!< Past-the-end position. |
| | | bool valid_; //!< Whether the parsing is valid. |
| | | }; |
| | | |
| | | //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. |
| | | template <typename OutputStream> |
| | | class PercentEncodeStream { |
| | | public: |
| | | PercentEncodeStream(OutputStream& os) : os_(os) {} |
| | | void Put(char c) { // UTF-8 must be byte |
| | | 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(static_cast<typename OutputStream::Ch>(hexDigits[u >> 4])); |
| | | os_.Put(static_cast<typename OutputStream::Ch>(hexDigits[u & 15])); |
| | | } |
| | | private: |
| | | OutputStream& os_; |
| | | }; |
| | | |
| | | Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. |
| | | Allocator* ownAllocator_; //!< Allocator owned by this Pointer. |
| | | Ch* nameBuffer_; //!< A buffer containing all names in tokens. |
| | | Token* tokens_; //!< A list of tokens. |
| | | size_t tokenCount_; //!< Number of tokens in tokens_. |
| | | size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. |
| | | PointerParseErrorCode parseErrorCode_; //!< Parsing error code. |
| | | }; |
| | | |
| | | //! GenericPointer for Value (UTF-8, default allocator). |
| | | typedef GenericPointer<Value> Pointer; |
| | | |
| | | //!@name Helper functions for GenericPointer |
| | | //@{ |
| | | |
| | | ////////////////////////////////////////////////////////////////////////////// |
| | | |
| | | template <typename T> |
| | | typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::AllocatorType& a) { |
| | | return pointer.Create(root, a); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Create(root, a); |
| | | } |
| | | |
| | | // No allocator parameter |
| | | |
| | | template <typename DocumentType> |
| | | typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer) { |
| | | return pointer.Create(document); |
| | | } |
| | | |
| | | template <typename DocumentType, typename CharType, size_t N> |
| | | typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Create(document); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////////////// |
| | | |
| | | template <typename T> |
| | | typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) { |
| | | return pointer.Get(root, unresolvedTokenIndex); |
| | | } |
| | | |
| | | template <typename T> |
| | | const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) { |
| | | return pointer.Get(root, unresolvedTokenIndex); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////////////// |
| | | |
| | | template <typename T> |
| | | typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { |
| | | return pointer.GetWithDefault(root, defaultValue, a); |
| | | } |
| | | |
| | | template <typename T> |
| | | typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { |
| | | return pointer.GetWithDefault(root, defaultValue, a); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template <typename T> |
| | | typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) { |
| | | return pointer.GetWithDefault(root, defaultValue, a); |
| | | } |
| | | #endif |
| | | |
| | | template <typename T, typename T2> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&)) |
| | | GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 defaultValue, typename T::AllocatorType& a) { |
| | | return pointer.GetWithDefault(root, defaultValue, a); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a); |
| | | } |
| | | #endif |
| | | |
| | | template <typename T, typename CharType, size_t N, typename T2> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&)) |
| | | GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a); |
| | | } |
| | | |
| | | // No allocator parameter |
| | | |
| | | template <typename DocumentType> |
| | | typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& defaultValue) { |
| | | return pointer.GetWithDefault(document, defaultValue); |
| | | } |
| | | |
| | | template <typename DocumentType> |
| | | typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* defaultValue) { |
| | | return pointer.GetWithDefault(document, defaultValue); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template <typename DocumentType> |
| | | typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& defaultValue) { |
| | | return pointer.GetWithDefault(document, defaultValue); |
| | | } |
| | | #endif |
| | | |
| | | template <typename DocumentType, typename T2> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&)) |
| | | GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 defaultValue) { |
| | | return pointer.GetWithDefault(document, defaultValue); |
| | | } |
| | | |
| | | template <typename DocumentType, typename CharType, size_t N> |
| | | typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue); |
| | | } |
| | | |
| | | template <typename DocumentType, typename CharType, size_t N> |
| | | typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template <typename DocumentType, typename CharType, size_t N> |
| | | typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& defaultValue) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue); |
| | | } |
| | | #endif |
| | | |
| | | template <typename DocumentType, typename CharType, size_t N, typename T2> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&)) |
| | | GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////////////// |
| | | |
| | | template <typename T> |
| | | typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { |
| | | return pointer.Set(root, value, a); |
| | | } |
| | | |
| | | template <typename T> |
| | | typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { |
| | | return pointer.Set(root, value, a); |
| | | } |
| | | |
| | | template <typename T> |
| | | typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { |
| | | return pointer.Set(root, value, a); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template <typename T> |
| | | typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) { |
| | | return pointer.Set(root, value, a); |
| | | } |
| | | #endif |
| | | |
| | | template <typename T, typename T2> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&)) |
| | | SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 value, typename T::AllocatorType& a) { |
| | | return pointer.Set(root, value, a); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); |
| | | } |
| | | #endif |
| | | |
| | | template <typename T, typename CharType, size_t N, typename T2> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&)) |
| | | SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); |
| | | } |
| | | |
| | | // No allocator parameter |
| | | |
| | | template <typename DocumentType> |
| | | typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) { |
| | | return pointer.Set(document, value); |
| | | } |
| | | |
| | | template <typename DocumentType> |
| | | typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& value) { |
| | | return pointer.Set(document, value); |
| | | } |
| | | |
| | | template <typename DocumentType> |
| | | typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* value) { |
| | | return pointer.Set(document, value); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template <typename DocumentType> |
| | | typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& value) { |
| | | return pointer.Set(document, value); |
| | | } |
| | | #endif |
| | | |
| | | template <typename DocumentType, typename T2> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&)) |
| | | SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 value) { |
| | | return pointer.Set(document, value); |
| | | } |
| | | |
| | | template <typename DocumentType, typename CharType, size_t N> |
| | | typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); |
| | | } |
| | | |
| | | template <typename DocumentType, typename CharType, size_t N> |
| | | typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); |
| | | } |
| | | |
| | | template <typename DocumentType, typename CharType, size_t N> |
| | | typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | template <typename DocumentType, typename CharType, size_t N> |
| | | typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& value) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); |
| | | } |
| | | #endif |
| | | |
| | | template <typename DocumentType, typename CharType, size_t N, typename T2> |
| | | RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&)) |
| | | SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////////////// |
| | | |
| | | template <typename T> |
| | | typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { |
| | | return pointer.Swap(root, value, a); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Swap(root, value, a); |
| | | } |
| | | |
| | | template <typename DocumentType> |
| | | typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) { |
| | | return pointer.Swap(document, value); |
| | | } |
| | | |
| | | template <typename DocumentType, typename CharType, size_t N> |
| | | typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { |
| | | return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Swap(document, value); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////////////// |
| | | |
| | | template <typename T> |
| | | bool EraseValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) { |
| | | return pointer.Erase(root); |
| | | } |
| | | |
| | | template <typename T, typename CharType, size_t N> |
| | | bool EraseValueByPointer(T& root, const CharType(&source)[N]) { |
| | | return GenericPointer<typename T::ValueType>(source, N - 1).Erase(root); |
| | | } |
| | | |
| | | //@} |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_POINTER_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_PRETTYWRITER_H_ |
| | | #define RAPIDJSON_PRETTYWRITER_H_ |
| | | |
| | | #include "writer.h" |
| | | |
| | | #ifdef __GNUC__ |
| | | 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. |
| | | /*! \see PrettyWriter::SetFormatOptions |
| | | */ |
| | | enum PrettyFormatOptions { |
| | | kFormatDefault = 0, //!< Default pretty formatting. |
| | | kFormatSingleLineArray = 1 //!< Format arrays on a single line. |
| | | }; |
| | | |
| | | //! Writer with indentation and spacing. |
| | | /*! |
| | | \tparam OutputStream Type of ouptut os. |
| | | \tparam SourceEncoding Encoding of source string. |
| | | \tparam TargetEncoding Encoding of output stream. |
| | | \tparam StackAllocator Type of allocator for allocating memory of stack. |
| | | */ |
| | | 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, writeFlags> Base; |
| | | typedef typename Base::Ch Ch; |
| | | |
| | | //! Constructor |
| | | /*! \param os Output stream. |
| | | \param allocator User supplied allocator. If it is null, it will create a private one. |
| | | \param levelDepth Initial capacity of stack. |
| | | */ |
| | | explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : |
| | | Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} |
| | | |
| | | |
| | | explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : |
| | | Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} |
| | | |
| | | #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'). |
| | | \param indentCharCount Number of indent characters for each indentation level. |
| | | \note The default indentation is 4 spaces. |
| | | */ |
| | | PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { |
| | | RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); |
| | | indentChar_ = indentChar; |
| | | indentCharCount_ = indentCharCount; |
| | | return *this; |
| | | } |
| | | |
| | | //! Set pretty writer formatting options. |
| | | /*! \param options Formatting options. |
| | | */ |
| | | PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { |
| | | formatOptions_ = options; |
| | | return *this; |
| | | } |
| | | |
| | | /*! @name Implementation of Handler |
| | | \see Handler |
| | | */ |
| | | //@{ |
| | | |
| | | 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 RawNumber(const Ch* str, SizeType length, bool copy = false) { |
| | | RAPIDJSON_ASSERT(str != 0); |
| | | (void)copy; |
| | | PrettyPrefix(kNumberType); |
| | | return 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); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | bool String(const std::basic_string<Ch>& str) { |
| | | return String(str.data(), SizeType(str.size())); |
| | | } |
| | | #endif |
| | | |
| | | bool StartObject() { |
| | | PrettyPrefix(kObjectType); |
| | | new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false); |
| | | return Base::WriteStartObject(); |
| | | } |
| | | |
| | | 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(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(); |
| | | (void)ret; |
| | | RAPIDJSON_ASSERT(ret == true); |
| | | if (Base::level_stack_.Empty()) // end of json text |
| | | Base::Flush(); |
| | | return true; |
| | | } |
| | | |
| | | bool StartArray() { |
| | | PrettyPrefix(kArrayType); |
| | | new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true); |
| | | return Base::WriteStartArray(); |
| | | } |
| | | |
| | | bool EndArray(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); |
| | | bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; |
| | | |
| | | if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { |
| | | Base::os_->Put('\n'); |
| | | WriteIndent(); |
| | | } |
| | | bool ret = Base::WriteEndArray(); |
| | | (void)ret; |
| | | RAPIDJSON_ASSERT(ret == true); |
| | | if (Base::level_stack_.Empty()) // end of json text |
| | | Base::Flush(); |
| | | return true; |
| | | } |
| | | |
| | | //@} |
| | | |
| | | /*! @name Convenience extensions */ |
| | | //@{ |
| | | |
| | | //! 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)); } |
| | | |
| | | //@} |
| | | |
| | | //! Write a raw JSON value. |
| | | /*! |
| | | For user to write a stringified JSON as a value. |
| | | |
| | | \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. |
| | | \param length Length of the json. |
| | | \param type Type of the root of json. |
| | | \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. |
| | | */ |
| | | bool RawValue(const Ch* json, size_t length, Type type) { |
| | | RAPIDJSON_ASSERT(json != 0); |
| | | PrettyPrefix(type); |
| | | return Base::WriteRawValue(json, length); |
| | | } |
| | | |
| | | protected: |
| | | void PrettyPrefix(Type type) { |
| | | (void)type; |
| | | if (Base::level_stack_.GetSize() != 0) { // this value is not at root |
| | | typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>(); |
| | | |
| | | if (level->inArray) { |
| | | if (level->valueCount > 0) { |
| | | Base::os_->Put(','); // add comma if it is not the first element in array |
| | | if (formatOptions_ & kFormatSingleLineArray) |
| | | Base::os_->Put(' '); |
| | | } |
| | | |
| | | if (!(formatOptions_ & kFormatSingleLineArray)) { |
| | | Base::os_->Put('\n'); |
| | | WriteIndent(); |
| | | } |
| | | } |
| | | else { // in object |
| | | if (level->valueCount > 0) { |
| | | if (level->valueCount % 2 == 0) { |
| | | Base::os_->Put(','); |
| | | Base::os_->Put('\n'); |
| | | } |
| | | else { |
| | | Base::os_->Put(':'); |
| | | Base::os_->Put(' '); |
| | | } |
| | | } |
| | | else |
| | | Base::os_->Put('\n'); |
| | | |
| | | if (level->valueCount % 2 == 0) |
| | | WriteIndent(); |
| | | } |
| | | if (!level->inArray && level->valueCount % 2 == 0) |
| | | RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name |
| | | level->valueCount++; |
| | | } |
| | | else { |
| | | RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. |
| | | Base::hasRoot_ = true; |
| | | } |
| | | } |
| | | |
| | | void WriteIndent() { |
| | | size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; |
| | | PutN(*Base::os_, static_cast<typename OutputStream::Ch>(indentChar_), count); |
| | | } |
| | | |
| | | Ch indentChar_; |
| | | unsigned indentCharCount_; |
| | | PrettyFormatOptions formatOptions_; |
| | | |
| | | private: |
| | | // Prohibit copy constructor & assignment operator. |
| | | PrettyWriter(const PrettyWriter&); |
| | | PrettyWriter& operator=(const PrettyWriter&); |
| | | }; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #if defined(__clang__) |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_RAPIDJSON_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_RAPIDJSON_H_ |
| | | #define RAPIDJSON_RAPIDJSON_H_ |
| | | |
| | | /*!\file rapidjson.h |
| | | \brief common definitions and configuration |
| | | |
| | | \see RAPIDJSON_CONFIG |
| | | */ |
| | | |
| | | /*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration |
| | | \brief Configuration macros for library features |
| | | |
| | | 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 |
| | | preprocessor macros at compile-time. |
| | | |
| | | Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. |
| | | |
| | | \note These macros should be given on the compiler command-line |
| | | (where applicable) to avoid inconsistent values when compiling |
| | | different translation units of a single application. |
| | | */ |
| | | |
| | | #include <cstdlib> // malloc(), realloc(), free(), size_t |
| | | #include <cstring> // memset(), memcpy(), memmove(), memcmp() |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_VERSION_STRING |
| | | // |
| | | // ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. |
| | | // |
| | | |
| | | //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN |
| | | // 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 |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief Major version of RapidJSON in integer. |
| | | */ |
| | | /*! \def RAPIDJSON_MINOR_VERSION |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief Minor version of RapidJSON in integer. |
| | | */ |
| | | /*! \def RAPIDJSON_PATCH_VERSION |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief Patch version of RapidJSON in integer. |
| | | */ |
| | | /*! \def RAPIDJSON_VERSION_STRING |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief Version of RapidJSON in "<major>.<minor>.<patch>" string format. |
| | | */ |
| | | #define RAPIDJSON_MAJOR_VERSION 1 |
| | | #define RAPIDJSON_MINOR_VERSION 1 |
| | | #define RAPIDJSON_PATCH_VERSION 0 |
| | | #define RAPIDJSON_VERSION_STRING \ |
| | | RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_NAMESPACE_(BEGIN|END) |
| | | /*! \def RAPIDJSON_NAMESPACE |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief provide custom rapidjson namespace |
| | | |
| | | In order to avoid symbol clashes and/or "One Definition Rule" errors |
| | | between multiple inclusions of (different versions of) RapidJSON in |
| | | a single binary, users can customize the name of the main RapidJSON |
| | | namespace. |
| | | |
| | | In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE |
| | | to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple |
| | | levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref |
| | | RAPIDJSON_NAMESPACE_END need to be defined as well: |
| | | |
| | | \code |
| | | // in some .cpp file |
| | | #define RAPIDJSON_NAMESPACE my::rapidjson |
| | | #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { |
| | | #define RAPIDJSON_NAMESPACE_END } } |
| | | #include "rapidjson/..." |
| | | \endcode |
| | | |
| | | \see rapidjson |
| | | */ |
| | | /*! \def RAPIDJSON_NAMESPACE_BEGIN |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief provide custom rapidjson namespace (opening expression) |
| | | \see RAPIDJSON_NAMESPACE |
| | | */ |
| | | /*! \def RAPIDJSON_NAMESPACE_END |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief provide custom rapidjson namespace (closing expression) |
| | | \see RAPIDJSON_NAMESPACE |
| | | */ |
| | | #ifndef RAPIDJSON_NAMESPACE |
| | | #define RAPIDJSON_NAMESPACE rapidjson |
| | | #endif |
| | | #ifndef RAPIDJSON_NAMESPACE_BEGIN |
| | | #define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { |
| | | #endif |
| | | #ifndef RAPIDJSON_NAMESPACE_END |
| | | #define RAPIDJSON_NAMESPACE_END } |
| | | #endif |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_HAS_STDSTRING |
| | | |
| | | #ifndef RAPIDJSON_HAS_STDSTRING |
| | | #ifdef RAPIDJSON_DOXYGEN_RUNNING |
| | | #define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation |
| | | #else |
| | | #define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default |
| | | #endif |
| | | /*! \def RAPIDJSON_HAS_STDSTRING |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief Enable RapidJSON support for \c std::string |
| | | |
| | | By defining this preprocessor symbol to \c 1, several convenience functions for using |
| | | \ref rapidjson::GenericValue with \c std::string are enabled, especially |
| | | for construction and comparison. |
| | | |
| | | \hideinitializer |
| | | */ |
| | | #endif // !defined(RAPIDJSON_HAS_STDSTRING) |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | #include <string> |
| | | #endif // RAPIDJSON_HAS_STDSTRING |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_NO_INT64DEFINE |
| | | |
| | | /*! \def RAPIDJSON_NO_INT64DEFINE |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief Use external 64-bit integer types. |
| | | |
| | | RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types |
| | | to be available at global scope. |
| | | |
| | | If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to |
| | | prevent RapidJSON from defining its own types. |
| | | */ |
| | | #ifndef RAPIDJSON_NO_INT64DEFINE |
| | | //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN |
| | | #if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 |
| | | #include "msinttypes/stdint.h" |
| | | #include "msinttypes/inttypes.h" |
| | | #else |
| | | // Other compilers should have this. |
| | | #include <stdint.h> |
| | | #include <inttypes.h> |
| | | #endif |
| | | //!@endcond |
| | | #ifdef RAPIDJSON_DOXYGEN_RUNNING |
| | | #define RAPIDJSON_NO_INT64DEFINE |
| | | #endif |
| | | #endif // RAPIDJSON_NO_INT64TYPEDEF |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_FORCEINLINE |
| | | |
| | | #ifndef RAPIDJSON_FORCEINLINE |
| | | //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN |
| | | #if defined(_MSC_VER) && defined(NDEBUG) |
| | | #define RAPIDJSON_FORCEINLINE __forceinline |
| | | #elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) |
| | | #define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) |
| | | #else |
| | | #define RAPIDJSON_FORCEINLINE |
| | | #endif |
| | | //!@endcond |
| | | #endif // RAPIDJSON_FORCEINLINE |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_ENDIAN |
| | | #define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine |
| | | #define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine |
| | | |
| | | //! Endianness of the machine. |
| | | /*! |
| | | \def RAPIDJSON_ENDIAN |
| | | \ingroup RAPIDJSON_CONFIG |
| | | |
| | | GCC 4.6 provided macro for detecting endianness of the target machine. But other |
| | | compilers may not have this. User can define RAPIDJSON_ENDIAN to either |
| | | \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. |
| | | |
| | | Default detection implemented with reference to |
| | | \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html |
| | | \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp |
| | | */ |
| | | #ifndef RAPIDJSON_ENDIAN |
| | | // Detect with GCC 4.6's macro |
| | | # ifdef __BYTE_ORDER__ |
| | | # if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| | | # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN |
| | | # elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| | | # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN |
| | | # else |
| | | # error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. |
| | | # endif // __BYTE_ORDER__ |
| | | // Detect with GLIBC's endian.h |
| | | # elif defined(__GLIBC__) |
| | | # include <endian.h> |
| | | # if (__BYTE_ORDER == __LITTLE_ENDIAN) |
| | | # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN |
| | | # elif (__BYTE_ORDER == __BIG_ENDIAN) |
| | | # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN |
| | | # else |
| | | # error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. |
| | | # endif // __GLIBC__ |
| | | // Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro |
| | | # elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) |
| | | # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN |
| | | # elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) |
| | | # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN |
| | | // Detect with architecture macros |
| | | # elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) |
| | | # 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) || 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. |
| | | # endif |
| | | #endif // RAPIDJSON_ENDIAN |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_64BIT |
| | | |
| | | //! Whether using 64-bit architecture |
| | | #ifndef RAPIDJSON_64BIT |
| | | #if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) |
| | | #define RAPIDJSON_64BIT 1 |
| | | #else |
| | | #define RAPIDJSON_64BIT 0 |
| | | #endif |
| | | #endif // RAPIDJSON_64BIT |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_ALIGN |
| | | |
| | | //! Data alignment of the machine. |
| | | /*! \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. |
| | | 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 |
| | | #endif |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_UINT64_C2 |
| | | |
| | | //! Construct a 64-bit literal by a pair of 32-bit integer. |
| | | /*! |
| | | 64-bit literal with or without ULL suffix is prone to compiler warnings. |
| | | UINT64_C() is C macro which cause compilation problems. |
| | | Use this macro to define 64-bit constants by a pair of 32-bit integer. |
| | | */ |
| | | #ifndef RAPIDJSON_UINT64_C2 |
| | | #define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32)) |
| | | #endif |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_48BITPOINTER_OPTIMIZATION |
| | | |
| | | //! Use only lower 48-bit address for some pointers. |
| | | /*! |
| | | \ingroup RAPIDJSON_CONFIG |
| | | |
| | | This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. |
| | | The higher 16-bit can be used for storing other data. |
| | | \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. |
| | | */ |
| | | #ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION |
| | | #if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) |
| | | #define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 |
| | | #else |
| | | #define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 |
| | | #endif |
| | | #endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION |
| | | |
| | | #if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 |
| | | #if RAPIDJSON_64BIT != 1 |
| | | #error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 |
| | | #endif |
| | | #define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x)))) |
| | | #define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) |
| | | #else |
| | | #define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) |
| | | #define RAPIDJSON_GETPOINTER(type, p) (p) |
| | | #endif |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD |
| | | |
| | | /*! \def RAPIDJSON_SIMD |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief Enable SSE2/SSE4.2/Neon optimization. |
| | | |
| | | RapidJSON supports optimized implementations for some parsing operations |
| | | based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel |
| | | or ARM compatible processors. |
| | | |
| | | To enable these optimizations, three different symbols can be defined; |
| | | \code |
| | | // Enable SSE2 optimization. |
| | | #define RAPIDJSON_SSE2 |
| | | |
| | | // Enable SSE4.2 optimization. |
| | | #define RAPIDJSON_SSE42 |
| | | \endcode |
| | | |
| | | // 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_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) |
| | | #define RAPIDJSON_SIMD |
| | | #endif |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_NO_SIZETYPEDEFINE |
| | | |
| | | #ifndef RAPIDJSON_NO_SIZETYPEDEFINE |
| | | /*! \def RAPIDJSON_NO_SIZETYPEDEFINE |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief User-provided \c SizeType definition. |
| | | |
| | | In order to avoid using 32-bit size types for indexing strings and arrays, |
| | | define this preprocessor symbol and provide the type rapidjson::SizeType |
| | | before including RapidJSON: |
| | | \code |
| | | #define RAPIDJSON_NO_SIZETYPEDEFINE |
| | | namespace rapidjson { typedef ::std::size_t SizeType; } |
| | | #include "rapidjson/..." |
| | | \endcode |
| | | |
| | | \see rapidjson::SizeType |
| | | */ |
| | | #ifdef RAPIDJSON_DOXYGEN_RUNNING |
| | | #define RAPIDJSON_NO_SIZETYPEDEFINE |
| | | #endif |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | //! Size type (for string lengths, array sizes, etc.) |
| | | /*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, |
| | | instead of using \c size_t. Users may override the SizeType by defining |
| | | \ref RAPIDJSON_NO_SIZETYPEDEFINE. |
| | | */ |
| | | typedef unsigned SizeType; |
| | | RAPIDJSON_NAMESPACE_END |
| | | #endif |
| | | |
| | | // always import std::size_t to rapidjson namespace |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | using std::size_t; |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_ASSERT |
| | | |
| | | //! Assertion. |
| | | /*! \ingroup RAPIDJSON_CONFIG |
| | | By default, rapidjson uses C \c assert() for internal assertions. |
| | | User can override it by defining RAPIDJSON_ASSERT(x) macro. |
| | | |
| | | \note Parsing errors are handled and can be customized by the |
| | | \ref RAPIDJSON_ERRORS APIs. |
| | | */ |
| | | #ifndef RAPIDJSON_ASSERT |
| | | #include <cassert> |
| | | #define RAPIDJSON_ASSERT(x) assert(x) |
| | | #endif // RAPIDJSON_ASSERT |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_STATIC_ASSERT |
| | | |
| | | // Prefer C++11 static_assert, if available |
| | | #ifndef RAPIDJSON_STATIC_ASSERT |
| | | #if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) |
| | | #define RAPIDJSON_STATIC_ASSERT(x) \ |
| | | static_assert(x, RAPIDJSON_STRINGIFY(x)) |
| | | #endif // C++11 |
| | | #endif // RAPIDJSON_STATIC_ASSERT |
| | | |
| | | // Adopt C++03 implementation from boost |
| | | #ifndef RAPIDJSON_STATIC_ASSERT |
| | | #ifndef __clang__ |
| | | //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN |
| | | #endif |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | template <bool x> struct STATIC_ASSERTION_FAILURE; |
| | | template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; }; |
| | | template <size_t x> struct StaticAssertTest {}; |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #if defined(__GNUC__) |
| | | #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) |
| | | #else |
| | | #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE |
| | | #endif |
| | | #ifndef __clang__ |
| | | //!@endcond |
| | | #endif |
| | | |
| | | /*! \def RAPIDJSON_STATIC_ASSERT |
| | | \brief (Internal) macro to check for conditions at compile-time |
| | | \param x compile-time condition |
| | | \hideinitializer |
| | | */ |
| | | #define RAPIDJSON_STATIC_ASSERT(x) \ |
| | | typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ |
| | | sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \ |
| | | RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE |
| | | #endif // RAPIDJSON_STATIC_ASSERT |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY |
| | | |
| | | //! Compiler branching hint for expression with high probability to be true. |
| | | /*! |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \param x Boolean expression likely to be true. |
| | | */ |
| | | #ifndef RAPIDJSON_LIKELY |
| | | #if defined(__GNUC__) || defined(__clang__) |
| | | #define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) |
| | | #else |
| | | #define RAPIDJSON_LIKELY(x) (x) |
| | | #endif |
| | | #endif |
| | | |
| | | //! Compiler branching hint for expression with low probability to be true. |
| | | /*! |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \param x Boolean expression unlikely to be true. |
| | | */ |
| | | #ifndef RAPIDJSON_UNLIKELY |
| | | #if defined(__GNUC__) || defined(__clang__) |
| | | #define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) |
| | | #else |
| | | #define RAPIDJSON_UNLIKELY(x) (x) |
| | | #endif |
| | | #endif |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Helpers |
| | | |
| | | //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN |
| | | |
| | | #define RAPIDJSON_MULTILINEMACRO_BEGIN do { |
| | | #define RAPIDJSON_MULTILINEMACRO_END \ |
| | | } while((void)0, 0) |
| | | |
| | | // adopted from Boost |
| | | #define RAPIDJSON_VERSION_CODE(x,y,z) \ |
| | | (((x)*100000) + ((y)*100) + (z)) |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF |
| | | |
| | | #if defined(__GNUC__) |
| | | #define RAPIDJSON_GNUC \ |
| | | RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) |
| | | #endif |
| | | |
| | | #if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) |
| | | |
| | | #define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) |
| | | #define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) |
| | | #define RAPIDJSON_DIAG_OFF(x) \ |
| | | RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) |
| | | |
| | | // push/pop support in Clang and GCC>=4.6 |
| | | #if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) |
| | | #define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) |
| | | #define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) |
| | | #else // GCC >= 4.2, < 4.6 |
| | | #define RAPIDJSON_DIAG_PUSH /* ignored */ |
| | | #define RAPIDJSON_DIAG_POP /* ignored */ |
| | | #endif |
| | | |
| | | #elif defined(_MSC_VER) |
| | | |
| | | // pragma (MSVC specific) |
| | | #define RAPIDJSON_PRAGMA(x) __pragma(x) |
| | | #define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) |
| | | |
| | | #define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) |
| | | #define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) |
| | | #define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) |
| | | |
| | | #else |
| | | |
| | | #define RAPIDJSON_DIAG_OFF(x) /* ignored */ |
| | | #define RAPIDJSON_DIAG_PUSH /* ignored */ |
| | | #define RAPIDJSON_DIAG_POP /* ignored */ |
| | | |
| | | #endif // RAPIDJSON_DIAG_* |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // C++11 features |
| | | |
| | | #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | #if defined(__clang__) |
| | | #if __has_feature(cxx_rvalue_references) && \ |
| | | (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) |
| | | |
| | | #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 |
| | | #else |
| | | #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 |
| | | #endif |
| | | #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | |
| | | #ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT |
| | | #if 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 |
| | | #define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 |
| | | #else |
| | | #define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 |
| | | #endif |
| | | #endif |
| | | #if RAPIDJSON_HAS_CXX11_NOEXCEPT |
| | | #define RAPIDJSON_NOEXCEPT noexcept |
| | | #else |
| | | #define RAPIDJSON_NOEXCEPT /* noexcept */ |
| | | #endif // RAPIDJSON_HAS_CXX11_NOEXCEPT |
| | | |
| | | // no automatic detection, yet |
| | | #ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS |
| | | #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 |
| | | #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,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ |
| | | (defined(_MSC_VER) && _MSC_VER >= 1700) |
| | | #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 |
| | | #else |
| | | #define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 |
| | | #endif |
| | | #endif // RAPIDJSON_HAS_CXX11_RANGE_FOR |
| | | |
| | | //!@endcond |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // new/delete |
| | | |
| | | #ifndef RAPIDJSON_NEW |
| | | ///! customization point for global \c new |
| | | #define RAPIDJSON_NEW(TypeName) new TypeName |
| | | #endif |
| | | #ifndef RAPIDJSON_DELETE |
| | | ///! customization point for global \c delete |
| | | #define RAPIDJSON_DELETE(x) delete x |
| | | #endif |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Type |
| | | |
| | | /*! \namespace rapidjson |
| | | \brief main RapidJSON namespace |
| | | \see RAPIDJSON_NAMESPACE |
| | | */ |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! Type of JSON value |
| | | enum Type { |
| | | kNullType = 0, //!< null |
| | | kFalseType = 1, //!< false |
| | | kTrueType = 2, //!< true |
| | | kObjectType = 3, //!< object |
| | | kArrayType = 4, //!< array |
| | | kStringType = 5, //!< string |
| | | kNumberType = 6 //!< number |
| | | }; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_RAPIDJSON_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_READER_H_ |
| | | #define RAPIDJSON_READER_H_ |
| | | |
| | | /*! \file reader.h */ |
| | | |
| | | #include "allocators.h" |
| | | #include "stream.h" |
| | | #include "encodedstream.h" |
| | | #include "internal/meta.h" |
| | | #include "internal/stack.h" |
| | | #include "internal/strtod.h" |
| | | #include <limits> |
| | | |
| | | #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) |
| | | #include <intrin.h> |
| | | #pragma intrinsic(_BitScanForward) |
| | | #endif |
| | | #ifdef RAPIDJSON_SSE42 |
| | | #include <nmmintrin.h> |
| | | #elif defined(RAPIDJSON_SSE2) |
| | | #include <emmintrin.h> |
| | | #elif defined(RAPIDJSON_NEON) |
| | | #include <arm_neon.h> |
| | | #endif |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant |
| | | RAPIDJSON_DIAG_OFF(4702) // unreachable code |
| | | #endif |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(old-style-cast) |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | RAPIDJSON_DIAG_OFF(switch-enum) |
| | | #endif |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(effc++) |
| | | #endif |
| | | |
| | | //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN |
| | | #define RAPIDJSON_NOTHING /* deliberately empty */ |
| | | #ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN |
| | | #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ |
| | | RAPIDJSON_MULTILINEMACRO_BEGIN \ |
| | | if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ |
| | | RAPIDJSON_MULTILINEMACRO_END |
| | | #endif |
| | | #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) |
| | | //!@endcond |
| | | |
| | | /*! \def RAPIDJSON_PARSE_ERROR_NORETURN |
| | | \ingroup RAPIDJSON_ERRORS |
| | | \brief Macro to indicate a parse error. |
| | | \param parseErrorCode \ref rapidjson::ParseErrorCode of the error |
| | | \param offset position of the error in JSON input (\c size_t) |
| | | |
| | | This macros can be used as a customization point for the internal |
| | | error handling mechanism of RapidJSON. |
| | | |
| | | A common usage model is to throw an exception instead of requiring the |
| | | caller to explicitly check the \ref rapidjson::GenericReader::Parse's |
| | | return value: |
| | | |
| | | \code |
| | | #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ |
| | | throw ParseException(parseErrorCode, #parseErrorCode, offset) |
| | | |
| | | #include <stdexcept> // std::runtime_error |
| | | #include "rapidjson/error/error.h" // rapidjson::ParseResult |
| | | |
| | | struct ParseException : std::runtime_error, rapidjson::ParseResult { |
| | | ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) |
| | | : std::runtime_error(msg), ParseResult(code, offset) {} |
| | | }; |
| | | |
| | | #include "rapidjson/reader.h" |
| | | \endcode |
| | | |
| | | \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse |
| | | */ |
| | | #ifndef RAPIDJSON_PARSE_ERROR_NORETURN |
| | | #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ |
| | | RAPIDJSON_MULTILINEMACRO_BEGIN \ |
| | | RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ |
| | | SetParseError(parseErrorCode, offset); \ |
| | | RAPIDJSON_MULTILINEMACRO_END |
| | | #endif |
| | | |
| | | /*! \def RAPIDJSON_PARSE_ERROR |
| | | \ingroup RAPIDJSON_ERRORS |
| | | \brief (Internal) macro to indicate and handle a parse error. |
| | | \param parseErrorCode \ref rapidjson::ParseErrorCode of the error |
| | | \param offset position of the error in JSON input (\c size_t) |
| | | |
| | | Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. |
| | | |
| | | \see RAPIDJSON_PARSE_ERROR_NORETURN |
| | | \hideinitializer |
| | | */ |
| | | #ifndef RAPIDJSON_PARSE_ERROR |
| | | #define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ |
| | | RAPIDJSON_MULTILINEMACRO_BEGIN \ |
| | | RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ |
| | | RAPIDJSON_MULTILINEMACRO_END |
| | | #endif |
| | | |
| | | #include "error/error.h" // ParseErrorCode, ParseResult |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // ParseFlag |
| | | |
| | | /*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief User-defined kParseDefaultFlags definition. |
| | | |
| | | User can define this as any \c ParseFlag combinations. |
| | | */ |
| | | #ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS |
| | | #define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags |
| | | #endif |
| | | |
| | | //! Combination of parseFlags |
| | | /*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream |
| | | */ |
| | | enum ParseFlag { |
| | | kParseNoFlags = 0, //!< No flags are set. |
| | | kParseInsituFlag = 1, //!< In-situ(destructive) parsing. |
| | | kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. |
| | | kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. |
| | | kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. |
| | | kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). |
| | | kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. |
| | | 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. |
| | | kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Handler |
| | | |
| | | /*! \class rapidjson::Handler |
| | | \brief Concept for receiving events from GenericReader upon parsing. |
| | | The functions return true if no error occurs. If they return false, |
| | | the event publisher should terminate the process. |
| | | \code |
| | | concept Handler { |
| | | typename Ch; |
| | | |
| | | bool Null(); |
| | | bool Bool(bool b); |
| | | bool Int(int i); |
| | | bool Uint(unsigned i); |
| | | bool Int64(int64_t i); |
| | | bool Uint64(uint64_t i); |
| | | bool Double(double d); |
| | | /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) |
| | | bool RawNumber(const Ch* str, SizeType length, bool copy); |
| | | bool String(const Ch* str, SizeType length, bool copy); |
| | | bool StartObject(); |
| | | bool Key(const Ch* str, SizeType length, bool copy); |
| | | bool EndObject(SizeType memberCount); |
| | | bool StartArray(); |
| | | bool EndArray(SizeType elementCount); |
| | | }; |
| | | \endcode |
| | | */ |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // BaseReaderHandler |
| | | |
| | | //! Default implementation of Handler. |
| | | /*! This can be used as base class of any reader handler. |
| | | \note implements Handler concept |
| | | */ |
| | | template<typename Encoding = UTF8<>, typename Derived = void> |
| | | struct BaseReaderHandler { |
| | | typedef typename Encoding::Ch Ch; |
| | | |
| | | typedef typename internal::SelectIf<internal::IsSame<Derived, void>, BaseReaderHandler, Derived>::Type Override; |
| | | |
| | | bool Default() { return true; } |
| | | bool Null() { return static_cast<Override&>(*this).Default(); } |
| | | bool Bool(bool) { return static_cast<Override&>(*this).Default(); } |
| | | bool Int(int) { return static_cast<Override&>(*this).Default(); } |
| | | bool Uint(unsigned) { return static_cast<Override&>(*this).Default(); } |
| | | bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); } |
| | | bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); } |
| | | bool Double(double) { return static_cast<Override&>(*this).Default(); } |
| | | /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) |
| | | bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); } |
| | | bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); } |
| | | bool StartObject() { return static_cast<Override&>(*this).Default(); } |
| | | bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); } |
| | | bool EndObject(SizeType) { return static_cast<Override&>(*this).Default(); } |
| | | bool StartArray() { return static_cast<Override&>(*this).Default(); } |
| | | bool EndArray(SizeType) { return static_cast<Override&>(*this).Default(); } |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // StreamLocalCopy |
| | | |
| | | namespace internal { |
| | | |
| | | template<typename Stream, int = StreamTraits<Stream>::copyOptimization> |
| | | class StreamLocalCopy; |
| | | |
| | | //! Do copy optimization. |
| | | template<typename Stream> |
| | | class StreamLocalCopy<Stream, 1> { |
| | | public: |
| | | StreamLocalCopy(Stream& original) : s(original), original_(original) {} |
| | | ~StreamLocalCopy() { original_ = s; } |
| | | |
| | | Stream s; |
| | | |
| | | private: |
| | | StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; |
| | | |
| | | Stream& original_; |
| | | }; |
| | | |
| | | //! Keep reference. |
| | | template<typename Stream> |
| | | class StreamLocalCopy<Stream, 0> { |
| | | public: |
| | | StreamLocalCopy(Stream& original) : s(original) {} |
| | | |
| | | Stream& s; |
| | | |
| | | private: |
| | | StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; |
| | | }; |
| | | |
| | | } // namespace internal |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // SkipWhitespace |
| | | |
| | | //! Skip the JSON white spaces in a stream. |
| | | /*! \param is A input stream for skipping white spaces. |
| | | \note This function has SSE2/SSE4.2 specialization. |
| | | */ |
| | | template<typename InputStream> |
| | | void SkipWhitespace(InputStream& is) { |
| | | internal::StreamLocalCopy<InputStream> copy(is); |
| | | InputStream& s(copy.s); |
| | | |
| | | typename InputStream::Ch c; |
| | | while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') |
| | | s.Take(); |
| | | } |
| | | |
| | | inline const char* SkipWhitespace(const char* p, const char* end) { |
| | | while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) |
| | | ++p; |
| | | return p; |
| | | } |
| | | |
| | | #ifdef RAPIDJSON_SSE42 |
| | | //! Skip whitespace with SSE 4.2 pcmpistrm instruction, 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; |
| | | |
| | | // The rest of string using SIMD |
| | | static const char whitespace[16] = " \n\r\t"; |
| | | const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0])); |
| | | |
| | | for (;; p += 16) { |
| | | const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p)); |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | 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; |
| | | |
| | | // The middle of string using SIMD |
| | | static const char whitespace[16] = " \n\r\t"; |
| | | const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0])); |
| | | |
| | | for (; p <= end - 16; p += 16) { |
| | | const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p)); |
| | | 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); |
| | | } |
| | | |
| | | #elif defined(RAPIDJSON_SSE2) |
| | | |
| | | //! Skip whitespace with SSE2 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; |
| | | |
| | | // The rest of string |
| | | #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } |
| | | static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; |
| | | #undef C16 |
| | | |
| | | const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0])); |
| | | const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0])); |
| | | const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0])); |
| | | const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0])); |
| | | |
| | | for (;; p += 16) { |
| | | const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p)); |
| | | __m128i x = _mm_cmpeq_epi8(s, w0); |
| | | x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); |
| | | x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); |
| | | x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); |
| | | unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x)); |
| | | if (r != 0) { // some of characters may be 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 |
| | | } |
| | | } |
| | | } |
| | | |
| | | 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; |
| | | |
| | | // The rest of string |
| | | #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } |
| | | static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; |
| | | #undef C16 |
| | | |
| | | const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0])); |
| | | const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0])); |
| | | const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0])); |
| | | const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0])); |
| | | |
| | | for (; p <= end - 16; p += 16) { |
| | | const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p)); |
| | | __m128i x = _mm_cmpeq_epi8(s, w0); |
| | | x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); |
| | | x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); |
| | | x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); |
| | | unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x)); |
| | | if (r != 0) { // some of characters may be 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 |
| | | } |
| | | } |
| | | |
| | | return SkipWhitespace(p, end); |
| | | } |
| | | |
| | | #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(reinterpret_cast<uint64x2_t>(x), 0); // extract |
| | | uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract |
| | | |
| | | if (low == 0) { |
| | | if (high != 0) { |
| | | int lz =__builtin_clzll(high);; |
| | | return p + 8 + (lz >> 3); |
| | | } |
| | | } else { |
| | | int lz = __builtin_clzll(low);; |
| | | return p + (lz >> 3); |
| | | } |
| | | } |
| | | } |
| | | |
| | | inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { |
| | | // Fast return for single non-whitespace |
| | | if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) |
| | | ++p; |
| | | else |
| | | return p; |
| | | |
| | | const uint8x16_t w0 = vmovq_n_u8(' '); |
| | | const uint8x16_t w1 = vmovq_n_u8('\n'); |
| | | const uint8x16_t w2 = vmovq_n_u8('\r'); |
| | | const uint8x16_t w3 = vmovq_n_u8('\t'); |
| | | |
| | | for (; p <= end - 16; p += 16) { |
| | | const uint8x16_t s = vld1q_u8(reinterpret_cast<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(reinterpret_cast<uint64x2_t>(x), 0); // extract |
| | | uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract |
| | | |
| | | if (low == 0) { |
| | | if (high != 0) { |
| | | int lz = __builtin_clzll(high); |
| | | return p + 8 + (lz >> 3); |
| | | } |
| | | } else { |
| | | int lz = __builtin_clzll(low); |
| | | return p + (lz >> 3); |
| | | } |
| | | } |
| | | |
| | | return SkipWhitespace(p, end); |
| | | } |
| | | |
| | | #endif // RAPIDJSON_NEON |
| | | |
| | | #ifdef RAPIDJSON_SIMD |
| | | //! Template function specialization for InsituStringStream |
| | | template<> inline void SkipWhitespace(InsituStringStream& is) { |
| | | is.src_ = const_cast<char*>(SkipWhitespace_SIMD(is.src_)); |
| | | } |
| | | |
| | | //! Template function specialization for StringStream |
| | | template<> inline void SkipWhitespace(StringStream& is) { |
| | | is.src_ = SkipWhitespace_SIMD(is.src_); |
| | | } |
| | | |
| | | template<> inline void SkipWhitespace(EncodedInputStream<UTF8<>, MemoryStream>& is) { |
| | | is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); |
| | | } |
| | | #endif // RAPIDJSON_SIMD |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // GenericReader |
| | | |
| | | //! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. |
| | | /*! GenericReader parses JSON text from a stream, and send events synchronously to an |
| | | object implementing Handler concept. |
| | | |
| | | It needs to allocate a stack for storing a single decoded string during |
| | | non-destructive parsing. |
| | | |
| | | For in-situ parsing, the decoded string is directly written to the source |
| | | text string, no temporary buffer is required. |
| | | |
| | | A GenericReader object can be reused for parsing multiple JSON text. |
| | | |
| | | \tparam SourceEncoding Encoding of the input stream. |
| | | \tparam TargetEncoding Encoding of the parse output. |
| | | \tparam StackAllocator Allocator type for stack. |
| | | */ |
| | | template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator = CrtAllocator> |
| | | class GenericReader { |
| | | public: |
| | | typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type |
| | | |
| | | //! Constructor. |
| | | /*! \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_(), state_(IterativeParsingStartState) {} |
| | | |
| | | //! Parse JSON text. |
| | | /*! \tparam parseFlags Combination of \ref ParseFlag. |
| | | \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> |
| | | ParseResult Parse(InputStream& is, Handler& handler) { |
| | | if (parseFlags & kParseIterativeFlag) |
| | | return IterativeParse<parseFlags>(is, handler); |
| | | |
| | | parseResult_.Clear(); |
| | | |
| | | ClearStackOnExit scope(*this); |
| | | |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); |
| | | |
| | | if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { |
| | | RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); |
| | | } |
| | | else { |
| | | ParseValue<parseFlags>(is, handler); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); |
| | | |
| | | if (!(parseFlags & kParseStopWhenDoneFlag)) { |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); |
| | | |
| | | if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { |
| | | RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return parseResult_; |
| | | } |
| | | |
| | | //! Parse JSON text (with \ref kParseDefaultFlags) |
| | | /*! \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 <typename InputStream, typename Handler> |
| | | ParseResult Parse(InputStream& is, Handler& handler) { |
| | | return Parse<kParseDefaultFlags>(is, handler); |
| | | } |
| | | |
| | | //! Initialize JSON text token-by-token parsing |
| | | /*! |
| | | */ |
| | | void IterativeParseInit() { |
| | | parseResult_.Clear(); |
| | | state_ = IterativeParsingStartState; |
| | | } |
| | | |
| | | //! Parse one token from JSON text |
| | | /*! \tparam InputStream Type of input stream, implementing Stream concept |
| | | \tparam Handler Type of handler, implementing Handler concept. |
| | | \param is Input stream to be parsed. |
| | | \param handler The handler to receive events. |
| | | \return Whether the parsing is successful. |
| | | */ |
| | | template <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() { |
| | | return IsIterativeParsingCompleteState(state_); |
| | | } |
| | | |
| | | //! Whether a parse error has occured in the last parsing. |
| | | bool HasParseError() const { return parseResult_.IsError(); } |
| | | |
| | | //! Get the \ref ParseErrorCode of last parsing. |
| | | ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } |
| | | |
| | | //! Get the position of last parsing error in input, 0 otherwise. |
| | | size_t GetErrorOffset() const { return parseResult_.Offset(); } |
| | | |
| | | protected: |
| | | void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } |
| | | |
| | | private: |
| | | // Prohibit copy constructor & assignment operator. |
| | | GenericReader(const GenericReader&); |
| | | GenericReader& operator=(const GenericReader&); |
| | | |
| | | void ClearStack() { stack_.Clear(); } |
| | | |
| | | // clear stack on any exit from ParseStream, e.g. due to exception |
| | | struct ClearStackOnExit { |
| | | explicit ClearStackOnExit(GenericReader& r) : r_(r) {} |
| | | ~ClearStackOnExit() { r_.ClearStack(); } |
| | | private: |
| | | GenericReader& r_; |
| | | ClearStackOnExit(const ClearStackOnExit&); |
| | | ClearStackOnExit& operator=(const ClearStackOnExit&); |
| | | }; |
| | | |
| | | template<unsigned parseFlags, typename InputStream> |
| | | void SkipWhitespaceAndComments(InputStream& is) { |
| | | SkipWhitespace(is); |
| | | |
| | | if (parseFlags & kParseCommentsFlag) { |
| | | while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { |
| | | if (Consume(is, '*')) { |
| | | while (true) { |
| | | if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); |
| | | else if (Consume(is, '*')) { |
| | | if (Consume(is, '/')) |
| | | break; |
| | | } |
| | | else |
| | | is.Take(); |
| | | } |
| | | } |
| | | else if (RAPIDJSON_LIKELY(Consume(is, '/'))) |
| | | while (is.Peek() != '\0' && is.Take() != '\n') {} |
| | | else |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); |
| | | |
| | | SkipWhitespace(is); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Parse object: { string : value, ... } |
| | | template<unsigned parseFlags, typename InputStream, typename Handler> |
| | | void ParseObject(InputStream& is, Handler& handler) { |
| | | RAPIDJSON_ASSERT(is.Peek() == '{'); |
| | | is.Take(); // Skip '{' |
| | | |
| | | if (RAPIDJSON_UNLIKELY(!handler.StartObject())) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | |
| | | if (Consume(is, '}')) { |
| | | if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | return; |
| | | } |
| | | |
| | | for (SizeType memberCount = 0;;) { |
| | | if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); |
| | | |
| | | ParseString<parseFlags>(is, handler, true); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | |
| | | if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); |
| | | |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | |
| | | ParseValue<parseFlags>(is, handler); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | |
| | | ++memberCount; |
| | | |
| | | switch (is.Peek()) { |
| | | case ',': |
| | | is.Take(); |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | break; |
| | | case '}': |
| | | is.Take(); |
| | | if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | return; |
| | | default: |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy |
| | | } |
| | | |
| | | if (parseFlags & kParseTrailingCommasFlag) { |
| | | if (is.Peek() == '}') { |
| | | if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | is.Take(); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Parse array: [ value, ... ] |
| | | template<unsigned parseFlags, typename InputStream, typename Handler> |
| | | void ParseArray(InputStream& is, Handler& handler) { |
| | | RAPIDJSON_ASSERT(is.Peek() == '['); |
| | | is.Take(); // Skip '[' |
| | | |
| | | if (RAPIDJSON_UNLIKELY(!handler.StartArray())) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | |
| | | if (Consume(is, ']')) { |
| | | if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | return; |
| | | } |
| | | |
| | | for (SizeType elementCount = 0;;) { |
| | | ParseValue<parseFlags>(is, handler); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | |
| | | ++elementCount; |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | |
| | | if (Consume(is, ',')) { |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | } |
| | | else if (Consume(is, ']')) { |
| | | if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | return; |
| | | } |
| | | else |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); |
| | | |
| | | if (parseFlags & kParseTrailingCommasFlag) { |
| | | if (is.Peek() == ']') { |
| | | if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | is.Take(); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | template<unsigned parseFlags, typename InputStream, typename Handler> |
| | | void ParseNull(InputStream& is, Handler& handler) { |
| | | RAPIDJSON_ASSERT(is.Peek() == 'n'); |
| | | is.Take(); |
| | | |
| | | if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { |
| | | if (RAPIDJSON_UNLIKELY(!handler.Null())) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | } |
| | | else |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); |
| | | } |
| | | |
| | | template<unsigned parseFlags, typename InputStream, typename Handler> |
| | | void ParseTrue(InputStream& is, Handler& handler) { |
| | | RAPIDJSON_ASSERT(is.Peek() == 't'); |
| | | is.Take(); |
| | | |
| | | if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { |
| | | if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | } |
| | | else |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); |
| | | } |
| | | |
| | | template<unsigned parseFlags, typename InputStream, typename Handler> |
| | | void ParseFalse(InputStream& is, Handler& handler) { |
| | | RAPIDJSON_ASSERT(is.Peek() == 'f'); |
| | | is.Take(); |
| | | |
| | | if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { |
| | | if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); |
| | | } |
| | | else |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); |
| | | } |
| | | |
| | | template<typename InputStream> |
| | | RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { |
| | | if (RAPIDJSON_LIKELY(is.Peek() == expect)) { |
| | | is.Take(); |
| | | return true; |
| | | } |
| | | else |
| | | return false; |
| | | } |
| | | |
| | | // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). |
| | | template<typename InputStream> |
| | | unsigned ParseHex4(InputStream& is, size_t escapeOffset) { |
| | | unsigned codepoint = 0; |
| | | for (int i = 0; i < 4; i++) { |
| | | Ch c = is.Peek(); |
| | | codepoint <<= 4; |
| | | codepoint += static_cast<unsigned>(c); |
| | | if (c >= '0' && c <= '9') |
| | | codepoint -= '0'; |
| | | else if (c >= 'A' && c <= 'F') |
| | | codepoint -= 'A' - 10; |
| | | else if (c >= 'a' && c <= 'f') |
| | | codepoint -= 'a' - 10; |
| | | else { |
| | | RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); |
| | | } |
| | | is.Take(); |
| | | } |
| | | return codepoint; |
| | | } |
| | | |
| | | template <typename CharType> |
| | | class StackStream { |
| | | public: |
| | | typedef CharType Ch; |
| | | |
| | | StackStream(internal::Stack<StackAllocator>& stack) : stack_(stack), length_(0) {} |
| | | RAPIDJSON_FORCEINLINE void Put(Ch c) { |
| | | *stack_.template Push<Ch>() = c; |
| | | ++length_; |
| | | } |
| | | |
| | | RAPIDJSON_FORCEINLINE void* Push(SizeType count) { |
| | | length_ += count; |
| | | return stack_.template Push<Ch>(count); |
| | | } |
| | | |
| | | size_t Length() const { return length_; } |
| | | |
| | | Ch* Pop() { |
| | | return stack_.template Pop<Ch>(length_); |
| | | } |
| | | |
| | | private: |
| | | StackStream(const StackStream&); |
| | | StackStream& operator=(const StackStream&); |
| | | |
| | | internal::Stack<StackAllocator>& stack_; |
| | | SizeType length_; |
| | | }; |
| | | |
| | | // Parse string and generate String event. Different code paths for kParseInsituFlag. |
| | | template<unsigned parseFlags, typename InputStream, typename Handler> |
| | | void ParseString(InputStream& is, Handler& handler, bool isKey = false) { |
| | | internal::StreamLocalCopy<InputStream> copy(is); |
| | | InputStream& s(copy.s); |
| | | |
| | | RAPIDJSON_ASSERT(s.Peek() == '\"'); |
| | | s.Take(); // Skip '\"' |
| | | |
| | | bool success = false; |
| | | if (parseFlags & kParseInsituFlag) { |
| | | typename InputStream::Ch *head = s.PutBegin(); |
| | | ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | size_t length = s.PutEnd(head) - 1; |
| | | RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); |
| | | const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head); |
| | | success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); |
| | | } |
| | | else { |
| | | StackStream<typename TargetEncoding::Ch> stackStream(stack_); |
| | | ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| | | SizeType length = static_cast<SizeType>(stackStream.Length()) - 1; |
| | | const typename TargetEncoding::Ch* const str = stackStream.Pop(); |
| | | success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); |
| | | } |
| | | if (RAPIDJSON_UNLIKELY(!success)) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); |
| | | } |
| | | |
| | | // Parse string to an output is |
| | | // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. |
| | | template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream> |
| | | RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { |
| | | //!@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, 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, |
| | | Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 |
| | | }; |
| | | #undef Z16 |
| | | //!@endcond |
| | | |
| | | for (;;) { |
| | | // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. |
| | | if (!(parseFlags & kParseValidateEncodingFlag)) |
| | | ScanCopyUnescapedString(is, os); |
| | | |
| | | Ch c = is.Peek(); |
| | | if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape |
| | | size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' 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 (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)) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); |
| | | codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; |
| | | } |
| | | TEncoding::Encode(os, codepoint); |
| | | } |
| | | else |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); |
| | | } |
| | | else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote |
| | | is.Take(); |
| | | os.Put('\0'); // null-terminate the string |
| | | return; |
| | | } |
| | | else if (RAPIDJSON_UNLIKELY(static_cast<unsigned>(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF |
| | | if (c == '\0') |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); |
| | | else |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); |
| | | } |
| | | else { |
| | | size_t offset = is.Tell(); |
| | | if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? |
| | | !Transcoder<SEncoding, TEncoding>::Validate(is, os) : |
| | | !Transcoder<SEncoding, TEncoding>::Transcode(is, os)))) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); |
| | | } |
| | | } |
| | | } |
| | | |
| | | template<typename InputStream, typename OutputStream> |
| | | static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { |
| | | // Do nothing for generic version |
| | | } |
| | | |
| | | #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) |
| | | // 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++); |
| | | |
| | | // The rest of string using SIMD |
| | | static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; |
| | | static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; |
| | | 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])); |
| | | |
| | | for (;; p += 16) { |
| | | 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, 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 |
| | | SizeType length; |
| | | #ifdef _MSC_VER // Find the index of first escaped |
| | | unsigned long offset; |
| | | _BitScanForward(&offset, r); |
| | | length = offset; |
| | | #else |
| | | length = static_cast<SizeType>(__builtin_ffs(r) - 1); |
| | | #endif |
| | | 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; |
| | | } |
| | | _mm_storeu_si128(reinterpret_cast<__m128i *>(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 |
| | | static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; |
| | | static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; |
| | | 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])); |
| | | |
| | | for (;; p += 16, q += 16) { |
| | | 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, 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 |
| | | size_t length; |
| | | #ifdef _MSC_VER // Find the index of first escaped |
| | | unsigned long offset; |
| | | _BitScanForward(&offset, r); |
| | | length = offset; |
| | | #else |
| | | length = static_cast<size_t>(__builtin_ffs(r) - 1); |
| | | #endif |
| | | for (const char* pend = p + length; p != pend; ) |
| | | *q++ = *p++; |
| | | break; |
| | | } |
| | | _mm_storeu_si128(reinterpret_cast<__m128i *>(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 |
| | | static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; |
| | | static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; |
| | | 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])); |
| | | |
| | | for (;; p += 16) { |
| | | 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, 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 |
| | | size_t length; |
| | | #ifdef _MSC_VER // Find the index of first escaped |
| | | unsigned long offset; |
| | | _BitScanForward(&offset, r); |
| | | length = offset; |
| | | #else |
| | | length = static_cast<size_t>(__builtin_ffs(r) - 1); |
| | | #endif |
| | | p += length; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | is.src_ = is.dst_ = p; |
| | | } |
| | | #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++); |
| | | |
| | | // 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(reinterpret_cast<uint64x2_t>(x), 0); // extract |
| | | uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract |
| | | |
| | | SizeType length = 0; |
| | | bool escaped = false; |
| | | if (low == 0) { |
| | | if (high != 0) { |
| | | unsigned lz = (unsigned)__builtin_clzll(high);; |
| | | length = 8 + (lz >> 3); |
| | | escaped = true; |
| | | } |
| | | } else { |
| | | unsigned lz = (unsigned)__builtin_clzll(low);; |
| | | length = lz >> 3; |
| | | escaped = true; |
| | | } |
| | | if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped |
| | | if (length != 0) { |
| | | char* q = reinterpret_cast<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(reinterpret_cast<uint64x2_t>(x), 0); // extract |
| | | uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract |
| | | |
| | | SizeType length = 0; |
| | | bool escaped = false; |
| | | if (low == 0) { |
| | | if (high != 0) { |
| | | unsigned lz = (unsigned)__builtin_clzll(high); |
| | | length = 8 + (lz >> 3); |
| | | escaped = true; |
| | | } |
| | | } else { |
| | | unsigned lz = (unsigned)__builtin_clzll(low); |
| | | length = lz >> 3; |
| | | escaped = true; |
| | | } |
| | | if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped |
| | | for (const char* pend = p + length; p != pend; ) { |
| | | *q++ = *p++; |
| | | } |
| | | break; |
| | | } |
| | | vst1q_u8(reinterpret_cast<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(reinterpret_cast<uint64x2_t>(x), 0); // extract |
| | | uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract |
| | | |
| | | if (low == 0) { |
| | | if (high != 0) { |
| | | int lz = __builtin_clzll(high); |
| | | p += 8 + (lz >> 3); |
| | | break; |
| | | } |
| | | } else { |
| | | int lz = __builtin_clzll(low); |
| | | p += lz >> 3; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | is.src_ = is.dst_ = p; |
| | | } |
| | | #endif // RAPIDJSON_NEON |
| | | |
| | | template<typename InputStream, bool backup, bool pushOnTake> |
| | | class NumberStream; |
| | | |
| | | template<typename InputStream> |
| | | class NumberStream<InputStream, false, false> { |
| | | public: |
| | | typedef typename InputStream::Ch Ch; |
| | | |
| | | NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } |
| | | |
| | | 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) {} |
| | | |
| | | size_t Tell() { return is.Tell(); } |
| | | size_t Length() { return 0; } |
| | | const char* Pop() { return 0; } |
| | | |
| | | protected: |
| | | NumberStream& operator=(const NumberStream&); |
| | | |
| | | InputStream& is; |
| | | }; |
| | | |
| | | template<typename InputStream> |
| | | class NumberStream<InputStream, true, false> : public NumberStream<InputStream, false, false> { |
| | | typedef NumberStream<InputStream, false, false> Base; |
| | | public: |
| | | NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} |
| | | |
| | | RAPIDJSON_FORCEINLINE Ch TakePush() { |
| | | stackStream.Put(static_cast<char>(Base::is.Peek())); |
| | | return Base::is.Take(); |
| | | } |
| | | |
| | | RAPIDJSON_FORCEINLINE void Push(char c) { |
| | | stackStream.Put(c); |
| | | } |
| | | |
| | | size_t Length() { return stackStream.Length(); } |
| | | |
| | | const char* Pop() { |
| | | stackStream.Put('\0'); |
| | | return stackStream.Pop(); |
| | | } |
| | | |
| | | private: |
| | | StackStream<char> stackStream; |
| | | }; |
| | | |
| | | template<typename InputStream> |
| | | class NumberStream<InputStream, true, true> : public NumberStream<InputStream, true, false> { |
| | | typedef NumberStream<InputStream, true, false> Base; |
| | | public: |
| | | NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} |
| | | |
| | | RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } |
| | | }; |
| | | |
| | | template<unsigned parseFlags, typename InputStream, typename Handler> |
| | | void ParseNumber(InputStream& is, Handler& handler) { |
| | | internal::StreamLocalCopy<InputStream> copy(is); |
| | | NumberStream<InputStream, |
| | | ((parseFlags & kParseNumbersAsStringsFlag) != 0) ? |
| | | ((parseFlags & kParseInsituFlag) == 0) : |
| | | ((parseFlags & kParseFullPrecisionFlag) != 0), |
| | | (parseFlags & kParseNumbersAsStringsFlag) != 0 && |
| | | (parseFlags & kParseInsituFlag) == 0> s(*this, copy.s); |
| | | |
| | | size_t startOffset = s.Tell(); |
| | | double d = 0.0; |
| | | bool useNanOrInf = false; |
| | | |
| | | // Parse minus |
| | | bool minus = Consume(s, '-'); |
| | | |
| | | // Parse int: zero / ( digit1-9 *DIGIT ) |
| | | unsigned i = 0; |
| | | uint64_t i64 = 0; |
| | | bool use64bit = false; |
| | | int significandDigit = 0; |
| | | if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { |
| | | i = 0; |
| | | s.TakePush(); |
| | | } |
| | | else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { |
| | | i = static_cast<unsigned>(s.TakePush() - '0'); |
| | | |
| | | if (minus) |
| | | while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { |
| | | if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 |
| | | if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { |
| | | i64 = i; |
| | | use64bit = true; |
| | | break; |
| | | } |
| | | } |
| | | i = i * 10 + static_cast<unsigned>(s.TakePush() - '0'); |
| | | significandDigit++; |
| | | } |
| | | else |
| | | while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { |
| | | if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 |
| | | if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { |
| | | i64 = i; |
| | | use64bit = true; |
| | | break; |
| | | } |
| | | } |
| | | i = i * 10 + static_cast<unsigned>(s.TakePush() - '0'); |
| | | significandDigit++; |
| | | } |
| | | } |
| | | // Parse NaN or Infinity here |
| | | else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { |
| | | 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'))) { |
| | | 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()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); |
| | | } |
| | | } |
| | | else |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); |
| | | |
| | | // Parse 64bit int |
| | | bool useDouble = false; |
| | | if (use64bit) { |
| | | if (minus) |
| | | while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { |
| | | if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 |
| | | if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { |
| | | d = static_cast<double>(i64); |
| | | useDouble = true; |
| | | break; |
| | | } |
| | | i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0'); |
| | | significandDigit++; |
| | | } |
| | | else |
| | | while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { |
| | | if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 |
| | | if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { |
| | | d = static_cast<double>(i64); |
| | | useDouble = true; |
| | | break; |
| | | } |
| | | i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0'); |
| | | significandDigit++; |
| | | } |
| | | } |
| | | |
| | | // 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'); |
| | | } |
| | | } |
| | | |
| | | // Parse frac = decimal-point 1*DIGIT |
| | | int expFrac = 0; |
| | | size_t decimalPosition; |
| | | if (Consume(s, '.')) { |
| | | decimalPosition = s.Length(); |
| | | |
| | | if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); |
| | | |
| | | if (!useDouble) { |
| | | #if RAPIDJSON_64BIT |
| | | // Use i64 to store significand in 64-bit architecture |
| | | if (!use64bit) |
| | | i64 = i; |
| | | |
| | | while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { |
| | | if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path |
| | | break; |
| | | else { |
| | | i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0'); |
| | | --expFrac; |
| | | if (i64 != 0) |
| | | significandDigit++; |
| | | } |
| | | } |
| | | |
| | | d = static_cast<double>(i64); |
| | | #else |
| | | // Use double to store significand in 32-bit architecture |
| | | d = static_cast<double>(use64bit ? i64 : i); |
| | | #endif |
| | | useDouble = true; |
| | | } |
| | | |
| | | while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { |
| | | if (significandDigit < 17) { |
| | | d = d * 10.0 + (s.TakePush() - '0'); |
| | | --expFrac; |
| | | if (RAPIDJSON_LIKELY(d > 0.0)) |
| | | significandDigit++; |
| | | } |
| | | else |
| | | s.TakePush(); |
| | | } |
| | | } |
| | | else |
| | | decimalPosition = s.Length(); // decimal position at the end of integer. |
| | | |
| | | // Parse exp = e [ minus / plus ] 1*DIGIT |
| | | int exp = 0; |
| | | if (Consume(s, 'e') || Consume(s, 'E')) { |
| | | if (!useDouble) { |
| | | d = static_cast<double>(use64bit ? i64 : i); |
| | | useDouble = true; |
| | | } |
| | | |
| | | bool expMinus = false; |
| | | if (Consume(s, '+')) |
| | | ; |
| | | else if (Consume(s, '-')) |
| | | expMinus = true; |
| | | |
| | | if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { |
| | | exp = static_cast<int>(s.Take() - '0'); |
| | | if (expMinus) { |
| | | 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 |
| | | while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent |
| | | s.Take(); |
| | | } |
| | | } |
| | | } |
| | | else { // positive exp |
| | | int maxExp = 308 - expFrac; |
| | | while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { |
| | | exp = exp * 10 + static_cast<int>(s.Take() - '0'); |
| | | if (RAPIDJSON_UNLIKELY(exp > maxExp)) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); |
| | | |
| | | if (expMinus) |
| | | exp = -exp; |
| | | } |
| | | |
| | | // Finish parsing, call event according to the type of number. |
| | | bool cont = true; |
| | | |
| | | if (parseFlags & kParseNumbersAsStringsFlag) { |
| | | if (parseFlags & kParseInsituFlag) { |
| | | s.Pop(); // Pop stack no matter if it will be used or not. |
| | | typename InputStream::Ch* head = is.PutBegin(); |
| | | const size_t length = s.Tell() - startOffset; |
| | | RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); |
| | | // unable to insert the \0 character here, it will erase the comma after this number |
| | | const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head); |
| | | cont = handler.RawNumber(str, SizeType(length), false); |
| | | } |
| | | else { |
| | | SizeType numCharsToCopy = static_cast<SizeType>(s.Length()); |
| | | StringStream srcStream(s.Pop()); |
| | | StackStream<typename TargetEncoding::Ch> dstStream(stack_); |
| | | while (numCharsToCopy--) { |
| | | Transcoder<UTF8<>, TargetEncoding>::Transcode(srcStream, dstStream); |
| | | } |
| | | dstStream.Put('\0'); |
| | | const typename TargetEncoding::Ch* str = dstStream.Pop(); |
| | | const SizeType length = static_cast<SizeType>(dstStream.Length()) - 1; |
| | | cont = handler.RawNumber(str, SizeType(length), true); |
| | | } |
| | | } |
| | | else { |
| | | size_t length = s.Length(); |
| | | const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. |
| | | |
| | | if (useDouble) { |
| | | int p = exp + expFrac; |
| | | if (parseFlags & kParseFullPrecisionFlag) |
| | | d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); |
| | | else |
| | | d = internal::StrtodNormalPrecision(d, p); |
| | | |
| | | cont = handler.Double(minus ? -d : d); |
| | | } |
| | | else if (useNanOrInf) { |
| | | cont = handler.Double(d); |
| | | } |
| | | else { |
| | | if (use64bit) { |
| | | if (minus) |
| | | cont = handler.Int64(static_cast<int64_t>(~i64 + 1)); |
| | | else |
| | | cont = handler.Uint64(i64); |
| | | } |
| | | else { |
| | | if (minus) |
| | | cont = handler.Int(static_cast<int32_t>(~i + 1)); |
| | | else |
| | | cont = handler.Uint(i); |
| | | } |
| | | } |
| | | } |
| | | if (RAPIDJSON_UNLIKELY(!cont)) |
| | | RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); |
| | | } |
| | | |
| | | // Parse any JSON value |
| | | template<unsigned parseFlags, typename InputStream, typename Handler> |
| | | void ParseValue(InputStream& is, Handler& handler) { |
| | | switch (is.Peek()) { |
| | | case 'n': ParseNull <parseFlags>(is, handler); break; |
| | | case 't': ParseTrue <parseFlags>(is, handler); break; |
| | | case 'f': ParseFalse <parseFlags>(is, handler); break; |
| | | case '"': ParseString<parseFlags>(is, handler); break; |
| | | case '{': ParseObject<parseFlags>(is, handler); break; |
| | | case '[': ParseArray <parseFlags>(is, handler); break; |
| | | default : |
| | | ParseNumber<parseFlags>(is, handler); |
| | | break; |
| | | |
| | | } |
| | | } |
| | | |
| | | // Iterative Parsing |
| | | |
| | | // States |
| | | enum IterativeParsingState { |
| | | IterativeParsingFinishState = 0, // sink states at top |
| | | IterativeParsingErrorState, // sink states at top |
| | | IterativeParsingStartState, |
| | | |
| | | // Object states |
| | | IterativeParsingObjectInitialState, |
| | | IterativeParsingMemberKeyState, |
| | | IterativeParsingMemberValueState, |
| | | IterativeParsingObjectFinishState, |
| | | |
| | | // Array states |
| | | IterativeParsingArrayInitialState, |
| | | IterativeParsingElementState, |
| | | IterativeParsingArrayFinishState, |
| | | |
| | | // Single value state |
| | | IterativeParsingValueState, |
| | | |
| | | // Delimiter states (at bottom) |
| | | IterativeParsingElementDelimiterState, |
| | | IterativeParsingMemberDelimiterState, |
| | | IterativeParsingKeyValueDelimiterState, |
| | | |
| | | cIterativeParsingStateCount |
| | | }; |
| | | |
| | | // Tokens |
| | | enum Token { |
| | | LeftBracketToken = 0, |
| | | RightBracketToken, |
| | | |
| | | LeftCurlyBracketToken, |
| | | RightCurlyBracketToken, |
| | | |
| | | CommaToken, |
| | | ColonToken, |
| | | |
| | | StringToken, |
| | | FalseToken, |
| | | TrueToken, |
| | | NullToken, |
| | | NumberToken, |
| | | |
| | | kTokenCount |
| | | }; |
| | | |
| | | RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { |
| | | |
| | | //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN |
| | | #define N NumberToken |
| | | #define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N |
| | | // Maps from ASCII to Token |
| | | static const unsigned char tokenMap[256] = { |
| | | N16, // 00~0F |
| | | N16, // 10~1F |
| | | N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F |
| | | N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F |
| | | N16, // 40~4F |
| | | N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F |
| | | N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F |
| | | N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F |
| | | N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF |
| | | }; |
| | | #undef N |
| | | #undef N16 |
| | | //!@endcond |
| | | |
| | | if (sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) |
| | | return static_cast<Token>(tokenMap[static_cast<unsigned char>(c)]); |
| | | else |
| | | return NumberToken; |
| | | } |
| | | |
| | | RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { |
| | | // current state x one lookahead token -> new state |
| | | static const char G[cIterativeParsingStateCount][kTokenCount] = { |
| | | // Finish(sink state) |
| | | { |
| | | IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, |
| | | IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, |
| | | IterativeParsingErrorState |
| | | }, |
| | | // Error(sink state) |
| | | { |
| | | IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, |
| | | IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, |
| | | IterativeParsingErrorState |
| | | }, |
| | | // Start |
| | | { |
| | | IterativeParsingArrayInitialState, // Left bracket |
| | | IterativeParsingErrorState, // Right bracket |
| | | IterativeParsingObjectInitialState, // Left curly bracket |
| | | IterativeParsingErrorState, // Right curly bracket |
| | | IterativeParsingErrorState, // Comma |
| | | IterativeParsingErrorState, // Colon |
| | | IterativeParsingValueState, // String |
| | | IterativeParsingValueState, // False |
| | | IterativeParsingValueState, // True |
| | | IterativeParsingValueState, // Null |
| | | IterativeParsingValueState // Number |
| | | }, |
| | | // ObjectInitial |
| | | { |
| | | 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 |
| | | }, |
| | | // MemberKey |
| | | { |
| | | IterativeParsingErrorState, // Left bracket |
| | | IterativeParsingErrorState, // Right bracket |
| | | IterativeParsingErrorState, // Left curly bracket |
| | | IterativeParsingErrorState, // Right curly bracket |
| | | IterativeParsingErrorState, // Comma |
| | | IterativeParsingKeyValueDelimiterState, // Colon |
| | | IterativeParsingErrorState, // String |
| | | IterativeParsingErrorState, // False |
| | | IterativeParsingErrorState, // True |
| | | IterativeParsingErrorState, // Null |
| | | IterativeParsingErrorState // Number |
| | | }, |
| | | // MemberValue |
| | | { |
| | | IterativeParsingErrorState, // Left bracket |
| | | IterativeParsingErrorState, // Right bracket |
| | | IterativeParsingErrorState, // Left curly bracket |
| | | IterativeParsingObjectFinishState, // Right curly bracket |
| | | IterativeParsingMemberDelimiterState, // Comma |
| | | IterativeParsingErrorState, // Colon |
| | | IterativeParsingErrorState, // String |
| | | IterativeParsingErrorState, // False |
| | | IterativeParsingErrorState, // True |
| | | IterativeParsingErrorState, // Null |
| | | IterativeParsingErrorState // Number |
| | | }, |
| | | // ObjectFinish(sink state) |
| | | { |
| | | IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, |
| | | IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, |
| | | IterativeParsingErrorState |
| | | }, |
| | | // ArrayInitial |
| | | { |
| | | IterativeParsingArrayInitialState, // Left bracket(push Element state) |
| | | IterativeParsingArrayFinishState, // Right bracket |
| | | IterativeParsingObjectInitialState, // Left curly bracket(push Element state) |
| | | IterativeParsingErrorState, // Right curly bracket |
| | | IterativeParsingErrorState, // Comma |
| | | IterativeParsingErrorState, // Colon |
| | | IterativeParsingElementState, // String |
| | | IterativeParsingElementState, // False |
| | | IterativeParsingElementState, // True |
| | | IterativeParsingElementState, // Null |
| | | IterativeParsingElementState // Number |
| | | }, |
| | | // Element |
| | | { |
| | | IterativeParsingErrorState, // Left bracket |
| | | IterativeParsingArrayFinishState, // Right bracket |
| | | IterativeParsingErrorState, // Left curly bracket |
| | | IterativeParsingErrorState, // Right curly bracket |
| | | IterativeParsingElementDelimiterState, // Comma |
| | | IterativeParsingErrorState, // Colon |
| | | IterativeParsingErrorState, // String |
| | | IterativeParsingErrorState, // False |
| | | IterativeParsingErrorState, // True |
| | | 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) |
| | | IterativeParsingArrayFinishState, // Right bracket |
| | | IterativeParsingObjectInitialState, // Left curly bracket(push Element state) |
| | | IterativeParsingErrorState, // Right curly bracket |
| | | IterativeParsingErrorState, // Comma |
| | | IterativeParsingErrorState, // Colon |
| | | IterativeParsingElementState, // String |
| | | IterativeParsingElementState, // False |
| | | IterativeParsingElementState, // True |
| | | IterativeParsingElementState, // Null |
| | | IterativeParsingElementState // 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 |
| | | }, |
| | | // 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 |
| | | }, |
| | | }; // End of G |
| | | |
| | | return static_cast<IterativeParsingState>(G[state][token]); |
| | | } |
| | | |
| | | // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). |
| | | // May return a new state on state pop. |
| | | template <unsigned parseFlags, typename InputStream, typename Handler> |
| | | RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { |
| | | (void)token; |
| | | |
| | | switch (dst) { |
| | | case IterativeParsingErrorState: |
| | | return dst; |
| | | |
| | | case IterativeParsingObjectInitialState: |
| | | case IterativeParsingArrayInitialState: |
| | | { |
| | | // Push the state(Element or MemeberValue) if we are nested in another array or value of member. |
| | | // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. |
| | | IterativeParsingState n = src; |
| | | if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) |
| | | n = IterativeParsingElementState; |
| | | else if (src == IterativeParsingKeyValueDelimiterState) |
| | | n = IterativeParsingMemberValueState; |
| | | // Push current state. |
| | | *stack_.template Push<SizeType>(1) = n; |
| | | // Initialize and push the member/element count. |
| | | *stack_.template Push<SizeType>(1) = 0; |
| | | // Call handler |
| | | bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); |
| | | // On handler short circuits the parsing. |
| | | if (!hr) { |
| | | RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); |
| | | return IterativeParsingErrorState; |
| | | } |
| | | else { |
| | | is.Take(); |
| | | return dst; |
| | | } |
| | | } |
| | | |
| | | case IterativeParsingMemberKeyState: |
| | | ParseString<parseFlags>(is, handler, true); |
| | | if (HasParseError()) |
| | | return IterativeParsingErrorState; |
| | | else |
| | | return dst; |
| | | |
| | | case IterativeParsingKeyValueDelimiterState: |
| | | RAPIDJSON_ASSERT(token == ColonToken); |
| | | is.Take(); |
| | | return dst; |
| | | |
| | | case IterativeParsingMemberValueState: |
| | | // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. |
| | | ParseValue<parseFlags>(is, handler); |
| | | if (HasParseError()) { |
| | | return IterativeParsingErrorState; |
| | | } |
| | | return dst; |
| | | |
| | | case IterativeParsingElementState: |
| | | // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. |
| | | ParseValue<parseFlags>(is, handler); |
| | | if (HasParseError()) { |
| | | return IterativeParsingErrorState; |
| | | } |
| | | return dst; |
| | | |
| | | case IterativeParsingMemberDelimiterState: |
| | | case IterativeParsingElementDelimiterState: |
| | | is.Take(); |
| | | // Update member/element count. |
| | | *stack_.template Top<SizeType>() = *stack_.template Top<SizeType>() + 1; |
| | | return dst; |
| | | |
| | | case IterativeParsingObjectFinishState: |
| | | { |
| | | // Transit from delimiter is only allowed when trailing commas are enabled |
| | | if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { |
| | | RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); |
| | | return IterativeParsingErrorState; |
| | | } |
| | | // Get member count. |
| | | SizeType c = *stack_.template Pop<SizeType>(1); |
| | | // If the object is not empty, count the last member. |
| | | if (src == IterativeParsingMemberValueState) |
| | | ++c; |
| | | // Restore the state. |
| | | IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1)); |
| | | // Transit to Finish state if this is the topmost scope. |
| | | if (n == IterativeParsingStartState) |
| | | n = IterativeParsingFinishState; |
| | | // Call handler |
| | | bool hr = handler.EndObject(c); |
| | | // On handler short circuits the parsing. |
| | | if (!hr) { |
| | | RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); |
| | | return IterativeParsingErrorState; |
| | | } |
| | | else { |
| | | is.Take(); |
| | | return n; |
| | | } |
| | | } |
| | | |
| | | case IterativeParsingArrayFinishState: |
| | | { |
| | | // Transit from delimiter is only allowed when trailing commas are enabled |
| | | if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { |
| | | RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); |
| | | return IterativeParsingErrorState; |
| | | } |
| | | // Get element count. |
| | | SizeType c = *stack_.template Pop<SizeType>(1); |
| | | // If the array is not empty, count the last element. |
| | | if (src == IterativeParsingElementState) |
| | | ++c; |
| | | // Restore the state. |
| | | IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1)); |
| | | // Transit to Finish state if this is the topmost scope. |
| | | if (n == IterativeParsingStartState) |
| | | n = IterativeParsingFinishState; |
| | | // Call handler |
| | | bool hr = handler.EndArray(c); |
| | | // On handler short circuits the parsing. |
| | | if (!hr) { |
| | | RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); |
| | | return IterativeParsingErrorState; |
| | | } |
| | | else { |
| | | is.Take(); |
| | | return n; |
| | | } |
| | | } |
| | | |
| | | default: |
| | | // This branch is for IterativeParsingValueState actually. |
| | | // Use `default:` rather than |
| | | // `case IterativeParsingValueState:` is for code coverage. |
| | | |
| | | // The IterativeParsingStartState is not enumerated in this switch-case. |
| | | // It is impossible for that case. And it can be caught by following assertion. |
| | | |
| | | // The IterativeParsingFinishState is not enumerated in this switch-case either. |
| | | // It is a "derivative" state which cannot triggered from Predict() directly. |
| | | // Therefore it cannot happen here. And it can be caught by following assertion. |
| | | RAPIDJSON_ASSERT(dst == IterativeParsingValueState); |
| | | |
| | | // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. |
| | | ParseValue<parseFlags>(is, handler); |
| | | if (HasParseError()) { |
| | | return IterativeParsingErrorState; |
| | | } |
| | | return IterativeParsingFinishState; |
| | | } |
| | | } |
| | | |
| | | template <typename InputStream> |
| | | void HandleError(IterativeParsingState src, InputStream& is) { |
| | | if (HasParseError()) { |
| | | // Error flag has been set. |
| | | return; |
| | | } |
| | | |
| | | switch (src) { |
| | | case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; |
| | | case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; |
| | | case IterativeParsingObjectInitialState: |
| | | case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; |
| | | case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; |
| | | case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; |
| | | case IterativeParsingKeyValueDelimiterState: |
| | | case IterativeParsingArrayInitialState: |
| | | case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; |
| | | default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; |
| | | } |
| | | } |
| | | |
| | | RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) { |
| | | return s >= IterativeParsingElementDelimiterState; |
| | | } |
| | | |
| | | RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) { |
| | | return s <= IterativeParsingErrorState; |
| | | } |
| | | |
| | | template <unsigned parseFlags, typename InputStream, typename Handler> |
| | | ParseResult IterativeParse(InputStream& is, Handler& handler) { |
| | | parseResult_.Clear(); |
| | | ClearStackOnExit scope(*this); |
| | | IterativeParsingState state = IterativeParsingStartState; |
| | | |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); |
| | | while (is.Peek() != '\0') { |
| | | Token t = Tokenize(is.Peek()); |
| | | IterativeParsingState n = Predict(state, t); |
| | | IterativeParsingState d = Transit<parseFlags>(state, t, n, is, handler); |
| | | |
| | | if (d == IterativeParsingErrorState) { |
| | | HandleError(state, is); |
| | | break; |
| | | } |
| | | |
| | | state = d; |
| | | |
| | | // Do not further consume streams if a root JSON has been parsed. |
| | | if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) |
| | | break; |
| | | |
| | | SkipWhitespaceAndComments<parseFlags>(is); |
| | | RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); |
| | | } |
| | | |
| | | // Handle the end of file. |
| | | if (state != IterativeParsingFinishState) |
| | | HandleError(state, is); |
| | | |
| | | return parseResult_; |
| | | } |
| | | |
| | | static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. |
| | | internal::Stack<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. |
| | | typedef GenericReader<UTF8<>, UTF8<> > Reader; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | |
| | | #ifdef __GNUC__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_READER_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_SCHEMA_H_ |
| | | #define RAPIDJSON_SCHEMA_H_ |
| | | |
| | | #include "document.h" |
| | | #include "pointer.h" |
| | | #include <cmath> // abs, floor |
| | | |
| | | #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) |
| | | #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 |
| | | #else |
| | | #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)) |
| | | #define RAPIDJSON_SCHEMA_USE_STDREGEX 1 |
| | | #else |
| | | #define RAPIDJSON_SCHEMA_USE_STDREGEX 0 |
| | | #endif |
| | | |
| | | #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX |
| | | #include "internal/regex.h" |
| | | #elif RAPIDJSON_SCHEMA_USE_STDREGEX |
| | | #include <regex> |
| | | #endif |
| | | |
| | | #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX |
| | | #define RAPIDJSON_SCHEMA_HAS_REGEX 1 |
| | | #else |
| | | #define RAPIDJSON_SCHEMA_HAS_REGEX 0 |
| | | #endif |
| | | |
| | | #ifndef RAPIDJSON_SCHEMA_VERBOSE |
| | | #define RAPIDJSON_SCHEMA_VERBOSE 0 |
| | | #endif |
| | | |
| | | #if RAPIDJSON_SCHEMA_VERBOSE |
| | | #include "stringbuffer.h" |
| | | #endif |
| | | |
| | | RAPIDJSON_DIAG_PUSH |
| | | |
| | | #if defined(__GNUC__) |
| | | RAPIDJSON_DIAG_OFF(effc++) |
| | | #endif |
| | | |
| | | #ifdef __clang__ |
| | | 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 |
| | | RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Verbose Utilities |
| | | |
| | | #if RAPIDJSON_SCHEMA_VERBOSE |
| | | |
| | | namespace internal { |
| | | |
| | | inline void PrintInvalidKeyword(const char* keyword) { |
| | | printf("Fail keyword: %s\n", keyword); |
| | | } |
| | | |
| | | inline void PrintInvalidKeyword(const wchar_t* keyword) { |
| | | wprintf(L"Fail keyword: %ls\n", keyword); |
| | | } |
| | | |
| | | inline void PrintInvalidDocument(const char* document) { |
| | | printf("Fail document: %s\n\n", document); |
| | | } |
| | | |
| | | inline void PrintInvalidDocument(const wchar_t* document) { |
| | | wprintf(L"Fail document: %ls\n\n", document); |
| | | } |
| | | |
| | | 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 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); |
| | | } |
| | | |
| | | } // namespace internal |
| | | |
| | | #endif // RAPIDJSON_SCHEMA_VERBOSE |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // RAPIDJSON_INVALID_KEYWORD_RETURN |
| | | |
| | | #if RAPIDJSON_SCHEMA_VERBOSE |
| | | #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) |
| | | #else |
| | | #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) |
| | | #endif |
| | | |
| | | #define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ |
| | | RAPIDJSON_MULTILINEMACRO_BEGIN\ |
| | | context.invalidKeyword = keyword.GetString();\ |
| | | RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ |
| | | return false;\ |
| | | RAPIDJSON_MULTILINEMACRO_END |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Forward declarations |
| | | |
| | | template <typename ValueType, typename Allocator> |
| | | class GenericSchemaDocument; |
| | | |
| | | namespace internal { |
| | | |
| | | template <typename SchemaDocumentType> |
| | | class Schema; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // ISchemaValidator |
| | | |
| | | class ISchemaValidator { |
| | | public: |
| | | virtual ~ISchemaValidator() {} |
| | | virtual bool IsValid() const = 0; |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // ISchemaStateFactory |
| | | |
| | | template <typename SchemaType> |
| | | class ISchemaStateFactory { |
| | | public: |
| | | virtual ~ISchemaStateFactory() {} |
| | | virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; |
| | | virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; |
| | | virtual void* CreateHasher() = 0; |
| | | virtual uint64_t GetHashCode(void* hasher) = 0; |
| | | virtual void DestroryHasher(void* hasher) = 0; |
| | | virtual void* MallocState(size_t size) = 0; |
| | | virtual void FreeState(void* p) = 0; |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Hasher |
| | | |
| | | // For comparison of compound value |
| | | template<typename Encoding, typename Allocator> |
| | | class Hasher { |
| | | public: |
| | | typedef typename Encoding::Ch Ch; |
| | | |
| | | Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} |
| | | |
| | | bool Null() { return WriteType(kNullType); } |
| | | bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } |
| | | bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); } |
| | | 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; |
| | | if (d < 0) n.u.i = static_cast<int64_t>(d); |
| | | else n.u.u = static_cast<uint64_t>(d); |
| | | n.d = d; |
| | | return WriteNumber(n); |
| | | } |
| | | |
| | | bool RawNumber(const Ch* str, SizeType len, bool) { |
| | | WriteBuffer(kNumberType, str, len * sizeof(Ch)); |
| | | return true; |
| | | } |
| | | |
| | | bool String(const Ch* str, SizeType len, bool) { |
| | | WriteBuffer(kStringType, str, len * sizeof(Ch)); |
| | | return true; |
| | | } |
| | | |
| | | bool StartObject() { return true; } |
| | | bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } |
| | | bool EndObject(SizeType memberCount) { |
| | | uint64_t h = Hash(0, kObjectType); |
| | | uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2); |
| | | for (SizeType i = 0; i < memberCount; i++) |
| | | h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive |
| | | *stack_.template Push<uint64_t>() = h; |
| | | return true; |
| | | } |
| | | |
| | | bool StartArray() { return true; } |
| | | bool EndArray(SizeType elementCount) { |
| | | uint64_t h = Hash(0, kArrayType); |
| | | uint64_t* e = stack_.template Pop<uint64_t>(elementCount); |
| | | for (SizeType i = 0; i < elementCount; i++) |
| | | h = Hash(h, e[i]); // Use hash to achieve element order sensitive |
| | | *stack_.template Push<uint64_t>() = h; |
| | | return true; |
| | | } |
| | | |
| | | bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } |
| | | |
| | | uint64_t GetHashCode() const { |
| | | RAPIDJSON_ASSERT(IsValid()); |
| | | return *stack_.template Top<uint64_t>(); |
| | | } |
| | | |
| | | private: |
| | | static const size_t kDefaultSize = 256; |
| | | struct Number { |
| | | union U { |
| | | uint64_t u; |
| | | int64_t i; |
| | | }u; |
| | | double d; |
| | | }; |
| | | |
| | | bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } |
| | | |
| | | bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } |
| | | |
| | | bool WriteBuffer(Type type, const void* data, size_t len) { |
| | | // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ |
| | | uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); |
| | | const unsigned char* d = static_cast<const unsigned char*>(data); |
| | | for (size_t i = 0; i < len; i++) |
| | | h = Hash(h, d[i]); |
| | | *stack_.template Push<uint64_t>() = h; |
| | | return true; |
| | | } |
| | | |
| | | static uint64_t Hash(uint64_t h, uint64_t d) { |
| | | static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); |
| | | h ^= d; |
| | | h *= kPrime; |
| | | return h; |
| | | } |
| | | |
| | | Stack<Allocator> stack_; |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // SchemaValidationContext |
| | | |
| | | template <typename SchemaDocumentType> |
| | | struct SchemaValidationContext { |
| | | typedef Schema<SchemaDocumentType> SchemaType; |
| | | typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType; |
| | | typedef typename SchemaType::ValueType ValueType; |
| | | typedef typename ValueType::Ch Ch; |
| | | |
| | | enum PatternValidatorType { |
| | | kPatternValidatorOnly, |
| | | kPatternValidatorWithProperty, |
| | | kPatternValidatorWithAdditionalProperty |
| | | }; |
| | | |
| | | SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : |
| | | factory(f), |
| | | schema(s), |
| | | valueSchema(), |
| | | invalidKeyword(), |
| | | hasher(), |
| | | arrayElementHashCodes(), |
| | | validators(), |
| | | validatorCount(), |
| | | patternPropertiesValidators(), |
| | | patternPropertiesValidatorCount(), |
| | | patternPropertiesSchemas(), |
| | | patternPropertiesSchemaCount(), |
| | | valuePatternValidatorType(kPatternValidatorOnly), |
| | | propertyExist(), |
| | | inArray(false), |
| | | valueUniqueness(false), |
| | | arrayUniqueness(false) |
| | | { |
| | | } |
| | | |
| | | ~SchemaValidationContext() { |
| | | if (hasher) |
| | | factory.DestroryHasher(hasher); |
| | | if (validators) { |
| | | for (SizeType i = 0; i < validatorCount; i++) |
| | | factory.DestroySchemaValidator(validators[i]); |
| | | factory.FreeState(validators); |
| | | } |
| | | if (patternPropertiesValidators) { |
| | | for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) |
| | | factory.DestroySchemaValidator(patternPropertiesValidators[i]); |
| | | factory.FreeState(patternPropertiesValidators); |
| | | } |
| | | if (patternPropertiesSchemas) |
| | | factory.FreeState(patternPropertiesSchemas); |
| | | if (propertyExist) |
| | | factory.FreeState(propertyExist); |
| | | } |
| | | |
| | | SchemaValidatorFactoryType& factory; |
| | | const SchemaType* schema; |
| | | const SchemaType* valueSchema; |
| | | const Ch* invalidKeyword; |
| | | void* hasher; // Only validator access |
| | | void* arrayElementHashCodes; // Only validator access this |
| | | ISchemaValidator** validators; |
| | | SizeType validatorCount; |
| | | ISchemaValidator** patternPropertiesValidators; |
| | | SizeType patternPropertiesValidatorCount; |
| | | const SchemaType** patternPropertiesSchemas; |
| | | SizeType patternPropertiesSchemaCount; |
| | | PatternValidatorType valuePatternValidatorType; |
| | | PatternValidatorType objectPatternValidatorType; |
| | | SizeType arrayElementIndex; |
| | | bool* propertyExist; |
| | | bool inArray; |
| | | bool valueUniqueness; |
| | | bool arrayUniqueness; |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Schema |
| | | |
| | | template <typename SchemaDocumentType> |
| | | class Schema { |
| | | public: |
| | | typedef typename SchemaDocumentType::ValueType ValueType; |
| | | typedef typename SchemaDocumentType::AllocatorType AllocatorType; |
| | | typedef typename SchemaDocumentType::PointerType PointerType; |
| | | typedef typename ValueType::EncodingType EncodingType; |
| | | typedef typename EncodingType::Ch Ch; |
| | | typedef SchemaValidationContext<SchemaDocumentType> Context; |
| | | typedef Schema<SchemaDocumentType> SchemaType; |
| | | typedef GenericValue<EncodingType, AllocatorType> SValue; |
| | | friend class GenericSchemaDocument<ValueType, AllocatorType>; |
| | | |
| | | Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : |
| | | allocator_(allocator), |
| | | typeless_(schemaDocument->GetTypeless()), |
| | | enum_(), |
| | | enumCount_(), |
| | | not_(), |
| | | type_((1 << kTotalSchemaType) - 1), // typeless |
| | | validatorCount_(), |
| | | notValidatorIndex_(), |
| | | properties_(), |
| | | additionalPropertiesSchema_(), |
| | | patternProperties_(), |
| | | patternPropertyCount_(), |
| | | propertyCount_(), |
| | | minProperties_(), |
| | | maxProperties_(SizeType(~0)), |
| | | additionalProperties_(true), |
| | | hasDependencies_(), |
| | | hasRequired_(), |
| | | hasSchemaDependencies_(), |
| | | additionalItemsSchema_(), |
| | | itemsList_(), |
| | | itemsTuple_(), |
| | | itemsTupleCount_(), |
| | | minItems_(), |
| | | maxItems_(SizeType(~0)), |
| | | additionalItems_(true), |
| | | uniqueItems_(false), |
| | | pattern_(), |
| | | minLength_(0), |
| | | maxLength_(~SizeType(0)), |
| | | exclusiveMinimum_(false), |
| | | exclusiveMaximum_(false) |
| | | { |
| | | typedef typename SchemaDocumentType::ValueType ValueType; |
| | | typedef typename ValueType::ConstValueIterator ConstValueIterator; |
| | | typedef typename ValueType::ConstMemberIterator ConstMemberIterator; |
| | | |
| | | if (!value.IsObject()) |
| | | return; |
| | | |
| | | if (const ValueType* v = GetMember(value, GetTypeString())) { |
| | | type_ = 0; |
| | | if (v->IsString()) |
| | | AddType(*v); |
| | | else if (v->IsArray()) |
| | | for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) |
| | | AddType(*itr); |
| | | } |
| | | |
| | | 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)); |
| | | EnumHasherType h(&hasherAllocator, 256); |
| | | itr->Accept(h); |
| | | enum_[enumCount_++] = h.GetHashCode(); |
| | | } |
| | | } |
| | | |
| | | if (schemaDocument) { |
| | | AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); |
| | | 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_++; |
| | | } |
| | | |
| | | // Object |
| | | |
| | | const ValueType* properties = GetMember(value, GetPropertiesString()); |
| | | const ValueType* required = GetMember(value, GetRequiredString()); |
| | | const ValueType* dependencies = GetMember(value, GetDependenciesString()); |
| | | { |
| | | // Gather properties from properties/required/dependencies |
| | | SValue allProperties(kArrayType); |
| | | |
| | | 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); |
| | | |
| | | if (dependencies && dependencies->IsObject()) |
| | | for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { |
| | | AddUniqueElement(allProperties, itr->name); |
| | | if (itr->value.IsArray()) |
| | | for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) |
| | | if (i->IsString()) |
| | | AddUniqueElement(allProperties, *i); |
| | | } |
| | | |
| | | if (allProperties.Size() > 0) { |
| | | propertyCount_ = allProperties.Size(); |
| | | properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_)); |
| | | for (SizeType i = 0; i < propertyCount_; i++) { |
| | | new (&properties_[i]) Property(); |
| | | properties_[i].name = allProperties[i]; |
| | | properties_[i].schema = typeless_; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (properties && properties->IsObject()) { |
| | | PointerType q = p.Append(GetPropertiesString(), allocator_); |
| | | 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); |
| | | } |
| | | } |
| | | |
| | | if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { |
| | | PointerType q = p.Append(GetPatternPropertiesString(), allocator_); |
| | | patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); |
| | | patternPropertyCount_ = 0; |
| | | |
| | | 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); |
| | | patternPropertyCount_++; |
| | | } |
| | | } |
| | | |
| | | if (required && required->IsArray()) |
| | | for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) |
| | | if (itr->IsString()) { |
| | | SizeType index; |
| | | if (FindPropertyIndex(*itr, &index)) { |
| | | properties_[index].required = true; |
| | | hasRequired_ = true; |
| | | } |
| | | } |
| | | |
| | | if (dependencies && dependencies->IsObject()) { |
| | | PointerType q = p.Append(GetDependenciesString(), allocator_); |
| | | hasDependencies_ = true; |
| | | for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { |
| | | SizeType sourceIndex; |
| | | if (FindPropertyIndex(itr->name, &sourceIndex)) { |
| | | if (itr->value.IsArray()) { |
| | | properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_)); |
| | | std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); |
| | | for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { |
| | | SizeType targetIndex; |
| | | if (FindPropertyIndex(*targetItr, &targetIndex)) |
| | | properties_[sourceIndex].dependencies[targetIndex] = true; |
| | | } |
| | | } |
| | | else if (itr->value.IsObject()) { |
| | | hasSchemaDependencies_ = true; |
| | | schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); |
| | | properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; |
| | | validatorCount_++; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { |
| | | if (v->IsBool()) |
| | | additionalProperties_ = v->GetBool(); |
| | | else if (v->IsObject()) |
| | | schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); |
| | | } |
| | | |
| | | AssignIfExist(minProperties_, value, GetMinPropertiesString()); |
| | | AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); |
| | | |
| | | // Array |
| | | if (const ValueType* v = GetMember(value, GetItemsString())) { |
| | | PointerType q = p.Append(GetItemsString(), allocator_); |
| | | if (v->IsObject()) // List validation |
| | | schemaDocument->CreateSchema(&itemsList_, q, *v, document); |
| | | 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); |
| | | } |
| | | } |
| | | |
| | | AssignIfExist(minItems_, value, GetMinItemsString()); |
| | | AssignIfExist(maxItems_, value, GetMaxItemsString()); |
| | | |
| | | 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); |
| | | } |
| | | |
| | | AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); |
| | | |
| | | // String |
| | | AssignIfExist(minLength_, value, GetMinLengthString()); |
| | | AssignIfExist(maxLength_, value, GetMaxLengthString()); |
| | | |
| | | if (const ValueType* v = GetMember(value, GetPatternString())) |
| | | pattern_ = CreatePattern(*v); |
| | | |
| | | // Number |
| | | if (const ValueType* v = GetMember(value, GetMinimumString())) |
| | | if (v->IsNumber()) |
| | | minimum_.CopyFrom(*v, *allocator_); |
| | | |
| | | if (const ValueType* v = GetMember(value, GetMaximumString())) |
| | | if (v->IsNumber()) |
| | | maximum_.CopyFrom(*v, *allocator_); |
| | | |
| | | AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); |
| | | AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); |
| | | |
| | | if (const ValueType* v = GetMember(value, GetMultipleOfString())) |
| | | if (v->IsNumber() && v->GetDouble() > 0.0) |
| | | multipleOf_.CopyFrom(*v, *allocator_); |
| | | } |
| | | |
| | | ~Schema() { |
| | | AllocatorType::Free(enum_); |
| | | if (properties_) { |
| | | for (SizeType i = 0; i < propertyCount_; i++) |
| | | properties_[i].~Property(); |
| | | AllocatorType::Free(properties_); |
| | | } |
| | | if (patternProperties_) { |
| | | for (SizeType i = 0; i < patternPropertyCount_; i++) |
| | | patternProperties_[i].~PatternProperty(); |
| | | AllocatorType::Free(patternProperties_); |
| | | } |
| | | AllocatorType::Free(itemsTuple_); |
| | | #if RAPIDJSON_SCHEMA_HAS_REGEX |
| | | if (pattern_) { |
| | | pattern_->~RegexType(); |
| | | AllocatorType::Free(pattern_); |
| | | } |
| | | #endif |
| | | } |
| | | |
| | | bool BeginValue(Context& context) const { |
| | | if (context.inArray) { |
| | | if (uniqueItems_) |
| | | context.valueUniqueness = true; |
| | | |
| | | if (itemsList_) |
| | | context.valueSchema = itemsList_; |
| | | else if (itemsTuple_) { |
| | | if (context.arrayElementIndex < itemsTupleCount_) |
| | | context.valueSchema = itemsTuple_[context.arrayElementIndex]; |
| | | else if (additionalItemsSchema_) |
| | | context.valueSchema = additionalItemsSchema_; |
| | | else if (additionalItems_) |
| | | context.valueSchema = typeless_; |
| | | else |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); |
| | | } |
| | | else |
| | | context.valueSchema = typeless_; |
| | | |
| | | context.arrayElementIndex++; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { |
| | | if (context.patternPropertiesValidatorCount > 0) { |
| | | bool otherValid = false; |
| | | SizeType count = context.patternPropertiesValidatorCount; |
| | | if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) |
| | | otherValid = context.patternPropertiesValidators[--count]->IsValid(); |
| | | |
| | | bool patternValid = true; |
| | | for (SizeType i = 0; i < count; i++) |
| | | if (!context.patternPropertiesValidators[i]->IsValid()) { |
| | | patternValid = false; |
| | | break; |
| | | } |
| | | |
| | | if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { |
| | | if (!patternValid) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); |
| | | } |
| | | else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { |
| | | if (!patternValid || !otherValid) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); |
| | | } |
| | | else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); |
| | | } |
| | | |
| | | if (enum_) { |
| | | 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()); |
| | | 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; |
| | | } |
| | | if (!oneValid) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); |
| | | } |
| | | |
| | | if (not_ && context.validators[notValidatorIndex_]->IsValid()) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool Null(Context& context) const { |
| | | if (!(type_ & (1 << kNullSchemaType))) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); |
| | | return CreateParallelValidator(context); |
| | | } |
| | | |
| | | bool Bool(Context& context, bool) const { |
| | | if (!(type_ & (1 << kBooleanSchemaType))) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); |
| | | return CreateParallelValidator(context); |
| | | } |
| | | |
| | | bool Int(Context& context, int i) const { |
| | | if (!CheckInt(context, i)) |
| | | return false; |
| | | return CreateParallelValidator(context); |
| | | } |
| | | |
| | | bool Uint(Context& context, unsigned u) const { |
| | | if (!CheckUint(context, u)) |
| | | return false; |
| | | return CreateParallelValidator(context); |
| | | } |
| | | |
| | | bool Int64(Context& context, int64_t i) const { |
| | | if (!CheckInt(context, i)) |
| | | return false; |
| | | return CreateParallelValidator(context); |
| | | } |
| | | |
| | | bool Uint64(Context& context, uint64_t u) const { |
| | | 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()); |
| | | |
| | | 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()); |
| | | |
| | | 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 (pattern_ && !IsPatternMatch(pattern_, str, length)) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); |
| | | |
| | | return CreateParallelValidator(context); |
| | | } |
| | | |
| | | bool StartObject(Context& context) const { |
| | | if (!(type_ & (1 << kObjectSchemaType))) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); |
| | | |
| | | if (hasDependencies_ || hasRequired_) { |
| | | context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_)); |
| | | std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); |
| | | } |
| | | |
| | | if (patternProperties_) { // pre-allocate schema array |
| | | SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType |
| | | context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count)); |
| | | context.patternPropertiesSchemaCount = 0; |
| | | std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); |
| | | } |
| | | |
| | | return CreateParallelValidator(context); |
| | | } |
| | | |
| | | bool Key(Context& context, const Ch* str, SizeType len, bool) const { |
| | | if (patternProperties_) { |
| | | context.patternPropertiesSchemaCount = 0; |
| | | for (SizeType i = 0; i < patternPropertyCount_; i++) |
| | | if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { |
| | | context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; |
| | | context.valueSchema = typeless_; |
| | | } |
| | | } |
| | | |
| | | SizeType index; |
| | | if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { |
| | | if (context.patternPropertiesSchemaCount > 0) { |
| | | context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; |
| | | context.valueSchema = typeless_; |
| | | context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; |
| | | } |
| | | else |
| | | context.valueSchema = properties_[index].schema; |
| | | |
| | | if (context.propertyExist) |
| | | context.propertyExist[index] = true; |
| | | |
| | | return true; |
| | | } |
| | | |
| | | if (additionalPropertiesSchema_) { |
| | | if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { |
| | | context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; |
| | | context.valueSchema = typeless_; |
| | | context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; |
| | | } |
| | | else |
| | | context.valueSchema = additionalPropertiesSchema_; |
| | | return true; |
| | | } |
| | | else if (additionalProperties_) { |
| | | context.valueSchema = typeless_; |
| | | return true; |
| | | } |
| | | |
| | | if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool EndObject(Context& context, SizeType memberCount) const { |
| | | if (hasRequired_) |
| | | for (SizeType index = 0; index < propertyCount_; index++) |
| | | if (properties_[index].required) |
| | | if (!context.propertyExist[index]) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); |
| | | |
| | | if (memberCount < minProperties_) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); |
| | | |
| | | if (memberCount > maxProperties_) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); |
| | | |
| | | if (hasDependencies_) { |
| | | for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) |
| | | if (context.propertyExist[sourceIndex]) { |
| | | if (properties_[sourceIndex].dependencies) { |
| | | for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) |
| | | if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); |
| | | } |
| | | else if (properties_[sourceIndex].dependenciesSchema) |
| | | if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool StartArray(Context& context) const { |
| | | if (!(type_ & (1 << kArraySchemaType))) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); |
| | | |
| | | context.arrayElementIndex = 0; |
| | | context.inArray = true; |
| | | |
| | | return CreateParallelValidator(context); |
| | | } |
| | | |
| | | bool EndArray(Context& context, SizeType elementCount) const { |
| | | context.inArray = false; |
| | | |
| | | if (elementCount < minItems_) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); |
| | | |
| | | if (elementCount > maxItems_) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | // 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, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\ |
| | | return v;\ |
| | | } |
| | | |
| | | RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') |
| | | RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') |
| | | RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') |
| | | RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') |
| | | RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') |
| | | RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') |
| | | RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') |
| | | RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') |
| | | RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') |
| | | RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') |
| | | RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') |
| | | RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') |
| | | RAPIDJSON_STRING_(Not, 'n', 'o', 't') |
| | | RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') |
| | | RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') |
| | | RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') |
| | | RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') |
| | | RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') |
| | | RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') |
| | | RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') |
| | | RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') |
| | | RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') |
| | | RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') |
| | | RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') |
| | | RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') |
| | | RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') |
| | | RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') |
| | | RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') |
| | | RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') |
| | | RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') |
| | | 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') |
| | | |
| | | #undef RAPIDJSON_STRING_ |
| | | |
| | | private: |
| | | enum SchemaValueType { |
| | | kNullSchemaType, |
| | | kBooleanSchemaType, |
| | | kObjectSchemaType, |
| | | kArraySchemaType, |
| | | kStringSchemaType, |
| | | kNumberSchemaType, |
| | | kIntegerSchemaType, |
| | | kTotalSchemaType |
| | | }; |
| | | |
| | | #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX |
| | | typedef internal::GenericRegex<EncodingType, AllocatorType> RegexType; |
| | | #elif RAPIDJSON_SCHEMA_USE_STDREGEX |
| | | typedef std::basic_regex<Ch> RegexType; |
| | | #else |
| | | typedef char RegexType; |
| | | #endif |
| | | |
| | | struct SchemaArray { |
| | | SchemaArray() : schemas(), count() {} |
| | | ~SchemaArray() { AllocatorType::Free(schemas); } |
| | | const SchemaType** schemas; |
| | | SizeType begin; // begin index of context.validators |
| | | SizeType count; |
| | | }; |
| | | |
| | | template <typename V1, typename V2> |
| | | void AddUniqueElement(V1& a, const V2& v) { |
| | | for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) |
| | | if (*itr == v) |
| | | return; |
| | | V1 c(v, *allocator_); |
| | | a.PushBack(c, *allocator_); |
| | | } |
| | | |
| | | static const ValueType* GetMember(const ValueType& value, const ValueType& name) { |
| | | typename ValueType::ConstMemberIterator itr = value.FindMember(name); |
| | | return itr != value.MemberEnd() ? &(itr->value) : 0; |
| | | } |
| | | |
| | | static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { |
| | | if (const ValueType* v = GetMember(value, name)) |
| | | if (v->IsBool()) |
| | | out = v->GetBool(); |
| | | } |
| | | |
| | | static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { |
| | | if (const ValueType* v = GetMember(value, name)) |
| | | if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) |
| | | out = static_cast<SizeType>(v->GetUint64()); |
| | | } |
| | | |
| | | void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { |
| | | if (const ValueType* v = GetMember(value, name)) { |
| | | if (v->IsArray() && v->Size() > 0) { |
| | | PointerType q = p.Append(name, allocator_); |
| | | out.count = v->Size(); |
| | | 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); |
| | | out.begin = validatorCount_; |
| | | validatorCount_ += out.count; |
| | | } |
| | | } |
| | | } |
| | | |
| | | #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX |
| | | template <typename ValueType> |
| | | RegexType* CreatePattern(const ValueType& value) { |
| | | if (value.IsString()) { |
| | | RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); |
| | | if (!r->IsValid()) { |
| | | r->~RegexType(); |
| | | AllocatorType::Free(r); |
| | | r = 0; |
| | | } |
| | | return r; |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { |
| | | GenericRegexSearch<RegexType> rs(*pattern); |
| | | return rs.Search(str); |
| | | } |
| | | #elif RAPIDJSON_SCHEMA_USE_STDREGEX |
| | | template <typename ValueType> |
| | | RegexType* CreatePattern(const ValueType& value) { |
| | | if (value.IsString()) |
| | | try { |
| | | return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); |
| | | } |
| | | catch (const std::regex_error&) { |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { |
| | | std::match_results<const Ch*> r; |
| | | return std::regex_search(str, str + length, r, *pattern); |
| | | } |
| | | #else |
| | | template <typename ValueType> |
| | | RegexType* CreatePattern(const ValueType&) { return 0; } |
| | | |
| | | static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } |
| | | #endif // RAPIDJSON_SCHEMA_USE_STDREGEX |
| | | |
| | | void AddType(const ValueType& type) { |
| | | if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; |
| | | else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; |
| | | else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; |
| | | else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; |
| | | else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; |
| | | else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; |
| | | else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); |
| | | } |
| | | |
| | | bool CreateParallelValidator(Context& context) const { |
| | | if (enum_ || context.arrayUniqueness) |
| | | context.hasher = context.factory.CreateHasher(); |
| | | |
| | | if (validatorCount_) { |
| | | RAPIDJSON_ASSERT(context.validators == 0); |
| | | context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); |
| | | context.validatorCount = validatorCount_; |
| | | |
| | | if (allOf_.schemas) |
| | | CreateSchemaValidators(context, allOf_); |
| | | |
| | | if (anyOf_.schemas) |
| | | CreateSchemaValidators(context, anyOf_); |
| | | |
| | | if (oneOf_.schemas) |
| | | CreateSchemaValidators(context, oneOf_); |
| | | |
| | | if (not_) |
| | | context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); |
| | | |
| | | if (hasSchemaDependencies_) { |
| | | for (SizeType i = 0; i < propertyCount_; i++) |
| | | if (properties_[i].dependenciesSchema) |
| | | context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { |
| | | for (SizeType i = 0; i < schemas.count; i++) |
| | | context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); |
| | | } |
| | | |
| | | // O(n) |
| | | bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { |
| | | SizeType len = name.GetStringLength(); |
| | | const Ch* str = name.GetString(); |
| | | for (SizeType index = 0; index < propertyCount_; index++) |
| | | if (properties_[index].name.GetStringLength() == len && |
| | | (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) |
| | | { |
| | | *outIndex = index; |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | bool CheckInt(Context& context, int64_t i) const { |
| | | if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); |
| | | |
| | | if (!minimum_.IsNull()) { |
| | | if (minimum_.IsInt64()) { |
| | | if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); |
| | | } |
| | | else if (minimum_.IsUint64()) { |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() |
| | | } |
| | | else if (!CheckDoubleMinimum(context, static_cast<double>(i))) |
| | | return false; |
| | | } |
| | | |
| | | if (!maximum_.IsNull()) { |
| | | if (maximum_.IsInt64()) { |
| | | if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); |
| | | } |
| | | else if (maximum_.IsUint64()) { } |
| | | /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() |
| | | else if (!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()); |
| | | } |
| | | else if (!CheckDoubleMultipleOf(context, static_cast<double>(i))) |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool CheckUint(Context& context, uint64_t i) const { |
| | | if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); |
| | | |
| | | if (!minimum_.IsNull()) { |
| | | if (minimum_.IsUint64()) { |
| | | if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); |
| | | } |
| | | else if (minimum_.IsInt64()) |
| | | /* do nothing */; // i >= 0 > minimum.Getint64() |
| | | else if (!CheckDoubleMinimum(context, static_cast<double>(i))) |
| | | return false; |
| | | } |
| | | |
| | | if (!maximum_.IsNull()) { |
| | | if (maximum_.IsUint64()) { |
| | | if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); |
| | | } |
| | | 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()); |
| | | } |
| | | else if (!CheckDoubleMultipleOf(context, static_cast<double>(i))) |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool CheckDoubleMinimum(Context& context, double d) const { |
| | | if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); |
| | | return true; |
| | | } |
| | | |
| | | bool CheckDoubleMaximum(Context& context, double d) const { |
| | | if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) |
| | | RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); |
| | | return true; |
| | | } |
| | | |
| | | bool CheckDoubleMultipleOf(Context& context, double d) const { |
| | | 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()); |
| | | return true; |
| | | } |
| | | |
| | | struct Property { |
| | | Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} |
| | | ~Property() { AllocatorType::Free(dependencies); } |
| | | SValue name; |
| | | const SchemaType* schema; |
| | | const SchemaType* dependenciesSchema; |
| | | SizeType dependenciesValidatorIndex; |
| | | bool* dependencies; |
| | | bool required; |
| | | }; |
| | | |
| | | struct PatternProperty { |
| | | PatternProperty() : schema(), pattern() {} |
| | | ~PatternProperty() { |
| | | if (pattern) { |
| | | pattern->~RegexType(); |
| | | AllocatorType::Free(pattern); |
| | | } |
| | | } |
| | | const SchemaType* schema; |
| | | RegexType* pattern; |
| | | }; |
| | | |
| | | AllocatorType* allocator_; |
| | | const SchemaType* typeless_; |
| | | uint64_t* enum_; |
| | | SizeType enumCount_; |
| | | SchemaArray allOf_; |
| | | SchemaArray anyOf_; |
| | | SchemaArray oneOf_; |
| | | const SchemaType* not_; |
| | | unsigned type_; // bitmask of kSchemaType |
| | | SizeType validatorCount_; |
| | | SizeType notValidatorIndex_; |
| | | |
| | | Property* properties_; |
| | | const SchemaType* additionalPropertiesSchema_; |
| | | PatternProperty* patternProperties_; |
| | | SizeType patternPropertyCount_; |
| | | SizeType propertyCount_; |
| | | SizeType minProperties_; |
| | | SizeType maxProperties_; |
| | | bool additionalProperties_; |
| | | bool hasDependencies_; |
| | | bool hasRequired_; |
| | | bool hasSchemaDependencies_; |
| | | |
| | | const SchemaType* additionalItemsSchema_; |
| | | const SchemaType* itemsList_; |
| | | const SchemaType** itemsTuple_; |
| | | SizeType itemsTupleCount_; |
| | | SizeType minItems_; |
| | | SizeType maxItems_; |
| | | bool additionalItems_; |
| | | bool uniqueItems_; |
| | | |
| | | RegexType* pattern_; |
| | | SizeType minLength_; |
| | | SizeType maxLength_; |
| | | |
| | | SValue minimum_; |
| | | SValue maximum_; |
| | | SValue multipleOf_; |
| | | bool exclusiveMinimum_; |
| | | bool exclusiveMaximum_; |
| | | }; |
| | | |
| | | template<typename Stack, typename Ch> |
| | | struct TokenHelper { |
| | | RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { |
| | | *documentStack.template Push<Ch>() = '/'; |
| | | 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>() = static_cast<Ch>(buffer[i]); |
| | | } |
| | | }; |
| | | |
| | | // Partial specialized version for char to prevent buffer copying. |
| | | template <typename Stack> |
| | | struct TokenHelper<Stack, char> { |
| | | RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { |
| | | if (sizeof(SizeType) == 4) { |
| | | char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint |
| | | *buffer++ = '/'; |
| | | const char* end = internal::u32toa(index, buffer); |
| | | documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer))); |
| | | } |
| | | else { |
| | | char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64 |
| | | *buffer++ = '/'; |
| | | const char* end = internal::u64toa(index, buffer); |
| | | documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer))); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | } // namespace internal |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // IGenericRemoteSchemaDocumentProvider |
| | | |
| | | template <typename SchemaDocumentType> |
| | | class IGenericRemoteSchemaDocumentProvider { |
| | | public: |
| | | typedef typename SchemaDocumentType::Ch Ch; |
| | | |
| | | virtual ~IGenericRemoteSchemaDocumentProvider() {} |
| | | virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; |
| | | }; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // GenericSchemaDocument |
| | | |
| | | //! JSON schema document. |
| | | /*! |
| | | A JSON schema document is a compiled version of a JSON schema. |
| | | It is basically a tree of internal::Schema. |
| | | |
| | | \note This is an immutable class (i.e. its instance cannot be modified after construction). |
| | | \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. |
| | | \tparam Allocator Allocator type for allocating memory of this document. |
| | | */ |
| | | template <typename ValueT, typename Allocator = CrtAllocator> |
| | | class GenericSchemaDocument { |
| | | public: |
| | | typedef ValueT ValueType; |
| | | typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType; |
| | | typedef Allocator AllocatorType; |
| | | typedef typename ValueType::EncodingType EncodingType; |
| | | typedef typename EncodingType::Ch Ch; |
| | | typedef internal::Schema<GenericSchemaDocument> SchemaType; |
| | | typedef GenericPointer<ValueType, Allocator> PointerType; |
| | | friend class internal::Schema<GenericSchemaDocument>; |
| | | template <typename, typename, typename> |
| | | friend class GenericSchemaValidator; |
| | | |
| | | //! Constructor. |
| | | /*! |
| | | Compile a JSON document into schema document. |
| | | |
| | | \param document A JSON document as source. |
| | | \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. |
| | | */ |
| | | explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : |
| | | remoteProvider_(remoteProvider), |
| | | allocator_(allocator), |
| | | ownAllocator_(), |
| | | root_(), |
| | | typeless_(), |
| | | schemaMap_(allocator, kInitialSchemaMapSize), |
| | | schemaRef_(allocator, kInitialSchemaRefSize) |
| | | { |
| | | if (!allocator_) |
| | | ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); |
| | | |
| | | typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType))); |
| | | new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); |
| | | |
| | | // Generate root schema, it will call CreateSchema() to create sub-schemas, |
| | | // And call AddRefSchema() if there are $ref. |
| | | CreateSchemaRecursive(&root_, PointerType(), document, document); |
| | | |
| | | // 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_); |
| | | } |
| | | } |
| | | else if (refEntry->schema) |
| | | *refEntry->schema = typeless_; |
| | | |
| | | refEntry->~SchemaRefEntry(); |
| | | } |
| | | |
| | | RAPIDJSON_ASSERT(root_ != 0); |
| | | |
| | | schemaRef_.ShrinkToFit(); // Deallocate all memory for ref |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | //! Move constructor in C++11 |
| | | GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : |
| | | remoteProvider_(rhs.remoteProvider_), |
| | | allocator_(rhs.allocator_), |
| | | ownAllocator_(rhs.ownAllocator_), |
| | | root_(rhs.root_), |
| | | typeless_(rhs.typeless_), |
| | | schemaMap_(std::move(rhs.schemaMap_)), |
| | | schemaRef_(std::move(rhs.schemaRef_)) |
| | | { |
| | | rhs.remoteProvider_ = 0; |
| | | rhs.allocator_ = 0; |
| | | rhs.ownAllocator_ = 0; |
| | | rhs.typeless_ = 0; |
| | | } |
| | | #endif |
| | | |
| | | //! Destructor |
| | | ~GenericSchemaDocument() { |
| | | while (!schemaMap_.Empty()) |
| | | schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry(); |
| | | |
| | | if (typeless_) { |
| | | typeless_->~SchemaType(); |
| | | Allocator::Free(typeless_); |
| | | } |
| | | |
| | | RAPIDJSON_DELETE(ownAllocator_); |
| | | } |
| | | |
| | | //! Get the root schema. |
| | | const SchemaType& GetRoot() const { return *root_; } |
| | | |
| | | 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; |
| | | }; |
| | | |
| | | struct SchemaEntry { |
| | | SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} |
| | | ~SchemaEntry() { |
| | | if (owned) { |
| | | schema->~SchemaType(); |
| | | Allocator::Free(schema); |
| | | } |
| | | } |
| | | PointerType pointer; |
| | | SchemaType* schema; |
| | | bool owned; |
| | | }; |
| | | |
| | | void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { |
| | | if (schema) |
| | | *schema = typeless_; |
| | | |
| | | if (v.GetType() == kObjectType) { |
| | | const SchemaType* s = GetSchema(pointer); |
| | | if (!s) |
| | | CreateSchema(schema, pointer, v, document); |
| | | |
| | | for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) |
| | | CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); |
| | | } |
| | | else if (v.GetType() == kArrayType) |
| | | for (SizeType i = 0; i < v.Size(); i++) |
| | | CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); |
| | | } |
| | | |
| | | void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { |
| | | RAPIDJSON_ASSERT(pointer.IsValid()); |
| | | 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 (schema) |
| | | *schema = s; |
| | | } |
| | | } |
| | | } |
| | | |
| | | 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); |
| | | if (itr == v.MemberEnd()) |
| | | return false; |
| | | |
| | | 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; |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | 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; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | const SchemaType* GetSchema(const PointerType& pointer) const { |
| | | for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target) |
| | | if (pointer == target->pointer) |
| | | return target->schema; |
| | | return 0; |
| | | } |
| | | |
| | | PointerType GetPointer(const SchemaType* schema) const { |
| | | for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target) |
| | | if (schema == target->schema) |
| | | return target->pointer; |
| | | return PointerType(); |
| | | } |
| | | |
| | | const SchemaType* GetTypeless() const { return typeless_; } |
| | | |
| | | static const size_t kInitialSchemaMapSize = 64; |
| | | static const size_t kInitialSchemaRefSize = 64; |
| | | |
| | | IRemoteSchemaDocumentProviderType* remoteProvider_; |
| | | 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 |
| | | }; |
| | | |
| | | //! GenericSchemaDocument using Value type. |
| | | typedef GenericSchemaDocument<Value> SchemaDocument; |
| | | //! IGenericRemoteSchemaDocumentProvider using SchemaDocument. |
| | | typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // GenericSchemaValidator |
| | | |
| | | //! JSON Schema Validator. |
| | | /*! |
| | | A SAX style JSON schema validator. |
| | | It uses a \c GenericSchemaDocument to validate SAX events. |
| | | It delegates the incoming SAX events to an output handler. |
| | | The default output handler does nothing. |
| | | It can be reused multiple times by calling \c Reset(). |
| | | |
| | | \tparam SchemaDocumentType Type of schema document. |
| | | \tparam OutputHandler Type of output handler. Default handler does nothing. |
| | | \tparam StateAllocator Allocator for storing the internal validation states. |
| | | */ |
| | | template < |
| | | typename SchemaDocumentType, |
| | | typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>, |
| | | typename StateAllocator = CrtAllocator> |
| | | class GenericSchemaValidator : |
| | | public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>, |
| | | public internal::ISchemaValidator |
| | | { |
| | | public: |
| | | typedef typename SchemaDocumentType::SchemaType SchemaType; |
| | | typedef typename SchemaDocumentType::PointerType PointerType; |
| | | typedef typename SchemaType::EncodingType EncodingType; |
| | | typedef typename EncodingType::Ch Ch; |
| | | |
| | | //! Constructor without output handler. |
| | | /*! |
| | | \param schemaDocument The schema document to conform to. |
| | | \param allocator Optional allocator for storing internal validation states. |
| | | \param schemaStackCapacity Optional initial capacity of schema path stack. |
| | | \param documentStackCapacity Optional initial capacity of document path stack. |
| | | */ |
| | | GenericSchemaValidator( |
| | | const SchemaDocumentType& schemaDocument, |
| | | StateAllocator* allocator = 0, |
| | | size_t schemaStackCapacity = kDefaultSchemaStackCapacity, |
| | | size_t documentStackCapacity = kDefaultDocumentStackCapacity) |
| | | : |
| | | schemaDocument_(&schemaDocument), |
| | | root_(schemaDocument.GetRoot()), |
| | | stateAllocator_(allocator), |
| | | ownStateAllocator_(0), |
| | | schemaStack_(allocator, schemaStackCapacity), |
| | | documentStack_(allocator, documentStackCapacity), |
| | | outputHandler_(0), |
| | | valid_(true) |
| | | #if RAPIDJSON_SCHEMA_VERBOSE |
| | | , depth_(0) |
| | | #endif |
| | | { |
| | | } |
| | | |
| | | //! Constructor with output handler. |
| | | /*! |
| | | \param schemaDocument The schema document to conform to. |
| | | \param allocator Optional allocator for storing internal validation states. |
| | | \param schemaStackCapacity Optional initial capacity of schema path stack. |
| | | \param documentStackCapacity Optional initial capacity of document path stack. |
| | | */ |
| | | GenericSchemaValidator( |
| | | const SchemaDocumentType& schemaDocument, |
| | | OutputHandler& outputHandler, |
| | | StateAllocator* allocator = 0, |
| | | size_t schemaStackCapacity = kDefaultSchemaStackCapacity, |
| | | size_t documentStackCapacity = kDefaultDocumentStackCapacity) |
| | | : |
| | | schemaDocument_(&schemaDocument), |
| | | root_(schemaDocument.GetRoot()), |
| | | stateAllocator_(allocator), |
| | | ownStateAllocator_(0), |
| | | schemaStack_(allocator, schemaStackCapacity), |
| | | documentStack_(allocator, documentStackCapacity), |
| | | outputHandler_(&outputHandler), |
| | | valid_(true) |
| | | #if RAPIDJSON_SCHEMA_VERBOSE |
| | | , depth_(0) |
| | | #endif |
| | | { |
| | | } |
| | | |
| | | //! Destructor. |
| | | ~GenericSchemaValidator() { |
| | | Reset(); |
| | | RAPIDJSON_DELETE(ownStateAllocator_); |
| | | } |
| | | |
| | | //! Reset the internal states. |
| | | void Reset() { |
| | | while (!schemaStack_.Empty()) |
| | | PopSchema(); |
| | | documentStack_.Clear(); |
| | | valid_ = true; |
| | | } |
| | | |
| | | //! Checks whether the current state is valid. |
| | | // Implementation of ISchemaValidator |
| | | virtual bool IsValid() const { return valid_; } |
| | | |
| | | //! Gets the JSON pointer pointed to the invalid schema. |
| | | PointerType GetInvalidSchemaPointer() const { |
| | | return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); |
| | | } |
| | | |
| | | //! Gets the keyword of invalid schema. |
| | | const Ch* GetInvalidSchemaKeyword() const { |
| | | return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; |
| | | } |
| | | |
| | | //! Gets the JSON pointer pointed to the invalid value. |
| | | PointerType GetInvalidDocumentPointer() const { |
| | | return documentStack_.Empty() ? PointerType() : 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 |
| | | |
| | | #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ |
| | | if (!valid_) return false; \ |
| | | if (!BeginValue() || !CurrentSchema().method arg1) {\ |
| | | RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ |
| | | return valid_ = false;\ |
| | | } |
| | | |
| | | #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ |
| | | for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\ |
| | | if (context->hasher)\ |
| | | static_cast<HasherType*>(context->hasher)->method arg2;\ |
| | | if (context->validators)\ |
| | | for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ |
| | | static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\ |
| | | if (context->patternPropertiesValidators)\ |
| | | for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ |
| | | static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\ |
| | | } |
| | | |
| | | #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ |
| | | return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2) |
| | | |
| | | #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 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)); } |
| | | bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } |
| | | bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } |
| | | bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } |
| | | bool RawNumber(const Ch* str, SizeType length, bool copy) |
| | | { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } |
| | | bool String(const Ch* str, SizeType length, bool copy) |
| | | { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } |
| | | |
| | | bool StartObject() { |
| | | RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); |
| | | RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); |
| | | return valid_ = !outputHandler_ || outputHandler_->StartObject(); |
| | | } |
| | | |
| | | bool Key(const Ch* str, SizeType len, bool copy) { |
| | | if (!valid_) return false; |
| | | AppendToken(str, len); |
| | | if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; |
| | | RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); |
| | | return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); |
| | | } |
| | | |
| | | bool EndObject(SizeType memberCount) { |
| | | if (!valid_) return false; |
| | | RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); |
| | | if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; |
| | | RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); |
| | | } |
| | | |
| | | bool StartArray() { |
| | | RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); |
| | | RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); |
| | | return valid_ = !outputHandler_ || outputHandler_->StartArray(); |
| | | } |
| | | |
| | | bool EndArray(SizeType elementCount) { |
| | | if (!valid_) return false; |
| | | RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); |
| | | if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; |
| | | 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 |
| | | depth_ + 1, |
| | | #endif |
| | | &GetStateAllocator()); |
| | | } |
| | | |
| | | virtual void DestroySchemaValidator(ISchemaValidator* validator) { |
| | | GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator); |
| | | v->~GenericSchemaValidator(); |
| | | StateAllocator::Free(v); |
| | | } |
| | | |
| | | virtual void* CreateHasher() { |
| | | return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); |
| | | } |
| | | |
| | | virtual uint64_t GetHashCode(void* hasher) { |
| | | return static_cast<HasherType*>(hasher)->GetHashCode(); |
| | | } |
| | | |
| | | virtual void DestroryHasher(void* hasher) { |
| | | HasherType* h = static_cast<HasherType*>(hasher); |
| | | h->~HasherType(); |
| | | StateAllocator::Free(h); |
| | | } |
| | | |
| | | virtual void* MallocState(size_t size) { |
| | | return GetStateAllocator().Malloc(size); |
| | | } |
| | | |
| | | virtual void FreeState(void* p) { |
| | | StateAllocator::Free(p); |
| | | } |
| | | |
| | | private: |
| | | typedef typename SchemaType::Context Context; |
| | | typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray; |
| | | typedef internal::Hasher<EncodingType, StateAllocator> HasherType; |
| | | |
| | | GenericSchemaValidator( |
| | | const SchemaDocumentType& schemaDocument, |
| | | const SchemaType& root, |
| | | #if RAPIDJSON_SCHEMA_VERBOSE |
| | | unsigned depth, |
| | | #endif |
| | | StateAllocator* allocator = 0, |
| | | size_t schemaStackCapacity = kDefaultSchemaStackCapacity, |
| | | size_t documentStackCapacity = kDefaultDocumentStackCapacity) |
| | | : |
| | | schemaDocument_(&schemaDocument), |
| | | root_(root), |
| | | stateAllocator_(allocator), |
| | | ownStateAllocator_(0), |
| | | schemaStack_(allocator, schemaStackCapacity), |
| | | documentStack_(allocator, documentStackCapacity), |
| | | outputHandler_(0), |
| | | valid_(true) |
| | | #if RAPIDJSON_SCHEMA_VERBOSE |
| | | , depth_(depth) |
| | | #endif |
| | | { |
| | | } |
| | | |
| | | StateAllocator& GetStateAllocator() { |
| | | if (!stateAllocator_) |
| | | stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); |
| | | return *stateAllocator_; |
| | | } |
| | | |
| | | bool BeginValue() { |
| | | if (schemaStack_.Empty()) |
| | | PushSchema(root_); |
| | | else { |
| | | if (CurrentContext().inArray) |
| | | internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); |
| | | |
| | | if (!CurrentSchema().BeginValue(CurrentContext())) |
| | | return false; |
| | | |
| | | SizeType count = CurrentContext().patternPropertiesSchemaCount; |
| | | const SchemaType** sa = CurrentContext().patternPropertiesSchemas; |
| | | typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; |
| | | bool valueUniqueness = CurrentContext().valueUniqueness; |
| | | 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)); |
| | | for (SizeType i = 0; i < count; i++) |
| | | va[validatorCount++] = CreateSchemaValidator(*sa[i]); |
| | | } |
| | | |
| | | CurrentContext().arrayUniqueness = valueUniqueness; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | bool EndValue() { |
| | | if (!CurrentSchema().EndValue(CurrentContext())) |
| | | return false; |
| | | |
| | | #if RAPIDJSON_SCHEMA_VERBOSE |
| | | GenericStringBuffer<EncodingType> sb; |
| | | schemaDocument_->GetPointer(&CurrentSchema()).Stringify(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; |
| | | |
| | | PopSchema(); |
| | | |
| | | if (!schemaStack_.Empty()) { |
| | | Context& context = CurrentContext(); |
| | | if (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()); |
| | | a->PushBack(h, GetStateAllocator()); |
| | | } |
| | | } |
| | | |
| | | // Remove the last token of document pointer |
| | | while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/') |
| | | ; |
| | | |
| | | return true; |
| | | } |
| | | |
| | | void AppendToken(const Ch* str, SizeType len) { |
| | | documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters |
| | | *documentStack_.template PushUnsafe<Ch>() = '/'; |
| | | for (SizeType i = 0; i < len; i++) { |
| | | if (str[i] == '~') { |
| | | *documentStack_.template PushUnsafe<Ch>() = '~'; |
| | | *documentStack_.template PushUnsafe<Ch>() = '0'; |
| | | } |
| | | else if (str[i] == '/') { |
| | | *documentStack_.template PushUnsafe<Ch>() = '~'; |
| | | *documentStack_.template PushUnsafe<Ch>() = '1'; |
| | | } |
| | | else |
| | | *documentStack_.template PushUnsafe<Ch>() = str[i]; |
| | | } |
| | | } |
| | | |
| | | RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); } |
| | | |
| | | RAPIDJSON_FORCEINLINE void PopSchema() { |
| | | Context* c = schemaStack_.template Pop<Context>(1); |
| | | if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) { |
| | | a->~HashCodeArray(); |
| | | StateAllocator::Free(a); |
| | | } |
| | | c->~Context(); |
| | | } |
| | | |
| | | 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 const size_t kDefaultSchemaStackCapacity = 1024; |
| | | static const size_t kDefaultDocumentStackCapacity = 256; |
| | | const SchemaDocumentType* schemaDocument_; |
| | | const SchemaType& root_; |
| | | 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_; |
| | | bool valid_; |
| | | #if RAPIDJSON_SCHEMA_VERBOSE |
| | | unsigned depth_; |
| | | #endif |
| | | }; |
| | | |
| | | typedef GenericSchemaValidator<SchemaDocument> SchemaValidator; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // SchemaValidatingReader |
| | | |
| | | //! A helper class for parsing with validation. |
| | | /*! |
| | | This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). |
| | | |
| | | \tparam parseFlags Combination of \ref ParseFlag. |
| | | \tparam InputStream Type of input stream, implementing Stream concept. |
| | | \tparam SourceEncoding Encoding of the input stream. |
| | | \tparam SchemaDocumentType Type of schema document. |
| | | \tparam StackAllocator Allocator type for stack. |
| | | */ |
| | | template < |
| | | unsigned parseFlags, |
| | | typename InputStream, |
| | | typename SourceEncoding, |
| | | typename SchemaDocumentType = SchemaDocument, |
| | | typename StackAllocator = CrtAllocator> |
| | | class SchemaValidatingReader { |
| | | public: |
| | | typedef typename SchemaDocumentType::PointerType PointerType; |
| | | typedef typename InputStream::Ch Ch; |
| | | |
| | | //! Constructor |
| | | /*! |
| | | \param is Input stream. |
| | | \param sd Schema document. |
| | | */ |
| | | SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} |
| | | |
| | | template <typename Handler> |
| | | bool operator()(Handler& handler) { |
| | | GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader; |
| | | GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler); |
| | | parseResult_ = reader.template Parse<parseFlags>(is_, validator); |
| | | |
| | | isValid_ = validator.IsValid(); |
| | | if (isValid_) { |
| | | invalidSchemaPointer_ = PointerType(); |
| | | invalidSchemaKeyword_ = 0; |
| | | invalidDocumentPointer_ = PointerType(); |
| | | } |
| | | else { |
| | | invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); |
| | | invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); |
| | | invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); |
| | | } |
| | | |
| | | return parseResult_; |
| | | } |
| | | |
| | | const ParseResult& GetParseResult() const { return parseResult_; } |
| | | bool IsValid() const { return isValid_; } |
| | | const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } |
| | | const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } |
| | | const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } |
| | | |
| | | private: |
| | | InputStream& is_; |
| | | const SchemaDocumentType& sd_; |
| | | |
| | | ParseResult parseResult_; |
| | | PointerType invalidSchemaPointer_; |
| | | const Ch* invalidSchemaKeyword_; |
| | | PointerType invalidDocumentPointer_; |
| | | bool isValid_; |
| | | }; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | RAPIDJSON_DIAG_POP |
| | | |
| | | #endif // RAPIDJSON_SCHEMA_H_ |
New file |
| | |
| | | // 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 |
| | | // 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. |
| | | |
| | | #include "rapidjson.h" |
| | | |
| | | #ifndef RAPIDJSON_STREAM_H_ |
| | | #define RAPIDJSON_STREAM_H_ |
| | | |
| | | #include "encodings.h" |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // Stream |
| | | |
| | | /*! \class rapidjson::Stream |
| | | \brief Concept for reading and writing characters. |
| | | |
| | | For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). |
| | | |
| | | For write-only stream, only need to implement Put() and Flush(). |
| | | |
| | | \code |
| | | concept Stream { |
| | | typename Ch; //!< Character type of the stream. |
| | | |
| | | //! Read the current character from stream without moving the read cursor. |
| | | Ch Peek() const; |
| | | |
| | | //! Read the current character from stream and moving the read cursor to next character. |
| | | Ch Take(); |
| | | |
| | | //! Get the current read cursor. |
| | | //! \return Number of characters read from start. |
| | | size_t Tell(); |
| | | |
| | | //! Begin writing operation at the current read pointer. |
| | | //! \return The begin writer pointer. |
| | | Ch* PutBegin(); |
| | | |
| | | //! Write a character. |
| | | void Put(Ch c); |
| | | |
| | | //! Flush the buffer. |
| | | void Flush(); |
| | | |
| | | //! End the writing operation. |
| | | //! \param begin The begin write pointer returned by PutBegin(). |
| | | //! \return Number of characters written. |
| | | size_t PutEnd(Ch* begin); |
| | | } |
| | | \endcode |
| | | */ |
| | | |
| | | //! Provides additional information for stream. |
| | | /*! |
| | | By using traits pattern, this type provides a default configuration for stream. |
| | | For custom stream, this type can be specialized for other configuration. |
| | | See TEST(Reader, CustomStringStream) in readertest.cpp for example. |
| | | */ |
| | | template<typename Stream> |
| | | struct StreamTraits { |
| | | //! Whether to make local copy of stream for optimization during parsing. |
| | | /*! |
| | | By default, for safety, streams do not use local copy optimization. |
| | | Stream that can be copied fast should specialize this, like StreamTraits<StringStream>. |
| | | */ |
| | | enum { copyOptimization = 0 }; |
| | | }; |
| | | |
| | | //! Reserve n characters for writing to a stream. |
| | | template<typename Stream> |
| | | inline void PutReserve(Stream& stream, size_t count) { |
| | | (void)stream; |
| | | (void)count; |
| | | } |
| | | |
| | | //! Write character to a stream, presuming buffer is reserved. |
| | | template<typename Stream> |
| | | inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { |
| | | stream.Put(c); |
| | | } |
| | | |
| | | //! Put N copies of a character to a stream. |
| | | template<typename Stream, typename Ch> |
| | | inline void PutN(Stream& stream, Ch c, size_t n) { |
| | | PutReserve(stream, n); |
| | | for (size_t i = 0; i < n; i++) |
| | | 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 |
| | | |
| | | //! Read-only string stream. |
| | | /*! \note implements Stream concept |
| | | */ |
| | | template <typename Encoding> |
| | | struct GenericStringStream { |
| | | typedef typename Encoding::Ch Ch; |
| | | |
| | | GenericStringStream(const Ch *src) : src_(src), head_(src) {} |
| | | |
| | | Ch Peek() const { return *src_; } |
| | | Ch Take() { return *src_++; } |
| | | size_t Tell() const { return static_cast<size_t>(src_ - head_); } |
| | | |
| | | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } |
| | | void Put(Ch) { RAPIDJSON_ASSERT(false); } |
| | | void Flush() { RAPIDJSON_ASSERT(false); } |
| | | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } |
| | | |
| | | const Ch* src_; //!< Current read position. |
| | | const Ch* head_; //!< Original head of the string. |
| | | }; |
| | | |
| | | template <typename Encoding> |
| | | struct StreamTraits<GenericStringStream<Encoding> > { |
| | | enum { copyOptimization = 1 }; |
| | | }; |
| | | |
| | | //! String stream with UTF8 encoding. |
| | | typedef GenericStringStream<UTF8<> > StringStream; |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // InsituStringStream |
| | | |
| | | //! A read-write string stream. |
| | | /*! This string stream is particularly designed for in-situ parsing. |
| | | \note implements Stream concept |
| | | */ |
| | | template <typename Encoding> |
| | | struct GenericInsituStringStream { |
| | | typedef typename Encoding::Ch Ch; |
| | | |
| | | GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} |
| | | |
| | | // Read |
| | | Ch Peek() { return *src_; } |
| | | Ch Take() { return *src_++; } |
| | | size_t Tell() { return static_cast<size_t>(src_ - head_); } |
| | | |
| | | // Write |
| | | void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } |
| | | |
| | | Ch* PutBegin() { return dst_ = src_; } |
| | | size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); } |
| | | void Flush() {} |
| | | |
| | | Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } |
| | | void Pop(size_t count) { dst_ -= count; } |
| | | |
| | | Ch* src_; |
| | | Ch* dst_; |
| | | Ch* head_; |
| | | }; |
| | | |
| | | template <typename Encoding> |
| | | struct StreamTraits<GenericInsituStringStream<Encoding> > { |
| | | enum { copyOptimization = 1 }; |
| | | }; |
| | | |
| | | //! Insitu string stream with UTF8 encoding. |
| | | typedef GenericInsituStringStream<UTF8<> > InsituStringStream; |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #endif // RAPIDJSON_STREAM_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_STRINGBUFFER_H_ |
| | | #define RAPIDJSON_STRINGBUFFER_H_ |
| | | |
| | | #include "stream.h" |
| | | #include "internal/stack.h" |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | #include <utility> // std::move |
| | | #endif |
| | | |
| | | #include "internal/stack.h" |
| | | |
| | | #if defined(__clang__) |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(c++98-compat) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | //! Represents an in-memory output stream. |
| | | /*! |
| | | \tparam Encoding Encoding of the stream. |
| | | \tparam Allocator type for allocating memory buffer. |
| | | \note implements Stream concept |
| | | */ |
| | | template <typename Encoding, typename Allocator = CrtAllocator> |
| | | class GenericStringBuffer { |
| | | public: |
| | | typedef typename Encoding::Ch Ch; |
| | | |
| | | GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} |
| | | |
| | | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| | | GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} |
| | | GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { |
| | | if (&rhs != this) |
| | | stack_ = std::move(rhs.stack_); |
| | | return *this; |
| | | } |
| | | #endif |
| | | |
| | | void Put(Ch c) { *stack_.template Push<Ch>() = c; } |
| | | void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; } |
| | | void Flush() {} |
| | | |
| | | void Clear() { stack_.Clear(); } |
| | | void ShrinkToFit() { |
| | | // Push and pop a null terminator. This is safe. |
| | | *stack_.template Push<Ch>() = '\0'; |
| | | stack_.ShrinkToFit(); |
| | | stack_.template Pop<Ch>(1); |
| | | } |
| | | |
| | | void Reserve(size_t count) { stack_.template Reserve<Ch>(count); } |
| | | Ch* Push(size_t count) { return stack_.template Push<Ch>(count); } |
| | | Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); } |
| | | void Pop(size_t count) { stack_.template Pop<Ch>(count); } |
| | | |
| | | const Ch* GetString() const { |
| | | // Push and pop a null terminator. This is safe. |
| | | *stack_.template Push<Ch>() = '\0'; |
| | | stack_.template Pop<Ch>(1); |
| | | |
| | | 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_; |
| | | |
| | | private: |
| | | // Prohibit copy constructor & assignment operator. |
| | | GenericStringBuffer(const GenericStringBuffer&); |
| | | GenericStringBuffer& operator=(const GenericStringBuffer&); |
| | | }; |
| | | |
| | | //! String buffer with UTF8 encoding |
| | | typedef GenericStringBuffer<UTF8<> > StringBuffer; |
| | | |
| | | template<typename Encoding, typename Allocator> |
| | | inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) { |
| | | stream.Reserve(count); |
| | | } |
| | | |
| | | template<typename Encoding, typename Allocator> |
| | | inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) { |
| | | stream.PutUnsafe(c); |
| | | } |
| | | |
| | | //! Implement specialized version of PutN() with memset() for better performance. |
| | | template<> |
| | | inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) { |
| | | std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c)); |
| | | } |
| | | |
| | | RAPIDJSON_NAMESPACE_END |
| | | |
| | | #if defined(__clang__) |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_STRINGBUFFER_H_ |
New file |
| | |
| | | // 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 |
| | | // 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_WRITER_H_ |
| | | #define RAPIDJSON_WRITER_H_ |
| | | |
| | | #include "stream.h" |
| | | #include "internal/meta.h" |
| | | #include "internal/stack.h" |
| | | #include "internal/strfunc.h" |
| | | #include "internal/dtoa.h" |
| | | #include "internal/itoa.h" |
| | | #include "stringbuffer.h" |
| | | #include <new> // placement new |
| | | |
| | | #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) |
| | | #include <intrin.h> |
| | | #pragma intrinsic(_BitScanForward) |
| | | #endif |
| | | #ifdef RAPIDJSON_SSE42 |
| | | #include <nmmintrin.h> |
| | | #elif defined(RAPIDJSON_SSE2) |
| | | #include <emmintrin.h> |
| | | #elif defined(RAPIDJSON_NEON) |
| | | #include <arm_neon.h> |
| | | #endif |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant |
| | | #endif |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_PUSH |
| | | RAPIDJSON_DIAG_OFF(padded) |
| | | RAPIDJSON_DIAG_OFF(unreachable-code) |
| | | RAPIDJSON_DIAG_OFF(c++98-compat) |
| | | #endif |
| | | |
| | | RAPIDJSON_NAMESPACE_BEGIN |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | // WriteFlag |
| | | |
| | | /*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS |
| | | \ingroup RAPIDJSON_CONFIG |
| | | \brief User-defined kWriteDefaultFlags definition. |
| | | |
| | | User can define this as any \c WriteFlag combinations. |
| | | */ |
| | | #ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS |
| | | #define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags |
| | | #endif |
| | | |
| | | //! Combination of writeFlags |
| | | enum WriteFlag { |
| | | kWriteNoFlags = 0, //!< No flags are set. |
| | | kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. |
| | | kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. |
| | | kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS |
| | | }; |
| | | |
| | | //! JSON writer |
| | | /*! Writer implements the concept Handler. |
| | | It generates JSON text by events to an output os. |
| | | |
| | | User may programmatically calls the functions of a writer to generate JSON text. |
| | | |
| | | On the other side, a writer can also be passed to objects that generates events, |
| | | |
| | | for example Reader::Parse() and Document::Accept(). |
| | | |
| | | \tparam OutputStream Type of output stream. |
| | | \tparam SourceEncoding Encoding of source string. |
| | | \tparam TargetEncoding Encoding of output stream. |
| | | \tparam StackAllocator Type of allocator for allocating memory of stack. |
| | | \note implements Handler concept |
| | | */ |
| | | template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> |
| | | class Writer { |
| | | public: |
| | | typedef typename SourceEncoding::Ch Ch; |
| | | |
| | | static const int kDefaultMaxDecimalPlaces = 324; |
| | | |
| | | //! Constructor |
| | | /*! \param os Output stream. |
| | | \param stackAllocator User supplied allocator. If it is null, it will create a private one. |
| | | \param levelDepth Initial capacity of stack. |
| | | */ |
| | | explicit |
| | | Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : |
| | | os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} |
| | | |
| | | explicit |
| | | 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, |
| | | in order to make a Writer object reusable for output multiple JSONs. |
| | | |
| | | \param os New output stream. |
| | | \code |
| | | Writer<OutputStream> writer(os1); |
| | | writer.StartObject(); |
| | | // ... |
| | | writer.EndObject(); |
| | | |
| | | writer.Reset(os2); |
| | | writer.StartObject(); |
| | | // ... |
| | | writer.EndObject(); |
| | | \endcode |
| | | */ |
| | | void Reset(OutputStream& os) { |
| | | os_ = &os; |
| | | hasRoot_ = false; |
| | | level_stack_.Clear(); |
| | | } |
| | | |
| | | //! Checks whether the output is a complete JSON. |
| | | /*! |
| | | A complete JSON has a complete root object or array. |
| | | */ |
| | | bool IsComplete() const { |
| | | return hasRoot_ && level_stack_.Empty(); |
| | | } |
| | | |
| | | int GetMaxDecimalPlaces() const { |
| | | return maxDecimalPlaces_; |
| | | } |
| | | |
| | | //! Sets the maximum number of decimal places for double output. |
| | | /*! |
| | | This setting truncates the output with specified number of decimal places. |
| | | |
| | | For example, |
| | | |
| | | \code |
| | | writer.SetMaxDecimalPlaces(3); |
| | | writer.StartArray(); |
| | | writer.Double(0.12345); // "0.123" |
| | | writer.Double(0.0001); // "0.0" |
| | | writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) |
| | | writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) |
| | | writer.EndArray(); |
| | | \endcode |
| | | |
| | | The default setting does not truncate any decimal places. You can restore to this setting by calling |
| | | \code |
| | | writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); |
| | | \endcode |
| | | */ |
| | | void SetMaxDecimalPlaces(int maxDecimalPlaces) { |
| | | maxDecimalPlaces_ = maxDecimalPlaces; |
| | | } |
| | | |
| | | /*!@name Implementation of Handler |
| | | \see Handler |
| | | */ |
| | | //@{ |
| | | |
| | | bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } |
| | | bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } |
| | | bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } |
| | | bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } |
| | | bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } |
| | | bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } |
| | | |
| | | //! Writes the given \c double value to the stream |
| | | /*! |
| | | \param d The value to be written. |
| | | \return Whether it is succeed. |
| | | */ |
| | | bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } |
| | | |
| | | bool RawNumber(const Ch* str, SizeType length, bool copy = false) { |
| | | RAPIDJSON_ASSERT(str != 0); |
| | | (void)copy; |
| | | Prefix(kNumberType); |
| | | return EndValue(WriteString(str, length)); |
| | | } |
| | | |
| | | bool String(const Ch* str, SizeType length, bool copy = false) { |
| | | RAPIDJSON_ASSERT(str != 0); |
| | | (void)copy; |
| | | Prefix(kStringType); |
| | | return EndValue(WriteString(str, length)); |
| | | } |
| | | |
| | | #if RAPIDJSON_HAS_STDSTRING |
| | | bool String(const std::basic_string<Ch>& str) { |
| | | return String(str.data(), SizeType(str.size())); |
| | | } |
| | | #endif |
| | | |
| | | bool StartObject() { |
| | | Prefix(kObjectType); |
| | | new (level_stack_.template Push<Level>()) Level(false); |
| | | return WriteStartObject(); |
| | | } |
| | | |
| | | 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)); // 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()); |
| | | } |
| | | |
| | | bool StartArray() { |
| | | Prefix(kArrayType); |
| | | new (level_stack_.template Push<Level>()) Level(true); |
| | | return WriteStartArray(); |
| | | } |
| | | |
| | | bool EndArray(SizeType elementCount = 0) { |
| | | (void)elementCount; |
| | | RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); |
| | | RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray); |
| | | level_stack_.template Pop<Level>(1); |
| | | return EndValue(WriteEndArray()); |
| | | } |
| | | //@} |
| | | |
| | | /*! @name Convenience extensions */ |
| | | //@{ |
| | | |
| | | //! Simpler but slower overload. |
| | | 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. |
| | | /*! |
| | | For user to write a stringified JSON as a value. |
| | | |
| | | \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. |
| | | \param length Length of the json. |
| | | \param type Type of the root of json. |
| | | */ |
| | | bool RawValue(const Ch* json, size_t length, Type type) { |
| | | RAPIDJSON_ASSERT(json != 0); |
| | | Prefix(type); |
| | | return EndValue(WriteRawValue(json, length)); |
| | | } |
| | | |
| | | //! Flush the output stream. |
| | | /*! |
| | | Allows the user to flush the output stream immediately. |
| | | */ |
| | | void Flush() { |
| | | os_->Flush(); |
| | | } |
| | | |
| | | protected: |
| | | //! Information for each nested level |
| | | struct Level { |
| | | Level(bool inArray_) : valueCount(0), inArray(inArray_) {} |
| | | size_t valueCount; //!< number of values in this level |
| | | 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; |
| | | } |
| | | |
| | | bool WriteBool(bool b) { |
| | | if (b) { |
| | | PutReserve(*os_, 4); |
| | | PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); |
| | | } |
| | | else { |
| | | PutReserve(*os_, 5); |
| | | PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | bool WriteInt(int i) { |
| | | char buffer[11]; |
| | | 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 OutputStream::Ch>(*p)); |
| | | return true; |
| | | } |
| | | |
| | | bool WriteUint(unsigned u) { |
| | | char buffer[10]; |
| | | 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 OutputStream::Ch>(*p)); |
| | | return true; |
| | | } |
| | | |
| | | bool WriteInt64(int64_t i64) { |
| | | char buffer[21]; |
| | | 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 OutputStream::Ch>(*p)); |
| | | return true; |
| | | } |
| | | |
| | | bool WriteUint64(uint64_t u64) { |
| | | char buffer[20]; |
| | | 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 OutputStream::Ch>(*p)); |
| | | return true; |
| | | } |
| | | |
| | | bool WriteDouble(double d) { |
| | | if (internal::Double(d).IsNanOrInf()) { |
| | | if (!(writeFlags & kWriteNanAndInfFlag)) |
| | | return false; |
| | | if (internal::Double(d).IsNan()) { |
| | | PutReserve(*os_, 3); |
| | | PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); |
| | | return true; |
| | | } |
| | | if (internal::Double(d).Sign()) { |
| | | PutReserve(*os_, 9); |
| | | PutUnsafe(*os_, '-'); |
| | | } |
| | | else |
| | | PutReserve(*os_, 8); |
| | | PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); |
| | | PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); |
| | | return true; |
| | | } |
| | | |
| | | char buffer[25]; |
| | | 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 OutputStream::Ch>(*p)); |
| | | return true; |
| | | } |
| | | |
| | | bool WriteString(const Ch* str, SizeType length) { |
| | | 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 |
| | | 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 |
| | | 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 |
| | | 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 |
| | | Z16, Z16, // 30~4F |
| | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 |
| | | Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF |
| | | #undef Z16 |
| | | }; |
| | | |
| | | if (TargetEncoding::supportUnicode) |
| | | PutReserve(*os_, 2 + length * 6); // "\uxxxx..." |
| | | else |
| | | PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." |
| | | |
| | | PutUnsafe(*os_, '\"'); |
| | | GenericStringStream<SourceEncoding> is(str); |
| | | while (ScanWriteUnescapedString(is, length)) { |
| | | const Ch c = is.Peek(); |
| | | if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) { |
| | | // Unicode escaping |
| | | unsigned codepoint; |
| | | if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) |
| | | return false; |
| | | PutUnsafe(*os_, '\\'); |
| | | PutUnsafe(*os_, 'u'); |
| | | if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { |
| | | PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); |
| | | PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); |
| | | PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); |
| | | PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); |
| | | } |
| | | else { |
| | | RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); |
| | | // Surrogate pair |
| | | unsigned s = codepoint - 0x010000; |
| | | unsigned lead = (s >> 10) + 0xD800; |
| | | unsigned trail = (s & 0x3FF) + 0xDC00; |
| | | PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); |
| | | PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); |
| | | PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); |
| | | PutUnsafe(*os_, hexDigits[(lead ) & 15]); |
| | | PutUnsafe(*os_, '\\'); |
| | | PutUnsafe(*os_, 'u'); |
| | | PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); |
| | | PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); |
| | | PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); |
| | | PutUnsafe(*os_, hexDigits[(trail ) & 15]); |
| | | } |
| | | } |
| | | 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 OutputStream::Ch>(escape[static_cast<unsigned char>(c)])); |
| | | if (escape[static_cast<unsigned char>(c)] == 'u') { |
| | | PutUnsafe(*os_, '0'); |
| | | PutUnsafe(*os_, '0'); |
| | | PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]); |
| | | PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]); |
| | | } |
| | | } |
| | | else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? |
| | | Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) : |
| | | Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_)))) |
| | | return false; |
| | | } |
| | | PutUnsafe(*os_, '\"'); |
| | | return true; |
| | | } |
| | | |
| | | bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, size_t length) { |
| | | return RAPIDJSON_LIKELY(is.Tell() < length); |
| | | } |
| | | |
| | | bool WriteStartObject() { os_->Put('{'); return true; } |
| | | bool WriteEndObject() { os_->Put('}'); return true; } |
| | | bool WriteStartArray() { os_->Put('['); return true; } |
| | | bool WriteEndArray() { os_->Put(']'); return true; } |
| | | |
| | | 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]); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | void Prefix(Type type) { |
| | | (void)type; |
| | | if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root |
| | | Level* level = level_stack_.template Top<Level>(); |
| | | if (level->valueCount > 0) { |
| | | if (level->inArray) |
| | | os_->Put(','); // add comma if it is not the first element in array |
| | | else // in object |
| | | os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); |
| | | } |
| | | if (!level->inArray && level->valueCount % 2 == 0) |
| | | RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name |
| | | level->valueCount++; |
| | | } |
| | | else { |
| | | RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. |
| | | hasRoot_ = true; |
| | | } |
| | | } |
| | | |
| | | // Flush the value if it is the top level one. |
| | | bool EndValue(bool ret) { |
| | | if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text |
| | | Flush(); |
| | | return ret; |
| | | } |
| | | |
| | | OutputStream* os_; |
| | | internal::Stack<StackAllocator> level_stack_; |
| | | int maxDecimalPlaces_; |
| | | bool hasRoot_; |
| | | |
| | | private: |
| | | // Prohibit copy constructor & assignment operator. |
| | | Writer(const Writer&); |
| | | Writer& operator=(const Writer&); |
| | | }; |
| | | |
| | | // Full specialization for StringStream to prevent memory copying |
| | | |
| | | template<> |
| | | inline bool Writer<StringBuffer>::WriteInt(int i) { |
| | | char *buffer = os_->Push(11); |
| | | const char* end = internal::i32toa(i, buffer); |
| | | os_->Pop(static_cast<size_t>(11 - (end - buffer))); |
| | | return true; |
| | | } |
| | | |
| | | template<> |
| | | inline bool Writer<StringBuffer>::WriteUint(unsigned u) { |
| | | char *buffer = os_->Push(10); |
| | | const char* end = internal::u32toa(u, buffer); |
| | | os_->Pop(static_cast<size_t>(10 - (end - buffer))); |
| | | return true; |
| | | } |
| | | |
| | | template<> |
| | | inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) { |
| | | char *buffer = os_->Push(21); |
| | | const char* end = internal::i64toa(i64, buffer); |
| | | os_->Pop(static_cast<size_t>(21 - (end - buffer))); |
| | | return true; |
| | | } |
| | | |
| | | template<> |
| | | inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) { |
| | | char *buffer = os_->Push(20); |
| | | const char* end = internal::u64toa(u, buffer); |
| | | os_->Pop(static_cast<size_t>(20 - (end - buffer))); |
| | | return true; |
| | | } |
| | | |
| | | 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)) |
| | | return false; |
| | | if (internal::Double(d).IsNan()) { |
| | | PutReserve(*os_, 3); |
| | | PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); |
| | | return true; |
| | | } |
| | | if (internal::Double(d).Sign()) { |
| | | PutReserve(*os_, 9); |
| | | PutUnsafe(*os_, '-'); |
| | | } |
| | | else |
| | | PutReserve(*os_, 8); |
| | | PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); |
| | | PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); |
| | | return true; |
| | | } |
| | | |
| | | char *buffer = os_->Push(25); |
| | | char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); |
| | | os_->Pop(static_cast<size_t>(25 - (end - buffer))); |
| | | return true; |
| | | } |
| | | |
| | | #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) |
| | | template<> |
| | | inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) { |
| | | if (length < 16) |
| | | return RAPIDJSON_LIKELY(is.Tell() < length); |
| | | |
| | | if (!RAPIDJSON_LIKELY(is.Tell() < length)) |
| | | return false; |
| | | |
| | | const char* p = is.src_; |
| | | const char* end = is.head_ + length; |
| | | const char* nextAligned = reinterpret_cast<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; |
| | | |
| | | 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 |
| | | static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; |
| | | static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; |
| | | 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])); |
| | | |
| | | for (; p != endAligned; p += 16) { |
| | | 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, 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 |
| | | SizeType len; |
| | | #ifdef _MSC_VER // Find the index of first escaped |
| | | unsigned long offset; |
| | | _BitScanForward(&offset, r); |
| | | len = offset; |
| | | #else |
| | | len = static_cast<SizeType>(__builtin_ffs(r) - 1); |
| | | #endif |
| | | char* q = reinterpret_cast<char*>(os_->PushUnsafe(len)); |
| | | for (size_t i = 0; i < len; i++) |
| | | q[i] = p[i]; |
| | | |
| | | p += len; |
| | | break; |
| | | } |
| | | _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); |
| | | } |
| | | |
| | | is.src_ = p; |
| | | return RAPIDJSON_LIKELY(is.Tell() < length); |
| | | } |
| | | #elif defined(RAPIDJSON_NEON) |
| | | template<> |
| | | inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) { |
| | | if (length < 16) |
| | | return RAPIDJSON_LIKELY(is.Tell() < length); |
| | | |
| | | if (!RAPIDJSON_LIKELY(is.Tell() < length)) |
| | | return false; |
| | | |
| | | const char* p = is.src_; |
| | | const char* end = is.head_ + length; |
| | | const char* nextAligned = reinterpret_cast<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; |
| | | |
| | | 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(reinterpret_cast<uint64x2_t>(x), 0); // extract |
| | | uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract |
| | | |
| | | SizeType len = 0; |
| | | bool escaped = false; |
| | | if (low == 0) { |
| | | if (high != 0) { |
| | | unsigned lz = (unsigned)__builtin_clzll(high); |
| | | len = 8 + (lz >> 3); |
| | | escaped = true; |
| | | } |
| | | } else { |
| | | unsigned lz = (unsigned)__builtin_clzll(low); |
| | | len = lz >> 3; |
| | | escaped = true; |
| | | } |
| | | if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped |
| | | char* q = reinterpret_cast<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 |
| | | |
| | | #ifdef _MSC_VER |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #ifdef __clang__ |
| | | RAPIDJSON_DIAG_POP |
| | | #endif |
| | | |
| | | #endif // RAPIDJSON_RAPIDJSON_H_ |
| | |
| | | #include "../common/net.h" |
| | | #include "platform.h" |
| | | #include "../mcu/mcu_if.h" |
| | | #include "../utils/xconvert.h" |
| | | |
| | | using namespace std; |
| | | |
| | |
| | | sem_post(&sem_tx_mgr); |
| | | } |
| | | |
| | | void PlatformTxInit(const uint8_t *phone) |
| | | void PlatformTxInit(void) |
| | | { |
| | | //2019101500000001 |
| | | memcpy(PhoneNumber, phone, PHONE_NUM_SIZE); |
| | | |
| | | /* PhoneNumber[0] = 0x20; |
| | | PhoneNumber[1] = 0x19; |
| | | PhoneNumber[2] = 0x10; |
| | |
| | | pthread_create(&pid, &attr, TxQueueMgrThread, NULL); |
| | | } |
| | | |
| | | void SetPlatformTxPhoneNum(const char *phone) |
| | | { |
| | | ConvertPhoneNum(PhoneNumber, sizeof(PhoneNumber), phone); |
| | | } |
| | | |
| | | void CommonRespend(uint8_t access, uint16_t seq, uint16_t id, uint8_t value) |
| | | { |
| | | uint8_t data[5]; |
| | |
| | | #define EX_MESSAGE_ATTRIB_SHA1 4 |
| | | #define EX_MESSAGE_ATTRIB_SHA256 8 |
| | | |
| | | void PlatformTxInit(const uint8_t *phone); |
| | | void PlatformTxInit(void); |
| | | void SetPlatformTxPhoneNum(const char *phone); |
| | | void Parse(uint8_t access, const uint8_t *buff, uint16_t length); |
| | | void CommonRespend(uint8_t access, uint16_t seq, uint16_t id, uint8_t value); |
| | | |
| | |
| | | #include "../defs.h" |
| | | #include "../rtk_module/rtk.h" |
| | | #include "../mcu/mcu_if.h" |
| | | #include "../master/comm_if.h" |
| | | #include "../utils/xconvert.h" |
| | | |
| | | #define PARSE_BUFF_SIZE 4096 |
| | | |
| | |
| | | uint32_t downloadRtk : 1; |
| | | } platformStatus; |
| | | |
| | | |
| | | |
| | | struct platformSocket exceptSocket, currSocket; |
| | | static uint32_t eventMask; |
| | | static sem_t sem_status_changed; |
| | |
| | | |
| | | eventMask = 0; |
| | | platform_tcp_fd = -1; |
| | | |
| | | memset(&currSocket, 0, sizeof(currSocket)); |
| | | strcpy(exceptSocket.domain_name, domain_name); |
| | | exceptSocket.port = port; |
| | | memset(&platformStatus, 0, sizeof(platformStatus)); |
| | | |
| | | sem_init(&sem_status_changed, 0, 0); |
| | | |
| | | strcpy(deviceInfo.imei, GetImei()); |
| | | deviceInfo.province = 53; |
| | | deviceInfo.city = 100; |
| | | strcpy((char *)deviceInfo.device_model, "RTKBaseStation"); |
| | | strcpy((char *)deviceInfo.device_sn, "2019101500000001"); |
| | | |
| | | PlatformTxInit(phone); |
| | | PlatformTxInit(); |
| | | |
| | | pthread_t pid; |
| | | pthread_attr_t attr; |
| | |
| | | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached |
| | | pthread_create(&pid, &attr, StatusListenThread, NULL); |
| | | |
| | | ConnectPlatform(domain_name, port); |
| | | MA_ReqRtkPlatformConfig(); |
| | | } |
| | | |
| | | void ConfigPlatform(const rtk_platform_cfg_t *p) |
| | | { |
| | | DEBUG("ConfigPlatform"); |
| | | |
| | | strcpy(exceptSocket.domain_name, p->domain_name); |
| | | exceptSocket.port = p->port; |
| | | deviceInfo.province = p->province; |
| | | deviceInfo.city = p->city; |
| | | strcpy((char *)deviceInfo.device_model, p->device_model); |
| | | strcpy((char *)deviceInfo.device_sn, p->device_sn); |
| | | strcpy(deviceInfo.imei, p->imei); |
| | | SetPlatformTxPhoneNum(p->phone); |
| | | |
| | | platformStatus.registed = p->registered; |
| | | platformStatus.platformKeyLength = strlen(p->password) / 2; |
| | | |
| | | if (platformStatus.platformKeyLength > 0) { |
| | | ConvertString2Hex(platformStatus.platformKey, sizeof(platformStatus.platformKey), p->password); |
| | | } else { |
| | | platformStatus.registed = 0; |
| | | } |
| | | |
| | | pthread_mutex_lock(&platform_tx_mutex); |
| | | if (platform_tcp_fd > 0 && (exceptSocket.port != currSocket.port || strcmp(currSocket.domain_name, exceptSocket.domain_name))) { |
| | | DisconnectTCP(platform_tcp_fd); |
| | | platform_tcp_fd = -1; |
| | | } else if (platform_tcp_fd <= 0) { |
| | | ConnectPlatform(exceptSocket.domain_name, exceptSocket.port); |
| | | } |
| | | pthread_mutex_unlock(&platform_tx_mutex); |
| | | } |
| | | |
| | | void PlatformStatusChanged(uint32_t event) |
| | |
| | | char out[64]; |
| | | sprintf(out, "平台连接成功 %s:%d", currSocket.domain_name, currSocket.port); |
| | | DisplayText(out); |
| | | MA_RtkPlatformConnect(1, currSocket.domain_name, currSocket.port); |
| | | |
| | | platformStatus.connected = 1; |
| | | if (!platformStatus.registed || platformStatus.platformKeyLength == 0) { |
| | |
| | | char out[64]; |
| | | sprintf(out, "平台断开 %s:%d", currSocket.domain_name, currSocket.port); |
| | | DisplayText(out); |
| | | MA_RtkPlatformConnect(0, currSocket.domain_name, currSocket.port); |
| | | |
| | | AppTimer_delete(ConnectPlatformLater); |
| | | AppTimer_add(ConnectPlatformLater, D_SEC(2)); |
| | |
| | | } |
| | | if (events & PLATFORM_REGISTER_EVT) { |
| | | DEBUG("PLATFORM_REGISTER_EVT"); |
| | | platformStatus.platformKeyLength = GetPlatformKey(platformStatus.platformKey); |
| | | platformStatus.registed = GetSharedValue(VK_REG); |
| | | |
| | | platformStatus.login = 0; |
| | | DEBUG("platformStatus.platformKeyLength = %d", platformStatus.platformKeyLength); |
| | | |
| | |
| | | TextSpeak("终端注册成功"); |
| | | DisplayText("终端注册成功"); |
| | | SetPlatformKey(data, length); |
| | | SetSharedValue(VK_REG, 1); |
| | | PlatformStatusChanged(PLATFORM_REGISTER_EVT); |
| | | } |
| | | } |
| | |
| | | #define RTK_UPDATE_EVT 0x0010 |
| | | #define GPS_UPDATE_EVT 0x0020 |
| | | |
| | | typedef struct { |
| | | char domain_name[32]; |
| | | int port; |
| | | uint16_t province; |
| | | uint16_t city; |
| | | char device_model[21]; |
| | | char device_sn[17]; |
| | | char imei[16]; |
| | | char phone[17]; |
| | | char password[17]; |
| | | int registered; |
| | | int rtk_interval; |
| | | } rtk_platform_cfg_t; |
| | | |
| | | void InitPlatform(const uint8_t *phone, const char *domain_name, int port); |
| | | void ConfigPlatform(const rtk_platform_cfg_t *p); |
| | | |
| | | void PlatformStatusChanged(uint32_t event); |
| | | int WritePlatform(const uint8_t * buf, uint32_t len); |
| | | void DeviceRegisterCallback(uint8_t res, const uint8_t *data, int length); |
New file |
| | |
| | | // |
| | | // Created by YY on 2020/1/7. |
| | | // |
| | | |
| | | #include "xconvert.h" |
| | | #include <cstdint> |
| | | #include <cstring> |
| | | |
| | | void ConvertPhoneNum(uint8_t *dst, int length, const char *src) |
| | | { |
| | | int p = length - 1; |
| | | int q = strlen(src) - 1; |
| | | |
| | | memset(dst, 0, length); |
| | | |
| | | while (q >= 0) { |
| | | dst[p] = src[q] - '0'; |
| | | q--; |
| | | if (q >= 0) { |
| | | dst[p] |= (src[q] - '0')<<4; |
| | | p--; |
| | | q--; |
| | | } |
| | | } |
| | | } |
| | | |
| | | void ConvertHex2String(char *str, const uint8_t *hex, int length) |
| | | { |
| | | const char HEX[] = "0123456789ABCDEF"; |
| | | int j = 0; |
| | | |
| | | for (int i = 0; i < length; ++i) { |
| | | str[j++] = HEX[(hex[i]>>4)&0x0F]; |
| | | str[j++] = HEX[hex[i]&0x0F]; |
| | | } |
| | | str[j++] = 0; |
| | | } |
| | | |
| | | void ConvertString2Hex(uint8_t *hex, int length, const char *str) |
| | | { |
| | | if (length < strlen(str)/2) return; |
| | | |
| | | for (int i = 0, j = 0; i < strlen(str); ++i, ++j) { |
| | | if (str[i] >= '0' && str[i] <= '9') { |
| | | hex[j] = str[i] - '0'; |
| | | } else if (str[i] >= 'A' && str[i] <= 'F') { |
| | | hex[j] = 10 + str[i] - 'A'; |
| | | } else if (str[i] >= 'a' && str[i] <= 'f') { |
| | | hex[j] = 10 + str[i] - 'a'; |
| | | } |
| | | hex[j] <<= 4; |
| | | i++; |
| | | if (str[i] >= '0' && str[i] <= '9') { |
| | | hex[j] |= str[i] - '0'; |
| | | } else if (str[i] >= 'A' && str[i] <= 'F') { |
| | | hex[j] |= 10 + str[i] - 'A'; |
| | | } else if (str[i] >= 'a' && str[i] <= 'f') { |
| | | hex[j] |= 10 + str[i] - 'a'; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | // |
| | | // Created by YY on 2020/1/7. |
| | | // |
| | | |
| | | #ifndef MYAPPLICATION2_XCONVERT_H |
| | | #define MYAPPLICATION2_XCONVERT_H |
| | | |
| | | #include <cstdint> |
| | | |
| | | void ConvertPhoneNum(uint8_t *dst, int length, const char *src); |
| | | void ConvertHex2String(char *str, const uint8_t *hex, int length); |
| | | void ConvertString2Hex(uint8_t *hex, int length, const char *str); |
| | | |
| | | #endif //MYAPPLICATION2_XCONVERT_H |
| | |
| | | |
| | | @Override |
| | | public void SendCmd(int cmd, String value) throws RemoteException { |
| | | |
| | | MainProcMsgEntry(cmd, value); |
| | | } |
| | | }; |
| | | @Nullable |
| | |
| | | Log.i(TAG,"onCreate()"); |
| | | speaker = new Speaker(getApplicationContext()); |
| | | |
| | | // startNative(); |
| | | new Thread(new Worker()).start(); |
| | | } |
| | | |
| | | private class Worker implements Runnable{ |
| | | |
| | | @Override |
| | | public void run() { |
| | | while (!mIsServiceDestroyed.get()){ |
| | | try { |
| | | Thread.sleep(1000); |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | onMessageArrived(msgId, String.format("消息ID:%d,请注意查收",msgId++)); |
| | | } |
| | | |
| | | } |
| | | startNative(); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | } |
| | | mListenerList.finishBroadcast(); |
| | | } |
| | | |
| | | public void SendMsgToMainProc(int cmd, String value) { |
| | | Log.d(TAG, "SendMsgToMainProc cmd = " + String.format("%04X", cmd) + " json = " + value); |
| | | if (!mIsServiceDestroyed.get()){ |
| | | onMessageArrived(cmd, value); |
| | | } |
| | | } |
| | | |
| | | public String javaGetImei() { |
| | |
| | | } |
| | | |
| | | public native void startNative(); |
| | | public native void MainProcMsgEntry(int cmd, String value); |
| | | } |