From 0f7d2dad519a115c0405aa83f7788cec455d870f Mon Sep 17 00:00:00 2001 From: miloyip Date: Thu, 31 Jul 2014 14:22:59 +0800 Subject: [PATCH 01/17] Add 2 overloads of Erase() for removing elements of array. --- include/rapidjson/document.h | 32 +++++++++++++++++++++ test/unittest/valuetest.cpp | 54 ++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 4448600..77250a3 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -933,6 +933,38 @@ int z = a[0u].GetInt(); // This works too. data_.a.elements[--data_.a.size].~GenericValue(); return *this; } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + */ + ValueIterator Erase(ValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param pos iterator to the element to remove + \param first,last range of elements to remove + \pre IsArray() == true + \return Iterator following the last removed element. If the iterator pos refers to the last element, the End() iterator is returned. + */ + ValueIterator Erase(ValueIterator first, ValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(data_.a.elements != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + for (ValueIterator itr = first; itr != last; ++itr) + itr->~GenericValue(); + memmove(first, last, (End() - last) * sizeof(GenericValue)); + data_.a.size -= (last - first); + return first; + } + //@} //!@name Number diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index fcf36a3..8eaf84a 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -568,6 +568,60 @@ TEST(Value, Array) { EXPECT_TRUE(y.Empty()); EXPECT_EQ(0u, y.Size()); + // Erase(ValueIterator) + + // Use array of array to ensure removed elements' destructor is called. + // [[0],[1],[2],...] + for (int i = 0; i < 10; i++) + x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator); + + // Erase the first + itr = x.Erase(x.Begin()); + EXPECT_EQ(x.Begin(), itr); + EXPECT_EQ(9u, x.Size()); + for (int i = 0; i < 9; i++) + EXPECT_EQ(i + 1, x[i][0u].GetInt()); + + // Ease the last + itr = x.Erase(x.End() - 1); + EXPECT_EQ(x.End(), itr); + EXPECT_EQ(8u, x.Size()); + for (int i = 0; i < 8; i++) + EXPECT_EQ(i + 1, x[i][0u].GetInt()); + + // Erase the middle + itr = x.Erase(x.Begin() + 4); + EXPECT_EQ(x.Begin() + 4, itr); + EXPECT_EQ(7u, x.Size()); + for (int i = 0; i < 4; i++) + EXPECT_EQ(i + 1, x[i][0u].GetInt()); + for (int i = 4; i < 7; i++) + EXPECT_EQ(i + 2, x[i][0u].GetInt()); + + // Erase(ValueIterator, ValueIterator) + // Exhaustive test with all 0 <= first < n, first <= last <= n cases + const unsigned n = 10; + for (unsigned first = 0; first < n; first++) { + for (unsigned last = first; last <= n; last++) { + x.Clear(); + for (unsigned i = 0; i < n; i++) + x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator); + + itr = x.Erase(x.Begin() + first, x.Begin() + last); + if (last == n) + EXPECT_EQ(x.End(), itr); + else + EXPECT_EQ(x.Begin() + first, itr); + + size_t removeCount = last - first; + EXPECT_EQ(n - removeCount, x.Size()); + for (unsigned i = 0; i < first; i++) + EXPECT_EQ(i, x[i][0u].GetUint()); + for (unsigned i = first; i < n - removeCount; i++) + EXPECT_EQ(i + removeCount, x[i][0u].GetUint()); + } + } + // SetArray() Value z; z.SetArray(); From 71c8402549136ff12a20c040ba8b336c960a906c Mon Sep 17 00:00:00 2001 From: miloyip Date: Thu, 31 Jul 2014 18:43:59 +0800 Subject: [PATCH 02/17] Add equal-to and non-equal-to operators --- include/rapidjson/document.h | 75 +++++++++++++++++++++++++++++++++++- test/unittest/valuetest.cpp | 52 +++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 77250a3..16167ff 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -556,6 +556,71 @@ public: GenericValue& Move() { return *this; } //@} + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + bool operator==(const GenericValue& rhs) const { + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (size_t i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.GetDouble()) + return GetDouble() == rhs.GetDouble(); // May convert one operand from integer to double. + else + return data_.n.u64 == rhs.data_.n.u64; + + default: // kTrueType, kFalseType, kNullType + return true; + } + } + + //! Not-equal-to operator + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! (Not-)Equal-to operator with const C-string pointer. + friend bool operator==(const GenericValue& lhs, const Ch* rhs) { return lhs == GenericValue(StringRef(rhs)); } + friend bool operator!=(const GenericValue& lhs, const Ch* rhs) { return !(lhs == rhs); } + friend bool operator==(const Ch* lhs, const GenericValue& rhs) { return GenericValue(StringRef(lhs)) == rhs; } + friend bool operator!=(const Ch* lhs, const GenericValue& rhs) { return !(lhs == rhs); } + + //! (Not-)Equal-to operator with non-const C-string pointer. + friend bool operator==(const GenericValue& lhs, Ch* rhs) { return lhs == GenericValue(StringRef(rhs)); } + friend bool operator!=(const GenericValue& lhs, Ch* rhs) { return !(lhs == rhs); } + friend bool operator==(Ch* lhs, const GenericValue& rhs) { return GenericValue(StringRef(lhs)) == rhs; } + friend bool operator!=(Ch* lhs, const GenericValue& rhs) { return !(lhs == rhs); } + + //! (Not-)Equal-to operator with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template friend bool operator==(const GenericValue& lhs, const T& rhs) { return lhs == GenericValue(rhs); } + template friend bool operator!=(const GenericValue& lhs, const T& rhs) { return !(lhs == rhs); } + template friend bool operator==(const T& lhs, const GenericValue& rhs) { return GenericValue(lhs) == rhs; } + template friend bool operator!=(const T& lhs, const GenericValue& rhs) { return !(lhs == rhs); } + //@} + //!@name Type //@{ @@ -672,10 +737,9 @@ public: MemberIterator FindMember(const GenericValue& name) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); - SizeType len = name.data_.s.length; MemberIterator member = MemberBegin(); for ( ; member != MemberEnd(); ++member) - if (member->name.data_.s.length == len && memcmp(member->name.data_.s.str, name.data_.s.str, len * sizeof(Ch)) == 0) + if (name.StringEqual(member->name)) break; return member; } @@ -1214,6 +1278,13 @@ private: rhs.flags_ = kNullFlag; } + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + return data_.s.str == rhs.data_.s.str || // fast path for constant string + ((data_.s.length == rhs.data_.s.length) && memcmp(data_.s.str, rhs.data_.s.str, sizeof(Ch) * data_.s.length) == 0); + } + Data data_; unsigned flags_; }; diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 8eaf84a..3bd0afc 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -44,6 +44,58 @@ TEST(Value, assignment_operator) { EXPECT_EQ(y.GetString(),mstr); } +template +void TestEqual(const A& a, const B& b) { + EXPECT_TRUE (a == b); + EXPECT_FALSE(a != b); + EXPECT_TRUE (b == a); + EXPECT_FALSE(b != a); +} + +template +void TestUnequal(const A& a, const B& b) { + EXPECT_FALSE(a == b); + EXPECT_TRUE (a != b); + EXPECT_FALSE(b == a); + EXPECT_TRUE (b != a); +} + +TEST(Value, equalto_operator) { + Value::AllocatorType allocator; + Value x(kObjectType); + x.AddMember("hello", "world", allocator) + .AddMember("t", Value(true).Move(), allocator) + .AddMember("f", Value(false).Move(), allocator) + .AddMember("n", Value(kNullType).Move(), allocator) + .AddMember("i", 123, allocator) + .AddMember("pi", 3.14, allocator) + .AddMember("a", Value(kArrayType).Move().PushBack(1, allocator).PushBack(2, allocator).PushBack(3, allocator), allocator); + + // Test templated operator==() and operator!=() + TestEqual(x["hello"], "world"); + const char* cc = "world"; + TestEqual(x["hello"], cc); + char* c = strdup("world"); + TestEqual(x["hello"], c); + free(c); + + TestEqual(x["t"], true); + TestEqual(x["f"], false); + TestEqual(x["i"], 123); + TestEqual(x["pi"], 3.14); + + // Test operator==() + Value y; + y.CopyFrom(x, allocator); + TestEqual(x, y); + + // Swapping member order should be fine. + y.RemoveMember("t"); + TestUnequal(x, y); + y.AddMember("t", Value(true).Move(), allocator); + TestEqual(x, y); +} + template void TestCopyFrom() { typename Value::AllocatorType a; From 9ab2621e855a9abb8377520b42d6dfddcd49a8e1 Mon Sep 17 00:00:00 2001 From: miloyip Date: Thu, 31 Jul 2014 18:53:17 +0800 Subject: [PATCH 03/17] Add Erase(ConstValueIterator) and Erase(ConstValueIterator,ConstValueIterator) As std::vector::erase() overloads added in C++11 --- include/rapidjson/document.h | 8 ++++++++ test/unittest/valuetest.cpp | 10 +++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 16167ff..6a0e1a0 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1008,6 +1008,10 @@ int z = a[0u].GetInt(); // This works too. return Erase(pos, pos + 1); } + ValueIterator Erase(ConstValueIterator pos) { + return Erase(const_cast(pos)); + } + //! Remove elements in the range [first, last) of the array. /*! \param pos iterator to the element to remove @@ -1029,6 +1033,10 @@ int z = a[0u].GetInt(); // This works too. return first; } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + return Erase(const_cast(first), const_cast(last)); + } + //@} //!@name Number diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 3bd0afc..46f9a59 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -628,11 +628,11 @@ TEST(Value, Array) { x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator); // Erase the first - itr = x.Erase(x.Begin()); - EXPECT_EQ(x.Begin(), itr); - EXPECT_EQ(9u, x.Size()); + itr = x.Erase(y.Begin()); // const iteator is also OK + EXPECT_EQ(y.Begin(), itr); + EXPECT_EQ(9u, y.Size()); for (int i = 0; i < 9; i++) - EXPECT_EQ(i + 1, x[i][0u].GetInt()); + EXPECT_EQ(i + 1, y[i][0u].GetInt()); // Ease the last itr = x.Erase(x.End() - 1); @@ -659,7 +659,7 @@ TEST(Value, Array) { for (unsigned i = 0; i < n; i++) x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator); - itr = x.Erase(x.Begin() + first, x.Begin() + last); + itr = x.Erase(y.Begin() + first, y.Begin() + last); // const iterator is also OK if (last == n) EXPECT_EQ(x.End(), itr); else From 71ae5660eded26b3ef637f220a48abb412dde229 Mon Sep 17 00:00:00 2001 From: miloyip Date: Thu, 31 Jul 2014 18:56:57 +0800 Subject: [PATCH 04/17] Revert "Add Erase(ConstValueIterator) and Erase(ConstValueIterator,ConstValueIterator)" This reverts commit 9ab2621e855a9abb8377520b42d6dfddcd49a8e1. --- include/rapidjson/document.h | 8 -------- test/unittest/valuetest.cpp | 10 +++++----- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 6a0e1a0..16167ff 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1008,10 +1008,6 @@ int z = a[0u].GetInt(); // This works too. return Erase(pos, pos + 1); } - ValueIterator Erase(ConstValueIterator pos) { - return Erase(const_cast(pos)); - } - //! Remove elements in the range [first, last) of the array. /*! \param pos iterator to the element to remove @@ -1033,10 +1029,6 @@ int z = a[0u].GetInt(); // This works too. return first; } - ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { - return Erase(const_cast(first), const_cast(last)); - } - //@} //!@name Number diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 46f9a59..3bd0afc 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -628,11 +628,11 @@ TEST(Value, Array) { x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator); // Erase the first - itr = x.Erase(y.Begin()); // const iteator is also OK - EXPECT_EQ(y.Begin(), itr); - EXPECT_EQ(9u, y.Size()); + itr = x.Erase(x.Begin()); + EXPECT_EQ(x.Begin(), itr); + EXPECT_EQ(9u, x.Size()); for (int i = 0; i < 9; i++) - EXPECT_EQ(i + 1, y[i][0u].GetInt()); + EXPECT_EQ(i + 1, x[i][0u].GetInt()); // Ease the last itr = x.Erase(x.End() - 1); @@ -659,7 +659,7 @@ TEST(Value, Array) { for (unsigned i = 0; i < n; i++) x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator); - itr = x.Erase(y.Begin() + first, y.Begin() + last); // const iterator is also OK + itr = x.Erase(x.Begin() + first, x.Begin() + last); if (last == n) EXPECT_EQ(x.End(), itr); else From 11d31aaa7be700e50f97819aa887310460e38484 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 30 Jul 2014 23:57:31 +0200 Subject: [PATCH 05/17] fix typo --- include/rapidjson/document.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 16167ff..f1fa850 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -967,7 +967,7 @@ int z = a[0u].GetInt(); // This works too. return (*this).template PushBack(value, allocator); } - //! Append a primitive value at the end of the array(.) + //! Append a primitive value at the end of the array. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t \param value Value of primitive type T to be appended. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). From 6ed9934143bdd5b144b4e38b73950f3ef25b6b1e Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 31 Jul 2014 01:45:26 +0200 Subject: [PATCH 06/17] GenericValue: document Array iterator functions --- include/rapidjson/document.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index f1fa850..250c33e 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -919,9 +919,16 @@ int z = a[0u].GetInt(); // This works too. const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } //! Element iterator + /*! \pre IsArray() == true */ ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ ConstValueIterator End() const { return const_cast(*this).End(); } //! Request the array to have enough capacity to store elements. From 9a9c6d68109e79c8dbf836e91a6c0302af29105d Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 31 Jul 2014 11:28:22 +0200 Subject: [PATCH 07/17] GenericValue: improve Erase documentation --- include/rapidjson/document.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 250c33e..b7e35c8 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1008,7 +1008,7 @@ int z = a[0u].GetInt(); // This works too. //! Remove an element of array by iterator. /*! \param pos iterator to the element to remove - \pre IsArray() == true + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. */ ValueIterator Erase(ValueIterator pos) { @@ -1017,10 +1017,10 @@ int z = a[0u].GetInt(); // This works too. //! Remove elements in the range [first, last) of the array. /*! - \param pos iterator to the element to remove - \param first,last range of elements to remove - \pre IsArray() == true - \return Iterator following the last removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. */ ValueIterator Erase(ValueIterator first, ValueIterator last) { RAPIDJSON_ASSERT(IsArray()); From 59fee54f9de93ca86af14b10efda1295af686202 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 31 Jul 2014 11:29:05 +0200 Subject: [PATCH 08/17] GenericValue::Erase: pass ConstValueIterators --- include/rapidjson/document.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index b7e35c8..fe20de6 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1011,7 +1011,7 @@ int z = a[0u].GetInt(); // This works too. \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. */ - ValueIterator Erase(ValueIterator pos) { + ValueIterator Erase(ConstValueIterator pos) { return Erase(pos, pos + 1); } @@ -1022,18 +1022,19 @@ int z = a[0u].GetInt(); // This works too. \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() \return Iterator following the last removed element. */ - ValueIterator Erase(ValueIterator first, ValueIterator last) { + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(data_.a.size > 0); RAPIDJSON_ASSERT(data_.a.elements != 0); RAPIDJSON_ASSERT(first >= Begin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= End()); - for (ValueIterator itr = first; itr != last; ++itr) + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) itr->~GenericValue(); - memmove(first, last, (End() - last) * sizeof(GenericValue)); + memmove(pos, last, (End() - last) * sizeof(GenericValue)); data_.a.size -= (last - first); - return first; + return pos; } //@} From c0fcdcc478664ffcb21bd76c4f0e55a06468d8c5 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 31 Jul 2014 11:21:18 +0200 Subject: [PATCH 09/17] GenericValue: use generated assignment/destructor for GenericMember --- include/rapidjson/document.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index fe20de6..a52d986 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -464,8 +464,7 @@ public: case kObjectFlag: for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { - m->name.~GenericValue(); - m->value.~GenericValue(); + m->~GenericMember(); } Allocator::Free(data_.o.members); break; @@ -861,13 +860,11 @@ public: MemberIterator last(data_.o.members + (data_.o.size - 1)); if (data_.o.size > 1 && m != last) { // Move the last one to this place - m->name = last->name; - m->value = last->value; + *m = *last; } else { // Only one left, just destroy - m->name.~GenericValue(); - m->value.~GenericValue(); + m->~GenericMember(); } --data_.o.size; return m; From 96af95c945970d935e5f9fdd57e6ece5d071e880 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 31 Jul 2014 12:47:02 +0200 Subject: [PATCH 10/17] GenericValue: add Erase function for object members --- include/rapidjson/document.h | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index a52d986..e7413eb 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -850,6 +850,7 @@ public: /*! \param m member iterator (obtained by FindMember() or MemberBegin()). \return the new iterator after removal. \note Removing member is implemented by moving the last member. So the ordering of members is changed. + \note Use \ref Erase(ConstMemberIterator) instead, if you need to rely on a stable member ordering. */ MemberIterator RemoveMember(MemberIterator m) { RAPIDJSON_ASSERT(IsObject()); @@ -870,6 +871,40 @@ public: return m; } + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note Other than \ref RemoveMember(MemberIterator), this function preserves the ordering of the members. + */ + MemberIterator Erase(ConstMemberIterator pos) { + return Erase(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note Other than \ref RemoveMember(MemberIterator), this function preserves the ordering of the members. + */ + MemberIterator Erase(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; ConstMemberIterator(itr) != last; ++itr) + itr->~Member(); + memmove(&*pos, &*last, (ConstMemberIterator(MemberEnd()) - last) * sizeof(Member)); + data_.o.size -= (last - first); + return pos; + } + //@} //!@name Array From b98852e1656207fcd7573eac1c8718431be1e88f Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 31 Jul 2014 12:47:41 +0200 Subject: [PATCH 11/17] GenericValue: add tests for Erase(ConstMemberIterator[, ConstMemberIterator]) --- test/unittest/valuetest.cpp | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 3bd0afc..9991ddb 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -789,6 +789,72 @@ TEST(Value, Object) { EXPECT_TRUE(x.MemberBegin() == x.MemberEnd()); + // Erase(ConstMemberIterator) + + // Use array members to ensure removed elements' destructor is called. + // { "a": [0], "b": [1],[2],...] + const char keys[][2] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" }; + for (int i = 0; i < 10; i++) + x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator); + + // Erase the first + itr = x.Erase(x.MemberBegin()); + EXPECT_FALSE(x.HasMember(keys[0])); + EXPECT_EQ(x.MemberBegin(), itr); + EXPECT_EQ(9u, x.MemberEnd() - x.MemberBegin()); + for (; itr != x.MemberEnd(); ++itr) { + int i = (itr - x.MemberBegin()) + 1; + EXPECT_STREQ(itr->name.GetString(), keys[i]); + EXPECT_EQ(i, itr->value[0u].GetInt()); + } + + // Erase the last + itr = x.Erase(x.MemberEnd() - 1); + EXPECT_FALSE(x.HasMember(keys[9])); + EXPECT_EQ(x.MemberEnd(), itr); + EXPECT_EQ(8u, x.MemberEnd() - x.MemberBegin()); + for (; itr != x.MemberEnd(); ++itr) { + int i = (itr - x.MemberBegin()) + 1; + EXPECT_STREQ(itr->name.GetString(), keys[i]); + EXPECT_EQ(i, itr->value[0u].GetInt()); + } + + // Erase the middle + itr = x.Erase(x.MemberBegin() + 4); + EXPECT_FALSE(x.HasMember(keys[5])); + EXPECT_EQ(x.MemberBegin() + 4, itr); + EXPECT_EQ(7u, x.MemberEnd() - x.MemberBegin()); + for (; itr != x.MemberEnd(); ++itr) { + int i = (itr - x.MemberBegin()); + i += (i<4) ? 1 : 2; + EXPECT_STREQ(itr->name.GetString(), keys[i]); + EXPECT_EQ(i, itr->value[0u].GetInt()); + } + + // Erase(ConstMemberIterator, ConstMemberIterator) + // Exhaustive test with all 0 <= first < n, first <= last <= n cases + const unsigned n = 10; + for (unsigned first = 0; first < n; first++) { + for (unsigned last = first; last <= n; last++) { + Value(kObjectType).Swap(x); + for (unsigned i = 0; i < n; i++) + x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator); + + itr = x.Erase(x.MemberBegin() + first, x.MemberBegin() + last); + if (last == n) + EXPECT_EQ(x.MemberEnd(), itr); + else + EXPECT_EQ(x.MemberBegin() + first, itr); + + size_t removeCount = last - first; + EXPECT_EQ(n - removeCount, size_t(x.MemberEnd() - x.MemberBegin()); + for (unsigned i = 0; i < first; i++) + EXPECT_EQ(i, x[keys[i]][0u].GetUint()); + for (unsigned i = first; i < n - removeCount; i++) + EXPECT_EQ(i + removeCount, x[keys[i+removeCount]][0u].GetUint()); + } + } + // SetObject() Value z; z.SetObject(); From 82b32a93d2a9a8df5780672a62a5c82b45b3dfdf Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 31 Jul 2014 13:06:28 +0200 Subject: [PATCH 12/17] fix missing bracket --- test/unittest/valuetest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 9991ddb..4561b8a 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -847,7 +847,7 @@ TEST(Value, Object) { EXPECT_EQ(x.MemberBegin() + first, itr); size_t removeCount = last - first; - EXPECT_EQ(n - removeCount, size_t(x.MemberEnd() - x.MemberBegin()); + EXPECT_EQ(n - removeCount, size_t(x.MemberEnd() - x.MemberBegin())); for (unsigned i = 0; i < first; i++) EXPECT_EQ(i, x[keys[i]][0u].GetUint()); for (unsigned i = first; i < n - removeCount; i++) From afe59a0db16703dc5e62f3a821e5192be4973ece Mon Sep 17 00:00:00 2001 From: miloyip Date: Thu, 31 Jul 2014 19:08:37 +0800 Subject: [PATCH 13/17] Makes `StringEqual()` more safe by always compares lengths. --- include/rapidjson/document.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 16167ff..c231385 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1281,8 +1281,9 @@ private: bool StringEqual(const GenericValue& rhs) const { RAPIDJSON_ASSERT(IsString()); RAPIDJSON_ASSERT(rhs.IsString()); - return data_.s.str == rhs.data_.s.str || // fast path for constant string - ((data_.s.length == rhs.data_.s.length) && memcmp(data_.s.str, rhs.data_.s.str, sizeof(Ch) * data_.s.length) == 0); + return data_.s.length == rhs.data_.s.length && + (data_.s.str == rhs.data_.s.str // fast path for constant string + || memcmp(data_.s.str, rhs.data_.s.str, sizeof(Ch) * data_.s.length) == 0); } Data data_; From fc6d7a59dca318d1742119b9cd76c89a76a71baa Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 31 Jul 2014 13:16:56 +0200 Subject: [PATCH 14/17] GenericValue: rename Erase(ConstMemberIterator) to EraseMember --- include/rapidjson/document.h | 8 ++++---- test/unittest/valuetest.cpp | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index e7413eb..b08a636 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -850,7 +850,7 @@ public: /*! \param m member iterator (obtained by FindMember() or MemberBegin()). \return the new iterator after removal. \note Removing member is implemented by moving the last member. So the ordering of members is changed. - \note Use \ref Erase(ConstMemberIterator) instead, if you need to rely on a stable member ordering. + \note Use \ref EraseMember(ConstMemberIterator) instead, if you need to rely on a stable member ordering. */ MemberIterator RemoveMember(MemberIterator m) { RAPIDJSON_ASSERT(IsObject()); @@ -878,8 +878,8 @@ public: If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. \note Other than \ref RemoveMember(MemberIterator), this function preserves the ordering of the members. */ - MemberIterator Erase(ConstMemberIterator pos) { - return Erase(pos, pos +1); + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); } //! Remove members in the range [first, last) from an object. @@ -889,7 +889,7 @@ public: \return Iterator following the last removed element. \note Other than \ref RemoveMember(MemberIterator), this function preserves the ordering of the members. */ - MemberIterator Erase(ConstMemberIterator first, ConstMemberIterator last) { + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); RAPIDJSON_ASSERT(data_.o.members != 0); diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 4561b8a..8655c85 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -789,7 +789,7 @@ TEST(Value, Object) { EXPECT_TRUE(x.MemberBegin() == x.MemberEnd()); - // Erase(ConstMemberIterator) + // EraseMember(ConstMemberIterator) // Use array members to ensure removed elements' destructor is called. // { "a": [0], "b": [1],[2],...] @@ -798,7 +798,7 @@ TEST(Value, Object) { x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator); // Erase the first - itr = x.Erase(x.MemberBegin()); + itr = x.EraseMember(x.MemberBegin()); EXPECT_FALSE(x.HasMember(keys[0])); EXPECT_EQ(x.MemberBegin(), itr); EXPECT_EQ(9u, x.MemberEnd() - x.MemberBegin()); @@ -809,7 +809,7 @@ TEST(Value, Object) { } // Erase the last - itr = x.Erase(x.MemberEnd() - 1); + itr = x.EraseMember(x.MemberEnd() - 1); EXPECT_FALSE(x.HasMember(keys[9])); EXPECT_EQ(x.MemberEnd(), itr); EXPECT_EQ(8u, x.MemberEnd() - x.MemberBegin()); @@ -820,7 +820,7 @@ TEST(Value, Object) { } // Erase the middle - itr = x.Erase(x.MemberBegin() + 4); + itr = x.EraseMember(x.MemberBegin() + 4); EXPECT_FALSE(x.HasMember(keys[5])); EXPECT_EQ(x.MemberBegin() + 4, itr); EXPECT_EQ(7u, x.MemberEnd() - x.MemberBegin()); @@ -831,7 +831,7 @@ TEST(Value, Object) { EXPECT_EQ(i, itr->value[0u].GetInt()); } - // Erase(ConstMemberIterator, ConstMemberIterator) + // EraseMember(ConstMemberIterator, ConstMemberIterator) // Exhaustive test with all 0 <= first < n, first <= last <= n cases const unsigned n = 10; for (unsigned first = 0; first < n; first++) { @@ -840,7 +840,7 @@ TEST(Value, Object) { for (unsigned i = 0; i < n; i++) x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator); - itr = x.Erase(x.MemberBegin() + first, x.MemberBegin() + last); + itr = x.EraseMember(x.MemberBegin() + first, x.MemberBegin() + last); if (last == n) EXPECT_EQ(x.MemberEnd(), itr); else From 9892847021d6602f89b2decf00b198b65a445ac8 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Thu, 31 Jul 2014 23:58:52 +0800 Subject: [PATCH 15/17] Update documents about erase member/elements, also added some time complexity information. --- doc/tutorial.md | 38 +++++++++++++++++++---- include/rapidjson/document.h | 58 ++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 12 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index ee5f873..66c9a50 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -222,11 +222,26 @@ The correct length of the value `"a\u0000b"` is 3. But `strlen()` returns 1. Besides, `std::string` also support a constructor: ~~~~~~~~~~cpp -string( const char* s, size_type count); +string(const char* s, size_t count); ~~~~~~~~~~ which accepts the length of string as parameter. This constructor supports storing null character within the string, and should also provide better performance. +## Comparing values + +You can use `==` and `!=` to compare values. Two values are equal if and only if they are have same type and contents. You can also compare values with primitive types. Here is an example. + +~~~~~~~~~~cpp +if (document["hello"] == document["n"]) /*...*/; // Compare values +if (document["hello"] == "world") /*...*/; // Compare value with literal string +if (document["i"] != 123) /*...*/; // Compare with integers +if (document["pi"] != 3.14) /*...*/; // Compare with double. +~~~~~~~~~~ + +Array and object must be `Value`s in order to be compared. They are equal if and only if their whole subtrees are equal. + +Note that, currently if an object contains duplicated named member, comparing equality with any object is always `false`. + # Create/Modify Values {#CreateModifyValues} There are several ways to create values. After a DOM tree is created and/or modified, it can be saved as JSON again using `Writer`. @@ -380,6 +395,8 @@ Value with array type provides similar APIs as `std::vector`. * `Value& PushBack(Value&, Allocator&)` * `template GenericValue& PushBack(T, Allocator&)` * `Value& PopBack()` +* `ValueIterator Erase(ConstValueIterator pos)` +* `ValueIterator Erase(ConstValueIterator first, ConstValueIterator last)` Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore require an allocator. @@ -411,12 +428,11 @@ contact.PushBack(val, document.GetAllocator()); ~~~~~~~~~~ ## Modify Object {#ModifyObject} -Object is a collection of key-value pairs. Each key must be a string value. The way to manipulating object is to add/remove members: +Object is a collection of key-value pairs. Each key must be a string value. The way to manipulating object is to add members: * `Value& AddMember(Value&, Value&, Allocator& allocator)` * `Value& AddMember(StringRefType, Value&, Allocator&)` * `template Value& AddMember(StringRefType, T value, Allocator&)` -* `bool RemoveMember(const Ch*)` Here is an example. @@ -442,6 +458,16 @@ Value val(42); // some value contact.AddMember(key, val, document.GetAllocator()); ~~~~~~~~~~ +For removing members, there are several choices: + +* `bool RemoveMember(const Ch* name)`: Remove a member by search its name (linear time complexity). +* `bool RemoveMember(const Value& name)`: same as above but `name` is a Value. +* `MemberIterator RemoveMember(MemberIterator)`: Remove a member by iterator (_constant_ time complexity). +* `MemberIterator EraseMember(MemberIterator)`: similar to the above but it preserves order of members (linear time complexity). +* `MemberIterator EraseMember(MemberIterator first, MemberIterator last)`: remove a range of members, preserves order (linear time complexity). + +`MemberIterator RemoveMember(MemberIterator)` uses a "move-last" trick to archive constant time complexity. Basically the member at iterator is destructed, and then the last element is moved to that position. So the order of the remaining members are changed. + ## Deep Copy Value {#DeepCopyValue} If we really need to copy a DOM tree, we can use two APIs for deep copy: constructor with allocator, and `CopyFrom()`. @@ -458,8 +484,8 @@ assert(v1.IsNull() && v2.IsNull()); // both moved to d v2.CopyFrom(d, a); // copy whole document to v2 assert(d.IsArray() && d.Size() == 2); // d untouched -v1.SetObject().AddMember( "array", v2, a ); -d.PushBack(v1,a); +v1.SetObject().AddMember("array", v2, a); +d.PushBack(v1, a); ~~~~~~~~~~ ## Swap Values {#SwapValues} @@ -474,7 +500,7 @@ assert(a.IsString()); assert(b.IsInt()); ~~~~~~~~~~ -Swapping two DOM trees is fast (constant time), despite the complexity of the tress. +Swapping two DOM trees is fast (constant time), despite the complexity of the trees. # What's next {#WhatsNext} diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 73292e7..4b15b5b 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -558,6 +558,10 @@ public: //!@name Equal-to and not-equal-to operators //@{ //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + */ bool operator==(const GenericValue& rhs) const { if (GetType() != rhs.GetType()) return false; @@ -572,7 +576,6 @@ public: return false; } return true; - case kArrayType: if (data_.a.size != rhs.data_.a.size) @@ -669,7 +672,8 @@ public: \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. Since 0.2, if the name is not correct, it will assert. If user is unsure whether a member exists, user should use HasMember() first. - A better approach is to use the now public FindMember(). + A better approach is to use FindMember(). + \note Linear time complexity. */ GenericValue& operator[](const Ch* name) { GenericValue n(StringRef(name)); @@ -706,16 +710,28 @@ public: //! Check whether a member exists in the 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 Ch* name) const { return FindMember(name) != MemberEnd(); } - // This version is faster because it does not need a StrLen(). - // It can also handle string with null character. + //! 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. + \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 GenericValue& name) const { return FindMember(name) != MemberEnd(); } //! Find member by name. /*! + \param name Member name to be searched. \pre IsObject() == true \return Iterator to member, if it exists. Otherwise returns \ref MemberEnd(). @@ -723,6 +739,7 @@ public: \note Earlier versions of Rapidjson returned a \c NULL pointer, in case the requested member doesn't exist. For consistency with e.g. \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. */ MemberIterator FindMember(const Ch* name) { GenericValue n(StringRef(name)); @@ -731,8 +748,19 @@ public: ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } - // This version is faster because it does not need a StrLen(). - // It can also handle string with null character. + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ MemberIterator FindMember(const GenericValue& name) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); @@ -752,6 +780,7 @@ public: \note The ownership of \c name and \c value will be transferred to this object on success. \pre IsObject() && name.IsString() \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. */ GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsObject()); @@ -783,6 +812,7 @@ public: \note The ownership of \c value will be transferred to this object on success. \pre IsObject() \post value.IsNull() + \note Amortized Constant time complexity. */ GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { GenericValue n(name); @@ -796,6 +826,7 @@ public: \return The value itself for fluent API. \pre IsObject() \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. */ GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { GenericValue v(value); @@ -817,6 +848,7 @@ public: AddMember(StringRefType, StringRefType, Allocator&). All other pointer types would implicitly convert to \c bool, use an explicit cast instead, if needed. + \note Amortized Constant time complexity. */ template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) @@ -830,6 +862,7 @@ public: /*! \param name Name of member to be removed. \return Whether the member existed. \note Removing member is implemented by moving the last member. So the ordering of members is changed. + \note Linear time complexity. */ bool RemoveMember(const Ch* name) { GenericValue n(StringRef(name)); @@ -851,6 +884,7 @@ public: \return the new iterator after removal. \note Removing member is implemented by moving the last member. So the ordering of members is changed. \note Use \ref EraseMember(ConstMemberIterator) instead, if you need to rely on a stable member ordering. + \note Constant time complexity. */ MemberIterator RemoveMember(MemberIterator m) { RAPIDJSON_ASSERT(IsObject()); @@ -877,6 +911,7 @@ public: \return Iterator following the removed element. If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. \note Other than \ref RemoveMember(MemberIterator), this function preserves the ordering of the members. + \note Linear time complexity. */ MemberIterator EraseMember(ConstMemberIterator pos) { return EraseMember(pos, pos +1); @@ -888,6 +923,7 @@ public: \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() \return Iterator following the last removed element. \note Other than \ref RemoveMember(MemberIterator), this function preserves the ordering of the members. + \note Linear time complexity. */ MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { RAPIDJSON_ASSERT(IsObject()); @@ -925,6 +961,7 @@ public: //! Remove all elements in the array. /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. */ void Clear() { RAPIDJSON_ASSERT(IsArray()); @@ -967,6 +1004,7 @@ int z = a[0u].GetInt(); // This works too. /*! \param newCapacity The capacity that the array at least need to have. \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. + \note Linear time complexity. */ GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { RAPIDJSON_ASSERT(IsArray()); @@ -985,6 +1023,7 @@ int z = a[0u].GetInt(); // This works too. \return The value itself for fluent API. \note The ownership of \c value will be transferred to this array on success. \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. */ GenericValue& PushBack(GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsArray()); @@ -1000,6 +1039,7 @@ int z = a[0u].GetInt(); // This works too. \pre IsArray() == true \return The value itself for fluent API. \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. \see GenericStringRef */ GenericValue& PushBack(StringRefType value, Allocator& allocator) { @@ -1021,6 +1061,7 @@ int z = a[0u].GetInt(); // This works too. PushBack(StringRefType, Allocator&). All other pointer types would implicitly convert to \c bool, use an explicit cast instead, if needed. + \note Amortized constant time complexity. */ template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) @@ -1030,6 +1071,9 @@ int z = a[0u].GetInt(); // This works too. } //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ GenericValue& PopBack() { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(!Empty()); @@ -1042,6 +1086,7 @@ int z = a[0u].GetInt(); // This works too. \param pos iterator to the element to remove \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. */ ValueIterator Erase(ConstValueIterator pos) { return Erase(pos, pos + 1); @@ -1053,6 +1098,7 @@ int z = a[0u].GetInt(); // This works too. \param last iterator following the last element to remove \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() \return Iterator following the last removed element. + \note Linear time complexity. */ ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { RAPIDJSON_ASSERT(IsArray()); From c3ad232c3e7cbfd8450184df09879705291d0958 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 1 Aug 2014 00:01:06 +0800 Subject: [PATCH 16/17] Fixes size type in Value::operator==() --- include/rapidjson/document.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 4b15b5b..b3e7613 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -580,7 +580,7 @@ public: case kArrayType: if (data_.a.size != rhs.data_.a.size) return false; - for (size_t i = 0; i < data_.a.size; i++) + for (SizeType i = 0; i < data_.a.size; i++) if ((*this)[i] != rhs[i]) return false; return true; From b52e07822e85ffff11012ce1b83423369ecbc935 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 1 Aug 2014 00:04:31 +0800 Subject: [PATCH 17/17] Try to add erase-remove idiom in unit test but fail to compile on VC2013. --- test/unittest/valuetest.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 8655c85..2aa6379 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1,5 +1,6 @@ #include "unittest.h" #include "rapidjson/document.h" +#include using namespace rapidjson; @@ -674,6 +675,22 @@ TEST(Value, Array) { } } + // Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed. +#if 0 + // http://en.wikipedia.org/wiki/Erase-remove_idiom + x.Clear(); + for (int i = 0; i < 10; i++) + if (i % 2 == 0) + x.PushBack(i, allocator); + else + x.PushBack(Value(kNullType).Move(), allocator); + + x.Erase(std::remove(x.Begin(), x.End(), Value(kNullType)), x.End()); + EXPECT_EQ(5u, x.Size()); + for (int i = 0; i < 5; i++) + EXPECT_EQ(i * 2, x[i]); +#endif + // SetArray() Value z; z.SetArray();