Merge remote-tracking branch 'origin/master' into schema
This commit is contained in:
commit
677af55f70
@ -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.
|
||||
@ -185,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.
|
||||
|
||||
|
@ -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<uint128>(a) * static_cast<uint128>(b);
|
||||
p += k;
|
||||
*outHigh = p >> 64;
|
||||
*outHigh = static_cast<uint64_t>(p >> 64);
|
||||
return static_cast<uint64_t>(p);
|
||||
#else
|
||||
const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
|
||||
|
@ -45,7 +45,7 @@ struct DiyFp {
|
||||
uint64_t u64;
|
||||
} 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);
|
||||
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<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);
|
||||
if (l & (uint64_t(1) << 63)) // rounding
|
||||
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 static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
|
||||
|
||||
bool IsNan() const { return (u & kExponentMask) == kExponentMask && Significand() != 0; }
|
||||
bool IsInf() const { return (u & kExponentMask) == kExponentMask && Significand() == 0; }
|
||||
bool 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_;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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_;
|
||||
|
||||
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;
|
||||
}
|
||||
else
|
||||
if (rhs.nameBuffer_)
|
||||
CopyFromRaw(rhs); // Normally parsed tokens.
|
||||
else {
|
||||
tokens_ = rhs.tokens_; // User supplied const tokens.
|
||||
|
||||
nameBuffer_ = 0;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
//!@name Append token
|
||||
//@{
|
||||
|
||||
//! Append a token and return a new Pointer
|
||||
/*!
|
||||
\param token Token to be appended.
|
||||
\param allocator Allocator for the newly return Pointer.
|
||||
\return A new Pointer with appended token.
|
||||
*/
|
||||
GenericPointer Append(const Token& token, Allocator* allocator = 0) const {
|
||||
GenericPointer r;
|
||||
r.allocator_ = allocator;
|
||||
Ch *p = r.CopyFromRaw(*this, 1, (token.length + 1) * 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 <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
|
||||
//@{
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -304,7 +376,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 +445,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,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:
|
||||
//! 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.
|
||||
@ -696,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
|
||||
@ -1131,6 +1278,18 @@ typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, con
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -45,12 +45,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
|
||||
@ -60,6 +70,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
|
||||
|
||||
@ -69,6 +80,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);
|
||||
@ -487,6 +499,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
|
||||
@ -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) {
|
||||
EXPECT_TRUE(Pointer("/foo/0") == Pointer("/foo/0"));
|
||||
EXPECT_FALSE(Pointer("/foo/0") == Pointer("/foo/1"));
|
||||
@ -824,6 +864,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();
|
||||
@ -1325,6 +1380,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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user