Commit 677af55f authored by Milo Yip's avatar Milo Yip

Merge remote-tracking branch 'origin/master' into schema

parents 24f060f7 5dee3940
...@@ -65,6 +65,12 @@ Pointer("/hello").Swap(d, x); ...@@ -65,6 +65,12 @@ Pointer("/hello").Swap(d, x);
// { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "C++" } // { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "C++" }
// x becomes "world" // 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} # Helper Functions {#HelperFunctions}
...@@ -88,6 +94,9 @@ Value& hello = GetValueByPointerWithDefault(d, "/hello", "world"); ...@@ -88,6 +94,9 @@ Value& hello = GetValueByPointerWithDefault(d, "/hello", "world");
Value x("C++"); Value x("C++");
SwapValueByPointer(d, "/hello", x); SwapValueByPointer(d, "/hello", x);
bool success = EraseValueByPointer(d, "/a");
assert(success);
~~~ ~~~
The conventions are shown here for comparison: The conventions are shown here for comparison:
...@@ -166,6 +175,8 @@ private: ...@@ -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} # 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. 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.
...@@ -185,7 +196,7 @@ String Representation | URI Fragment Representation | Pointer Tokens (UTF-8) ...@@ -185,7 +196,7 @@ String Representation | URI Fragment Representation | Pointer Tokens (UTF-8)
`"/m~0n"` | `"#/m~0n"` | `{"m~n"}` `"/m~0n"` | `"#/m~0n"` | `{"m~n"}`
`"/ "` | `"#/%20"` | `{" "}` `"/ "` | `"#/%20"` | `{" "}`
`"/\0"` | `"#/%00"` | `{"\0"}` `"/\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. RapidJSON fully support URI fragment representation. It automatically detects the pound sign during parsing.
......
...@@ -97,7 +97,7 @@ public: ...@@ -97,7 +97,7 @@ public:
if (u == 1) return *this; if (u == 1) return *this;
if (*this == 1) return *this = u; if (*this == 1) return *this = u;
uint32_t k = 0; uint64_t k = 0;
for (size_t i = 0; i < count_; i++) { for (size_t i = 0; i < count_; i++) {
const uint64_t c = digits_[i] >> 32; const uint64_t c = digits_[i] >> 32;
const uint64_t d = digits_[i] & 0xFFFFFFFF; const uint64_t d = digits_[i] & 0xFFFFFFFF;
...@@ -246,7 +246,7 @@ private: ...@@ -246,7 +246,7 @@ private:
__extension__ typedef unsigned __int128 uint128; __extension__ typedef unsigned __int128 uint128;
uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b); uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
p += k; p += k;
*outHigh = p >> 64; *outHigh = static_cast<uint64_t>(p >> 64);
return static_cast<uint64_t>(p); return static_cast<uint64_t>(p);
#else #else
const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
......
...@@ -45,7 +45,7 @@ struct DiyFp { ...@@ -45,7 +45,7 @@ struct DiyFp {
uint64_t u64; uint64_t u64;
} u = { d }; } u = { d };
int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize; int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
uint64_t significand = (u.u64 & kDpSignificandMask); uint64_t significand = (u.u64 & kDpSignificandMask);
if (biased_e != 0) { if (biased_e != 0) {
f = significand + kDpHiddenBit; f = significand + kDpHiddenBit;
...@@ -71,7 +71,7 @@ struct DiyFp { ...@@ -71,7 +71,7 @@ struct DiyFp {
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) #elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
__extension__ typedef unsigned __int128 uint128; __extension__ typedef unsigned __int128 uint128;
uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f); uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
uint64_t h = p >> 64; uint64_t h = static_cast<uint64_t>(p >> 64);
uint64_t l = static_cast<uint64_t>(p); uint64_t l = static_cast<uint64_t>(p);
if (l & (uint64_t(1) << 63)) // rounding if (l & (uint64_t(1) << 63)) // rounding
h++; h++;
......
...@@ -23,29 +23,29 @@ namespace internal { ...@@ -23,29 +23,29 @@ namespace internal {
class Double { class Double {
public: public:
Double() {} Double() {}
Double(double d) : d(d) {} Double(double d) : d_(d) {}
Double(uint64_t u) : u(u) {} Double(uint64_t u) : u_(u) {}
double Value() const { return d; } double Value() const { return d_; }
uint64_t Uint64Value() const { return u; } uint64_t Uint64Value() const { return u_; }
double NextPositiveDouble() const { double NextPositiveDouble() const {
RAPIDJSON_ASSERT(!Sign()); RAPIDJSON_ASSERT(!Sign());
return Double(u + 1).Value(); return Double(u_ + 1).Value();
} }
bool Sign() const { return (u & kSignMask) != 0; } bool Sign() const { return (u_ & kSignMask) != 0; }
uint64_t Significand() const { return u & kSignificandMask; } uint64_t Significand() const { return u_ & kSignificandMask; }
int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; } int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
bool IsNan() const { return (u & kExponentMask) == kExponentMask && Significand() != 0; } bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
bool IsInf() 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 IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
bool IsZero() const { return (u & (kExponentMask | kSignificandMask)) == 0; } bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } 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) { static unsigned EffectiveSignificandSize(int order) {
if (order >= -1021) if (order >= -1021)
...@@ -66,8 +66,8 @@ private: ...@@ -66,8 +66,8 @@ private:
static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
union { union {
double d; double d_;
uint64_t u; uint64_t u_;
}; };
}; };
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#define RAPIDJSON_POINTER_H_ #define RAPIDJSON_POINTER_H_
#include "document.h" #include "document.h"
#include "internal/itoa.h"
RAPIDJSON_NAMESPACE_BEGIN RAPIDJSON_NAMESPACE_BEGIN
...@@ -169,37 +170,108 @@ public: ...@@ -169,37 +170,108 @@ public:
//! Assignment operator. //! Assignment operator.
GenericPointer& operator=(const GenericPointer& rhs) { 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_; tokenCount_ = rhs.tokenCount_;
parseErrorOffset_ = rhs.parseErrorOffset_; parseErrorOffset_ = rhs.parseErrorOffset_;
parseErrorCode_ = rhs.parseErrorCode_; parseErrorCode_ = rhs.parseErrorCode_;
if (rhs.nameBuffer_) { // Normally parsed tokens. if (rhs.nameBuffer_)
if (!allocator_) // allocator is independently owned. CopyFromRaw(rhs); // Normally parsed tokens.
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); else {
tokens_ = rhs.tokens_; // User supplied const tokens.
nameBuffer_ = 0;
}
}
return *this;
}
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)); //!@name Append token
std::memcpy(tokens_, rhs.tokens_, tokenCount_ * sizeof(Token)); //@{
// Adjust pointers to name buffer //! Append a token and return a new Pointer
std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; /*!
for (Token *t = rhs.tokens_; t != rhs.tokens_ + tokenCount_; ++t) \param token Token to be appended.
t->name += diff; \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);
} }
else
tokens_ = rhs.tokens_; // User supplied const tokens.
return *this; //! 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);
}
}
//!@name Handling Parse Error //!@name Handling Parse Error
//@{ //@{
...@@ -240,7 +312,7 @@ public: ...@@ -240,7 +312,7 @@ public:
for (size_t i = 0; i < tokenCount_; i++) { for (size_t i = 0; i < tokenCount_; i++) {
if (tokens_[i].index != rhs.tokens_[i].index || if (tokens_[i].index != rhs.tokens_[i].index ||
tokens_[i].length != rhs.tokens_[i].length || 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; return false;
} }
...@@ -304,7 +376,7 @@ public: ...@@ -304,7 +376,7 @@ public:
RAPIDJSON_ASSERT(IsValid()); RAPIDJSON_ASSERT(IsValid());
ValueType* v = &root; ValueType* v = &root;
bool exist = true; 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) { if (v->IsArray() && t->name[0] == '-' && t->length == 1) {
v->PushBack(Value().Move(), allocator); v->PushBack(Value().Move(), allocator);
v = &((*v)[v->Size() - 1]); v = &((*v)[v->Size() - 1]);
...@@ -373,7 +445,7 @@ public: ...@@ -373,7 +445,7 @@ public:
ValueType* Get(ValueType& root) const { ValueType* Get(ValueType& root) const {
RAPIDJSON_ASSERT(IsValid()); RAPIDJSON_ASSERT(IsValid());
ValueType* v = &root; ValueType* v = &root;
for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
switch (v->GetType()) { switch (v->GetType()) {
case kObjectType: case kObjectType:
{ {
...@@ -588,7 +660,80 @@ public: ...@@ -588,7 +660,80 @@ 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<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;
}
private: 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. //! Check whether a character should be percent-encoded.
/*! /*!
According to RFC 3986 2.3 Unreserved Characters. According to RFC 3986 2.3 Unreserved Characters.
...@@ -696,6 +841,8 @@ private: ...@@ -696,6 +841,8 @@ private:
*name++ = c; *name++ = c;
} }
token.length = name - token.name; token.length = name - token.name;
if (token.length == 0)
isNumber = false;
*name++ = '\0'; // Null terminator *name++ = '\0'; // Null terminator
// Second check for index: more than one digit cannot have leading zero // Second check for index: more than one digit cannot have leading zero
...@@ -1131,6 +1278,18 @@ typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, con ...@@ -1131,6 +1278,18 @@ typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, con
return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Swap(document, value); return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Swap(document, value);
} }
//////////////////////////////////////////////////////////////////////////////
template <typename T>
bool EraseValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) {
return pointer.Erase(root);
}
template <typename T, typename CharType, size_t N>
bool EraseValueByPointer(T& root, const CharType(&source)[N]) {
return GenericPointer<typename T::ValueType>(source, N - 1).Erase(root);
}
//@} //@}
RAPIDJSON_NAMESPACE_END RAPIDJSON_NAMESPACE_END
......
...@@ -241,8 +241,12 @@ ...@@ -241,8 +241,12 @@
alignment. User can customize by defining the RAPIDJSON_ALIGN function macro., alignment. User can customize by defining the RAPIDJSON_ALIGN function macro.,
*/ */
#ifndef RAPIDJSON_ALIGN #ifndef RAPIDJSON_ALIGN
#if RAPIDJSON_64BIT == 1
#define RAPIDJSON_ALIGN(x) ((x + 7u) & ~7u)
#else
#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) #define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u)
#endif #endif
#endif
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_UINT64_C2 // RAPIDJSON_UINT64_C2
......
...@@ -276,7 +276,7 @@ TEST(Document, UTF16_Document) { ...@@ -276,7 +276,7 @@ TEST(Document, UTF16_Document) {
GenericValue< UTF16<> >& s = v[L"created_at"]; GenericValue< UTF16<> >& s = v[L"created_at"];
ASSERT_TRUE(s.IsString()); 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 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
......
...@@ -45,12 +45,22 @@ TEST(Pointer, Parse) { ...@@ -45,12 +45,22 @@ TEST(Pointer, Parse) {
EXPECT_EQ(0u, p.GetTokenCount()); 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"); Pointer p("/foo");
EXPECT_TRUE(p.IsValid()); EXPECT_TRUE(p.IsValid());
EXPECT_EQ(1u, p.GetTokenCount()); EXPECT_EQ(1u, p.GetTokenCount());
EXPECT_EQ(3u, p.GetTokens()[0].length); EXPECT_EQ(3u, p.GetTokens()[0].length);
EXPECT_STREQ("foo", p.GetTokens()[0].name); EXPECT_STREQ("foo", p.GetTokens()[0].name);
EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
} }
#if RAPIDJSON_HAS_STDSTRING #if RAPIDJSON_HAS_STDSTRING
...@@ -60,6 +70,7 @@ TEST(Pointer, Parse) { ...@@ -60,6 +70,7 @@ TEST(Pointer, Parse) {
EXPECT_EQ(1u, p.GetTokenCount()); EXPECT_EQ(1u, p.GetTokenCount());
EXPECT_EQ(3u, p.GetTokens()[0].length); EXPECT_EQ(3u, p.GetTokens()[0].length);
EXPECT_STREQ("foo", p.GetTokens()[0].name); EXPECT_STREQ("foo", p.GetTokens()[0].name);
EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
} }
#endif #endif
...@@ -69,6 +80,7 @@ TEST(Pointer, Parse) { ...@@ -69,6 +80,7 @@ TEST(Pointer, Parse) {
EXPECT_EQ(2u, p.GetTokenCount()); EXPECT_EQ(2u, p.GetTokenCount());
EXPECT_EQ(3u, p.GetTokens()[0].length); EXPECT_EQ(3u, p.GetTokens()[0].length);
EXPECT_STREQ("foo", p.GetTokens()[0].name); EXPECT_STREQ("foo", p.GetTokens()[0].name);
EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
EXPECT_EQ(1u, p.GetTokens()[1].length); EXPECT_EQ(1u, p.GetTokens()[1].length);
EXPECT_STREQ("0", p.GetTokens()[1].name); EXPECT_STREQ("0", p.GetTokens()[1].name);
EXPECT_EQ(0u, p.GetTokens()[1].index); EXPECT_EQ(0u, p.GetTokens()[1].index);
...@@ -487,6 +499,14 @@ TEST(Pointer, Assignment) { ...@@ -487,6 +499,14 @@ TEST(Pointer, Assignment) {
EXPECT_EQ(1u, q.GetTokens()[1].length); EXPECT_EQ(1u, q.GetTokens()[1].length);
EXPECT_STREQ("0", q.GetTokens()[1].name); EXPECT_STREQ("0", q.GetTokens()[1].name);
EXPECT_EQ(0u, q.GetTokens()[1].index); 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 // Static tokens
...@@ -504,6 +524,26 @@ TEST(Pointer, Assignment) { ...@@ -504,6 +524,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) { TEST(Pointer, Equality) {
EXPECT_TRUE(Pointer("/foo/0") == Pointer("/foo/0")); EXPECT_TRUE(Pointer("/foo/0") == Pointer("/foo/0"));
EXPECT_FALSE(Pointer("/foo/0") == Pointer("/foo/1")); EXPECT_FALSE(Pointer("/foo/0") == Pointer("/foo/1"));
...@@ -824,6 +864,21 @@ TEST(Pointer, Swap_NoAllocator) { ...@@ -824,6 +864,21 @@ TEST(Pointer, Swap_NoAllocator) {
EXPECT_STREQ("bar", d["foo"][1].GetString()); 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) { TEST(Pointer, CreateValueByPointer) {
Document d; Document d;
Document::AllocatorType& a = d.GetAllocator(); Document::AllocatorType& a = d.GetAllocator();
...@@ -1325,6 +1380,36 @@ TEST(Pointer, SwapValueByPointer_NoAllocator) { ...@@ -1325,6 +1380,36 @@ TEST(Pointer, SwapValueByPointer_NoAllocator) {
EXPECT_STREQ("baz", d["foo"][1].GetString()); 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) { TEST(Pointer, Ambiguity) {
{ {
Document d; Document d;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment