Commit 5fc59cb6 authored by Milo Yip's avatar Milo Yip

Merge branch 'master' into issue158_parsestdstring

parents 2358f82e ff12c04a
......@@ -2,17 +2,17 @@
## Status: experimental, shall be included in v1.1
JSON Schema is a draft standard for describing format of JSON. The schema itself is also a JSON. By validating a JSON with JSON Schema, your code can safely access the DOM without manually checking types, or whether a key exists, etc. It can also ensure that the serialized JSON conform to a specified schema.
JSON Schema is a draft standard for describing the format of JSON data. The schema itself is also JSON data. By validating a JSON structure with JSON Schema, your code can safely access the DOM without manually checking types, or whether a key exists, etc. It can also ensure that the serialized JSON conform to a specified schema.
RapidJSON implemented a JSON Schema validator for [JSON Schema Draft v4](http://json-schema.org/documentation.html). If you do not familiar with JSON Schema, you may refer to [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/).
RapidJSON implemented a JSON Schema validator for [JSON Schema Draft v4](http://json-schema.org/documentation.html). If you are not familiar with JSON Schema, you may refer to [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/).
[TOC]
## Basic Usage
First of all, you need to parse a JSON Schema into `Document`, and then compile the `Document` into `SchemaDocument`.
First of all, you need to parse a JSON Schema into `Document`, and then compile the `Document` into a `SchemaDocument`.
Secondly, construct a `SchemaValidator` with the `SchedmaDocument`. It is similar to a `Writer` in the sense of handling SAX events. So, you can use `document.Accept(validator)` to validate a document, and then check the validity.
Secondly, construct a `SchemaValidator` with the `SchemaDocument`. It is similar to a `Writer` in the sense of handling SAX events. So, you can use `document.Accept(validator)` to validate a document, and then check the validity.
~~~cpp
#include "rapidjson/schema.h"
......@@ -54,7 +54,7 @@ Some notes:
## Validation during parsing/serialization
Differ to most JSON Schema validator implementations, RapidJSON provides a SAX-based schema validator. Therefore, you can parse a JSON from a stream while validating it on the fly. If the validator encounters a JSON value that invalidates the supplied schema, the parsing will be terminated immediately. This design is especially useful for parsing large JSON files.
Unlike most JSON Schema validator implementations, RapidJSON provides a SAX-based schema validator. Therefore, you can parse a JSON from a stream while validating it on the fly. If the validator encounters a JSON value that invalidates the supplied schema, the parsing will be terminated immediately. This design is especially useful for parsing large JSON files.
### DOM parsing
......@@ -111,7 +111,7 @@ if (!reader.Parse(stream, validator)) {
}
~~~
This is exactly the method used in [schemavalidator](example/schemavalidator/schemavalidator.cpp) example. The distinct advantage is low memory usage, no matter how big the JSON was (the memory usage depends on the complexity of the schema).
This is exactly the method used in the [schemavalidator](example/schemavalidator/schemavalidator.cpp) example. The distinct advantage is low memory usage, no matter how big the JSON was (the memory usage depends on the complexity of the schema).
If you need to handle the SAX events further, then you need to use the template class `GenericSchemaValidator` to set the output handler of the validator:
......@@ -213,7 +213,7 @@ For C++11 compiler, it is also possible to use the `std::regex` by defining `RAP
## Performance
Most C++ JSON libraries have not yet supporting JSON Schema. So we tried to evaluate the performance of RapidJSON's JSON Schema validator according to [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark), which tests 11 JavaScript libraries running on Node.js.
Most C++ JSON libraries do not yet support JSON Schema. So we tried to evaluate the performance of RapidJSON's JSON Schema validator according to [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark), which tests 11 JavaScript libraries running on Node.js.
That benchmark runs validations on [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite), in which some test suites and tests are excluded. We made the same benchmarking procedure in [`schematest.cpp`](test/perftest/schematest.cpp).
......
......@@ -297,7 +297,7 @@ struct GenericStringRef {
*/
#endif
explicit GenericStringRef(const CharType* str)
: s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); }
: s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); }
//! Create constant string reference from pointer and length
#ifndef __clang__ // -Wdocumentation
......@@ -309,7 +309,7 @@ struct GenericStringRef {
*/
#endif
GenericStringRef(const CharType* str, SizeType len)
: s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); }
: s(str), length(len) { RAPIDJSON_ASSERT(s != 0); }
//! implicit conversion to plain CharType pointer
operator const Ch *() const { return s; }
......@@ -395,6 +395,127 @@ template <typename T> struct IsGenericValue : IsGenericValueImpl<T>::Type {};
} // namespace internal
///////////////////////////////////////////////////////////////////////////////
// TypeHelper
namespace internal {
template <typename ValueType, typename T>
struct TypeHelper {};
template<typename ValueType>
struct TypeHelper<ValueType, bool> {
static bool Is(const ValueType& v) { return v.IsBool(); }
static bool Get(const ValueType& v) { return v.GetBool(); }
static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); }
static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); }
};
template<typename ValueType>
struct TypeHelper<ValueType, int> {
static bool Is(const ValueType& v) { return v.IsInt(); }
static int Get(const ValueType& v) { return v.GetInt(); }
static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); }
static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); }
};
template<typename ValueType>
struct TypeHelper<ValueType, unsigned> {
static bool Is(const ValueType& v) { return v.IsUint(); }
static unsigned Get(const ValueType& v) { return v.GetUint(); }
static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); }
static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); }
};
template<typename ValueType>
struct TypeHelper<ValueType, int64_t> {
static bool Is(const ValueType& v) { return v.IsInt64(); }
static int64_t Get(const ValueType& v) { return v.GetInt64(); }
static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); }
static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); }
};
template<typename ValueType>
struct TypeHelper<ValueType, uint64_t> {
static bool Is(const ValueType& v) { return v.IsUint64(); }
static uint64_t Get(const ValueType& v) { return v.GetUint64(); }
static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); }
static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); }
};
template<typename ValueType>
struct TypeHelper<ValueType, double> {
static bool Is(const ValueType& v) { return v.IsDouble(); }
static double Get(const ValueType& v) { return v.GetDouble(); }
static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); }
static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); }
};
template<typename ValueType>
struct TypeHelper<ValueType, float> {
static bool Is(const ValueType& v) { return v.IsFloat(); }
static float Get(const ValueType& v) { return v.GetFloat(); }
static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); }
static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); }
};
template<typename ValueType>
struct TypeHelper<ValueType, const typename ValueType::Ch*> {
typedef const typename ValueType::Ch* StringType;
static bool Is(const ValueType& v) { return v.IsString(); }
static StringType Get(const ValueType& v) { return v.GetString(); }
static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); }
static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
};
#if RAPIDJSON_HAS_STDSTRING
template<typename ValueType>
struct TypeHelper<ValueType, std::basic_string<typename ValueType::Ch> > {
typedef std::basic_string<typename ValueType::Ch> StringType;
static bool Is(const ValueType& v) { return v.IsString(); }
static StringType Get(const ValueType& v) { return v.GetString(); }
static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
};
#endif
template<typename ValueType>
struct TypeHelper<ValueType, typename ValueType::Array> {
typedef typename ValueType::Array ArrayType;
static bool Is(const ValueType& v) { return v.IsArray(); }
static ArrayType Get(ValueType& v) { return v.GetArray(); }
static ValueType& Set(ValueType& v, ArrayType data) { return v = data; }
static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; }
};
template<typename ValueType>
struct TypeHelper<ValueType, typename ValueType::ConstArray> {
typedef typename ValueType::ConstArray ArrayType;
static bool Is(const ValueType& v) { return v.IsArray(); }
static ArrayType Get(const ValueType& v) { return v.GetArray(); }
};
template<typename ValueType>
struct TypeHelper<ValueType, typename ValueType::Object> {
typedef typename ValueType::Object ObjectType;
static bool Is(const ValueType& v) { return v.IsObject(); }
static ObjectType Get(ValueType& v) { return v.GetObject(); }
static ValueType& Set(ValueType& v, ObjectType data) { return v = data; }
static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; }
};
template<typename ValueType>
struct TypeHelper<ValueType, typename ValueType::ConstObject> {
typedef typename ValueType::ConstObject ObjectType;
static bool Is(const ValueType& v) { return v.IsObject(); }
static ObjectType Get(const ValueType& v) { return v.GetObject(); }
};
} // namespace internal
// Forward declarations
template <bool, typename> class GenericArray;
template <bool, typename> class GenericObject;
///////////////////////////////////////////////////////////////////////////////
// GenericValue
......@@ -422,17 +543,21 @@ public:
typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array.
typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array.
typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of itself.
typedef GenericArray<false, ValueType> Array;
typedef GenericArray<true, ValueType> ConstArray;
typedef GenericObject<false, ValueType> Object;
typedef GenericObject<true, ValueType> ConstObject;
//!@name Constructors and destructor.
//@{
//! Default constructor creates a null value.
GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {}
GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; }
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
//! Move constructor in C++11
GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) {
rhs.flags_ = kNullFlag; // give up contents
GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) {
rhs.data_.f.flags = kNullFlag; // give up contents
}
#endif
......@@ -457,13 +582,13 @@ public:
\param type Type of the value.
\note Default content for number is zero.
*/
explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() {
static const unsigned defaultFlags[7] = {
explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() {
static const uint16_t defaultFlags[7] = {
kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag,
kNumberAnyFlag
};
RAPIDJSON_ASSERT(type <= kNumberType);
flags_ = defaultFlags[type];
data_.f.flags = defaultFlags[type];
// Use ShortString to store empty string.
if (type == kStringType)
......@@ -492,92 +617,118 @@ public:
#else
explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT
#endif
: data_(), flags_(b ? kTrueFlag : kFalseFlag) {
: data_() {
// safe-guard against failing SFINAE
RAPIDJSON_STATIC_ASSERT((internal::IsSame<bool,T>::Value));
data_.f.flags = b ? kTrueFlag : kFalseFlag;
}
//! Constructor for int value.
explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) {
explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() {
data_.n.i64 = i;
if (i >= 0)
flags_ |= kUintFlag | kUint64Flag;
data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag;
}
//! Constructor for unsigned value.
explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) {
explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() {
data_.n.u64 = u;
if (!(u & 0x80000000))
flags_ |= kIntFlag | kInt64Flag;
data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag);
}
//! Constructor for int64_t value.
explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) {
explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() {
data_.n.i64 = i64;
data_.f.flags = kNumberInt64Flag;
if (i64 >= 0) {
flags_ |= kNumberUint64Flag;
data_.f.flags |= kNumberUint64Flag;
if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
flags_ |= kUintFlag;
data_.f.flags |= kUintFlag;
if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
flags_ |= kIntFlag;
data_.f.flags |= kIntFlag;
}
else if (i64 >= static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
flags_ |= kIntFlag;
data_.f.flags |= kIntFlag;
}
//! Constructor for uint64_t value.
explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) {
explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() {
data_.n.u64 = u64;
data_.f.flags = kNumberUint64Flag;
if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)))
flags_ |= kInt64Flag;
data_.f.flags |= kInt64Flag;
if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
flags_ |= kUintFlag;
data_.f.flags |= kUintFlag;
if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
flags_ |= kIntFlag;
data_.f.flags |= kIntFlag;
}
//! Constructor for double value.
explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; }
explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; }
//! Constructor for constant string (i.e. do not make a copy of string)
GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); }
GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); }
//! Constructor for constant string (i.e. do not make a copy of string)
explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); }
explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); }
//! Constructor for copy-string (i.e. do make a copy of string)
GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); }
GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); }
//! Constructor for copy-string (i.e. do make a copy of string)
GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); }
GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
#if RAPIDJSON_HAS_STDSTRING
//! Constructor for copy-string from a string object (i.e. do make a copy of string)
/*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
*/
GenericValue(const std::basic_string<Ch>& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); }
GenericValue(const std::basic_string<Ch>& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
#endif
//! Constructor for Array.
/*!
\param a An array obtained by \c GetArray().
\note \c Array is always pass-by-value.
\note the source array is moved into this value and the sourec array becomes empty.
*/
GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) {
a.value_.data_ = Data();
a.value_.data_.f.flags = kArrayFlag;
}
//! Constructor for Object.
/*!
\param o An object obtained by \c GetObject().
\note \c Object is always pass-by-value.
\note the source object is moved into this value and the sourec object becomes empty.
*/
GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) {
o.value_.data_ = Data();
o.value_.data_.f.flags = kObjectFlag;
}
//! Destructor.
/*! Need to destruct elements of array, members of object, or copy-string.
*/
~GenericValue() {
if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
switch(flags_) {
switch(data_.f.flags) {
case kArrayFlag:
for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v)
{
GenericValue* e = GetElementsPointer();
for (GenericValue* v = e; v != e + data_.a.size; ++v)
v->~GenericValue();
Allocator::Free(data_.a.elements);
Allocator::Free(e);
}
break;
case kObjectFlag:
for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
m->~Member();
Allocator::Free(data_.o.members);
Allocator::Free(GetMembersPointer());
break;
case kCopyStringFlag:
Allocator::Free(const_cast<Ch*>(data_.s.str));
Allocator::Free(const_cast<Ch*>(GetStringPointer()));
break;
default:
......@@ -775,20 +926,20 @@ public:
//!@name Type
//@{
Type GetType() const { return static_cast<Type>(flags_ & kTypeMask); }
bool IsNull() const { return flags_ == kNullFlag; }
bool IsFalse() const { return flags_ == kFalseFlag; }
bool IsTrue() const { return flags_ == kTrueFlag; }
bool IsBool() const { return (flags_ & kBoolFlag) != 0; }
bool IsObject() const { return flags_ == kObjectFlag; }
bool IsArray() const { return flags_ == kArrayFlag; }
bool IsNumber() const { return (flags_ & kNumberFlag) != 0; }
bool IsInt() const { return (flags_ & kIntFlag) != 0; }
bool IsUint() const { return (flags_ & kUintFlag) != 0; }
bool IsInt64() const { return (flags_ & kInt64Flag) != 0; }
bool IsUint64() const { return (flags_ & kUint64Flag) != 0; }
bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; }
bool IsString() const { return (flags_ & kStringFlag) != 0; }
Type GetType() const { return static_cast<Type>(data_.f.flags & kTypeMask); }
bool IsNull() const { return data_.f.flags == kNullFlag; }
bool IsFalse() const { return data_.f.flags == kFalseFlag; }
bool IsTrue() const { return data_.f.flags == kTrueFlag; }
bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; }
bool IsObject() const { return data_.f.flags == kObjectFlag; }
bool IsArray() const { return data_.f.flags == kArrayFlag; }
bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; }
bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; }
bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; }
bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; }
bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; }
bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; }
bool IsString() const { return (data_.f.flags & kStringFlag) != 0; }
// Checks whether a number can be losslessly converted to a double.
bool IsLosslessDouble() const {
......@@ -808,7 +959,7 @@ public:
// Checks whether a number is a float (possible lossy).
bool IsFloat() const {
if ((flags_ & kDoubleFlag) == 0)
if ((data_.f.flags & kDoubleFlag) == 0)
return false;
double d = GetDouble();
return d >= -3.4028234e38 && d <= 3.4028234e38;
......@@ -833,7 +984,7 @@ public:
//!@name Bool
//@{
bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; }
bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; }
//!< Set boolean value
/*! \post IsBool() == true */
GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; }
......@@ -907,16 +1058,16 @@ public:
//! Const member iterator
/*! \pre IsObject() == true */
ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); }
ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); }
//! Const \em past-the-end member iterator
/*! \pre IsObject() == true */
ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); }
ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); }
//! Member iterator
/*! \pre IsObject() == true */
MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); }
MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); }
//! \em Past-the-end member iterator
/*! \pre IsObject() == true */
MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); }
MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); }
//! Check whether a member exists in the object.
/*!
......@@ -1022,20 +1173,21 @@ public:
RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(name.IsString());
Object& o = data_.o;
ObjectData& o = data_.o;
if (o.size >= o.capacity) {
if (o.capacity == 0) {
o.capacity = kDefaultObjectCapacity;
o.members = reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member)));
SetMembersPointer(reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member))));
}
else {
SizeType oldCapacity = o.capacity;
o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5
o.members = reinterpret_cast<Member*>(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member)));
SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member))));
}
}
o.members[o.size].name.RawAssign(name);
o.members[o.size].value.RawAssign(value);
Member* members = GetMembersPointer();
members[o.size].name.RawAssign(name);
members[o.size].value.RawAssign(value);
o.size++;
return *this;
}
......@@ -1214,18 +1366,14 @@ public:
MemberIterator RemoveMember(MemberIterator m) {
RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(data_.o.size > 0);
RAPIDJSON_ASSERT(data_.o.members != 0);
RAPIDJSON_ASSERT(GetMembersPointer() != 0);
RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd());
MemberIterator last(data_.o.members + (data_.o.size - 1));
if (data_.o.size > 1 && m != last) {
// Move the last one to this place
*m = *last;
}
else {
// Only one left, just destroy
m->~Member();
}
MemberIterator last(GetMembersPointer() + (data_.o.size - 1));
if (data_.o.size > 1 && m != last)
*m = *last; // Move the last one to this place
else
m->~Member(); // Only one left, just destroy
--data_.o.size;
return m;
}
......@@ -1255,7 +1403,7 @@ public:
MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) {
RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(data_.o.size > 0);
RAPIDJSON_ASSERT(data_.o.members != 0);
RAPIDJSON_ASSERT(GetMembersPointer() != 0);
RAPIDJSON_ASSERT(first >= MemberBegin());
RAPIDJSON_ASSERT(first <= last);
RAPIDJSON_ASSERT(last <= MemberEnd());
......@@ -1293,6 +1441,9 @@ public:
return false;
}
Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); }
//@}
//!@name Array
......@@ -1317,8 +1468,9 @@ public:
*/
void Clear() {
RAPIDJSON_ASSERT(IsArray());
for (SizeType i = 0; i < data_.a.size; ++i)
data_.a.elements[i].~GenericValue();
GenericValue* e = GetElementsPointer();
for (GenericValue* v = e; v != e + data_.a.size; ++v)
v->~GenericValue();
data_.a.size = 0;
}
......@@ -1330,16 +1482,16 @@ public:
GenericValue& operator[](SizeType index) {
RAPIDJSON_ASSERT(IsArray());
RAPIDJSON_ASSERT(index < data_.a.size);
return data_.a.elements[index];
return GetElementsPointer()[index];
}
const GenericValue& operator[](SizeType index) const { return const_cast<GenericValue&>(*this)[index]; }
//! Element iterator
/*! \pre IsArray() == true */
ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; }
ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); }
//! \em Past-the-end element iterator
/*! \pre IsArray() == true */
ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; }
ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; }
//! Constant element iterator
/*! \pre IsArray() == true */
ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); }
......@@ -1356,7 +1508,7 @@ public:
GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) {
RAPIDJSON_ASSERT(IsArray());
if (newCapacity > data_.a.capacity) {
data_.a.elements = static_cast<GenericValue*>(allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)));
SetElementsPointer(reinterpret_cast<GenericValue*>(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue))));
data_.a.capacity = newCapacity;
}
return *this;
......@@ -1376,7 +1528,7 @@ public:
RAPIDJSON_ASSERT(IsArray());
if (data_.a.size >= data_.a.capacity)
Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator);
data_.a.elements[data_.a.size++].RawAssign(value);
GetElementsPointer()[data_.a.size++].RawAssign(value);
return *this;
}
......@@ -1430,7 +1582,7 @@ public:
GenericValue& PopBack() {
RAPIDJSON_ASSERT(IsArray());
RAPIDJSON_ASSERT(!Empty());
data_.a.elements[--data_.a.size].~GenericValue();
GetElementsPointer()[--data_.a.size].~GenericValue();
return *this;
}
......@@ -1456,7 +1608,7 @@ public:
ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) {
RAPIDJSON_ASSERT(IsArray());
RAPIDJSON_ASSERT(data_.a.size > 0);
RAPIDJSON_ASSERT(data_.a.elements != 0);
RAPIDJSON_ASSERT(GetElementsPointer() != 0);
RAPIDJSON_ASSERT(first >= Begin());
RAPIDJSON_ASSERT(first <= last);
RAPIDJSON_ASSERT(last <= End());
......@@ -1468,26 +1620,29 @@ public:
return pos;
}
Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); }
ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); }
//@}
//!@name Number
//@{
int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; }
unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; }
int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; }
uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; }
int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; }
unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; }
int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; }
uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; }
//! Get the value as double type.
/*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless.
*/
double GetDouble() const {
RAPIDJSON_ASSERT(IsNumber());
if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion.
if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double
if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double
if ((flags_ & kInt64Flag) != 0) return static_cast<double>(data_.n.i64); // int64_t -> double (may lose precision)
RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return static_cast<double>(data_.n.u64); // uint64_t -> double (may lose precision)
if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion.
if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double
if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double
if ((data_.f.flags & kInt64Flag) != 0) return static_cast<double>(data_.n.i64); // int64_t -> double (may lose precision)
RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast<double>(data_.n.u64); // uint64_t -> double (may lose precision)
}
//! Get the value as float type.
......@@ -1510,12 +1665,12 @@ public:
//!@name String
//@{
const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); }
const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); }
//! Get the length of string.
/*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength().
*/
SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); }
SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); }
//! Set this value as a string without copying source string.
/*! This version has better performance with supplied length, and also support string containing null character.
......@@ -1565,6 +1720,30 @@ public:
//@}
//!@name Array
//@{
//! Templated version for checking whether this value is type T.
/*!
\tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string<Ch>
*/
template <typename T>
bool Is() const { return internal::TypeHelper<ValueType, T>::Is(*this); }
template <typename T>
T Get() const { return internal::TypeHelper<ValueType, T>::Get(*this); }
template <typename T>
T Get() { return internal::TypeHelper<ValueType, T>::Get(*this); }
template<typename T>
ValueType& Set(const T& data) { return internal::TypeHelper<ValueType, T>::Set(*this, data); }
template<typename T>
ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper<ValueType, T>::Set(*this, data, allocator); }
//@}
//! Generate events of this value to a Handler.
/*! This function adopts the GoF visitor pattern.
Typical usage is to output this JSON value as JSON text via Writer, which is a Handler.
......@@ -1584,7 +1763,7 @@ public:
return false;
for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) {
RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator.
if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)))
if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0)))
return false;
if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler)))
return false;
......@@ -1594,13 +1773,13 @@ public:
case kArrayType:
if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
return false;
for (const GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v)
for (const GenericValue* v = Begin(); v != End(); ++v)
if (RAPIDJSON_UNLIKELY(!v->Accept(handler)))
return false;
return handler.EndArray(data_.a.size);
case kStringType:
return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0);
return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0);
default:
RAPIDJSON_ASSERT(GetType() == kNumberType);
......@@ -1617,16 +1796,16 @@ private:
template <typename, typename, typename> friend class GenericDocument;
enum {
kBoolFlag = 0x100,
kNumberFlag = 0x200,
kIntFlag = 0x400,
kUintFlag = 0x800,
kInt64Flag = 0x1000,
kUint64Flag = 0x2000,
kDoubleFlag = 0x4000,
kStringFlag = 0x100000,
kCopyFlag = 0x200000,
kInlineStrFlag = 0x400000,
kBoolFlag = 0x0008,
kNumberFlag = 0x0010,
kIntFlag = 0x0020,
kUintFlag = 0x0040,
kInt64Flag = 0x0080,
kUint64Flag = 0x0100,
kDoubleFlag = 0x0200,
kStringFlag = 0x0400,
kCopyFlag = 0x0800,
kInlineStrFlag = 0x1000,
// Initial flags of different types.
kNullFlag = kNullType,
......@@ -1644,16 +1823,27 @@ private:
kObjectFlag = kObjectType,
kArrayFlag = kArrayType,
kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler
kTypeMask = 0x07
};
static const SizeType kDefaultArrayCapacity = 16;
static const SizeType kDefaultObjectCapacity = 16;
struct Flag {
#if RAPIDJSON_48BITPOINTER_OPTIMIZATION
char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer
#elif RAPIDJSON_64BIT
char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes
#else
char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes
#endif
uint16_t flags;
};
struct String {
const Ch* str;
SizeType length;
unsigned hashcode; //!< reserved
SizeType hashcode; //!< reserved
const Ch* str;
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
// implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars
......@@ -1662,10 +1852,10 @@ private:
// to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as
// the string terminator as well. For getting the string length back from that value just use
// "MaxSize - str[LenPos]".
// This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode
// inline (for `UTF8`-encoded strings).
// This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode,
// 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings).
struct ShortString {
enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize };
enum { MaxChars = sizeof(static_cast<Flag*>(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize };
Ch str[MaxChars];
inline static bool Usable(SizeType len) { return (MaxSize >= len); }
......@@ -1699,69 +1889,79 @@ private:
double d;
}; // 8 bytes
struct Object {
Member* members;
struct ObjectData {
SizeType size;
SizeType capacity;
Member* members;
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
struct Array {
GenericValue* elements;
struct ArrayData {
SizeType size;
SizeType capacity;
GenericValue* elements;
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
union Data {
String s;
ShortString ss;
Number n;
Object o;
Array a;
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
ObjectData o;
ArrayData a;
Flag f;
}; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION
RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); }
RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); }
RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); }
RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); }
RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); }
RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); }
// Initialize this value as array with initial data, without calling destructor.
void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
flags_ = kArrayFlag;
data_.f.flags = kArrayFlag;
if (count) {
data_.a.elements = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
std::memcpy(data_.a.elements, values, count * sizeof(GenericValue));
GenericValue* e = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
SetElementsPointer(e);
std::memcpy(e, values, count * sizeof(GenericValue));
}
else
data_.a.elements = NULL;
SetElementsPointer(0);
data_.a.size = data_.a.capacity = count;
}
//! Initialize this value as object with initial data, without calling destructor.
void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
flags_ = kObjectFlag;
data_.f.flags = kObjectFlag;
if (count) {
data_.o.members = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
std::memcpy(data_.o.members, members, count * sizeof(Member));
Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
SetMembersPointer(m);
std::memcpy(m, members, count * sizeof(Member));
}
else
data_.o.members = NULL;
SetMembersPointer(0);
data_.o.size = data_.o.capacity = count;
}
//! Initialize this value as constant string, without calling destructor.
void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT {
flags_ = kConstStringFlag;
data_.s.str = s;
data_.f.flags = kConstStringFlag;
SetStringPointer(s);
data_.s.length = s.length;
}
//! Initialize this value as copy string with initial data, without calling destructor.
void SetStringRaw(StringRefType s, Allocator& allocator) {
Ch* str = NULL;
if(ShortString::Usable(s.length)) {
flags_ = kShortStringFlag;
Ch* str = 0;
if (ShortString::Usable(s.length)) {
data_.f.flags = kShortStringFlag;
data_.ss.SetLength(s.length);
str = data_.ss.str;
} else {
flags_ = kCopyStringFlag;
data_.f.flags = kCopyStringFlag;
data_.s.length = s.length;
str = static_cast<Ch *>(allocator.Malloc((s.length + 1) * sizeof(Ch)));
data_.s.str = str;
SetStringPointer(str);
}
std::memcpy(str, s, s.length * sizeof(Ch));
str[s.length] = '\0';
......@@ -1770,8 +1970,8 @@ private:
//! Assignment without calling destructor
void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
data_ = rhs.data_;
flags_ = rhs.flags_;
rhs.flags_ = kNullFlag;
// data_.f.flags = rhs.data_.f.flags;
rhs.data_.f.flags = kNullFlag;
}
template <typename SourceAllocator>
......@@ -1791,7 +1991,6 @@ private:
}
Data data_;
unsigned flags_;
};
//! GenericValue with UTF8 encoding
......@@ -2196,20 +2395,159 @@ GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,Sourc
}
break;
case kStringType:
if (rhs.flags_ == kConstStringFlag) {
flags_ = rhs.flags_;
if (rhs.data_.f.flags == kConstStringFlag) {
data_.f.flags = rhs.data_.f.flags;
data_ = *reinterpret_cast<const Data*>(&rhs.data_);
} else {
SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator);
}
break;
default:
flags_ = rhs.flags_;
data_.f.flags = rhs.data_.f.flags;
data_ = *reinterpret_cast<const Data*>(&rhs.data_);
break;
}
}
//! Helper class for accessing Value of array type.
/*!
Instance of this helper class is obtained by \c GenericValue::GetArray().
In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
*/
template <bool Const, typename ValueT>
class GenericArray {
public:
typedef GenericArray<true, ValueT> ConstArray;
typedef GenericArray<false, ValueT> Array;
typedef ValueT PlainType;
typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
typedef ValueType* ValueIterator; // This may be const or non-const iterator
typedef const ValueT* ConstValueIterator;
typedef typename ValueType::AllocatorType AllocatorType;
typedef typename ValueType::StringRefType StringRefType;
template <typename, typename>
friend class GenericValue;
GenericArray(const GenericArray& rhs) : value_(rhs.value_) {}
GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; }
~GenericArray() {}
SizeType Size() const { return value_.Size(); }
SizeType Capacity() const { return value_.Capacity(); }
bool Empty() const { return value_.Empty(); }
void Clear() const { value_.Clear(); }
ValueType& operator[](SizeType index) const { return value_[index]; }
ValueIterator Begin() const { return value_.Begin(); }
ValueIterator End() const { return value_.End(); }
GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; }
GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
GenericArray PopBack() const { value_.PopBack(); return *this; }
ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); }
ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); }
#if RAPIDJSON_HAS_CXX11_RANGE_FOR
ValueIterator begin() const { return value_.Begin(); }
ValueIterator end() const { return value_.End(); }
#endif
private:
GenericArray();
GenericArray(ValueType& value) : value_(value) {}
ValueType& value_;
};
//! Helper class for accessing Value of array type.
/*!
Instance of this helper class is obtained by \c GenericValue::GetArray().
In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
*/
template <bool Const, typename ValueT>
class GenericObject {
public:
typedef GenericObject<true, ValueT> ConstObject;
typedef GenericObject<false, ValueT> Object;
typedef ValueT PlainType;
typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
typedef GenericMemberIterator<Const, typename ValueT::EncodingType, typename ValueT::AllocatorType> MemberIterator; // This may be const or non-const iterator
typedef GenericMemberIterator<true, typename ValueT::EncodingType, typename ValueT::AllocatorType> ConstMemberIterator;
typedef typename ValueType::AllocatorType AllocatorType;
typedef typename ValueType::StringRefType StringRefType;
typedef typename ValueType::EncodingType EncodingType;
typedef typename ValueType::Ch Ch;
template <typename, typename>
friend class GenericValue;
GenericObject(const GenericObject& rhs) : value_(rhs.value_) {}
GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; }
~GenericObject() {}
SizeType MemberCount() const { return value_.MemberCount(); }
bool ObjectEmpty() const { return value_.ObjectEmpty(); }
template <typename T> ValueType& operator[](T* name) const { return value_[name]; }
template <typename SourceAllocator> ValueType& operator[](const GenericValue<EncodingType, SourceAllocator>& name) const { return value_[name]; }
#if RAPIDJSON_HAS_STDSTRING
ValueType& operator[](const std::basic_string<Ch>& name) const { return value_[name]; }
#endif
MemberIterator MemberBegin() const { return value_.MemberBegin(); }
MemberIterator MemberEnd() const { return value_.MemberEnd(); }
bool HasMember(const Ch* name) const { return value_.HasMember(name); }
#if RAPIDJSON_HAS_STDSTRING
bool HasMember(const std::basic_string<Ch>& name) const { return value_.HasMember(name); }
#endif
template <typename SourceAllocator> bool HasMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.HasMember(name); }
MemberIterator FindMember(const Ch* name) const { value_.FindMember(name); }
template <typename SourceAllocator> MemberIterator FindMember(const GenericValue<EncodingType, SourceAllocator>& name) const { value_.FindMember(name); }
#if RAPIDJSON_HAS_STDSTRING
MemberIterator FindMember(const std::basic_string<Ch>& name) const { return value_.FindMember(name); }
#endif
GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
#if RAPIDJSON_HAS_STDSTRING
GenericObject AddMember(ValueType& name, std::basic_string<Ch>& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
#endif
template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
void RemoveAllMembers() { return value_.RemoveAllMembers(); }
bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); }
#if RAPIDJSON_HAS_STDSTRING
bool RemoveMember(const std::basic_string<Ch>& name) const { return value_.RemoveMember(name); }
#endif
template <typename SourceAllocator> bool RemoveMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.RemoveMember(name); }
MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); }
MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); }
MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); }
bool EraseMember(const Ch* name) const { return value_.EraseMember(name); }
#if RAPIDJSON_HAS_STDSTRING
bool EraseMember(const std::basic_string<Ch>& name) const { return EraseMember(ValueType(StringRef(name))); }
#endif
template <typename SourceAllocator> bool EraseMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.EraseMember(name); }
#if RAPIDJSON_HAS_CXX11_RANGE_FOR
MemberIterator begin() const { return value_.MemberBegin(); }
MemberIterator end() const { return value_.MemberEnd(); }
#endif
private:
GenericObject();
GenericObject(ValueType& value) : value_(value) {}
ValueType& value_;
};
RAPIDJSON_NAMESPACE_END
#ifdef _MSC_VER
......
......@@ -265,7 +265,8 @@
\param x pointer to align
Some machines require strict data alignment. Currently the default uses 4 bytes
alignment. User can customize by defining the RAPIDJSON_ALIGN function macro.
alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms.
User can customize by defining the RAPIDJSON_ALIGN function macro.
*/
#ifndef RAPIDJSON_ALIGN
#if RAPIDJSON_64BIT == 1
......@@ -288,6 +289,36 @@
#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_48BITPOINTER_OPTIMIZATION
//! Use only lower 48-bit address for some pointers.
/*!
\ingroup RAPIDJSON_CONFIG
This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address.
The higher 16-bit can be used for storing other data.
\c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture.
*/
#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1
#else
#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0
#endif
#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION
#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1
#if RAPIDJSON_64BIT != 1
#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1
#endif
#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x))))
#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF))))
#else
#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x))
#define RAPIDJSON_GETPOINTER(type, p) (p)
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD
......@@ -530,6 +561,17 @@ RAPIDJSON_NAMESPACE_END
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
#endif
#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR
#if defined(__clang__)
#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for)
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
(defined(_MSC_VER) && _MSC_VER >= 1600)
#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
#else
#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0
#endif
#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR
//!@endcond
///////////////////////////////////////////////////////////////////////////////
......
......@@ -23,6 +23,18 @@ RAPIDJSON_DIAG_OFF(c++98-compat)
using namespace rapidjson;
TEST(Value, Size) {
if (sizeof(SizeType) == 4) {
#if RAPIDJSON_48BITPOINTER_OPTIMIZATION
EXPECT_EQ(16, sizeof(Value));
#elif RAPIDJSON_64BIT
EXPECT_EQ(24, sizeof(Value));
#else
EXPECT_EQ(16, sizeof(Value));
#endif
}
}
TEST(Value, DefaultConstructor) {
Value x;
EXPECT_EQ(kNullType, x.GetType());
......@@ -335,6 +347,12 @@ TEST(Value, True) {
Value z;
z.SetBool(true);
EXPECT_TRUE(z.IsTrue());
// Templated functions
EXPECT_TRUE(z.Is<bool>());
EXPECT_TRUE(z.Get<bool>());
EXPECT_FALSE(z.Set<bool>(false).Get<bool>());
EXPECT_TRUE(z.Set(true).Get<bool>());
}
TEST(Value, False) {
......@@ -414,6 +432,12 @@ TEST(Value, Int) {
// operator=(int)
z = 5678;
EXPECT_EQ(5678, z.GetInt());
// Templated functions
EXPECT_TRUE(z.Is<int>());
EXPECT_EQ(5678, z.Get<int>());
EXPECT_EQ(5679, z.Set(5679).Get<int>());
EXPECT_EQ(5680, z.Set<int>(5680).Get<int>());
}
TEST(Value, Uint) {
......@@ -453,6 +477,12 @@ TEST(Value, Uint) {
EXPECT_EQ(2147483648u, z.GetUint());
EXPECT_FALSE(z.IsInt());
EXPECT_TRUE(z.IsInt64()); // Issue 41: Incorrect parsing of unsigned int number types
// Templated functions
EXPECT_TRUE(z.Is<unsigned>());
EXPECT_EQ(2147483648u, z.Get<unsigned>());
EXPECT_EQ(2147483649u, z.Set(2147483649u).Get<unsigned>());
EXPECT_EQ(2147483650u, z.Set<unsigned>(2147483650u).Get<unsigned>());
}
TEST(Value, Int64) {
......@@ -505,8 +535,15 @@ TEST(Value, Int64) {
EXPECT_FALSE(z.IsInt());
EXPECT_NEAR(-2147483649.0, z.GetDouble(), 0.0);
z.SetInt64(static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 00000000)));
int64_t i = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 00000000));
z.SetInt64(i);
EXPECT_DOUBLE_EQ(-9223372036854775808.0, z.GetDouble());
// Templated functions
EXPECT_TRUE(z.Is<int64_t>());
EXPECT_EQ(i, z.Get<int64_t>());
EXPECT_EQ(i - 1, z.Set(i - 1).Get<int64_t>());
EXPECT_EQ(i - 2, z.Set<int64_t>(i - 2).Get<int64_t>());
}
TEST(Value, Uint64) {
......@@ -547,10 +584,17 @@ TEST(Value, Uint64) {
EXPECT_FALSE(z.IsUint());
EXPECT_TRUE(z.IsInt64());
z.SetUint64(RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)); // 2^63 cannot cast as int64
uint64_t u = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
z.SetUint64(u); // 2^63 cannot cast as int64
EXPECT_FALSE(z.IsInt64());
EXPECT_EQ(RAPIDJSON_UINT64_C2(0x80000000, 0x00000000), z.GetUint64()); // Issue 48
EXPECT_EQ(u, z.GetUint64()); // Issue 48
EXPECT_DOUBLE_EQ(9223372036854775808.0, z.GetDouble());
// Templated functions
EXPECT_TRUE(z.Is<uint64_t>());
EXPECT_EQ(u, z.Get<uint64_t>());
EXPECT_EQ(u + 1, z.Set(u + 1).Get<uint64_t>());
EXPECT_EQ(u + 2, z.Set<uint64_t>(u + 2).Get<uint64_t>());
}
TEST(Value, Double) {
......@@ -577,6 +621,12 @@ TEST(Value, Double) {
z = 56.78;
EXPECT_NEAR(56.78, z.GetDouble(), 0.0);
// Templated functions
EXPECT_TRUE(z.Is<double>());
EXPECT_EQ(56.78, z.Get<double>());
EXPECT_EQ(57.78, z.Set(57.78).Get<double>());
EXPECT_EQ(58.78, z.Set<double>(58.78).Get<double>());
}
TEST(Value, Float) {
......@@ -604,6 +654,12 @@ TEST(Value, Float) {
z = 56.78f;
EXPECT_NEAR(56.78f, z.GetFloat(), 0.0f);
// Templated functions
EXPECT_TRUE(z.Is<float>());
EXPECT_EQ(56.78f, z.Get<float>());
EXPECT_EQ(57.78f, z.Set(57.78f).Get<float>());
EXPECT_EQ(58.78f, z.Set<float>(58.78f).Get<float>());
}
TEST(Value, IsLosslessDouble) {
......@@ -724,6 +780,11 @@ TEST(Value, String) {
EXPECT_STREQ("World", w.GetString());
EXPECT_EQ(5u, w.GetStringLength());
// templated functions
EXPECT_TRUE(z.Is<const char*>());
EXPECT_STREQ(cstr, z.Get<const char*>());
EXPECT_STREQ("Apple", z.Set<const char*>("Apple").Get<const char*>());
#if RAPIDJSON_HAS_STDSTRING
{
std::string str = "Hello World";
......@@ -759,6 +820,14 @@ TEST(Value, String) {
vs1 = StringRef(str);
TestEqual(str, vs1);
TestEqual(vs0, vs1);
// Templated function.
EXPECT_TRUE(vs0.Is<std::string>());
EXPECT_EQ(str, vs0.Get<std::string>());
vs0.Set<std::string>(std::string("Apple"), allocator);
EXPECT_EQ(std::string("Apple"), vs0.Get<std::string>());
vs0.Set(std::string("Orange"), allocator);
EXPECT_EQ(std::string("Orange"), vs0.Get<std::string>());
}
#endif // RAPIDJSON_HAS_STDSTRING
}
......@@ -769,25 +838,9 @@ TEST(Value, SetStringNullException) {
EXPECT_THROW(v.SetString(0, 0), AssertException);
}
TEST(Value, Array) {
Value x(kArrayType);
const Value& y = x;
Value::AllocatorType allocator;
EXPECT_EQ(kArrayType, x.GetType());
EXPECT_TRUE(x.IsArray());
EXPECT_TRUE(x.Empty());
EXPECT_EQ(0u, x.Size());
EXPECT_TRUE(y.IsArray());
EXPECT_TRUE(y.Empty());
EXPECT_EQ(0u, y.Size());
EXPECT_FALSE(x.IsNull());
EXPECT_FALSE(x.IsBool());
EXPECT_FALSE(x.IsFalse());
EXPECT_FALSE(x.IsTrue());
EXPECT_FALSE(x.IsString());
EXPECT_FALSE(x.IsObject());
template <typename T, typename Allocator>
static void TestArray(T& x, Allocator& allocator) {
const T& y = x;
// PushBack()
Value v;
......@@ -834,7 +887,7 @@ TEST(Value, Array) {
#endif
// iterator
Value::ValueIterator itr = x.Begin();
typename T::ValueIterator itr = x.Begin();
EXPECT_TRUE(itr != x.End());
EXPECT_TRUE(itr->IsNull());
++itr;
......@@ -853,7 +906,7 @@ TEST(Value, Array) {
EXPECT_STREQ("foo", itr->GetString());
// const iterator
Value::ConstValueIterator citr = y.Begin();
typename T::ConstValueIterator citr = y.Begin();
EXPECT_TRUE(citr != y.End());
EXPECT_TRUE(citr->IsNull());
++citr;
......@@ -939,6 +992,29 @@ TEST(Value, Array) {
EXPECT_EQ(i + removeCount, x[static_cast<SizeType>(i)][0].GetUint());
}
}
}
TEST(Value, Array) {
Value x(kArrayType);
const Value& y = x;
Value::AllocatorType allocator;
EXPECT_EQ(kArrayType, x.GetType());
EXPECT_TRUE(x.IsArray());
EXPECT_TRUE(x.Empty());
EXPECT_EQ(0u, x.Size());
EXPECT_TRUE(y.IsArray());
EXPECT_TRUE(y.Empty());
EXPECT_EQ(0u, y.Size());
EXPECT_FALSE(x.IsNull());
EXPECT_FALSE(x.IsBool());
EXPECT_FALSE(x.IsFalse());
EXPECT_FALSE(x.IsTrue());
EXPECT_FALSE(x.IsString());
EXPECT_FALSE(x.IsObject());
TestArray(x, allocator);
// Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed.
// http://en.wikipedia.org/wiki/Erase-remove_idiom
......@@ -962,19 +1038,96 @@ TEST(Value, Array) {
EXPECT_TRUE(z.Empty());
}
TEST(Value, Object) {
Value x(kObjectType);
const Value& y = x; // const version
TEST(Value, ArrayHelper) {
Value::AllocatorType allocator;
{
Value x(kArrayType);
Value::Array a = x.GetArray();
TestArray(a, allocator);
}
EXPECT_EQ(kObjectType, x.GetType());
EXPECT_TRUE(x.IsObject());
EXPECT_TRUE(x.ObjectEmpty());
EXPECT_EQ(0u, x.MemberCount());
EXPECT_EQ(kObjectType, y.GetType());
EXPECT_TRUE(y.IsObject());
EXPECT_TRUE(y.ObjectEmpty());
EXPECT_EQ(0u, y.MemberCount());
{
Value x(kArrayType);
Value::Array a = x.GetArray();
a.PushBack(1, allocator);
Value::Array a2(a); // copy constructor
EXPECT_EQ(1, a2.Size());
Value::Array a3 = a;
EXPECT_EQ(1, a3.Size());
Value::ConstArray y = static_cast<const Value&>(x).GetArray();
(void)y;
// y.PushBack(1, allocator); // should not compile
// Templated functions
x.Clear();
EXPECT_TRUE(x.Is<Value::Array>());
EXPECT_TRUE(x.Is<Value::ConstArray>());
a.PushBack(1, allocator);
EXPECT_EQ(1, x.Get<Value::Array>()[0].GetInt());
EXPECT_EQ(1, x.Get<Value::ConstArray>()[0].GetInt());
Value x2;
x2.Set<Value::Array>(a);
EXPECT_TRUE(x.IsArray()); // IsArray() is invariant after moving.
EXPECT_EQ(1, x2.Get<Value::Array>()[0].GetInt());
}
{
Value y(kArrayType);
y.PushBack(123, allocator);
Value x(y.GetArray()); // Construct value form array.
EXPECT_TRUE(x.IsArray());
EXPECT_EQ(123, x[0].GetInt());
EXPECT_TRUE(y.IsArray()); // Invariant
EXPECT_TRUE(y.Empty());
}
{
Value x(kArrayType);
Value y(kArrayType);
y.PushBack(123, allocator);
x.PushBack(y.GetArray(), allocator); // Implicit constructor to convert Array to GenericValue
EXPECT_EQ(1, x.Size());
EXPECT_EQ(123, x[0][0].GetInt());
EXPECT_TRUE(y.IsArray());
EXPECT_TRUE(y.Empty());
}
}
#if RAPIDJSON_HAS_CXX11_RANGE_FOR
TEST(Value, ArrayHelperRangeFor) {
Value::AllocatorType allocator;
Value x(kArrayType);
for (int i = 0; i < 10; i++)
x.PushBack(i, allocator);
{
int i = 0;
for (auto& v : x.GetArray())
EXPECT_EQ(i++, v.GetInt());
EXPECT_EQ(i, 10);
}
{
int i = 0;
for (const auto& v : const_cast<const Value&>(x).GetArray())
EXPECT_EQ(i++, v.GetInt());
EXPECT_EQ(i, 10);
}
// Array a = x.GetArray();
// Array ca = const_cast<const Value&>(x).GetArray();
}
#endif
template <typename T, typename Allocator>
static void TestObject(T& x, Allocator& allocator) {
const T& y = x; // const version
// AddMember()
x.AddMember("A", "Apple", allocator);
......@@ -1215,7 +1368,7 @@ TEST(Value, Object) {
const unsigned n = 10;
for (unsigned first = 0; first < n; first++) {
for (unsigned last = first; last <= n; last++) {
Value(kObjectType).Swap(x);
x.RemoveAllMembers();
for (unsigned i = 0; i < n; i++)
x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator);
......@@ -1238,6 +1391,23 @@ TEST(Value, Object) {
x.RemoveAllMembers();
EXPECT_TRUE(x.ObjectEmpty());
EXPECT_EQ(0u, x.MemberCount());
}
TEST(Value, Object) {
Value x(kObjectType);
const Value& y = x; // const version
Value::AllocatorType allocator;
EXPECT_EQ(kObjectType, x.GetType());
EXPECT_TRUE(x.IsObject());
EXPECT_TRUE(x.ObjectEmpty());
EXPECT_EQ(0u, x.MemberCount());
EXPECT_EQ(kObjectType, y.GetType());
EXPECT_TRUE(y.IsObject());
EXPECT_TRUE(y.ObjectEmpty());
EXPECT_EQ(0u, y.MemberCount());
TestObject(x, allocator);
// SetObject()
Value z;
......@@ -1245,6 +1415,100 @@ TEST(Value, Object) {
EXPECT_TRUE(z.IsObject());
}
TEST(Value, ObjectHelper) {
Value::AllocatorType allocator;
{
Value x(kObjectType);
Value::Object o = x.GetObject();
TestObject(o, allocator);
}
{
Value x(kObjectType);
Value::Object o = x.GetObject();
o.AddMember("1", 1, allocator);
Value::Object o2(o); // copy constructor
EXPECT_EQ(1, o2.MemberCount());
Value::Object o3 = o;
EXPECT_EQ(1, o3.MemberCount());
Value::ConstObject y = static_cast<const Value&>(x).GetObject();
(void)y;
// y.AddMember("1", 1, allocator); // should not compile
// Templated functions
x.RemoveAllMembers();
EXPECT_TRUE(x.Is<Value::Object>());
EXPECT_TRUE(x.Is<Value::ConstObject>());
o.AddMember("1", 1, allocator);
EXPECT_EQ(1, x.Get<Value::Object>()["1"].GetInt());
EXPECT_EQ(1, x.Get<Value::ConstObject>()["1"].GetInt());
Value x2;
x2.Set<Value::Object>(o);
EXPECT_TRUE(x.IsObject()); // IsObject() is invariant after moving
EXPECT_EQ(1, x2.Get<Value::Object>()["1"].GetInt());
}
{
Value x(kObjectType);
x.AddMember("a", "apple", allocator);
Value y(x.GetObject());
EXPECT_STREQ("apple", y["a"].GetString());
EXPECT_TRUE(x.IsObject()); // Invariant
}
{
Value x(kObjectType);
x.AddMember("a", "apple", allocator);
Value y(kObjectType);
y.AddMember("fruits", x.GetObject(), allocator);
EXPECT_STREQ("apple", y["fruits"]["a"].GetString());
EXPECT_TRUE(x.IsObject()); // Invariant
}
}
#if RAPIDJSON_HAS_CXX11_RANGE_FOR
TEST(Value, ObjectHelperRangeFor) {
Value::AllocatorType allocator;
Value x(kObjectType);
for (int i = 0; i < 10; i++) {
char name[10];
Value n(name, static_cast<SizeType>(sprintf(name, "%d", i)), allocator);
x.AddMember(n, i, allocator);
}
{
int i = 0;
for (auto& m : x.GetObject()) {
char name[10];
sprintf(name, "%d", i);
EXPECT_STREQ(name, m.name.GetString());
EXPECT_EQ(i, m.value.GetInt());
i++;
}
EXPECT_EQ(i, 10);
}
{
int i = 0;
for (const auto& m : const_cast<const Value&>(x).GetObject()) {
char name[10];
sprintf(name, "%d", i);
EXPECT_STREQ(name, m.name.GetString());
EXPECT_EQ(i, m.value.GetInt());
i++;
}
EXPECT_EQ(i, 10);
}
// Object a = x.GetObject();
// Object ca = const_cast<const Value&>(x).GetObject();
}
#endif
TEST(Value, EraseMember_String) {
Value::AllocatorType allocator;
Value x(kObjectType);
......
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