Commit e731726c authored by Milo Yip's avatar Milo Yip

Optimize memory consumption with RAPIDJSON_48BITPOINTER_OPTIMIZATION

#330
parent 90c0235f
...@@ -295,7 +295,7 @@ struct GenericStringRef { ...@@ -295,7 +295,7 @@ struct GenericStringRef {
*/ */
#endif #endif
explicit GenericStringRef(const CharType* str) 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 //! Create constant string reference from pointer and length
#ifndef __clang__ // -Wdocumentation #ifndef __clang__ // -Wdocumentation
...@@ -307,7 +307,7 @@ struct GenericStringRef { ...@@ -307,7 +307,7 @@ struct GenericStringRef {
*/ */
#endif #endif
GenericStringRef(const CharType* str, SizeType len) 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 //! implicit conversion to plain CharType pointer
operator const Ch *() const { return s; } operator const Ch *() const { return s; }
...@@ -425,12 +425,12 @@ public: ...@@ -425,12 +425,12 @@ public:
//@{ //@{
//! Default constructor creates a null value. //! 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 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
//! Move constructor in C++11 //! Move constructor in C++11
GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) {
rhs.flags_ = kNullFlag; // give up contents rhs.data_.f.flags = kNullFlag; // give up contents
} }
#endif #endif
...@@ -455,13 +455,13 @@ public: ...@@ -455,13 +455,13 @@ public:
\param type Type of the value. \param type Type of the value.
\note Default content for number is zero. \note Default content for number is zero.
*/ */
explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() {
static const unsigned defaultFlags[7] = { static const uint16_t defaultFlags[7] = {
kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag,
kNumberAnyFlag kNumberAnyFlag
}; };
RAPIDJSON_ASSERT(type <= kNumberType); RAPIDJSON_ASSERT(type <= kNumberType);
flags_ = defaultFlags[type]; data_.f.flags = defaultFlags[type];
// Use ShortString to store empty string. // Use ShortString to store empty string.
if (type == kStringType) if (type == kStringType)
...@@ -490,70 +490,71 @@ public: ...@@ -490,70 +490,71 @@ public:
#else #else
explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT
#endif #endif
: data_(), flags_(b ? kTrueFlag : kFalseFlag) { : data_() {
// safe-guard against failing SFINAE // safe-guard against failing SFINAE
RAPIDJSON_STATIC_ASSERT((internal::IsSame<bool,T>::Value)); RAPIDJSON_STATIC_ASSERT((internal::IsSame<bool,T>::Value));
data_.f.flags = b ? kTrueFlag : kFalseFlag;
} }
//! Constructor for int value. //! Constructor for int value.
explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() {
data_.n.i64 = i; data_.n.i64 = i;
if (i >= 0) data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag;
flags_ |= kUintFlag | kUint64Flag;
} }
//! Constructor for unsigned value. //! Constructor for unsigned value.
explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() {
data_.n.u64 = u; data_.n.u64 = u;
if (!(u & 0x80000000)) data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag);
flags_ |= kIntFlag | kInt64Flag;
} }
//! Constructor for int64_t value. //! 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_.n.i64 = i64;
data_.f.flags = kNumberInt64Flag;
if (i64 >= 0) { if (i64 >= 0) {
flags_ |= kNumberUint64Flag; data_.f.flags |= kNumberUint64Flag;
if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) 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))) 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))) else if (i64 >= static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
flags_ |= kIntFlag; data_.f.flags |= kIntFlag;
} }
//! Constructor for uint64_t value. //! 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_.n.u64 = u64;
data_.f.flags = kNumberUint64Flag;
if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)))
flags_ |= kInt64Flag; data_.f.flags |= kInt64Flag;
if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
flags_ |= kUintFlag; data_.f.flags |= kUintFlag;
if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
flags_ |= kIntFlag; data_.f.flags |= kIntFlag;
} }
//! Constructor for double value. //! 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) //! 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) //! 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) //! 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) //! 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 #if RAPIDJSON_HAS_STDSTRING
//! Constructor for copy-string from a string object (i.e. do make a copy of string) //! 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. /*! \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 #endif
//! Destructor. //! Destructor.
...@@ -561,21 +562,24 @@ public: ...@@ -561,21 +562,24 @@ public:
*/ */
~GenericValue() { ~GenericValue() {
if (Allocator::kNeedFree) { // Shortcut by Allocator's trait if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
switch(flags_) { switch(data_.f.flags) {
case kArrayFlag: case kArrayFlag:
for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) {
v->~GenericValue(); GenericValue* e = GetElementsPointer();
Allocator::Free(data_.a.elements); for (GenericValue* v = e; v != e + data_.a.size; ++v)
v->~GenericValue();
Allocator::Free(e);
}
break; break;
case kObjectFlag: case kObjectFlag:
for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
m->~Member(); m->~Member();
Allocator::Free(data_.o.members); Allocator::Free(GetMembersPointer());
break; break;
case kCopyStringFlag: case kCopyStringFlag:
Allocator::Free(const_cast<Ch*>(data_.s.str)); Allocator::Free(GetStringPointer());
break; break;
default: default:
...@@ -773,20 +777,20 @@ public: ...@@ -773,20 +777,20 @@ public:
//!@name Type //!@name Type
//@{ //@{
Type GetType() const { return static_cast<Type>(flags_ & kTypeMask); } Type GetType() const { return static_cast<Type>(data_.f.flags & kTypeMask); }
bool IsNull() const { return flags_ == kNullFlag; } bool IsNull() const { return data_.f.flags == kNullFlag; }
bool IsFalse() const { return flags_ == kFalseFlag; } bool IsFalse() const { return data_.f.flags == kFalseFlag; }
bool IsTrue() const { return flags_ == kTrueFlag; } bool IsTrue() const { return data_.f.flags == kTrueFlag; }
bool IsBool() const { return (flags_ & kBoolFlag) != 0; } bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; }
bool IsObject() const { return flags_ == kObjectFlag; } bool IsObject() const { return data_.f.flags == kObjectFlag; }
bool IsArray() const { return flags_ == kArrayFlag; } bool IsArray() const { return data_.f.flags == kArrayFlag; }
bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; }
bool IsInt() const { return (flags_ & kIntFlag) != 0; } bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; }
bool IsUint() const { return (flags_ & kUintFlag) != 0; } bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; }
bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; }
bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; }
bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; }
bool IsString() const { return (flags_ & kStringFlag) != 0; } bool IsString() const { return (data_.f.flags & kStringFlag) != 0; }
// Checks whether a number can be losslessly converted to a double. // Checks whether a number can be losslessly converted to a double.
bool IsLosslessDouble() const { bool IsLosslessDouble() const {
...@@ -806,7 +810,7 @@ public: ...@@ -806,7 +810,7 @@ public:
// Checks whether a number is a float (possible lossy). // Checks whether a number is a float (possible lossy).
bool IsFloat() const { bool IsFloat() const {
if ((flags_ & kDoubleFlag) == 0) if ((data_.f.flags & kDoubleFlag) == 0)
return false; return false;
double d = GetDouble(); double d = GetDouble();
return d >= -3.4028234e38 && d <= 3.4028234e38; return d >= -3.4028234e38 && d <= 3.4028234e38;
...@@ -831,7 +835,7 @@ public: ...@@ -831,7 +835,7 @@ public:
//!@name Bool //!@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 //!< Set boolean value
/*! \post IsBool() == true */ /*! \post IsBool() == true */
GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; }
...@@ -905,16 +909,16 @@ public: ...@@ -905,16 +909,16 @@ public:
//! Const member iterator //! Const member iterator
/*! \pre IsObject() == true */ /*! \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 //! Const \em past-the-end member iterator
/*! \pre IsObject() == true */ /*! \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 //! Member iterator
/*! \pre IsObject() == true */ /*! \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 //! \em Past-the-end member iterator
/*! \pre IsObject() == true */ /*! \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. //! Check whether a member exists in the object.
/*! /*!
...@@ -1024,16 +1028,17 @@ public: ...@@ -1024,16 +1028,17 @@ public:
if (o.size >= o.capacity) { if (o.size >= o.capacity) {
if (o.capacity == 0) { if (o.capacity == 0) {
o.capacity = kDefaultObjectCapacity; 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 { else {
SizeType oldCapacity = o.capacity; SizeType oldCapacity = o.capacity;
o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 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); Member* members = GetMembersPointer();
o.members[o.size].value.RawAssign(value); members[o.size].name.RawAssign(name);
members[o.size].value.RawAssign(value);
o.size++; o.size++;
return *this; return *this;
} }
...@@ -1212,18 +1217,14 @@ public: ...@@ -1212,18 +1217,14 @@ public:
MemberIterator RemoveMember(MemberIterator m) { MemberIterator RemoveMember(MemberIterator m) {
RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(data_.o.size > 0); RAPIDJSON_ASSERT(data_.o.size > 0);
RAPIDJSON_ASSERT(data_.o.members != 0); RAPIDJSON_ASSERT(GetMembersPointer() != 0);
RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd());
MemberIterator last(data_.o.members + (data_.o.size - 1)); MemberIterator last(GetMembersPointer() + (data_.o.size - 1));
if (data_.o.size > 1 && m != last) { if (data_.o.size > 1 && m != last)
// Move the last one to this place *m = *last; // Move the last one to this place
*m = *last; else
} m->~Member(); // Only one left, just destroy
else {
// Only one left, just destroy
m->~Member();
}
--data_.o.size; --data_.o.size;
return m; return m;
} }
...@@ -1253,7 +1254,7 @@ public: ...@@ -1253,7 +1254,7 @@ public:
MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) {
RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(data_.o.size > 0); RAPIDJSON_ASSERT(data_.o.size > 0);
RAPIDJSON_ASSERT(data_.o.members != 0); RAPIDJSON_ASSERT(GetMembersPointer() != 0);
RAPIDJSON_ASSERT(first >= MemberBegin()); RAPIDJSON_ASSERT(first >= MemberBegin());
RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(first <= last);
RAPIDJSON_ASSERT(last <= MemberEnd()); RAPIDJSON_ASSERT(last <= MemberEnd());
...@@ -1315,8 +1316,9 @@ public: ...@@ -1315,8 +1316,9 @@ public:
*/ */
void Clear() { void Clear() {
RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(IsArray());
for (SizeType i = 0; i < data_.a.size; ++i) GenericValue* e = GetElementsPointer();
data_.a.elements[i].~GenericValue(); for (GenericValue* v = e; v != e + data_.a.size; ++v)
v->~GenericValue();
data_.a.size = 0; data_.a.size = 0;
} }
...@@ -1328,16 +1330,16 @@ public: ...@@ -1328,16 +1330,16 @@ public:
GenericValue& operator[](SizeType index) { GenericValue& operator[](SizeType index) {
RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(IsArray());
RAPIDJSON_ASSERT(index < data_.a.size); 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]; } const GenericValue& operator[](SizeType index) const { return const_cast<GenericValue&>(*this)[index]; }
//! Element iterator //! Element iterator
/*! \pre IsArray() == true */ /*! \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 //! \em Past-the-end element iterator
/*! \pre IsArray() == true */ /*! \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 //! Constant element iterator
/*! \pre IsArray() == true */ /*! \pre IsArray() == true */
ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); } ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); }
...@@ -1354,7 +1356,7 @@ public: ...@@ -1354,7 +1356,7 @@ public:
GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) {
RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(IsArray());
if (newCapacity > data_.a.capacity) { 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; data_.a.capacity = newCapacity;
} }
return *this; return *this;
...@@ -1374,7 +1376,7 @@ public: ...@@ -1374,7 +1376,7 @@ public:
RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(IsArray());
if (data_.a.size >= data_.a.capacity) if (data_.a.size >= data_.a.capacity)
Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); 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; return *this;
} }
...@@ -1428,7 +1430,7 @@ public: ...@@ -1428,7 +1430,7 @@ public:
GenericValue& PopBack() { GenericValue& PopBack() {
RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(IsArray());
RAPIDJSON_ASSERT(!Empty()); RAPIDJSON_ASSERT(!Empty());
data_.a.elements[--data_.a.size].~GenericValue(); GetElementsPointer()[--data_.a.size].~GenericValue();
return *this; return *this;
} }
...@@ -1454,7 +1456,7 @@ public: ...@@ -1454,7 +1456,7 @@ public:
ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) {
RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(IsArray());
RAPIDJSON_ASSERT(data_.a.size > 0); RAPIDJSON_ASSERT(data_.a.size > 0);
RAPIDJSON_ASSERT(data_.a.elements != 0); RAPIDJSON_ASSERT(GetElementsPointer() != 0);
RAPIDJSON_ASSERT(first >= Begin()); RAPIDJSON_ASSERT(first >= Begin());
RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(first <= last);
RAPIDJSON_ASSERT(last <= End()); RAPIDJSON_ASSERT(last <= End());
...@@ -1471,21 +1473,21 @@ public: ...@@ -1471,21 +1473,21 @@ public:
//!@name Number //!@name Number
//@{ //@{
int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; }
unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; }
int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; }
uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; }
//! Get the value as double type. //! 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. /*! \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 { double GetDouble() const {
RAPIDJSON_ASSERT(IsNumber()); RAPIDJSON_ASSERT(IsNumber());
if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion.
if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double
if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double if ((data_.f.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) if ((data_.f.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) 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. //! Get the value as float type.
...@@ -1508,12 +1510,12 @@ public: ...@@ -1508,12 +1510,12 @@ public:
//!@name String //!@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. //! Get the length of string.
/*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). /*! 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. //! 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. /*! This version has better performance with supplied length, and also support string containing null character.
...@@ -1582,7 +1584,7 @@ public: ...@@ -1582,7 +1584,7 @@ public:
return false; return false;
for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) {
RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. 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; return false;
if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler)))
return false; return false;
...@@ -1592,13 +1594,13 @@ public: ...@@ -1592,13 +1594,13 @@ public:
case kArrayType: case kArrayType:
if (RAPIDJSON_UNLIKELY(!handler.StartArray())) if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
return false; 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))) if (RAPIDJSON_UNLIKELY(!v->Accept(handler)))
return false; return false;
return handler.EndArray(data_.a.size); return handler.EndArray(data_.a.size);
case kStringType: case kStringType:
return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0);
default: default:
RAPIDJSON_ASSERT(GetType() == kNumberType); RAPIDJSON_ASSERT(GetType() == kNumberType);
...@@ -1615,16 +1617,16 @@ private: ...@@ -1615,16 +1617,16 @@ private:
template <typename, typename, typename> friend class GenericDocument; template <typename, typename, typename> friend class GenericDocument;
enum { enum {
kBoolFlag = 0x100, kBoolFlag = 0x0008,
kNumberFlag = 0x200, kNumberFlag = 0x0010,
kIntFlag = 0x400, kIntFlag = 0x0020,
kUintFlag = 0x800, kUintFlag = 0x0040,
kInt64Flag = 0x1000, kInt64Flag = 0x0080,
kUint64Flag = 0x2000, kUint64Flag = 0x0100,
kDoubleFlag = 0x4000, kDoubleFlag = 0x0200,
kStringFlag = 0x100000, kStringFlag = 0x0400,
kCopyFlag = 0x200000, kCopyFlag = 0x0800,
kInlineStrFlag = 0x400000, kInlineStrFlag = 0x1000,
// Initial flags of different types. // Initial flags of different types.
kNullFlag = kNullType, kNullFlag = kNullType,
...@@ -1642,16 +1644,27 @@ private: ...@@ -1642,16 +1644,27 @@ private:
kObjectFlag = kObjectType, kObjectFlag = kObjectType,
kArrayFlag = kArrayType, 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 kDefaultArrayCapacity = 16;
static const SizeType kDefaultObjectCapacity = 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 { struct String {
const Ch* str;
SizeType length; SizeType length;
unsigned hashcode; //!< reserved SizeType hashcode; //!< reserved
const Ch* str;
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
// implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars
...@@ -1660,10 +1673,10 @@ private: ...@@ -1660,10 +1673,10 @@ private:
// to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as // 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 // the string terminator as well. For getting the string length back from that value just use
// "MaxSize - str[LenPos]". // "MaxSize - str[LenPos]".
// This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode,
// inline (for `UTF8`-encoded strings). // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings).
struct ShortString { struct ShortString {
enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; enum { MaxChars = sizeof(Flag::payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize };
Ch str[MaxChars]; Ch str[MaxChars];
inline static bool Usable(SizeType len) { return (MaxSize >= len); } inline static bool Usable(SizeType len) { return (MaxSize >= len); }
...@@ -1698,15 +1711,15 @@ private: ...@@ -1698,15 +1711,15 @@ private:
}; // 8 bytes }; // 8 bytes
struct Object { struct Object {
Member* members;
SizeType size; SizeType size;
SizeType capacity; SizeType capacity;
Member* members;
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
struct Array { struct Array {
GenericValue* elements;
SizeType size; SizeType size;
SizeType capacity; SizeType capacity;
GenericValue* elements;
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
union Data { union Data {
...@@ -1715,51 +1728,61 @@ private: ...@@ -1715,51 +1728,61 @@ private:
Number n; Number n;
Object o; Object o;
Array a; Array a;
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode 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 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 const GenericValue* SetElementsPointer(const GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); }
RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); }
RAPIDJSON_FORCEINLINE const Member* SetMembersPointer(const Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); }
// Initialize this value as array with initial data, without calling destructor. // Initialize this value as array with initial data, without calling destructor.
void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
flags_ = kArrayFlag; data_.f.flags = kArrayFlag;
if (count) { if (count) {
data_.a.elements = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue))); GenericValue* e = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); SetElementsPointer(e);
std::memcpy(e, values, count * sizeof(GenericValue));
} }
else else
data_.a.elements = NULL; SetElementsPointer(0);
data_.a.size = data_.a.capacity = count; data_.a.size = data_.a.capacity = count;
} }
//! Initialize this value as object with initial data, without calling destructor. //! Initialize this value as object with initial data, without calling destructor.
void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
flags_ = kObjectFlag; data_.f.flags = kObjectFlag;
if (count) { if (count) {
data_.o.members = static_cast<Member*>(allocator.Malloc(count * sizeof(Member))); Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
std::memcpy(data_.o.members, members, count * sizeof(Member)); SetMembersPointer(m);
std::memcpy(m, members, count * sizeof(Member));
} }
else else
data_.o.members = NULL; SetMembersPointer(0);
data_.o.size = data_.o.capacity = count; data_.o.size = data_.o.capacity = count;
} }
//! Initialize this value as constant string, without calling destructor. //! Initialize this value as constant string, without calling destructor.
void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT {
flags_ = kConstStringFlag; data_.f.flags = kConstStringFlag;
data_.s.str = s; SetStringPointer(s);
data_.s.length = s.length; data_.s.length = s.length;
} }
//! Initialize this value as copy string with initial data, without calling destructor. //! Initialize this value as copy string with initial data, without calling destructor.
void SetStringRaw(StringRefType s, Allocator& allocator) { void SetStringRaw(StringRefType s, Allocator& allocator) {
Ch* str = NULL; Ch* str = 0;
if(ShortString::Usable(s.length)) { if (ShortString::Usable(s.length)) {
flags_ = kShortStringFlag; data_.f.flags = kShortStringFlag;
data_.ss.SetLength(s.length); data_.ss.SetLength(s.length);
str = data_.ss.str; str = data_.ss.str;
} else { } else {
flags_ = kCopyStringFlag; data_.f.flags = kCopyStringFlag;
data_.s.length = s.length; data_.s.length = s.length;
str = static_cast<Ch *>(allocator.Malloc((s.length + 1) * sizeof(Ch))); 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)); std::memcpy(str, s, s.length * sizeof(Ch));
str[s.length] = '\0'; str[s.length] = '\0';
...@@ -1768,8 +1791,8 @@ private: ...@@ -1768,8 +1791,8 @@ private:
//! Assignment without calling destructor //! Assignment without calling destructor
void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
data_ = rhs.data_; data_ = rhs.data_;
flags_ = rhs.flags_; // data_.f.flags = rhs.data_.f.flags;
rhs.flags_ = kNullFlag; rhs.data_.f.flags = kNullFlag;
} }
template <typename SourceAllocator> template <typename SourceAllocator>
...@@ -1789,7 +1812,6 @@ private: ...@@ -1789,7 +1812,6 @@ private:
} }
Data data_; Data data_;
unsigned flags_;
}; };
//! GenericValue with UTF8 encoding //! GenericValue with UTF8 encoding
...@@ -2158,15 +2180,15 @@ GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,Sourc ...@@ -2158,15 +2180,15 @@ GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,Sourc
} }
break; break;
case kStringType: case kStringType:
if (rhs.flags_ == kConstStringFlag) { if (rhs.data_.f.flags == kConstStringFlag) {
flags_ = rhs.flags_; data_.f.flags = rhs.data_.f.flags;
data_ = *reinterpret_cast<const Data*>(&rhs.data_); data_ = *reinterpret_cast<const Data*>(&rhs.data_);
} else { } else {
SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator);
} }
break; break;
default: default:
flags_ = rhs.flags_; data_.f.flags = rhs.data_.f.flags;
data_ = *reinterpret_cast<const Data*>(&rhs.data_); data_ = *reinterpret_cast<const Data*>(&rhs.data_);
break; break;
} }
......
...@@ -265,7 +265,8 @@ ...@@ -265,7 +265,8 @@
\param x pointer to align \param x pointer to align
Some machines require strict data alignment. Currently the default uses 4 bytes 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 #ifndef RAPIDJSON_ALIGN
#if RAPIDJSON_64BIT == 1 #if RAPIDJSON_64BIT == 1
...@@ -288,6 +289,36 @@ ...@@ -288,6 +289,36 @@
#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32)) #define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
#endif #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 // RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD
......
...@@ -23,6 +23,18 @@ RAPIDJSON_DIAG_OFF(c++98-compat) ...@@ -23,6 +23,18 @@ RAPIDJSON_DIAG_OFF(c++98-compat)
using namespace rapidjson; 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) { TEST(Value, DefaultConstructor) {
Value x; Value x;
EXPECT_EQ(kNullType, x.GetType()); EXPECT_EQ(kNullType, x.GetType());
......
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