From 8c01e7e1ce5e39d675170749a3ec297608b27463 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 4 May 2015 21:50:26 +0800 Subject: [PATCH 1/9] Add Pointer::Erase() and EraseValueByPointer() --- doc/pointer.md | 11 +++++++ include/rapidjson/pointer.h | 60 +++++++++++++++++++++++++++++++++-- test/unittest/pointertest.cpp | 45 ++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/doc/pointer.md b/doc/pointer.md index c75fe0d..d4d1cf2 100644 --- a/doc/pointer.md +++ b/doc/pointer.md @@ -65,6 +65,12 @@ Pointer("/hello").Swap(d, x); // { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "C++" } // x becomes "world" + +// Erase a member or element, return true if the value exists +bool success = Pointer("/a").Erase(d); +assert(success); + +// { "project" : "RapidJSON", "stars" : 10 } ~~~ # Helper Functions {#HelperFunctions} @@ -88,6 +94,9 @@ Value& hello = GetValueByPointerWithDefault(d, "/hello", "world"); Value x("C++"); SwapValueByPointer(d, "/hello", x); + +bool success = EraseValueByPointer(d, "/a"); +assert(success); ~~~ The conventions are shown here for comparison: @@ -166,6 +175,8 @@ private: }; ~~~ +`Erase()` or `EraseValueByPointer()` does not need allocator. And they return `true` if the value is erased successfully. + # Error Handling {#ErrorHandling} A `Pointer` parses a source string in its constructor. If there is parsing error, `Pointer::IsValid()` returns false. And you can use `Pointer::GetParseErrorCode()` and `GetParseErrorOffset()` to retrieve the error information. diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index 89dfa48..b68829c 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -304,7 +304,7 @@ public: RAPIDJSON_ASSERT(IsValid()); ValueType* v = &root; bool exist = true; - for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { if (v->IsArray() && t->name[0] == '-' && t->length == 1) { v->PushBack(Value().Move(), allocator); v = &((*v)[v->Size() - 1]); @@ -373,7 +373,7 @@ public: ValueType* Get(ValueType& root) const { RAPIDJSON_ASSERT(IsValid()); ValueType* v = &root; - for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { switch (v->GetType()) { case kObjectType: { @@ -588,6 +588,50 @@ public: //@} + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(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; + } + private: //! Check whether a character should be percent-encoded. /*! @@ -1131,6 +1175,18 @@ typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, con return GenericPointer(source, N - 1).Swap(document, value); } +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + //@} RAPIDJSON_NAMESPACE_END diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index 72bfdbf..cf2ab72 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -818,6 +818,21 @@ TEST(Pointer, Swap_NoAllocator) { EXPECT_STREQ("bar", d["foo"][1].GetString()); } +TEST(Pointer, Erase) { + Document d; + d.Parse(kJson); + + EXPECT_FALSE(Pointer("").Erase(d)); + EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d)); + EXPECT_TRUE(Pointer("/foo/0").Erase(d)); + EXPECT_EQ(1u, d["foo"].Size()); + EXPECT_STREQ("baz", d["foo"][0].GetString()); + EXPECT_TRUE(Pointer("/foo/0").Erase(d)); + EXPECT_TRUE(d["foo"].Empty()); + EXPECT_TRUE(Pointer("/foo").Erase(d)); + EXPECT_TRUE(Pointer("/foo").Get(d) == 0); +} + TEST(Pointer, CreateValueByPointer) { Document d; Document::AllocatorType& a = d.GetAllocator(); @@ -1319,6 +1334,36 @@ TEST(Pointer, SwapValueByPointer_NoAllocator) { EXPECT_STREQ("baz", d["foo"][1].GetString()); } +TEST(Pointer, EraseValueByPointer_Pointer) { + Document d; + d.Parse(kJson); + + EXPECT_FALSE(EraseValueByPointer(d, Pointer(""))); + EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d)); + EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo/0"))); + EXPECT_EQ(1u, d["foo"].Size()); + EXPECT_STREQ("baz", d["foo"][0].GetString()); + EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo/0"))); + EXPECT_TRUE(d["foo"].Empty()); + EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo"))); + EXPECT_TRUE(Pointer("/foo").Get(d) == 0); +} + +TEST(Pointer, EraseValueByPointer_String) { + Document d; + d.Parse(kJson); + + EXPECT_FALSE(EraseValueByPointer(d, "")); + EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d)); + EXPECT_TRUE(EraseValueByPointer(d, "/foo/0")); + EXPECT_EQ(1u, d["foo"].Size()); + EXPECT_STREQ("baz", d["foo"][0].GetString()); + EXPECT_TRUE(EraseValueByPointer(d, "/foo/0")); + EXPECT_TRUE(d["foo"].Empty()); + EXPECT_TRUE(EraseValueByPointer(d, "/foo")); + EXPECT_TRUE(Pointer("/foo").Get(d) == 0); +} + TEST(Pointer, Ambiguity) { { Document d; From ba7647531b2fbcc6e91dfc5898e15eb0bc0f178b Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 4 May 2015 22:13:14 +0800 Subject: [PATCH 2/9] Fix incorrect doxygen escapes --- doc/pointer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/pointer.md b/doc/pointer.md index d4d1cf2..1d75080 100644 --- a/doc/pointer.md +++ b/doc/pointer.md @@ -196,7 +196,7 @@ String Representation | URI Fragment Representation | Pointer Tokens (UTF-8) `"/m~0n"` | `"#/m~0n"` | `{"m~n"}` `"/ "` | `"#/%20"` | `{" "}` `"/\0"` | `"#/%00"` | `{"\0"}` -`"/\xE2\x82\xAC"` | `"#/%E2%82%AC"` | `{"\xE2\x82\xAC"}` +`"/€"` | `"#/%E2%82%AC"` | `{"€"}` RapidJSON fully support URI fragment representation. It automatically detects the pound sign during parsing. From 1576cde59276c7157ecaebdb24470667bd743271 Mon Sep 17 00:00:00 2001 From: Igor Kostenko Date: Tue, 5 May 2015 17:39:16 +0100 Subject: [PATCH 3/9] Fix alignment of 64bit platforms --- include/rapidjson/rapidjson.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 0c41ab6..b06e82b 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -241,8 +241,12 @@ alignment. User can customize by defining the RAPIDJSON_ALIGN function macro., */ #ifndef RAPIDJSON_ALIGN +#if RAPIDJSON_64BIT == 1 +#define RAPIDJSON_ALIGN(x) ((x + 7u) & ~7u) +#else #define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) #endif +#endif /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_UINT64_C2 From 97d489c247918fd127fe410a6c39e05bc2b71719 Mon Sep 17 00:00:00 2001 From: Justin Scheiber Date: Tue, 5 May 2015 13:42:06 -0600 Subject: [PATCH 4/9] fix shadow warnings on gcc 4.8 (-Wshadow) --- include/rapidjson/internal/ieee754.h | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h index 9a82880..e1e1008 100644 --- a/include/rapidjson/internal/ieee754.h +++ b/include/rapidjson/internal/ieee754.h @@ -23,29 +23,29 @@ namespace internal { class Double { public: Double() {} - Double(double d) : d(d) {} - Double(uint64_t u) : u(u) {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} - double Value() const { return d; } - uint64_t Uint64Value() const { return u; } + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } double NextPositiveDouble() const { RAPIDJSON_ASSERT(!Sign()); - return Double(u + 1).Value(); + return Double(u_ + 1).Value(); } - bool Sign() const { return (u & kSignMask) != 0; } - uint64_t Significand() const { return u & kSignificandMask; } - int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; } + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return ((u_ & kExponentMask) >> kSignificandSize) - kExponentBias; } - bool IsNan() const { return (u & kExponentMask) == kExponentMask && Significand() != 0; } - bool IsInf() const { return (u & kExponentMask) == kExponentMask && Significand() == 0; } - bool IsNormal() const { return (u & kExponentMask) != 0 || Significand() == 0; } - bool IsZero() const { return (u & (kExponentMask | kSignificandMask)) == 0; } + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } - uint64_t ToBias() const { return (u & kSignMask) ? ~u + 1 : u | kSignMask; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } static unsigned EffectiveSignificandSize(int order) { if (order >= -1021) @@ -66,8 +66,8 @@ private: static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); union { - double d; - uint64_t u; + double d_; + uint64_t u_; }; }; From 050be06e52f6e667ee246698f0e84c077a11f41b Mon Sep 17 00:00:00 2001 From: Justin Scheiber Date: Tue, 5 May 2015 14:37:18 -0600 Subject: [PATCH 5/9] fixing conversion warnings --- include/rapidjson/internal/biginteger.h | 4 ++-- include/rapidjson/internal/diyfp.h | 4 ++-- include/rapidjson/internal/ieee754.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h index 5baba9b..99a30ac 100755 --- a/include/rapidjson/internal/biginteger.h +++ b/include/rapidjson/internal/biginteger.h @@ -97,7 +97,7 @@ public: if (u == 1) return *this; if (*this == 1) return *this = u; - uint32_t k = 0; + uint64_t k = 0; for (size_t i = 0; i < count_; i++) { const uint64_t c = digits_[i] >> 32; const uint64_t d = digits_[i] & 0xFFFFFFFF; @@ -246,7 +246,7 @@ private: __extension__ typedef unsigned __int128 uint128; uint128 p = static_cast(a) * static_cast(b); p += k; - *outHigh = p >> 64; + *outHigh = static_cast(p >> 64); return static_cast(p); #else const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h index 4ef53d9..3b6c423 100644 --- a/include/rapidjson/internal/diyfp.h +++ b/include/rapidjson/internal/diyfp.h @@ -45,7 +45,7 @@ struct DiyFp { uint64_t u64; } u = { d }; - int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize; + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); uint64_t significand = (u.u64 & kDpSignificandMask); if (biased_e != 0) { f = significand + kDpHiddenBit; @@ -71,7 +71,7 @@ struct DiyFp { #elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) __extension__ typedef unsigned __int128 uint128; uint128 p = static_cast(f) * static_cast(rhs.f); - uint64_t h = p >> 64; + uint64_t h = static_cast(p >> 64); uint64_t l = static_cast(p); if (l & (uint64_t(1) << 63)) // rounding h++; diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h index e1e1008..e3f0336 100644 --- a/include/rapidjson/internal/ieee754.h +++ b/include/rapidjson/internal/ieee754.h @@ -36,7 +36,7 @@ public: bool Sign() const { return (u_ & kSignMask) != 0; } uint64_t Significand() const { return u_ & kSignificandMask; } - int Exponent() const { return ((u_ & kExponentMask) >> kSignificandSize) - kExponentBias; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } From d0c37814563cd842f7ee7638ad07e7c678032113 Mon Sep 17 00:00:00 2001 From: Justin Scheiber Date: Tue, 5 May 2015 15:33:31 -0600 Subject: [PATCH 6/9] add -Werror for clang and gcc --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 380bdcd..bc1c358 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,9 +26,9 @@ if(RAPIDJSON_HAS_STDSTRING) endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror") elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_definitions(-D_CRT_SECURE_NO_WARNINGS=1) endif() From f7c184d36a45245942abe06889875ed6a57b7611 Mon Sep 17 00:00:00 2001 From: Justin Scheiber Date: Tue, 5 May 2015 18:02:03 -0600 Subject: [PATCH 7/9] Revert "add -Werror for clang and gcc" This reverts commit d0c37814563cd842f7ee7638ad07e7c678032113. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc1c358..380bdcd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,9 +26,9 @@ if(RAPIDJSON_HAS_STDSTRING) endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra") elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_definitions(-D_CRT_SECURE_NO_WARNINGS=1) endif() From a72c35b9fa881a4c3eb8d8a5f9479dd332c44220 Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 6 May 2015 10:49:01 +0800 Subject: [PATCH 8/9] Workaround for Valgrind false alarm on wcscmp() --- test/unittest/documenttest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/documenttest.cpp b/test/unittest/documenttest.cpp index 7e5d766..940b295 100644 --- a/test/unittest/documenttest.cpp +++ b/test/unittest/documenttest.cpp @@ -276,7 +276,7 @@ TEST(Document, UTF16_Document) { GenericValue< UTF16<> >& s = v[L"created_at"]; ASSERT_TRUE(s.IsString()); - EXPECT_EQ(0, wcscmp(L"Wed Oct 30 17:13:20 +0000 2012", s.GetString())); + EXPECT_EQ(0, memcmp(L"Wed Oct 30 17:13:20 +0000 2012", s.GetString(), (s.GetStringLength() + 1) * sizeof(wchar_t))); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS From 5dee394004088cbfac1ecc80102a8353c0db8281 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 8 May 2015 21:26:56 +0800 Subject: [PATCH 9/9] Add Pointer::Append() and fixed bugs in assignment and Parse() --- include/rapidjson/pointer.h | 153 ++++++++++++++++++++++++++++------ test/unittest/pointertest.cpp | 40 +++++++++ 2 files changed, 168 insertions(+), 25 deletions(-) diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index b68829c..adb819e 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -16,6 +16,7 @@ #define RAPIDJSON_POINTER_H_ #include "document.h" +#include "internal/itoa.h" RAPIDJSON_NAMESPACE_BEGIN @@ -169,38 +170,109 @@ public: //! Assignment operator. GenericPointer& operator=(const GenericPointer& rhs) { - this->~GenericPointer(); + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) { + Allocator::Free(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) * sizeof(Ch)); + 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 + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::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& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(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); + } + } + //!@name Handling Parse Error //@{ @@ -240,7 +312,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; } @@ -633,6 +705,35 @@ public: } 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 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; + nameBuffer_ = (Ch*)allocator_->Malloc(nameBufferSize * sizeof(Ch)+extraNameBufferSize); + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = (Token*)allocator_->Malloc(tokenCount_ * sizeof(Token)); + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + + // 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 * sizeof(Ch); + } + //! Check whether a character should be percent-encoded. /*! According to RFC 3986 2.3 Unreserved Characters. @@ -740,6 +841,8 @@ private: *name++ = c; } 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 diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index cf2ab72..cb36330 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -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,26 @@ TEST(Pointer, Assignment) { } } +TEST(Pointer, Append) { + { + Pointer p; + Pointer q = p.Append("foo"); + EXPECT_TRUE(Pointer("/foo") == q); + q = q.Append(0); + EXPECT_TRUE(Pointer("/foo/0") == q); + q = q.Append(""); + EXPECT_TRUE(Pointer("/foo/0/") == 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"));