Merge pull request #1084
5850122
remove errant target_link_libraries (Riccardo Spagni)58abc09
don't build any miniupnpc executables (Riccardo Spagni)114e368
don't build miniupnpc test executables (Riccardo Spagni)bb69371
updated miniupnp (Riccardo Spagni)f62ebc5
update rapidjson (Riccardo Spagni)
This commit is contained in:
commit
77224bbf48
|
@ -59,15 +59,15 @@ else()
|
|||
|
||||
add_subdirectory(miniupnpc)
|
||||
|
||||
set_property(TARGET upnpc-static PROPERTY FOLDER "external")
|
||||
set_property(TARGET libminiupnpc-static PROPERTY FOLDER "external")
|
||||
if(MSVC)
|
||||
set_property(TARGET upnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267")
|
||||
set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267")
|
||||
elseif(NOT MSVC)
|
||||
set_property(TARGET upnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value")
|
||||
set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value")
|
||||
endif()
|
||||
|
||||
set(UPNP_STATIC true PARENT_SCOPE)
|
||||
set(UPNP_LIBRARIES "upnpc-static" PARENT_SCOPE)
|
||||
set(UPNP_LIBRARIES "libminiupnpc-static" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
find_package(Unbound)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required (VERSION 2.6)
|
||||
|
||||
project (miniupnpc C)
|
||||
set (MINIUPNPC_VERSION 1.9)
|
||||
set (MINIUPNPC_VERSION 2.0)
|
||||
set (MINIUPNPC_API_VERSION 16)
|
||||
|
||||
# - we comment out this block as we don't support these other build types
|
||||
|
@ -20,9 +20,7 @@ endif()
|
|||
|
||||
option (UPNPC_BUILD_STATIC "Build static library" TRUE)
|
||||
option (UPNPC_BUILD_SHARED "Build shared library" FALSE)
|
||||
if (NOT WIN32)
|
||||
option (UPNPC_BUILD_TESTS "Build test executables" FALSE)
|
||||
endif (NOT WIN32)
|
||||
option (UPNPC_BUILD_TESTS "Build test executables" FALSE)
|
||||
option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE)
|
||||
|
||||
mark_as_advanced (NO_GETADDRINFO)
|
||||
|
@ -34,6 +32,10 @@ endif (NO_GETADDRINFO)
|
|||
if (NOT WIN32)
|
||||
add_definitions (-DMINIUPNPC_SET_SOCKET_TIMEOUT)
|
||||
add_definitions (-D_BSD_SOURCE -D_DEFAULT_SOURCE)
|
||||
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||
# add_definitions (-D_POSIX_C_SOURCE=200112L)
|
||||
add_definitions (-D_XOPEN_SOURCE=600)
|
||||
endif (NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||
else (NOT WIN32)
|
||||
add_definitions (-D_WIN32_WINNT=0x0501) # XP or higher for getnameinfo and friends
|
||||
endif (NOT WIN32)
|
||||
|
@ -68,8 +70,8 @@ if (CMAKE_COMPILER_IS_GNUC)
|
|||
endif ()
|
||||
endif()
|
||||
|
||||
configure_file (miniupnpcstrings.h.cmake ${CMAKE_BINARY_DIR}/miniupnpcstrings.h)
|
||||
include_directories (${CMAKE_BINARY_DIR})
|
||||
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/miniupnpcstrings.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h)
|
||||
include_directories (${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set (MINIUPNPC_SOURCES
|
||||
igd_desc_parse.c
|
||||
|
@ -78,7 +80,6 @@ set (MINIUPNPC_SOURCES
|
|||
minisoap.c
|
||||
minissdpc.c
|
||||
miniwget.c
|
||||
upnpc.c
|
||||
upnpcommands.c
|
||||
upnpdev.c
|
||||
upnpreplyparse.c
|
||||
|
@ -117,21 +118,25 @@ if (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED)
|
|||
endif (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED)
|
||||
|
||||
if (UPNPC_BUILD_STATIC)
|
||||
add_library (upnpc-static STATIC ${MINIUPNPC_SOURCES})
|
||||
set_target_properties (upnpc-static PROPERTIES OUTPUT_NAME "miniupnpc")
|
||||
target_link_libraries (upnpc-static ${LDLIBS})
|
||||
set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} upnpc-static)
|
||||
set (UPNPC_LIBRARY_TARGET upnpc-static)
|
||||
add_library (libminiupnpc-static STATIC ${MINIUPNPC_SOURCES})
|
||||
set_target_properties (libminiupnpc-static PROPERTIES OUTPUT_NAME "miniupnpc")
|
||||
target_link_libraries (libminiupnpc-static ${LDLIBS})
|
||||
set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} libminiupnpc-static)
|
||||
set (UPNPC_LIBRARY_TARGET libminiupnpc-static)
|
||||
# add_executable (upnpc-static upnpc.c)
|
||||
# target_link_libraries (upnpc-static LINK_PRIVATE libminiupnpc-static)
|
||||
endif (UPNPC_BUILD_STATIC)
|
||||
|
||||
if (UPNPC_BUILD_SHARED)
|
||||
add_library (upnpc-shared SHARED ${MINIUPNPC_SOURCES})
|
||||
set_target_properties (upnpc-shared PROPERTIES OUTPUT_NAME "miniupnpc")
|
||||
set_target_properties (upnpc-shared PROPERTIES VERSION ${MINIUPNPC_VERSION})
|
||||
set_target_properties (upnpc-shared PROPERTIES SOVERSION ${MINIUPNPC_API_VERSION})
|
||||
target_link_libraries (upnpc-shared ${LDLIBS})
|
||||
set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} upnpc-shared)
|
||||
set (UPNPC_LIBRARY_TARGET upnpc-shared)
|
||||
add_library (libminiupnpc-shared SHARED ${MINIUPNPC_SOURCES})
|
||||
set_target_properties (libminiupnpc-shared PROPERTIES OUTPUT_NAME "miniupnpc")
|
||||
set_target_properties (libminiupnpc-shared PROPERTIES VERSION ${MINIUPNPC_VERSION})
|
||||
set_target_properties (libminiupnpc-shared PROPERTIES SOVERSION ${MINIUPNPC_API_VERSION})
|
||||
target_link_libraries (libminiupnpc-shared ${LDLIBS})
|
||||
set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} libminiupnpc-shared)
|
||||
set (UPNPC_LIBRARY_TARGET libminiupnpc-shared)
|
||||
add_executable (upnpc-shared upnpc.c)
|
||||
target_link_libraries (upnpc-shared LINK_PRIVATE libminiupnpc-shared)
|
||||
endif (UPNPC_BUILD_SHARED)
|
||||
|
||||
if (UPNPC_BUILD_TESTS)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
$Id: Changelog.txt,v 1.222 2016/01/24 17:24:35 nanard Exp $
|
||||
$Id: Changelog.txt,v 1.223 2016/04/19 21:06:20 nanard Exp $
|
||||
miniUPnP client Changelog.
|
||||
|
||||
VERSION 2.0 : released 2016/04/19
|
||||
|
||||
2016/01/24:
|
||||
change miniwget to return HTTP status code
|
||||
increments API_VERSION to 16
|
||||
|
|
|
@ -38,6 +38,9 @@ CFLAGS += -DMINIUPNPC_SET_SOCKET_TIMEOUT
|
|||
CFLAGS += -DMINIUPNPC_GET_SRC_ADDR
|
||||
CFLAGS += -D_BSD_SOURCE
|
||||
CFLAGS += -D_DEFAULT_SOURCE
|
||||
ifeq ($(OS), NetBSD)
|
||||
CFLAGS += -D_NETBSD_SOURCE
|
||||
endif
|
||||
ifneq ($(OS), FreeBSD)
|
||||
ifneq ($(OS), Darwin)
|
||||
#CFLAGS += -D_POSIX_C_SOURCE=200112L
|
||||
|
@ -136,7 +139,11 @@ endif
|
|||
|
||||
LIBDIR ?= lib
|
||||
# install directories
|
||||
INSTALLPREFIX ?= $(PREFIX)/usr
|
||||
ifeq ($(strip $(PREFIX)),)
|
||||
INSTALLPREFIX ?= /usr
|
||||
else
|
||||
INSTALLPREFIX ?= $(PREFIX)
|
||||
endif
|
||||
INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc
|
||||
INSTALLDIRLIB = $(INSTALLPREFIX)/$(LIBDIR)
|
||||
INSTALLDIRBIN = $(INSTALLPREFIX)/bin
|
||||
|
|
|
@ -3,7 +3,7 @@ Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
|||
github: https://github.com/miniupnp/miniupnp
|
||||
freecode: http://freecode.com/projects/miniupnp
|
||||
Author: Thomas Bernard
|
||||
Copyright (c) 2005-2014 Thomas Bernard
|
||||
Copyright (c) 2005-2016 Thomas Bernard
|
||||
This software is subject to the conditions detailed in the
|
||||
LICENSE file provided within this distribution.
|
||||
|
||||
|
@ -32,6 +32,7 @@ To use the libminiupnpc in your application, link it with
|
|||
libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h,
|
||||
upnpcommands.h and miniwget.h :
|
||||
- upnpDiscover()
|
||||
- UPNP_GetValidIGD()
|
||||
- miniwget()
|
||||
- parserootdesc()
|
||||
- GetUPNPUrls()
|
||||
|
@ -59,3 +60,5 @@ send me an email !
|
|||
For any question, you can use the web forum :
|
||||
http://miniupnp.tuxfamily.org/forum/
|
||||
|
||||
Bugs should be reported on github :
|
||||
https://github.com/miniupnp/miniupnp/issues
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.9
|
||||
2.0
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#if defined (__NetBSD__)
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: miniupnpc.h,v 1.49 2016/01/24 17:24:36 nanard Exp $ */
|
||||
/* $Id: miniupnpc.h,v 1.50 2016/04/19 21:06:21 nanard Exp $ */
|
||||
/* Project: miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
* Author: Thomas Bernard
|
||||
|
@ -19,7 +19,7 @@
|
|||
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
||||
|
||||
/* versions : */
|
||||
#define MINIUPNPC_VERSION "1.9"
|
||||
#define MINIUPNPC_VERSION "2.0"
|
||||
#define MINIUPNPC_API_VERSION 16
|
||||
|
||||
/* Source port:
|
||||
|
|
|
@ -179,7 +179,8 @@ public:
|
|||
|
||||
size = RAPIDJSON_ALIGN(size);
|
||||
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
|
||||
AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size);
|
||||
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;
|
||||
|
@ -194,6 +195,9 @@ public:
|
|||
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;
|
||||
|
@ -201,7 +205,6 @@ public:
|
|||
// 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);
|
||||
increment = RAPIDJSON_ALIGN(increment);
|
||||
if (chunkHead_->size + increment <= chunkHead_->capacity) {
|
||||
chunkHead_->size += increment;
|
||||
return originalPtr;
|
||||
|
@ -209,12 +212,14 @@ public:
|
|||
}
|
||||
|
||||
// Realloc process: allocate and copy memory, do not free original buffer.
|
||||
void* newBuffer = Malloc(newSize);
|
||||
RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly.
|
||||
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
|
||||
|
@ -227,15 +232,20 @@ private:
|
|||
|
||||
//! Creates a new chunk.
|
||||
/*! \param capacity Capacity of the chunk in bytes.
|
||||
\return true if success.
|
||||
*/
|
||||
void AddChunk(size_t capacity) {
|
||||
bool AddChunk(size_t capacity) {
|
||||
if (!baseAllocator_)
|
||||
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
|
||||
ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity));
|
||||
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.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,7 +15,8 @@
|
|||
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
|
||||
#define RAPIDJSON_ENCODEDSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "stream.h"
|
||||
#include "memorystream.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
|
@ -62,6 +63,34 @@ private:
|
|||
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.
|
||||
|
|
|
@ -120,6 +120,28 @@ struct UTF8 {
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -132,7 +154,11 @@ struct UTF8 {
|
|||
}
|
||||
|
||||
unsigned char type = GetRange(static_cast<unsigned char>(c));
|
||||
if (type >= 32) {
|
||||
*codepoint = 0;
|
||||
} else {
|
||||
*codepoint = (0xFF >> type) & static_cast<unsigned char>(c);
|
||||
}
|
||||
bool result = true;
|
||||
switch (type) {
|
||||
case 2: TAIL(); return result;
|
||||
|
@ -261,6 +287,22 @@ struct UTF16 {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
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, (v & 0x3FF) | 0xDC00);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
|
@ -386,6 +428,13 @@ struct UTF32 {
|
|||
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);
|
||||
|
@ -501,6 +550,12 @@ struct ASCII {
|
|||
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());
|
||||
|
@ -571,6 +626,13 @@ struct AutoUTF {
|
|||
(*f[os.GetType()])(os, codepoint);
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
|
||||
(*f[os.GetType()])(os, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
|
||||
|
@ -604,6 +666,15 @@ struct Transcoder {
|
|||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static 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>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
|
@ -611,6 +682,10 @@ struct Transcoder {
|
|||
}
|
||||
};
|
||||
|
||||
// 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> {
|
||||
|
@ -620,6 +695,12 @@ struct Transcoder<Encoding, Encoding> {
|
|||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
|
||||
PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Encoding::Validate(is, os); // source/target encoding are the same
|
||||
|
|
|
@ -38,7 +38,7 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro
|
|||
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 follow by other values.");
|
||||
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
|
||||
|
||||
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef RAPIDJSON_FILEREADSTREAM_H_
|
||||
#define RAPIDJSON_FILEREADSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "stream.h"
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef __clang__
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef RAPIDJSON_FILEWRITESTREAM_H_
|
||||
#define RAPIDJSON_FILEWRITESTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "stream.h"
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef __clang__
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
// 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_
|
|
@ -41,7 +41,7 @@ RAPIDJSON_DIAG_OFF(padded)
|
|||
#endif
|
||||
|
||||
struct DiyFp {
|
||||
DiyFp() {}
|
||||
DiyFp() : f(), e() {}
|
||||
|
||||
DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ 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) {
|
||||
|
@ -101,7 +102,8 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff
|
|||
kappa--;
|
||||
if (p2 < delta) {
|
||||
*K += kappa;
|
||||
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-static_cast<int>(kappa)]);
|
||||
int index = -static_cast<int>(kappa);
|
||||
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -145,10 +147,10 @@ inline char* WriteExponent(int K, char* buffer) {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
inline char* Prettify(char* buffer, int length, int k) {
|
||||
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
|
||||
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||
|
||||
if (length <= kk && kk <= 21) {
|
||||
if (0 <= k && kk <= 21) {
|
||||
// 1234e7 -> 12340000000
|
||||
for (int i = length; i < kk; i++)
|
||||
buffer[i] = '0';
|
||||
|
@ -160,6 +162,15 @@ inline char* Prettify(char* buffer, int length, int k) {
|
|||
// 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) {
|
||||
|
@ -170,8 +181,24 @@ inline char* Prettify(char* buffer, int length, int k) {
|
|||
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';
|
||||
|
@ -186,7 +213,8 @@ inline char* Prettify(char* buffer, int length, int k) {
|
|||
}
|
||||
}
|
||||
|
||||
inline char* dtoa(double value, char* buffer) {
|
||||
inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
|
||||
RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
|
||||
Double d(value);
|
||||
if (d.IsZero()) {
|
||||
if (d.Sign())
|
||||
|
@ -203,7 +231,7 @@ inline char* dtoa(double value, char* buffer) {
|
|||
}
|
||||
int length, K;
|
||||
Grisu2(value, buffer, &length, &K);
|
||||
return Prettify(buffer, length, K);
|
||||
return Prettify(buffer, length, K, maxDecimalPlaces);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ public:
|
|||
|
||||
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; }
|
||||
|
||||
|
|
|
@ -0,0 +1,731 @@
|
|||
// 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++)
|
||||
#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] & (1 << (index & 31)))) {
|
||||
stateSet_[index >> 5] |= (1 << (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_
|
|
@ -15,9 +15,14 @@
|
|||
#ifndef RAPIDJSON_INTERNAL_STACK_H_
|
||||
#define RAPIDJSON_INTERNAL_STACK_H_
|
||||
|
||||
#include "../rapidjson.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 {
|
||||
|
||||
|
@ -33,7 +38,6 @@ 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) {
|
||||
RAPIDJSON_ASSERT(stackCapacity > 0);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
|
@ -108,11 +112,21 @@ public:
|
|||
// 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 T* Push(size_t count = 1) {
|
||||
RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
|
||||
// Expand the stack if needed
|
||||
if (stackTop_ + sizeof(T) * count >= stackEnd_)
|
||||
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_ + sizeof(T) * count <= stackEnd_);
|
||||
T* ret = reinterpret_cast<T*>(stackTop_);
|
||||
stackTop_ += sizeof(T) * count;
|
||||
return ret;
|
||||
|
@ -131,9 +145,24 @@ public:
|
|||
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;
|
||||
}
|
||||
|
@ -142,6 +171,7 @@ public:
|
|||
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_); }
|
||||
|
@ -193,4 +223,8 @@ private:
|
|||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_STACK_H_
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
#define RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
#include "../stream.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
@ -28,11 +28,30 @@ namespace internal {
|
|||
*/
|
||||
template <typename Ch>
|
||||
inline SizeType StrLen(const Ch* s) {
|
||||
RAPIDJSON_ASSERT(s != 0);
|
||||
const Ch* p = s;
|
||||
while (*p) ++p;
|
||||
return SizeType(p - 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
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#ifndef RAPIDJSON_STRTOD_
|
||||
#define RAPIDJSON_STRTOD_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
#include "ieee754.h"
|
||||
#include "biginteger.h"
|
||||
#include "diyfp.h"
|
||||
|
@ -143,7 +142,7 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit
|
|||
size_t remaining = length - i;
|
||||
const unsigned kUlpShift = 3;
|
||||
const unsigned kUlp = 1 << kUlpShift;
|
||||
int error = (remaining == 0) ? 0 : kUlp / 2;
|
||||
int64_t error = (remaining == 0) ? 0 : kUlp / 2;
|
||||
|
||||
DiyFp v(significand, 0);
|
||||
v = v.Normalize();
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
|
@ -34,4 +39,8 @@ inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT {
|
|||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_SWAP_H_
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
// 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) : '\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_
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef RAPIDJSON_MEMORYBUFFER_H_
|
||||
#define RAPIDJSON_MEMORYBUFFER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "stream.h"
|
||||
#include "internal/stack.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef RAPIDJSON_MEMORYSTREAM_H_
|
||||
#define RAPIDJSON_MEMORYSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "stream.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
|
@ -42,8 +42,8 @@ struct MemoryStream {
|
|||
|
||||
MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
|
||||
|
||||
Ch Peek() const { return (src_ == end_) ? '\0' : *src_; }
|
||||
Ch Take() { return (src_ == end_) ? '\0' : *src_++; }
|
||||
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; }
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
// 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_
|
|
@ -23,6 +23,11 @@ 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
|
||||
|
@ -101,7 +106,7 @@ public:
|
|||
//@{
|
||||
|
||||
//! Default constructor.
|
||||
GenericPointer() : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
|
||||
GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
|
||||
|
||||
//! Constructor that parses a string or URI fragment representation.
|
||||
/*!
|
||||
|
@ -160,7 +165,7 @@ public:
|
|||
GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast<Token*>(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
|
||||
|
||||
//! Copy constructor.
|
||||
GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
|
||||
GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
|
||||
*this = rhs;
|
||||
}
|
||||
|
||||
|
@ -305,6 +310,9 @@ public:
|
|||
|
||||
//@}
|
||||
|
||||
//! Get the allocator of this pointer.
|
||||
Allocator& GetAllocator() { return *allocator_; }
|
||||
|
||||
//!@name Tokens
|
||||
//@{
|
||||
|
||||
|
@ -457,9 +465,18 @@ public:
|
|||
//! 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) const {
|
||||
ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const {
|
||||
RAPIDJSON_ASSERT(IsValid());
|
||||
ValueType* v = &root;
|
||||
for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
|
||||
|
@ -468,18 +485,23 @@ public:
|
|||
{
|
||||
typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
|
||||
if (m == v->MemberEnd())
|
||||
return 0;
|
||||
break;
|
||||
v = &m->value;
|
||||
}
|
||||
break;
|
||||
continue;
|
||||
case kArrayType:
|
||||
if (t->index == kPointerInvalidIndex || t->index >= v->Size())
|
||||
return 0;
|
||||
v = &((*v)[t->index]);
|
||||
break;
|
||||
v = &((*v)[t->index]);
|
||||
continue;
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Error: unresolved token
|
||||
if (unresolvedTokenIndex)
|
||||
*unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
|
||||
return 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
@ -489,7 +511,9 @@ public:
|
|||
\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) const { return Get(const_cast<ValueType&>(root)); }
|
||||
const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const {
|
||||
return Get(const_cast<ValueType&>(root), unresolvedTokenIndex);
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
|
@ -743,8 +767,12 @@ private:
|
|||
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_;
|
||||
|
@ -968,11 +996,11 @@ private:
|
|||
src_++;
|
||||
Ch c = 0;
|
||||
for (int j = 0; j < 2; j++) {
|
||||
c <<= 4;
|
||||
c = static_cast<Ch>(c << 4);
|
||||
Ch h = *src_;
|
||||
if (h >= '0' && h <= '9') c += h - '0';
|
||||
else if (h >= 'A' && h <= 'F') c += h - 'A' + 10;
|
||||
else if (h >= 'a' && h <= 'f') c += h - 'a' + 10;
|
||||
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;
|
||||
|
@ -1050,23 +1078,23 @@ typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, c
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) {
|
||||
return pointer.Get(root);
|
||||
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) {
|
||||
return pointer.Get(root);
|
||||
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]) {
|
||||
return GenericPointer<typename T::ValueType>(source, N - 1).Get(root);
|
||||
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]) {
|
||||
return GenericPointer<typename T::ValueType>(source, N - 1).Get(root);
|
||||
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);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1323,4 +1351,8 @@ RAPIDJSON_NAMESPACE_END
|
|||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_POINTER_H_
|
||||
|
|
|
@ -24,6 +24,14 @@ RAPIDJSON_DIAG_OFF(effc++)
|
|||
|
||||
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.
|
||||
|
@ -31,8 +39,8 @@ RAPIDJSON_NAMESPACE_BEGIN
|
|||
\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>
|
||||
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> {
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
|
||||
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
|
||||
public:
|
||||
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base;
|
||||
typedef typename Base::Ch Ch;
|
||||
|
@ -42,8 +50,12 @@ public:
|
|||
\param allocator User supplied allocator. If it is null, it will create a private one.
|
||||
\param levelDepth Initial capacity of stack.
|
||||
*/
|
||||
PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
||||
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
|
||||
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) {}
|
||||
|
||||
//! Set custom indentation.
|
||||
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
|
||||
|
@ -57,6 +69,14 @@ public:
|
|||
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
|
||||
*/
|
||||
|
@ -70,7 +90,15 @@ public:
|
|||
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);
|
||||
|
@ -90,6 +118,12 @@ public:
|
|||
|
||||
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool Key(const std::basic_string<Ch>& str) {
|
||||
return Key(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
|
@ -120,7 +154,7 @@ public:
|
|||
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) {
|
||||
if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
|
@ -142,6 +176,22 @@ public:
|
|||
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;
|
||||
|
@ -151,12 +201,15 @@ protected:
|
|||
if (level->inArray) {
|
||||
if (level->valueCount > 0) {
|
||||
Base::os_->Put(','); // add comma if it is not the first element in array
|
||||
Base::os_->Put('\n');
|
||||
if (formatOptions_ & kFormatSingleLineArray)
|
||||
Base::os_->Put(' ');
|
||||
}
|
||||
else
|
||||
|
||||
if (!(formatOptions_ & kFormatSingleLineArray)) {
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
}
|
||||
else { // in object
|
||||
if (level->valueCount > 0) {
|
||||
if (level->valueCount % 2 == 0) {
|
||||
|
@ -191,6 +244,7 @@ protected:
|
|||
|
||||
Ch indentChar_;
|
||||
unsigned indentCharCount_;
|
||||
PrettyFormatOptions formatOptions_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
|
|
|
@ -68,8 +68,8 @@
|
|||
\brief Version of RapidJSON in "<major>.<minor>.<patch>" string format.
|
||||
*/
|
||||
#define RAPIDJSON_MAJOR_VERSION 1
|
||||
#define RAPIDJSON_MINOR_VERSION 0
|
||||
#define RAPIDJSON_PATCH_VERSION 2
|
||||
#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)
|
||||
|
||||
|
@ -159,7 +159,7 @@
|
|||
*/
|
||||
#ifndef RAPIDJSON_NO_INT64DEFINE
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013
|
||||
#include "msinttypes/stdint.h"
|
||||
#include "msinttypes/inttypes.h"
|
||||
#else
|
||||
|
@ -178,9 +178,9 @@
|
|||
|
||||
#ifndef RAPIDJSON_FORCEINLINE
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
#if defined(_MSC_VER) && !defined(NDEBUG)
|
||||
#if defined(_MSC_VER) && defined(NDEBUG)
|
||||
#define RAPIDJSON_FORCEINLINE __forceinline
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG)
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG)
|
||||
#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
|
||||
#else
|
||||
#define RAPIDJSON_FORCEINLINE
|
||||
|
@ -250,7 +250,7 @@
|
|||
|
||||
//! Whether using 64-bit architecture
|
||||
#ifndef RAPIDJSON_64BIT
|
||||
#if defined(__LP64__) || defined(_WIN64) || defined(__EMSCRIPTEN__)
|
||||
#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__)
|
||||
#define RAPIDJSON_64BIT 1
|
||||
#else
|
||||
#define RAPIDJSON_64BIT 0
|
||||
|
@ -265,7 +265,8 @@
|
|||
\param x pointer to align
|
||||
|
||||
Some machines require strict data alignment. Currently the default uses 4 bytes
|
||||
alignment. User can customize by defining the RAPIDJSON_ALIGN function macro.
|
||||
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
|
||||
|
@ -288,6 +289,36 @@
|
|||
#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_SIMD
|
||||
|
||||
|
@ -409,6 +440,35 @@ RAPIDJSON_NAMESPACE_END
|
|||
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
|
||||
|
@ -469,8 +529,12 @@ RAPIDJSON_NAMESPACE_END
|
|||
|
||||
#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
#if defined(__clang__)
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) && \
|
||||
#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)
|
||||
|
||||
|
@ -501,6 +565,17 @@ RAPIDJSON_NAMESPACE_END
|
|||
#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,3,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
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -516,10 +591,7 @@ RAPIDJSON_NAMESPACE_END
|
|||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocators and Encodings
|
||||
|
||||
#include "allocators.h"
|
||||
#include "encodings.h"
|
||||
// Type
|
||||
|
||||
/*! \namespace rapidjson
|
||||
\brief main RapidJSON namespace
|
||||
|
@ -527,148 +599,6 @@ RAPIDJSON_NAMESPACE_END
|
|||
*/
|
||||
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 };
|
||||
};
|
||||
|
||||
//! Put N copies of a character to a stream.
|
||||
template<typename Stream, typename Ch>
|
||||
inline void PutN(Stream& stream, Ch c, size_t n) {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
stream.Put(c);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Type
|
||||
|
||||
//! Type of JSON value
|
||||
enum Type {
|
||||
kNullType = 0, //!< null
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,179 @@
|
|||
// 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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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_
|
|
@ -15,7 +15,8 @@
|
|||
#ifndef RAPIDJSON_STRINGBUFFER_H_
|
||||
#define RAPIDJSON_STRINGBUFFER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "stream.h"
|
||||
#include "internal/stack.h"
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
#include <utility> // std::move
|
||||
|
@ -23,6 +24,11 @@
|
|||
|
||||
#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.
|
||||
|
@ -48,6 +54,7 @@ public:
|
|||
#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(); }
|
||||
|
@ -57,7 +64,10 @@ public:
|
|||
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 {
|
||||
|
@ -82,6 +92,16 @@ private:
|
|||
//! 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) {
|
||||
|
@ -90,4 +110,8 @@ inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
|
|||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_STRINGBUFFER_H_
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef RAPIDJSON_WRITER_H_
|
||||
#define RAPIDJSON_WRITER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "stream.h"
|
||||
#include "internal/stack.h"
|
||||
#include "internal/strfunc.h"
|
||||
#include "internal/dtoa.h"
|
||||
|
@ -23,6 +23,16 @@
|
|||
#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>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
||||
|
@ -31,10 +41,32 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
|||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||
#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.
|
||||
|
@ -51,11 +83,13 @@ RAPIDJSON_NAMESPACE_BEGIN
|
|||
\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>
|
||||
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.
|
||||
|
@ -63,11 +97,11 @@ public:
|
|||
*/
|
||||
explicit
|
||||
Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
|
||||
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)), hasRoot_(false) {}
|
||||
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
|
||||
|
||||
//! Reset the writer with a new stream.
|
||||
/*!
|
||||
|
@ -101,29 +135,66 @@ public:
|
|||
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 WriteNull(); }
|
||||
bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); }
|
||||
bool Int(int i) { Prefix(kNumberType); return WriteInt(i); }
|
||||
bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); }
|
||||
bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); }
|
||||
bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); }
|
||||
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 WriteDouble(d); }
|
||||
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 WriteString(str, length);
|
||||
return EndValue(WriteString(str, length));
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
|
@ -145,10 +216,7 @@ public:
|
|||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
bool ret = WriteEndObject();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_->Flush();
|
||||
return ret;
|
||||
return EndValue(WriteEndObject());
|
||||
}
|
||||
|
||||
bool StartArray() {
|
||||
|
@ -162,10 +230,7 @@ public:
|
|||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
bool ret = WriteEndArray();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_->Flush();
|
||||
return ret;
|
||||
return EndValue(WriteEndArray());
|
||||
}
|
||||
//@}
|
||||
|
||||
|
@ -178,6 +243,20 @@ public:
|
|||
|
||||
//@}
|
||||
|
||||
//! 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));
|
||||
}
|
||||
|
||||
protected:
|
||||
//! Information for each nested level
|
||||
struct Level {
|
||||
|
@ -189,15 +268,18 @@ protected:
|
|||
static const size_t kDefaultLevelDepth = 32;
|
||||
|
||||
bool WriteNull() {
|
||||
os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true;
|
||||
PutReserve(*os_, 4);
|
||||
PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
|
||||
}
|
||||
|
||||
bool WriteBool(bool b) {
|
||||
if (b) {
|
||||
os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e');
|
||||
PutReserve(*os_, 4);
|
||||
PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e');
|
||||
}
|
||||
else {
|
||||
os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e');
|
||||
PutReserve(*os_, 5);
|
||||
PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -205,40 +287,64 @@ protected:
|
|||
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)
|
||||
os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
|
||||
PutUnsafe(*os_, static_cast<typename TargetEncoding::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)
|
||||
os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
|
||||
PutUnsafe(*os_, static_cast<typename TargetEncoding::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)
|
||||
os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
|
||||
PutUnsafe(*os_, static_cast<typename TargetEncoding::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)
|
||||
os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
|
||||
PutUnsafe(*os_, static_cast<typename TargetEncoding::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);
|
||||
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
|
||||
PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -256,22 +362,27 @@ protected:
|
|||
#undef Z16
|
||||
};
|
||||
|
||||
os_->Put('\"');
|
||||
if (TargetEncoding::supportUnicode)
|
||||
PutReserve(*os_, 2 + length * 6); // "\uxxxx..."
|
||||
else
|
||||
PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..."
|
||||
|
||||
PutUnsafe(*os_, '\"');
|
||||
GenericStringStream<SourceEncoding> is(str);
|
||||
while (is.Tell() < length) {
|
||||
while (ScanWriteUnescapedString(is, length)) {
|
||||
const Ch c = is.Peek();
|
||||
if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) {
|
||||
// Unicode escaping
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint)))
|
||||
return false;
|
||||
os_->Put('\\');
|
||||
os_->Put('u');
|
||||
PutUnsafe(*os_, '\\');
|
||||
PutUnsafe(*os_, 'u');
|
||||
if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
|
||||
os_->Put(hexDigits[(codepoint >> 12) & 15]);
|
||||
os_->Put(hexDigits[(codepoint >> 8) & 15]);
|
||||
os_->Put(hexDigits[(codepoint >> 4) & 15]);
|
||||
os_->Put(hexDigits[(codepoint ) & 15]);
|
||||
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);
|
||||
|
@ -279,45 +390,59 @@ protected:
|
|||
unsigned s = codepoint - 0x010000;
|
||||
unsigned lead = (s >> 10) + 0xD800;
|
||||
unsigned trail = (s & 0x3FF) + 0xDC00;
|
||||
os_->Put(hexDigits[(lead >> 12) & 15]);
|
||||
os_->Put(hexDigits[(lead >> 8) & 15]);
|
||||
os_->Put(hexDigits[(lead >> 4) & 15]);
|
||||
os_->Put(hexDigits[(lead ) & 15]);
|
||||
os_->Put('\\');
|
||||
os_->Put('u');
|
||||
os_->Put(hexDigits[(trail >> 12) & 15]);
|
||||
os_->Put(hexDigits[(trail >> 8) & 15]);
|
||||
os_->Put(hexDigits[(trail >> 4) & 15]);
|
||||
os_->Put(hexDigits[(trail ) & 15]);
|
||||
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) && escape[static_cast<unsigned char>(c)]) {
|
||||
else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)])) {
|
||||
is.Take();
|
||||
os_->Put('\\');
|
||||
os_->Put(static_cast<typename TargetEncoding::Ch>(escape[static_cast<unsigned char>(c)]));
|
||||
PutUnsafe(*os_, '\\');
|
||||
PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(escape[static_cast<unsigned char>(c)]));
|
||||
if (escape[static_cast<unsigned char>(c)] == 'u') {
|
||||
os_->Put('0');
|
||||
os_->Put('0');
|
||||
os_->Put(hexDigits[static_cast<unsigned char>(c) >> 4]);
|
||||
os_->Put(hexDigits[static_cast<unsigned char>(c) & 0xF]);
|
||||
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 (!Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_))
|
||||
else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
|
||||
Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
|
||||
Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
|
||||
return false;
|
||||
}
|
||||
os_->Put('\"');
|
||||
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 (level_stack_.GetSize() != 0) { // this value is not at root
|
||||
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)
|
||||
|
@ -335,8 +460,16 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
// Flush the value if it is the top level one.
|
||||
bool EndValue(bool ret) {
|
||||
if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text
|
||||
os_->Flush();
|
||||
return ret;
|
||||
}
|
||||
|
||||
OutputStream* os_;
|
||||
internal::Stack<StackAllocator> level_stack_;
|
||||
int maxDecimalPlaces_;
|
||||
bool hasRoot_;
|
||||
|
||||
private:
|
||||
|
@ -381,12 +514,95 @@ inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
|
|||
|
||||
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);
|
||||
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] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
|
||||
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, 0x19) == 0x19
|
||||
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);
|
||||
}
|
||||
#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
Loading…
Reference in New Issue