diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index 82a4458d..1ec157e5 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -33,6 +33,16 @@ public: static const SizeType kInvalidIndex = ~SizeType(0); + GenericPointer() + : allocator_(), + ownAllocator_(), + nameBuffer_(), + tokens_(), + tokenCount_(), + valid_(true) + { + } + GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), @@ -55,16 +65,27 @@ public: Parse(source, length); } - GenericPointer(const Token* tokens, size_t tokenCount) : + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), - tokens_(tokens), + tokens_(const_cast(tokens)), tokenCount_(tokenCount), valid_(true) { } + GenericPointer(const GenericPointer& rhs) + : allocator_(), + ownAllocator_(), + nameBuffer_(), + tokens_(), + tokenCount_(), + valid_() + { + *this = rhs; + } + ~GenericPointer() { if (nameBuffer_) { Allocator::Free(nameBuffer_); @@ -73,6 +94,36 @@ public: RAPIDJSON_DELETE(ownAllocator_); } + GenericPointer& operator=(const GenericPointer& rhs) { + this->~GenericPointer(); + + tokenCount_ = rhs.tokenCount_; + valid_ = rhs.valid_; + + if (rhs.nameBuffer_) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + size_t nameBufferSize = tokenCount_; // null terminators + 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); + + 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 + tokens_ = rhs.tokens_; + + return *this; + } + bool IsValid() const { return valid_; } const Token* GetTokens() const { return tokens_; } @@ -192,7 +243,7 @@ private: // Create a buffer as same size of source RAPIDJSON_ASSERT(nameBuffer_ == 0); - nameBuffer_ = (Ch*)allocator_->Malloc(length); + nameBuffer_ = (Ch*)allocator_->Malloc(length * sizeof(Ch)); RAPIDJSON_ASSERT(tokens_ == 0); tokens_ = (Token*)allocator_->Malloc(length * sizeof(Token)); // Maximum possible tokens in the source @@ -266,9 +317,6 @@ private: return; } - GenericPointer(const GenericPointer& rhs); - GenericPointer& operator=(const GenericPointer& rhs); - Allocator* allocator_; Allocator* ownAllocator_; Ch* nameBuffer_; diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index dbe04c06..4d4aece4 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -166,6 +166,82 @@ TEST(Pointer, Stringify) { } } +// Construct a Pointer with static tokens, no dynamic allocation involved. +#define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, Pointer::kInvalidIndex } +#define INDEX(i) { #i, sizeof(#i) - 1, i } + +static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(0) }; // equivalent to "/foo/0" + +#undef NAME +#undef INDEX + +TEST(Pointer, ConstructorWithToken) { + Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + EXPECT_TRUE(p.IsValid()); + EXPECT_EQ(2, p.GetTokenCount()); + EXPECT_EQ(3, p.GetTokens()[0].length); + EXPECT_STREQ("foo", p.GetTokens()[0].name); + EXPECT_EQ(1, p.GetTokens()[1].length); + EXPECT_STREQ("0", p.GetTokens()[1].name); + EXPECT_EQ(0, p.GetTokens()[1].index); +} + +TEST(Pointer, CopyConstructor) { + { + Pointer p("/foo/0"); + Pointer q(p); + EXPECT_TRUE(q.IsValid()); + EXPECT_EQ(2, q.GetTokenCount()); + EXPECT_EQ(3, q.GetTokens()[0].length); + EXPECT_STREQ("foo", q.GetTokens()[0].name); + EXPECT_EQ(1, q.GetTokens()[1].length); + EXPECT_STREQ("0", q.GetTokens()[1].name); + EXPECT_EQ(0, q.GetTokens()[1].index); + } + + // Static tokens + { + Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + Pointer q(p); + EXPECT_TRUE(q.IsValid()); + EXPECT_EQ(2, q.GetTokenCount()); + EXPECT_EQ(3, q.GetTokens()[0].length); + EXPECT_STREQ("foo", q.GetTokens()[0].name); + EXPECT_EQ(1, q.GetTokens()[1].length); + EXPECT_STREQ("0", q.GetTokens()[1].name); + EXPECT_EQ(0, q.GetTokens()[1].index); + } +} + +TEST(Pointer, Assignment) { + { + Pointer p("/foo/0"); + Pointer q; + q = p; + EXPECT_TRUE(q.IsValid()); + EXPECT_EQ(2, q.GetTokenCount()); + EXPECT_EQ(3, q.GetTokens()[0].length); + EXPECT_STREQ("foo", q.GetTokens()[0].name); + EXPECT_EQ(1, q.GetTokens()[1].length); + EXPECT_STREQ("0", q.GetTokens()[1].name); + EXPECT_EQ(0, q.GetTokens()[1].index); + } + + // Static tokens + { + Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + Pointer q; + q = p; + EXPECT_TRUE(q.IsValid()); + EXPECT_EQ(2, q.GetTokenCount()); + EXPECT_EQ(3, q.GetTokens()[0].length); + EXPECT_STREQ("foo", q.GetTokens()[0].name); + EXPECT_EQ(1, q.GetTokens()[1].length); + EXPECT_STREQ("0", q.GetTokens()[1].name); + EXPECT_EQ(0, q.GetTokens()[1].index); + } +} + TEST(Pointer, Create) { Document d; EXPECT_EQ(&d, &Pointer("").Create(d, d.GetAllocator()));