Merge branch 'master' of https://github.com/miloyip/rapidjson
16
CHANGELOG.md
@ -4,7 +4,20 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.0.2] - 2015-05-14
|
||||
|
||||
### Added
|
||||
* Add Value::XXXMember(...) overloads for std::string (#335)
|
||||
|
||||
### Fixed
|
||||
* Include rapidjson.h for all internal/error headers.
|
||||
* Parsing some numbers incorrectly in full-precision mode (`kFullPrecisionParseFlag`) (#342)
|
||||
* Fix alignment of 64bit platforms (#328)
|
||||
* Fix MemoryPoolAllocator::Clear() to clear user-buffer (0691502573f1afd3341073dd24b12c3db20fbde4)
|
||||
|
||||
### Changed
|
||||
* CMakeLists for include as a thirdparty in projects (#334, #337)
|
||||
* Change Document::ParseStream() to use stack allocator for Reader (ffbe38614732af8e0b3abdc8b50071f386a4a685)
|
||||
|
||||
## [1.0.1] - 2015-04-25
|
||||
|
||||
@ -60,6 +73,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 0.1 - 2011-11-18
|
||||
|
||||
[Unreleased]: https://github.com/miloyip/rapidjson/compare/v1.0.1...HEAD
|
||||
[Unreleased]: https://github.com/miloyip/rapidjson/compare/v1.0.2...HEAD
|
||||
[1.0.2]: https://github.com/miloyip/rapidjson/compare/v1.0.1...v1.0.2
|
||||
[1.0.1]: https://github.com/miloyip/rapidjson/compare/v1.0.0...v1.0.1
|
||||
[1.0.0]: https://github.com/miloyip/rapidjson/compare/v1.0-beta...v1.0.0
|
||||
|
@ -1,11 +1,11 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMakeModules)
|
||||
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules)
|
||||
|
||||
PROJECT(RapidJSON CXX)
|
||||
|
||||
set(LIB_MAJOR_VERSION "1")
|
||||
set(LIB_MINOR_VERSION "0")
|
||||
set(LIB_PATCH_VERSION "1")
|
||||
set(LIB_PATCH_VERSION "2")
|
||||
set(LIB_VERSION_STRING "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_PATCH_VERSION}")
|
||||
|
||||
# compile in release with debug info mode by default
|
||||
@ -17,7 +17,7 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." ON)
|
||||
option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." ON)
|
||||
option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." ON)
|
||||
option(RAPIDJSON_BUILD_THIRDPARTY_GTEST
|
||||
option(RAPIDJSON_BUILD_THIRDPARTY_GTEST
|
||||
"Use gtest installation in `thirdparty/gtest` by default if available" OFF)
|
||||
|
||||
option(RAPIDJSON_HAS_STDSTRING "" OFF)
|
||||
@ -45,7 +45,7 @@ ELSEIF(WIN32)
|
||||
ENDIF()
|
||||
SET(CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" CACHE PATH "The directory cmake fiels are installed in")
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/include)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
if(RAPIDJSON_BUILD_DOC)
|
||||
add_subdirectory(doc)
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
SET(GTEST_SEARCH_PATH
|
||||
SET(GTEST_SEARCH_PATH
|
||||
"${GTEST_SOURCE_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/thirdparty/gtest")
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../thirdparty/gtest")
|
||||
|
||||
IF(UNIX)
|
||||
IF(RAPIDJSON_BUILD_THIRDPARTY_GTEST)
|
||||
@ -15,6 +15,7 @@ FIND_PATH(GTEST_SOURCE_DIR
|
||||
NAMES CMakeLists.txt src/gtest_main.cc
|
||||
PATHS ${GTEST_SEARCH_PATH})
|
||||
|
||||
|
||||
# Debian installs gtest include directory in /usr/include, thus need to look
|
||||
# for include directory separately from source directory.
|
||||
FIND_PATH(GTEST_INCLUDE_DIR
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: 1.0.1.{build}
|
||||
version: 1.0.2.{build}
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
|
@ -3,9 +3,9 @@ find_package(Doxygen)
|
||||
IF(NOT DOXYGEN_FOUND)
|
||||
MESSAGE(STATUS "No Doxygen found. Documentation won't be built")
|
||||
ELSE()
|
||||
file(GLOB SOURCES ${CMAKE_SOURCE_DIR}/include/*)
|
||||
file(GLOB MARKDOWN_DOC ${CMAKE_SOURCE_DIR}/doc/*.md)
|
||||
list(APPEND MARKDOWN_DOC ${CMAKE_SOURCE_DIR}/readme.md)
|
||||
file(GLOB SOURCES ${CMAKE_CURRENT_LIST_DIR}/../include/*)
|
||||
file(GLOB MARKDOWN_DOC ${CMAKE_CURRENT_LIST_DIR}/../doc/*.md)
|
||||
list(APPEND MARKDOWN_DOC ${CMAKE_CURRENT_LIST_DIR}/../readme.md)
|
||||
|
||||
CONFIGURE_FILE(Doxyfile.in Doxyfile @ONLY)
|
||||
CONFIGURE_FILE(Doxyfile.zh-cn.in Doxyfile.zh-cn @ONLY)
|
||||
@ -15,7 +15,7 @@ ELSE()
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.zh-cn
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||
DEPENDS ${MARKDOWN_DOC} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile*
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../
|
||||
)
|
||||
|
||||
add_custom_target(doc ALL DEPENDS html)
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 98 KiB |
@ -16,6 +16,15 @@ $mathjax
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
$extrastylesheet
|
||||
</head>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-63929386-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
<body>
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
<div id="topbanner"><a href="https://github.com/miloyip/rapidjson" title="RapidJSON GitHub"><i class="githublogo"></i></a></div>
|
||||
|
@ -143,11 +143,13 @@ public:
|
||||
|
||||
//! Deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
void Clear() {
|
||||
while(chunkHead_ != 0 && chunkHead_ != userBuffer_) {
|
||||
while (chunkHead_ && chunkHead_ != userBuffer_) {
|
||||
ChunkHeader* next = chunkHead_->next;
|
||||
baseAllocator_->Free(chunkHead_);
|
||||
chunkHead_ = next;
|
||||
}
|
||||
if (chunkHead_ && chunkHead_ == userBuffer_)
|
||||
chunkHead_->size = 0; // Clear user buffer
|
||||
}
|
||||
|
||||
//! Computes the total capacity of allocated memory chunks.
|
||||
@ -179,7 +181,7 @@ public:
|
||||
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
|
||||
AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size);
|
||||
|
||||
void *buffer = reinterpret_cast<char *>(chunkHead_ + 1) + chunkHead_->size;
|
||||
void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
|
||||
chunkHead_->size += size;
|
||||
return buffer;
|
||||
}
|
||||
@ -197,7 +199,7 @@ public:
|
||||
return originalPtr;
|
||||
|
||||
// Simply expand it if it is the last allocation and there is sufficient space
|
||||
if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) {
|
||||
if (originalPtr == (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) {
|
||||
@ -229,7 +231,7 @@ private:
|
||||
void AddChunk(size_t capacity) {
|
||||
if (!baseAllocator_)
|
||||
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
|
||||
ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity));
|
||||
ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity));
|
||||
chunk->capacity = capacity;
|
||||
chunk->size = 0;
|
||||
chunk->next = chunkHead_;
|
||||
|
@ -844,6 +844,12 @@ public:
|
||||
template <typename SourceAllocator>
|
||||
const GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this)[name]; }
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
//! Get a value from an object associated with name (string object).
|
||||
GenericValue& operator[](const std::basic_string<Ch>& name) { return (*this)[GenericValue(StringRef(name))]; }
|
||||
const GenericValue& operator[](const std::basic_string<Ch>& name) const { return (*this)[GenericValue(StringRef(name))]; }
|
||||
#endif
|
||||
|
||||
//! Const member iterator
|
||||
/*! \pre IsObject() == true */
|
||||
ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); }
|
||||
@ -867,6 +873,18 @@ public:
|
||||
*/
|
||||
bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); }
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
//! Check whether a member exists in the object with string object.
|
||||
/*!
|
||||
\param name Member name to be searched.
|
||||
\pre IsObject() == true
|
||||
\return Whether a member with that name exists.
|
||||
\note It is better to use FindMember() directly if you need the obtain the value as well.
|
||||
\note Linear time complexity.
|
||||
*/
|
||||
bool HasMember(const std::basic_string<Ch>& name) const { return FindMember(name) != MemberEnd(); }
|
||||
#endif
|
||||
|
||||
//! Check whether a member exists in the object with GenericValue name.
|
||||
/*!
|
||||
This version is faster because it does not need a StrLen(). It can also handle string with null character.
|
||||
@ -923,6 +941,18 @@ public:
|
||||
}
|
||||
template <typename SourceAllocator> ConstMemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
//! Find member by string object name.
|
||||
/*!
|
||||
\param name Member name to be searched.
|
||||
\pre IsObject() == true
|
||||
\return Iterator to member, if it exists.
|
||||
Otherwise returns \ref MemberEnd().
|
||||
*/
|
||||
MemberIterator FindMember(const std::basic_string<Ch>& name) { return FindMember(StringRef(name)); }
|
||||
ConstMemberIterator FindMember(const std::basic_string<Ch>& name) const { return FindMember(StringRef(name)); }
|
||||
#endif
|
||||
|
||||
//! Add a member (name-value pair) to the object.
|
||||
/*! \param name A string value as name of member.
|
||||
\param value Value of any type.
|
||||
@ -969,6 +999,22 @@ public:
|
||||
return AddMember(name, v, allocator);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
//! Add a string object as member (name-value pair) to the object.
|
||||
/*! \param name A string value as name of member.
|
||||
\param value constant string reference as value of member.
|
||||
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
\pre IsObject()
|
||||
\note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below.
|
||||
\note Amortized Constant time complexity.
|
||||
*/
|
||||
GenericValue& AddMember(GenericValue& name, std::basic_string<Ch>& value, Allocator& allocator) {
|
||||
GenericValue v(value, allocator);
|
||||
return AddMember(name, v, allocator);
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Add any primitive value as member (name-value pair) to the object.
|
||||
/*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
|
||||
\param name A string value as name of member.
|
||||
@ -1087,6 +1133,10 @@ public:
|
||||
return RemoveMember(n);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool RemoveMember(const std::basic_string<Ch>& name) { return RemoveMember(GenericValue(StringRef(name))); }
|
||||
#endif
|
||||
|
||||
template <typename SourceAllocator>
|
||||
bool RemoveMember(const GenericValue<Encoding, SourceAllocator>& name) {
|
||||
MemberIterator m = FindMember(name);
|
||||
@ -1163,6 +1213,31 @@ public:
|
||||
return pos;
|
||||
}
|
||||
|
||||
//! Erase a member in object by its name.
|
||||
/*! \param name Name of member to be removed.
|
||||
\return Whether the member existed.
|
||||
\note Linear time complexity.
|
||||
*/
|
||||
bool EraseMember(const Ch* name) {
|
||||
GenericValue n(StringRef(name));
|
||||
return EraseMember(n);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool EraseMember(const std::basic_string<Ch>& name) { return EraseMember(GenericValue(StringRef(name))); }
|
||||
#endif
|
||||
|
||||
template <typename SourceAllocator>
|
||||
bool EraseMember(const GenericValue<Encoding, SourceAllocator>& name) {
|
||||
MemberIterator m = FindMember(name);
|
||||
if (m != MemberEnd()) {
|
||||
EraseMember(m);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
//!@name Array
|
||||
@ -1741,7 +1816,7 @@ public:
|
||||
template <unsigned parseFlags, typename SourceEncoding, typename InputStream>
|
||||
GenericDocument& ParseStream(InputStream& is) {
|
||||
ValueType::SetNull(); // Remove existing root if exist
|
||||
GenericReader<SourceEncoding, Encoding, Allocator> reader(&GetAllocator());
|
||||
GenericReader<SourceEncoding, Encoding, StackAllocator> reader(&stack_.GetAllocator());
|
||||
ClearStackOnExit scope(*this);
|
||||
parseResult_ = reader.template Parse<parseFlags>(is, *this);
|
||||
if (parseResult_) {
|
||||
|
@ -191,8 +191,13 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit
|
||||
DiyFp rounded(v.f >> precisionSize, v.e + precisionSize);
|
||||
const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
|
||||
const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
|
||||
if (precisionBits >= halfWay + error)
|
||||
if (precisionBits >= halfWay + error) {
|
||||
rounded.f++;
|
||||
if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340)
|
||||
rounded.f >>= 1;
|
||||
rounded.e++;
|
||||
}
|
||||
}
|
||||
|
||||
*result = rounded.ToDouble();
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define RAPIDJSON_POINTER_H_
|
||||
|
||||
#include "document.h"
|
||||
#include "internal/itoa.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
@ -160,47 +161,130 @@ public:
|
||||
|
||||
//! Destructor.
|
||||
~GenericPointer() {
|
||||
if (nameBuffer_) { // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated.
|
||||
Allocator::Free(nameBuffer_);
|
||||
if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated.
|
||||
Allocator::Free(tokens_);
|
||||
}
|
||||
RAPIDJSON_DELETE(ownAllocator_);
|
||||
}
|
||||
|
||||
//! Assignment operator.
|
||||
GenericPointer& operator=(const GenericPointer& rhs) {
|
||||
this->~GenericPointer();
|
||||
if (this != &rhs) {
|
||||
// Do not delete ownAllcator
|
||||
if (nameBuffer_)
|
||||
Allocator::Free(tokens_);
|
||||
|
||||
tokenCount_ = rhs.tokenCount_;
|
||||
parseErrorOffset_ = rhs.parseErrorOffset_;
|
||||
parseErrorCode_ = rhs.parseErrorCode_;
|
||||
tokenCount_ = rhs.tokenCount_;
|
||||
parseErrorOffset_ = rhs.parseErrorOffset_;
|
||||
parseErrorCode_ = rhs.parseErrorCode_;
|
||||
|
||||
if (rhs.nameBuffer_) { // Normally parsed tokens.
|
||||
if (!allocator_) // allocator is independently owned.
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
|
||||
|
||||
size_t nameBufferSize = tokenCount_; // null terminators for tokens
|
||||
for (Token *t = rhs.tokens_; t != rhs.tokens_ + tokenCount_; ++t)
|
||||
nameBufferSize += t->length;
|
||||
nameBuffer_ = (Ch*)allocator_->Malloc(nameBufferSize * sizeof(Ch));
|
||||
std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch));
|
||||
|
||||
tokens_ = (Token*)allocator_->Malloc(tokenCount_ * sizeof(Token));
|
||||
std::memcpy(tokens_, rhs.tokens_, tokenCount_ * sizeof(Token));
|
||||
|
||||
// Adjust pointers to name buffer
|
||||
std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_;
|
||||
for (Token *t = rhs.tokens_; t != rhs.tokens_ + tokenCount_; ++t)
|
||||
t->name += diff;
|
||||
if (rhs.nameBuffer_)
|
||||
CopyFromRaw(rhs); // Normally parsed tokens.
|
||||
else {
|
||||
tokens_ = rhs.tokens_; // User supplied const tokens.
|
||||
nameBuffer_ = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
tokens_ = rhs.tokens_; // User supplied const tokens.
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
//!@name Append token
|
||||
//@{
|
||||
|
||||
//! Append a token and return a new Pointer
|
||||
/*!
|
||||
\param token Token to be appended.
|
||||
\param allocator Allocator for the newly return Pointer.
|
||||
\return A new Pointer with appended token.
|
||||
*/
|
||||
GenericPointer Append(const Token& token, Allocator* allocator = 0) const {
|
||||
GenericPointer r;
|
||||
r.allocator_ = allocator;
|
||||
Ch *p = r.CopyFromRaw(*this, 1, token.length + 1);
|
||||
std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch));
|
||||
r.tokens_[tokenCount_].name = p;
|
||||
r.tokens_[tokenCount_].length = token.length;
|
||||
r.tokens_[tokenCount_].index = token.index;
|
||||
return r;
|
||||
}
|
||||
|
||||
//! Append a name token with length, and return a new Pointer
|
||||
/*!
|
||||
\param name Name to be appended.
|
||||
\param length Length of name.
|
||||
\param allocator Allocator for the newly return Pointer.
|
||||
\return A new Pointer with appended token.
|
||||
*/
|
||||
GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const {
|
||||
Token token = { name, length, kPointerInvalidIndex };
|
||||
return Append(token, allocator);
|
||||
}
|
||||
|
||||
//! Append a name token without length, and return a new Pointer
|
||||
/*!
|
||||
\param name Name (const Ch*) to be appended.
|
||||
\param allocator Allocator for the newly return Pointer.
|
||||
\return A new Pointer with appended token.
|
||||
*/
|
||||
template <typename T>
|
||||
RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >), (GenericPointer))
|
||||
Append(T* name, Allocator* allocator = 0) const {
|
||||
return Append(name, StrLen(name), allocator);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
//! Append a name token, and return a new Pointer
|
||||
/*!
|
||||
\param name Name to be appended.
|
||||
\param allocator Allocator for the newly return Pointer.
|
||||
\return A new Pointer with appended token.
|
||||
*/
|
||||
GenericPointer Append(const std::basic_string<Ch>& name, Allocator* allocator = 0) const {
|
||||
return Append(name.c_str(), static_cast<SizeType>(name.size()), allocator);
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Append a index token, and return a new Pointer
|
||||
/*!
|
||||
\param index Index to be appended.
|
||||
\param allocator Allocator for the newly return Pointer.
|
||||
\return A new Pointer with appended token.
|
||||
*/
|
||||
GenericPointer Append(SizeType index, Allocator* allocator = 0) const {
|
||||
char buffer[21];
|
||||
SizeType length = (sizeof(SizeType) == 4 ? internal::u32toa(index, buffer): internal::u64toa(index, buffer)) - buffer;
|
||||
buffer[length] = '\0';
|
||||
|
||||
if (sizeof(Ch) == 1) {
|
||||
Token token = { (Ch*)buffer, length, index };
|
||||
return Append(token, allocator);
|
||||
}
|
||||
else {
|
||||
Ch name[21];
|
||||
for (size_t i = 0; i <= length; i++)
|
||||
name[i] = buffer[i];
|
||||
Token token = { name, length, index };
|
||||
return Append(token, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
//! Append a token by value, and return a new Pointer
|
||||
/*!
|
||||
\param value Value (either Uint or String) to be appended.
|
||||
\param allocator Allocator for the newly return Pointer.
|
||||
\return A new Pointer with appended token.
|
||||
*/
|
||||
GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const {
|
||||
if (token.IsString())
|
||||
return Append(token.GetString(), token.GetStringLength(), allocator);
|
||||
else {
|
||||
RAPIDJSON_ASSERT(token.IsUint64());
|
||||
RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0));
|
||||
return Append(static_cast<SizeType>(token.GetUint64()), allocator);
|
||||
}
|
||||
}
|
||||
|
||||
//!@name Handling Parse Error
|
||||
//@{
|
||||
|
||||
@ -240,7 +324,7 @@ public:
|
||||
for (size_t i = 0; i < tokenCount_; i++) {
|
||||
if (tokens_[i].index != rhs.tokens_[i].index ||
|
||||
tokens_[i].length != rhs.tokens_[i].length ||
|
||||
std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length) != 0)
|
||||
(tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -602,37 +686,69 @@ public:
|
||||
|
||||
ValueType* v = &root;
|
||||
const Token* last = tokens_ + (tokenCount_ - 1);
|
||||
for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
|
||||
for (const Token *t = tokens_; t != last; ++t) {
|
||||
switch (v->GetType()) {
|
||||
case kObjectType:
|
||||
{
|
||||
typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
|
||||
if (m == v->MemberEnd())
|
||||
return false;
|
||||
if (t == last) {
|
||||
v->EraseMember(m);
|
||||
return true;
|
||||
}
|
||||
v = &m->value;
|
||||
}
|
||||
break;
|
||||
case kArrayType:
|
||||
if (t->index == kPointerInvalidIndex || t->index >= v->Size())
|
||||
return false;
|
||||
if (t == last) {
|
||||
v->Erase(v->Begin() + t->index);
|
||||
return true;
|
||||
}
|
||||
v = &((*v)[t->index]);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
switch (v->GetType()) {
|
||||
case kObjectType:
|
||||
return v->EraseMember(GenericStringRef<Ch>(last->name, last->length));
|
||||
case kArrayType:
|
||||
if (last->index == kPointerInvalidIndex || last->index >= v->Size())
|
||||
return false;
|
||||
v->Erase(v->Begin() + last->index);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//! Clone the content from rhs to this.
|
||||
/*!
|
||||
\param rhs Source pointer.
|
||||
\param extraToken Extra tokens to be allocated.
|
||||
\param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated.
|
||||
\return Start of non-occupied name buffer, for storing extra names.
|
||||
*/
|
||||
Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) {
|
||||
if (!allocator_) // allocator is independently owned.
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
|
||||
|
||||
size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens
|
||||
for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t)
|
||||
nameBufferSize += t->length;
|
||||
|
||||
tokenCount_ = rhs.tokenCount_ + extraToken;
|
||||
tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch)));
|
||||
nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_);
|
||||
std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token));
|
||||
std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch));
|
||||
|
||||
// Adjust pointers to name buffer
|
||||
std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_;
|
||||
for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t)
|
||||
t->name += diff;
|
||||
|
||||
return nameBuffer_ + nameBufferSize;
|
||||
}
|
||||
|
||||
//! Check whether a character should be percent-encoded.
|
||||
/*!
|
||||
According to RFC 3986 2.3 Unreserved Characters.
|
||||
@ -657,11 +773,14 @@ private:
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
|
||||
|
||||
// Create a buffer as same size of source
|
||||
nameBuffer_ = (Ch*)allocator_->Malloc(length * sizeof(Ch));
|
||||
tokens_ = (Token*)allocator_->Malloc(length * sizeof(Token)); // Maximum possible tokens in the source
|
||||
// Count number of '/' as tokenCount
|
||||
tokenCount_ = 0;
|
||||
Ch* name = nameBuffer_;
|
||||
for (const Ch* s = source; s != source + length; s++)
|
||||
if (*s == '/')
|
||||
tokenCount_++;
|
||||
|
||||
Token* token = tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch)));
|
||||
Ch* name = nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_);
|
||||
size_t i = 0;
|
||||
|
||||
// Detect if it is a URI fragment
|
||||
@ -680,8 +799,7 @@ private:
|
||||
RAPIDJSON_ASSERT(source[i] == '/');
|
||||
i++; // consumes '/'
|
||||
|
||||
Token& token = tokens_[tokenCount_++];
|
||||
token.name = name;
|
||||
token->name = name;
|
||||
bool isNumber = true;
|
||||
|
||||
while (i < length && source[i] != '/') {
|
||||
@ -739,18 +857,20 @@ private:
|
||||
|
||||
*name++ = c;
|
||||
}
|
||||
token.length = name - token.name;
|
||||
token->length = name - token->name;
|
||||
if (token->length == 0)
|
||||
isNumber = false;
|
||||
*name++ = '\0'; // Null terminator
|
||||
|
||||
// Second check for index: more than one digit cannot have leading zero
|
||||
if (isNumber && token.length > 1 && token.name[0] == '0')
|
||||
if (isNumber && token->length > 1 && token->name[0] == '0')
|
||||
isNumber = false;
|
||||
|
||||
// String to SizeType conversion
|
||||
SizeType n = 0;
|
||||
if (isNumber) {
|
||||
for (size_t j = 0; j < token.length; j++) {
|
||||
SizeType m = n * 10 + static_cast<SizeType>(token.name[j] - '0');
|
||||
for (size_t j = 0; j < token->length; j++) {
|
||||
SizeType m = n * 10 + static_cast<SizeType>(token->name[j] - '0');
|
||||
if (m < n) { // overflow detection
|
||||
isNumber = false;
|
||||
break;
|
||||
@ -759,16 +879,15 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
token.index = isNumber ? n : kPointerInvalidIndex;
|
||||
token->index = isNumber ? n : kPointerInvalidIndex;
|
||||
token++;
|
||||
}
|
||||
|
||||
RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer
|
||||
tokens_ = (Token*)allocator_->Realloc(tokens_, length * sizeof(Token), tokenCount_ * sizeof(Token)); // Shrink tokens_
|
||||
parseErrorCode_ = kPointerParseErrorNone;
|
||||
return;
|
||||
|
||||
error:
|
||||
Allocator::Free(nameBuffer_);
|
||||
Allocator::Free(tokens_);
|
||||
nameBuffer_ = 0;
|
||||
tokens_ = 0;
|
||||
|
@ -69,7 +69,7 @@
|
||||
*/
|
||||
#define RAPIDJSON_MAJOR_VERSION 1
|
||||
#define RAPIDJSON_MINOR_VERSION 0
|
||||
#define RAPIDJSON_PATCH_VERSION 1
|
||||
#define RAPIDJSON_PATCH_VERSION 2
|
||||
#define RAPIDJSON_VERSION_STRING \
|
||||
RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION)
|
||||
|
||||
@ -223,7 +223,7 @@
|
||||
|
||||
//! Whether using 64-bit architecture
|
||||
#ifndef RAPIDJSON_64BIT
|
||||
#if defined(__LP64__) || defined(_WIN64)
|
||||
#if defined(__LP64__) || defined(_WIN64) || defined(__EMSCRIPTEN__)
|
||||
#define RAPIDJSON_64BIT 1
|
||||
#else
|
||||
#define RAPIDJSON_64BIT 0
|
||||
|
@ -271,7 +271,7 @@ inline const char *SkipWhitespace_SIMD(const char* p) {
|
||||
|
||||
// The rest of string using SIMD
|
||||
static const char whitespace[16] = " \n\r\t";
|
||||
const __m128i w = _mm_load_si128((const __m128i *)&whitespace[0]);
|
||||
const __m128i w = _mm_loadu_si128((const __m128i *)&whitespace[0]);
|
||||
|
||||
for (;; p += 16) {
|
||||
const __m128i s = _mm_load_si128((const __m128i *)p);
|
||||
@ -967,13 +967,13 @@ private:
|
||||
else {
|
||||
if (use64bit) {
|
||||
if (minus)
|
||||
cont = handler.Int64(-(int64_t)i64);
|
||||
cont = handler.Int64(static_cast<int64_t>(~i64 + 1));
|
||||
else
|
||||
cont = handler.Uint64(i64);
|
||||
}
|
||||
else {
|
||||
if (minus)
|
||||
cont = handler.Int(-(int)i);
|
||||
cont = handler.Int(static_cast<int32_t>(~i + 1));
|
||||
else
|
||||
cont = handler.Uint(i);
|
||||
}
|
||||
@ -1387,13 +1387,13 @@ private:
|
||||
}
|
||||
|
||||
switch (src) {
|
||||
case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell());
|
||||
case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell());
|
||||
case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return;
|
||||
case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return;
|
||||
case IterativeParsingObjectInitialState:
|
||||
case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
|
||||
case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell());
|
||||
case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell());
|
||||
case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
|
||||
case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return;
|
||||
case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return;
|
||||
case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return;
|
||||
case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return;
|
||||
default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## A fast JSON parser/generator for C++ with both SAX/DOM style API
|
||||
|
||||
@ -10,8 +10,8 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights
|
||||
|
||||
* [RapidJSON GitHub](https://github.com/miloyip/rapidjson/)
|
||||
* RapidJSON Documentation
|
||||
* [English](http://miloyip.github.io/rapidjson/)
|
||||
* [简体中文](http://miloyip.github.io/rapidjson/zh-cn/)
|
||||
* [English](http://rapidjson.org/)
|
||||
* [简体中文](http://rapidjson.org/zh-cn/)
|
||||
* [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/) with downloadable PDF/EPUB/MOBI, without API reference.
|
||||
|
||||
## Build status
|
||||
|
@ -1,6 +1,6 @@
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## 高效的C++ JSON解析/生成器,提供SAX及DOM风格API
|
||||
|
||||
@ -10,8 +10,8 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights
|
||||
|
||||
* [RapidJSON GitHub](https://github.com/miloyip/rapidjson/)
|
||||
* RapidJSON 文档
|
||||
* [English](http://miloyip.github.io/rapidjson/)
|
||||
* [简体中文](http://miloyip.github.io/rapidjson/zh-cn/)
|
||||
* [English](http://rapidjson.org/)
|
||||
* [简体中文](http://rapidjson.org/zh-cn/)
|
||||
* [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/)可下载PDF/EPUB/MOBI,但不含API参考手册。
|
||||
|
||||
## Build 状态
|
||||
|
@ -298,11 +298,28 @@ TEST_F(RapidJson, internal_Pow10) {
|
||||
EXPECT_GT(sum, 0.0);
|
||||
}
|
||||
|
||||
TEST_F(RapidJson, SIMD_SUFFIX(Whitespace)) {
|
||||
TEST_F(RapidJson, SkipWhitespace_Basic) {
|
||||
for (size_t i = 0; i < kTrialCount; i++) {
|
||||
Document doc;
|
||||
ASSERT_TRUE(doc.Parse(whitespace_).IsArray());
|
||||
}
|
||||
rapidjson::StringStream s(whitespace_);
|
||||
while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t')
|
||||
s.Take();
|
||||
ASSERT_EQ('[', s.Peek());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(RapidJson, SIMD_SUFFIX(SkipWhitespace)) {
|
||||
for (size_t i = 0; i < kTrialCount; i++) {
|
||||
rapidjson::StringStream s(whitespace_);
|
||||
rapidjson::SkipWhitespace(s);
|
||||
ASSERT_EQ('[', s.Peek());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(RapidJson, SkipWhitespace_strspn) {
|
||||
for (size_t i = 0; i < kTrialCount; i++) {
|
||||
const char* s = whitespace_ + std::strspn(whitespace_, " \t\r\n");
|
||||
ASSERT_EQ('[', *s);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(RapidJson, UTF8_Validate) {
|
||||
|
@ -21,7 +21,7 @@ set(UNITTEST_SOURCES
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal")
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal -Wimplicit-fallthrough")
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS=1)
|
||||
endif()
|
||||
|
@ -241,7 +241,7 @@ TEST(Document, UserBuffer) {
|
||||
char parseBuffer[1024];
|
||||
MemoryPoolAllocator<> valueAllocator(valueBuffer, sizeof(valueBuffer));
|
||||
MemoryPoolAllocator<> parseAllocator(parseBuffer, sizeof(parseBuffer));
|
||||
DocumentType doc(&valueAllocator, sizeof(parseBuffer), &parseAllocator);
|
||||
DocumentType doc(&valueAllocator, sizeof(parseBuffer) / 2, &parseAllocator);
|
||||
doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
|
||||
EXPECT_FALSE(doc.HasParseError());
|
||||
EXPECT_LE(valueAllocator.Size(), sizeof(valueBuffer));
|
||||
|
@ -39,12 +39,22 @@ TEST(Pointer, Parse) {
|
||||
EXPECT_EQ(0u, p.GetTokenCount());
|
||||
}
|
||||
|
||||
{
|
||||
Pointer p("/");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_EQ(0u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("", p.GetTokens()[0].name);
|
||||
EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
|
||||
}
|
||||
|
||||
{
|
||||
Pointer p("/foo");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_EQ(3u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("foo", p.GetTokens()[0].name);
|
||||
EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
@ -54,6 +64,7 @@ TEST(Pointer, Parse) {
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_EQ(3u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("foo", p.GetTokens()[0].name);
|
||||
EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -63,6 +74,7 @@ TEST(Pointer, Parse) {
|
||||
EXPECT_EQ(2u, p.GetTokenCount());
|
||||
EXPECT_EQ(3u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("foo", p.GetTokens()[0].name);
|
||||
EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
|
||||
EXPECT_EQ(1u, p.GetTokens()[1].length);
|
||||
EXPECT_STREQ("0", p.GetTokens()[1].name);
|
||||
EXPECT_EQ(0u, p.GetTokens()[1].index);
|
||||
@ -481,6 +493,14 @@ TEST(Pointer, Assignment) {
|
||||
EXPECT_EQ(1u, q.GetTokens()[1].length);
|
||||
EXPECT_STREQ("0", q.GetTokens()[1].name);
|
||||
EXPECT_EQ(0u, q.GetTokens()[1].index);
|
||||
q = q;
|
||||
EXPECT_TRUE(q.IsValid());
|
||||
EXPECT_EQ(2u, q.GetTokenCount());
|
||||
EXPECT_EQ(3u, q.GetTokens()[0].length);
|
||||
EXPECT_STREQ("foo", q.GetTokens()[0].name);
|
||||
EXPECT_EQ(1u, q.GetTokens()[1].length);
|
||||
EXPECT_STREQ("0", q.GetTokens()[1].name);
|
||||
EXPECT_EQ(0u, q.GetTokens()[1].index);
|
||||
}
|
||||
|
||||
// Static tokens
|
||||
@ -498,6 +518,36 @@ TEST(Pointer, Assignment) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Pointer, Append) {
|
||||
{
|
||||
Pointer p;
|
||||
Pointer q = p.Append("foo");
|
||||
EXPECT_TRUE(Pointer("/foo") == q);
|
||||
q = q.Append(1234);
|
||||
EXPECT_TRUE(Pointer("/foo/1234") == q);
|
||||
q = q.Append("");
|
||||
EXPECT_TRUE(Pointer("/foo/1234/") == q);
|
||||
}
|
||||
|
||||
{
|
||||
Pointer p;
|
||||
Pointer q = p.Append(Value("foo").Move());
|
||||
EXPECT_TRUE(Pointer("/foo") == q);
|
||||
q = q.Append(Value(1234).Move());
|
||||
EXPECT_TRUE(Pointer("/foo/1234") == q);
|
||||
q = q.Append(Value(kStringType).Move());
|
||||
EXPECT_TRUE(Pointer("/foo/1234/") == q);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
{
|
||||
Pointer p;
|
||||
Pointer q = p.Append(std::string("foo"));
|
||||
EXPECT_TRUE(Pointer("/foo") == q);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Pointer, Equality) {
|
||||
EXPECT_TRUE(Pointer("/foo/0") == Pointer("/foo/0"));
|
||||
EXPECT_FALSE(Pointer("/foo/0") == Pointer("/foo/1"));
|
||||
@ -823,7 +873,13 @@ TEST(Pointer, Erase) {
|
||||
d.Parse(kJson);
|
||||
|
||||
EXPECT_FALSE(Pointer("").Erase(d));
|
||||
EXPECT_FALSE(Pointer("/nonexist").Erase(d));
|
||||
EXPECT_FALSE(Pointer("/nonexist/nonexist").Erase(d));
|
||||
EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
|
||||
EXPECT_FALSE(Pointer("/foo/nonexist/nonexist").Erase(d));
|
||||
EXPECT_FALSE(Pointer("/foo/0/nonexist").Erase(d));
|
||||
EXPECT_FALSE(Pointer("/foo/0/nonexist/nonexist").Erase(d));
|
||||
EXPECT_FALSE(Pointer("/foo/2/nonexist").Erase(d));
|
||||
EXPECT_TRUE(Pointer("/foo/0").Erase(d));
|
||||
EXPECT_EQ(1u, d["foo"].Size());
|
||||
EXPECT_STREQ("baz", d["foo"][0].GetString());
|
||||
@ -831,6 +887,24 @@ TEST(Pointer, Erase) {
|
||||
EXPECT_TRUE(d["foo"].Empty());
|
||||
EXPECT_TRUE(Pointer("/foo").Erase(d));
|
||||
EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
|
||||
|
||||
Pointer("/a/0/b/0").Create(d);
|
||||
|
||||
EXPECT_TRUE(Pointer("/a/0/b/0").Get(d) != 0);
|
||||
EXPECT_TRUE(Pointer("/a/0/b/0").Erase(d));
|
||||
EXPECT_TRUE(Pointer("/a/0/b/0").Get(d) == 0);
|
||||
|
||||
EXPECT_TRUE(Pointer("/a/0/b").Get(d) != 0);
|
||||
EXPECT_TRUE(Pointer("/a/0/b").Erase(d));
|
||||
EXPECT_TRUE(Pointer("/a/0/b").Get(d) == 0);
|
||||
|
||||
EXPECT_TRUE(Pointer("/a/0").Get(d) != 0);
|
||||
EXPECT_TRUE(Pointer("/a/0").Erase(d));
|
||||
EXPECT_TRUE(Pointer("/a/0").Get(d) == 0);
|
||||
|
||||
EXPECT_TRUE(Pointer("/a").Get(d) != 0);
|
||||
EXPECT_TRUE(Pointer("/a").Erase(d));
|
||||
EXPECT_TRUE(Pointer("/a").Get(d) == 0);
|
||||
}
|
||||
|
||||
TEST(Pointer, CreateValueByPointer) {
|
||||
|
@ -193,7 +193,7 @@ static void TestParseDouble() {
|
||||
EXPECT_DOUBLE_EQ(x, h.actual_); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
TEST_DOUBLE(fullPrecision, "0.0", 0.0);
|
||||
TEST_DOUBLE(fullPrecision, "-0.0", -0.0); // For checking issue #289
|
||||
TEST_DOUBLE(fullPrecision, "1.0", 1.0);
|
||||
@ -327,15 +327,44 @@ static void TestParseDouble() {
|
||||
if (fullPrecision) {
|
||||
EXPECT_EQ(d.Uint64Value(), a.Uint64Value());
|
||||
if (d.Uint64Value() != a.Uint64Value())
|
||||
printf(" String: %sn Actual: %.17gnExpected: %.17gn", buffer, h.actual_, d.Value());
|
||||
printf(" String: %s\n Actual: %.17g\nExpected: %.17g\n", buffer, h.actual_, d.Value());
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(d.Sign(), a.Sign()); /* for 0.0 != -0.0 */
|
||||
EXPECT_EQ(d.Sign(), a.Sign()); // for 0.0 != -0.0
|
||||
EXPECT_DOUBLE_EQ(d.Value(), h.actual_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #340
|
||||
TEST_DOUBLE(fullPrecision, "7.450580596923828e-9", 7.450580596923828e-9);
|
||||
{
|
||||
internal::Double d(1.0);
|
||||
for (int i = 0; i < 324; i++) {
|
||||
char buffer[32];
|
||||
*internal::dtoa(d.Value(), buffer) = '\0';
|
||||
|
||||
StringStream s(buffer);
|
||||
ParseDoubleHandler h;
|
||||
Reader reader;
|
||||
ASSERT_EQ(kParseErrorNone, reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(s, h).Code());
|
||||
EXPECT_EQ(1u, h.step_);
|
||||
internal::Double a(h.actual_);
|
||||
if (fullPrecision) {
|
||||
EXPECT_EQ(d.Uint64Value(), a.Uint64Value());
|
||||
if (d.Uint64Value() != a.Uint64Value())
|
||||
printf(" String: %s\n Actual: %.17g\nExpected: %.17g\n", buffer, h.actual_, d.Value());
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(d.Sign(), a.Sign()); // for 0.0 != -0.0
|
||||
EXPECT_DOUBLE_EQ(d.Value(), h.actual_);
|
||||
}
|
||||
|
||||
|
||||
d = d.Value() * 0.5;
|
||||
}
|
||||
}
|
||||
#undef TEST_DOUBLE
|
||||
}
|
||||
|
||||
|
@ -957,6 +957,19 @@ TEST(Value, Object) {
|
||||
EXPECT_EQ(2u, o.MemberCount());
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
{
|
||||
// AddMember(StringRefType, const std::string&, Allocator)
|
||||
Value o(kObjectType);
|
||||
o.AddMember("b", std::string("Banana"), allocator);
|
||||
EXPECT_STREQ("Banana", o["b"].GetString());
|
||||
|
||||
// RemoveMember(const std::string&)
|
||||
o.RemoveMember(std::string("b"));
|
||||
EXPECT_TRUE(o.ObjectEmpty());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
// AddMember(GenericValue&&, ...) variants
|
||||
{
|
||||
@ -986,6 +999,10 @@ TEST(Value, Object) {
|
||||
EXPECT_TRUE(y.HasMember("A"));
|
||||
EXPECT_TRUE(y.HasMember("B"));
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
EXPECT_TRUE(x.HasMember(std::string("A")));
|
||||
#endif
|
||||
|
||||
name.SetString("C\0D");
|
||||
EXPECT_TRUE(x.HasMember(name));
|
||||
EXPECT_TRUE(y.HasMember(name));
|
||||
@ -1009,6 +1026,11 @@ TEST(Value, Object) {
|
||||
EXPECT_STREQ("Banana", y["B"].GetString());
|
||||
EXPECT_STREQ("CherryD", y[C0D].GetString());
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
EXPECT_STREQ("Apple", x["A"].GetString());
|
||||
EXPECT_STREQ("Apple", y[std::string("A")].GetString());
|
||||
#endif
|
||||
|
||||
// member iterator
|
||||
Value::MemberIterator itr = x.MemberBegin();
|
||||
EXPECT_TRUE(itr != x.MemberEnd());
|
||||
@ -1160,6 +1182,24 @@ TEST(Value, Object) {
|
||||
EXPECT_TRUE(z.IsObject());
|
||||
}
|
||||
|
||||
TEST(Value, EraseMember_String) {
|
||||
Value::AllocatorType allocator;
|
||||
Value x(kObjectType);
|
||||
x.AddMember("A", "Apple", allocator);
|
||||
x.AddMember("B", "Banana", allocator);
|
||||
|
||||
EXPECT_TRUE(x.EraseMember("B"));
|
||||
EXPECT_FALSE(x.HasMember("B"));
|
||||
|
||||
EXPECT_FALSE(x.EraseMember("nonexist"));
|
||||
|
||||
GenericValue<UTF8<>, CrtAllocator> othername("A");
|
||||
EXPECT_TRUE(x.EraseMember(othername));
|
||||
EXPECT_FALSE(x.HasMember("A"));
|
||||
|
||||
EXPECT_TRUE(x.MemberBegin() == x.MemberEnd());
|
||||
}
|
||||
|
||||
TEST(Value, BigNestedArray) {
|
||||
MemoryPoolAllocator<> allocator;
|
||||
Value x(kArrayType);
|
||||
|
@ -78,6 +78,7 @@ gh_pages_prepare()
|
||||
|
||||
gh_pages_commit() {
|
||||
cd "${TRAVIS_BUILD_DIR}/build/doc/html";
|
||||
echo "rapidjson.org" > CNAME
|
||||
git add --all;
|
||||
git diff-index --quiet HEAD || git commit -m "Automatic doxygen build";
|
||||
}
|
||||
|