Commit 71c84025 authored by miloyip's avatar miloyip

Add equal-to and non-equal-to operators

parent 0f7d2dad
...@@ -556,6 +556,71 @@ public: ...@@ -556,6 +556,71 @@ public:
GenericValue& Move() { return *this; } 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 <typename T> friend bool operator==(const GenericValue& lhs, const T& rhs) { return lhs == GenericValue(rhs); }
template <typename T> friend bool operator!=(const GenericValue& lhs, const T& rhs) { return !(lhs == rhs); }
template <typename T> friend bool operator==(const T& lhs, const GenericValue& rhs) { return GenericValue(lhs) == rhs; }
template <typename T> friend bool operator!=(const T& lhs, const GenericValue& rhs) { return !(lhs == rhs); }
//@}
//!@name Type //!@name Type
//@{ //@{
...@@ -672,10 +737,9 @@ public: ...@@ -672,10 +737,9 @@ public:
MemberIterator FindMember(const GenericValue& name) { MemberIterator FindMember(const GenericValue& name) {
RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(name.IsString()); RAPIDJSON_ASSERT(name.IsString());
SizeType len = name.data_.s.length;
MemberIterator member = MemberBegin(); MemberIterator member = MemberBegin();
for ( ; member != MemberEnd(); ++member) 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; break;
return member; return member;
} }
...@@ -1214,6 +1278,13 @@ private: ...@@ -1214,6 +1278,13 @@ private:
rhs.flags_ = kNullFlag; 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_; Data data_;
unsigned flags_; unsigned flags_;
}; };
......
...@@ -44,6 +44,58 @@ TEST(Value, assignment_operator) { ...@@ -44,6 +44,58 @@ TEST(Value, assignment_operator) {
EXPECT_EQ(y.GetString(),mstr); EXPECT_EQ(y.GetString(),mstr);
} }
template <typename A, typename B>
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 <typename A, typename B>
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 <typename Value> template <typename Value>
void TestCopyFrom() { void TestCopyFrom() {
typename Value::AllocatorType a; typename Value::AllocatorType a;
......
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