diff --git a/build/Doxyfile b/build/Doxyfile index 65de7e944..e34a3ffe9 100644 --- a/build/Doxyfile +++ b/build/Doxyfile @@ -1993,7 +1993,9 @@ INCLUDE_FILE_PATTERNS = PREDEFINED = \ RAPIDJSON_DOXYGEN_RUNNING \ - RAPIDJSON_DISABLEIF_RETURN(cond,returntype)=returntype + RAPIDJSON_REMOVEFPTR_(x)=x \ + RAPIDJSON_ENABLEIF_RETURN(cond,returntype)="RAPIDJSON_REMOVEFPTR_ returntype" \ + RAPIDJSON_DISABLEIF_RETURN(cond,returntype)="RAPIDJSON_REMOVEFPTR_ returntype" # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2002,7 +2004,8 @@ PREDEFINED = \ # definition found in the source code. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = \ + RAPIDJSON_NOEXCEPT # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have diff --git a/example/capitalize/capitalize.cpp b/example/capitalize/capitalize.cpp index b8d60860a..dd94d2438 100644 --- a/example/capitalize/capitalize.cpp +++ b/example/capitalize/capitalize.cpp @@ -31,6 +31,7 @@ struct CapitalizeFilter { return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string } bool StartObject() { return out_.StartObject(); } + bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); } bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); } bool StartArray() { return out_.StartArray(); } bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); } diff --git a/example/simplereader/simplereader.cpp b/example/simplereader/simplereader.cpp index 9914253d9..edbdb6352 100644 --- a/example/simplereader/simplereader.cpp +++ b/example/simplereader/simplereader.cpp @@ -17,6 +17,10 @@ struct MyHandler { return true; } bool StartObject() { cout << "StartObject()" << endl; return true; } + bool Key(const char* str, SizeType length, bool copy) { + cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl; + return true; + } bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; } bool StartArray() { cout << "StartArray()" << endl; return true; } bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; } diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index f5244e8c0..310fc7104 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -24,6 +24,7 @@ /*! \file document.h */ #include "reader.h" +#include "internal/meta.h" #include "internal/strfunc.h" #include // placement new @@ -38,15 +39,17 @@ RAPIDJSON_DIAG_OFF(effc++) /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_HAS_STDSTRING +#ifndef RAPIDJSON_HAS_STDSTRING #ifdef RAPIDJSON_DOXYGEN_RUNNING #define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default #endif -#ifdef RAPIDJSON_HAS_STDSTRING /*! \def RAPIDJSON_HAS_STDSTRING \ingroup RAPIDJSON_CONFIG \brief Enable RapidJSON support for \c std::string - By defining this preprocessor symbol, several convenience functions for using + By defining this preprocessor symbol to \c 1, several convenience functions for using \ref rapidjson::GenericValue with \c std::string are enabled, especially for construction and comparison. @@ -56,7 +59,6 @@ RAPIDJSON_DIAG_OFF(effc++) #endif // RAPIDJSON_HAS_STDSTRING #ifndef RAPIDJSON_NOMEMBERITERATORCLASS -#include "internal/meta.h" #include // std::iterator, std::random_access_iterator_tag #endif @@ -277,7 +279,7 @@ struct GenericStringRef { GenericValue instead. */ template - GenericStringRef(const CharType (&str)[N]) + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT : s(str), length(N-1) {} //! Explicitly create string reference from \c const character pointer @@ -300,7 +302,7 @@ struct GenericStringRef { GenericValue instead. */ explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){} + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } //! Create constant string reference from pointer and length /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue @@ -363,7 +365,7 @@ inline GenericStringRef StringRef(const CharType* str, size_t length) return GenericStringRef(str, SizeType(length)); } -#ifdef RAPIDJSON_HAS_STDSTRING +#if RAPIDJSON_HAS_STDSTRING //! Mark a string object as constant string /*! Mark a string object (e.g. \c std::string) as a "string literal". This function can be used to avoid copying a string to be referenced as a @@ -382,6 +384,22 @@ inline GenericStringRef StringRef(const std::basic_string& s } #endif +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -414,7 +432,14 @@ class GenericValue { //@{ //! Default constructor creates a null value. - GenericValue() : data_(), flags_(kNullFlag) {} + GenericValue() RAPIDJSON_NOEXCEPT : data_(), 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 + } +#endif private: //! Copy constructor is not permitted. @@ -427,7 +452,7 @@ class GenericValue { \param type Type of the value. \note Default content for number is zero. */ - GenericValue(Type type) : data_(), flags_() { + GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { static const unsigned defaultFlags[7] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kConstStringFlag, kNumberAnyFlag @@ -444,7 +469,7 @@ class GenericValue { \see CopyFrom() */ template< typename SourceAllocator > - GenericValue(const GenericValue& rhs, Allocator & allocator); + GenericValue(const GenericValue& rhs, Allocator & allocator); //! Constructor for boolean value. /*! \param b Boolean value @@ -454,28 +479,31 @@ class GenericValue { */ #ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen template - explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT #else - explicit GenericValue(bool b) + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT #endif - : data_(), flags_(b ? kTrueFlag : kFalseFlag) {} + : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + } //! Constructor for int value. - explicit GenericValue(int i) : data_(), flags_(kNumberIntFlag) { + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { data_.n.i64 = i; if (i >= 0) flags_ |= kUintFlag | kUint64Flag; } //! Constructor for unsigned value. - explicit GenericValue(unsigned u) : data_(), flags_(kNumberUintFlag) { + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { data_.n.u64 = u; if (!(u & 0x80000000)) flags_ |= kIntFlag | kInt64Flag; } //! Constructor for int64_t value. - explicit GenericValue(int64_t i64) : data_(), flags_(kNumberInt64Flag) { + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { data_.n.i64 = i64; if (i64 >= 0) { flags_ |= kNumberUint64Flag; @@ -489,7 +517,7 @@ class GenericValue { } //! Constructor for uint64_t value. - explicit GenericValue(uint64_t u64) : data_(), flags_(kNumberUint64Flag) { + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { data_.n.u64 = u64; if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) flags_ |= kInt64Flag; @@ -500,13 +528,13 @@ class GenericValue { } //! Constructor for double value. - explicit GenericValue(double d) : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } //! Constructor for constant string (i.e. do not make a copy of string) - GenericValue(const Ch* s, SizeType length) : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } //! Constructor for constant string (i.e. do not make a copy of string) - explicit GenericValue(StringRefType s) : data_(), flags_() { SetStringRaw(s); } + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { 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); } @@ -514,7 +542,7 @@ class GenericValue { //! Constructor for copy-string (i.e. do make a copy of string) GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } -#ifdef RAPIDJSON_HAS_STDSTRING +#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. */ @@ -557,19 +585,26 @@ class GenericValue { //! Assignment with move semantics. /*! \param rhs Source of the assignment. It will become a null value after assignment. */ - GenericValue& operator=(GenericValue& rhs) { + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { RAPIDJSON_ASSERT(this != &rhs); this->~GenericValue(); RawAssign(rhs); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + //! Assignment of constant string reference (no copy) /*! \param str Constant string reference to be assigned \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. \see GenericStringRef, operator=(T) */ - GenericValue& operator=(StringRefType str) { + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { GenericValue s(str); return *this = s; } @@ -587,7 +622,7 @@ class GenericValue { use \ref SetBool() instead. */ template - RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) operator=(T value) { GenericValue v(value); return *this = v; @@ -600,10 +635,10 @@ class GenericValue { \param allocator Allocator to use for copying */ template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { RAPIDJSON_ASSERT((void*)this != (void const*)&rhs); this->~GenericValue(); - new (this) GenericValue(rhs,allocator); + new (this) GenericValue(rhs, allocator); return *this; } @@ -612,7 +647,7 @@ class GenericValue { \param other Another value. \note Constant complexity. */ - GenericValue& Swap(GenericValue& other) { + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { GenericValue temp; temp.RawAssign(*this); RawAssign(other); @@ -622,7 +657,7 @@ class GenericValue { //! Prepare Value for move semantics /*! \return *this */ - GenericValue& Move() { return *this; } + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } //@} //!@name Equal-to and not-equal-to operators @@ -632,7 +667,9 @@ class GenericValue { \note If an object contains duplicated named member, comparing equality with any object is always \c false. \note Linear time complexity (number of all values in the subtree and total lengths of all strings). */ - bool operator==(const GenericValue& rhs) const { + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; if (GetType() != rhs.GetType()) return false; @@ -641,7 +678,7 @@ class GenericValue { if (data_.o.size != rhs.data_.o.size) return false; for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { - ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) return false; } @@ -659,7 +696,7 @@ class GenericValue { return StringEqual(rhs); case kNumberType: - if (IsDouble() || rhs.GetDouble()) + if (IsDouble() || rhs.IsDouble()) return GetDouble() == rhs.GetDouble(); // May convert one operand from integer to double. else return data_.n.u64 == rhs.data_.n.u64; @@ -672,7 +709,7 @@ class GenericValue { //! Equal-to operator with const C-string pointer bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } -#ifdef RAPIDJSON_HAS_STDSTRING +#if RAPIDJSON_HAS_STDSTRING //! Equal-to operator with string object /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ @@ -682,22 +719,31 @@ class GenericValue { //! Equal-to operator with primitive types /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false */ - template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer, bool) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } //! Not-equal-to operator with arbitrary types /*! \return !(*this == rhs) */ - template bool operator!=(const T& rhs) const { return !(*this == rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } //! Equal-to operator with arbitrary types (symmetric version) /*! \return (rhs == lhs) */ - template friend bool operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } //! Not-Equal-to operator with arbitrary types (symmetric version) /*! \return !(rhs == lhs) */ - template friend bool operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } //@} //!@name Type @@ -766,7 +812,8 @@ class GenericValue { // This version is faster because it does not need a StrLen(). // It can also handle string with null character. - GenericValue& operator[](const GenericValue& name) { + template + GenericValue& operator[](const GenericValue& name) { MemberIterator member = FindMember(name); if (member != MemberEnd()) return member->value; @@ -776,7 +823,8 @@ class GenericValue { return NullValue; } } - const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } //! Const member iterator /*! \pre IsObject() == true */ @@ -810,7 +858,8 @@ class GenericValue { \note It is better to use FindMember() directly if you need the obtain the value as well. \note Linear time complexity. */ - bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } //! Find member by name. /*! @@ -844,7 +893,8 @@ class GenericValue { \c std::map, this has been changed to MemberEnd() now. \note Linear time complexity. */ - MemberIterator FindMember(const GenericValue& name) { + template + MemberIterator FindMember(const GenericValue& name) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); MemberIterator member = MemberBegin(); @@ -853,7 +903,7 @@ class GenericValue { break; return member; } - ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } //! Add a member (name-value pair) to the object. /*! \param name A string value as name of member. @@ -877,7 +927,7 @@ class GenericValue { } else { SizeType oldCapacity = o.capacity; - o.capacity *= 2; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); } } @@ -887,6 +937,23 @@ class GenericValue { return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Add a member (name-value pair) to the object. /*! \param name A constant string reference as name of member. \param value Value of any type. @@ -934,7 +1001,7 @@ class GenericValue { \note Amortized Constant time complexity. */ template - RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) AddMember(StringRefType name, T value, Allocator& allocator) { GenericValue n(name); GenericValue v(value); @@ -963,7 +1030,8 @@ class GenericValue { return RemoveMember(n); } - bool RemoveMember(const GenericValue& name) { + template + bool RemoveMember(const GenericValue& name) { MemberIterator m = FindMember(name); if (m != MemberEnd()) { RemoveMember(m); @@ -1122,11 +1190,17 @@ int z = a[0u].GetInt(); // This works too. GenericValue& PushBack(GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsArray()); if (data_.a.size >= data_.a.capacity) - Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : data_.a.capacity * 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); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Append a constant string reference at the end of the array. /*! \param value Constant string reference to be appended. \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). @@ -1158,7 +1232,7 @@ int z = a[0u].GetInt(); // This works too. \note Amortized constant time complexity. */ template - RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) PushBack(T value, Allocator& allocator) { GenericValue v(value); return PushBack(v, allocator); @@ -1239,12 +1313,12 @@ int z = a[0u].GetInt(); // This works too. //!@name String //@{ - const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return data_.s.str; } + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } //! 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 data_.s.length; } + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((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. @@ -1281,7 +1355,7 @@ int z = a[0u].GetInt(); // This works too. */ GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } -#ifdef RAPIDJSON_HAS_STDSTRING +#if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. /*! \param s source string. \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). @@ -1312,7 +1386,7 @@ int z = a[0u].GetInt(); // This works too. if (!handler.StartObject()) return false; for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { - if (!handler.String(m->name.data_.s.str, m->name.data_.s.length, (m->name.flags_ & kCopyFlag) != 0)) + if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) return false; if (!m->value.Accept(handler)) return false; @@ -1328,7 +1402,7 @@ int z = a[0u].GetInt(); // This works too. return handler.EndArray(data_.a.size); case kStringType: - return handler.String(data_.s.str, data_.s.length, (flags_ & kCopyFlag) != 0); + return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); case kNumberType: if (IsInt()) return handler.Int(data_.n.i.i); @@ -1344,8 +1418,8 @@ int z = a[0u].GetInt(); // This works too. } private: - template - friend class GenericDocument; + template friend class GenericValue; + template friend class GenericDocument; enum { kBoolFlag = 0x100, @@ -1357,6 +1431,7 @@ int z = a[0u].GetInt(); // This works too. kDoubleFlag = 0x4000, kStringFlag = 0x100000, kCopyFlag = 0x200000, + kInlineStrFlag = 0x400000, // Initial flags of different types. kNullFlag = kNullType, @@ -1370,6 +1445,7 @@ int z = a[0u].GetInt(); // This works too. kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, kConstStringFlag = kStringType | kStringFlag, kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, kObjectFlag = kObjectType, kArrayFlag = kArrayType, @@ -1385,6 +1461,23 @@ int z = a[0u].GetInt(); // This works too. unsigned hashcode; //!< reserved }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // 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). + struct ShortString { + enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = (Ch)(MaxSize - len); } + inline SizeType GetLength() const { return (SizeType)(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + // By using proper binary layout, retrieval of different integer types do not need conversions. union Number { #if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN @@ -1425,6 +1518,7 @@ int z = a[0u].GetInt(); // This works too. union Data { String s; + ShortString ss; Number n; Object o; Array a; @@ -1447,7 +1541,7 @@ int z = a[0u].GetInt(); // This works too. } //! Initialize this value as constant string, without calling destructor. - void SetStringRaw(StringRefType s) { + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { flags_ = kConstStringFlag; data_.s.str = s; data_.s.length = s.length; @@ -1455,26 +1549,42 @@ int z = a[0u].GetInt(); // This works too. //! Initialize this value as copy string with initial data, without calling destructor. void SetStringRaw(StringRefType s, Allocator& allocator) { - flags_ = kCopyStringFlag; - data_.s.str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); - data_.s.length = s.length; - memcpy(const_cast(data_.s.str), s, s.length * sizeof(Ch)); - const_cast(data_.s.str)[s.length] = '\0'; + Ch* str = NULL; + if(ShortString::Usable(s.length)) { + flags_ = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + flags_ = kCopyStringFlag; + data_.s.length = s.length; + str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); + data_.s.str = str; + } + memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; } //! Assignment without calling destructor - void RawAssign(GenericValue& rhs) { + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { data_ = rhs.data_; flags_ = rhs.flags_; rhs.flags_ = kNullFlag; } - bool StringEqual(const GenericValue& rhs) const { + template + bool StringEqual(const GenericValue& rhs) const { RAPIDJSON_ASSERT(IsString()); RAPIDJSON_ASSERT(rhs.IsString()); - return data_.s.length == rhs.data_.s.length && - (data_.s.str == rhs.data_.s.str // fast path for constant string - || memcmp(data_.s.str, rhs.data_.s.str, sizeof(Ch) * data_.s.length) == 0); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (memcmp(str1, str2, sizeof(Ch) * len1) == 0); } Data data_; @@ -1663,7 +1773,7 @@ class GenericDocument : public GenericValue { // callers of the following private Handler functions template friend class GenericReader; // for parsing - friend class GenericValue; // for deep copying + template friend class GenericValue; // for deep copying // Implementation of Handler bool Null() { new (stack_.template Push()) ValueType(); return true; } @@ -1684,6 +1794,8 @@ class GenericDocument : public GenericValue { bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + bool EndObject(SizeType memberCount) { typename ValueType::Member* members = stack_.template Pop(memberCount); stack_.template Top()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index b19a9f4b7..dbe5450d6 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -21,57 +21,129 @@ #ifndef RAPIDJSON_INTERNAL_META_H_ #define RAPIDJSON_INTERNAL_META_H_ +#ifndef RAPIDJSON_RAPIDJSON_H_ +#error not yet included. Do not include this file directly. +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif //@cond RAPIDJSON_INTERNAL namespace rapidjson { namespace internal { -template struct IntegralC { enum { Value = N }; }; -template struct BoolType : IntegralC {}; -struct TrueType : BoolType {}; -struct FalseType : BoolType {}; +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; template struct RemoveConst { typedef T Type; }; template struct RemoveConst { typedef T Type; }; -template struct SelectIfCond; -template struct SelectIfCond { typedef T1 Type; }; -template struct SelectIfCond { typedef T2 Type; }; - -template -struct SelectIf : SelectIfCond {}; - -template -struct MaybeAddConst : SelectIfCond {}; -template struct IsSame : FalseType {}; -template struct IsSame : TrueType {}; +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; template struct IsConst : FalseType {}; template struct IsConst : TrueType {}; +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + template struct IsPointer : FalseType {}; template struct IsPointer : TrueType {}; -template -struct IsMoreConst { - enum { Value = - ( IsSame< typename RemoveConst::Type, typename RemoveConst::Type>::Value - && ( IsConst::Value >= IsConst::Value ) ) +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; }; -template struct EnableIfCond; -template struct EnableIfCond { typedef T Type; }; +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; template struct EnableIfCond { /* empty */ }; -template -struct DisableIfCond : EnableIfCond {}; +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; template struct EnableIf : EnableIfCond {}; @@ -80,26 +152,37 @@ template struct DisableIf : DisableIfCond {}; // SFINAE helpers -struct SfinaeResultTag {}; -template struct RemoveSfinaeFptr {}; -template struct RemoveSfinaeFptr { typedef T Type; }; +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; #define RAPIDJSON_REMOVEFPTR_(type) \ - typename ::rapidjson::internal::RemoveSfinaeFptr \ - < ::rapidjson::internal::SfinaeResultTag&(*) type>::Type + typename ::rapidjson::internal::RemoveSfinaeTag \ + < ::rapidjson::internal::SfinaeTag&(*) type>::Type #define RAPIDJSON_ENABLEIF(cond) \ typename ::rapidjson::internal::EnableIf \ ::Type * = NULL +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::rapidjson::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::rapidjson::internal::EnableIf \ + ::Type + #define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ - typename ::rapidjson::internal::DisableIf::Type + typename ::rapidjson::internal::DisableIf \ + ::Type } // namespace internal } // namespace rapidjson //@endcond -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index f89be1b10..4eac8d76f 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -88,6 +88,8 @@ class PrettyWriter : public Writer= sizeof(typename Base::Level)); @@ -135,6 +137,7 @@ class PrettyWriter : public Writer struct StaticAssertTest {}; /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF -#if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,2,0)) +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) #define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) #define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) @@ -326,7 +331,7 @@ template struct StaticAssertTest {}; RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) // push/pop support in Clang and GCC>=4.6 -#if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,6,0)) +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) #define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) #define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) #else // GCC >= 4.2, < 4.6 @@ -352,6 +357,42 @@ template struct StaticAssertTest {}; #endif // RAPIDJSON_DIAG_* +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) +#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_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + //!@endcond /////////////////////////////////////////////////////////////////////////////// diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 60d1f0a37..4aa98b18f 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -48,6 +48,11 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_DIAG_OFF(4702) // unreachable code #endif +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define RAPIDJSON_NOTHING /* deliberately empty */ #ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN @@ -156,6 +161,7 @@ concept Handler { bool Double(double d); bool String(const Ch* str, SizeType length, bool copy); bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); bool EndObject(SizeType memberCount); bool StartArray(); bool EndArray(SizeType elementCount); @@ -185,6 +191,7 @@ struct BaseReaderHandler { bool Double(double) { return static_cast(*this).Default(); } bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } bool EndObject(SizeType) { return static_cast(*this).Default(); } bool StartArray() { return static_cast(*this).Default(); } bool EndArray(SizeType) { return static_cast(*this).Default(); } @@ -475,7 +482,7 @@ class GenericReader { if (is.Peek() != '"') RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); - ParseString(is, handler); + ParseString(is, handler, true); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespace(is); @@ -627,27 +634,30 @@ class GenericReader { // Parse string and generate String event. Different code paths for kParseInsituFlag. template - void ParseString(InputStream& is, Handler& handler) { + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { internal::StreamLocalCopy copy(is); InputStream& s(copy.s); + bool success = false; if (parseFlags & kParseInsituFlag) { typename InputStream::Ch *head = s.PutBegin(); ParseStringToStream(s, s); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - if (!handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + const typename TargetEncoding::Ch* const str = (typename TargetEncoding::Ch*)head; + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); } else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - size_t length = stackStream.Length(); - if (!handler.String(stackStream.Pop(), SizeType(length - 1), true)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); } + if (!success) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } // Parse string to an output is @@ -712,29 +722,35 @@ class GenericReader { } template - class NumberStream { + class NumberStream {}; + + template + class NumberStream { public: - NumberStream(GenericReader& reader, InputStream& is) : reader(reader), is(is) {} + NumberStream(GenericReader& reader, InputStream& is) : is(is) { (void)reader; } + ~NumberStream() {} + Ch Peek() { return is.Peek(); } Ch Take() { return is.Take(); } size_t Tell() { return is.Tell(); } const char* Pop() { return 0; } - private: + protected: NumberStream& operator=(const NumberStream&); - GenericReader& reader; InputStream& is; }; template - struct NumberStream { + class NumberStream : public NumberStream { + typedef NumberStream Base; public: - NumberStream(GenericReader& reader, InputStream& is) : reader(reader), is(is), stackStream(reader.stack_) {} + NumberStream(GenericReader& reader, InputStream& is) : NumberStream(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} Ch Take() { - stackStream.Put((char)is.Peek()); - return is.Take(); + stackStream.Put((char)Base::is.Peek()); + return Base::is.Take(); } const char* Pop() { @@ -743,15 +759,22 @@ class GenericReader { } private: - GenericReader& reader; - InputStream& is; StackStream stackStream; }; + double StrtodFastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); + } + template void ParseNumber(InputStream& is, Handler& handler) { internal::StreamLocalCopy copy(is); - NumberStream s(*this, copy.s); + NumberStream s(*this, copy.s); // Parse minus bool minus = false; @@ -800,11 +823,13 @@ class GenericReader { // Parse 64bit int bool useDouble = false; bool useStrtod = false; + double d = 0.0; if (use64bit) { if (minus) while (s.Peek() >= '0' && s.Peek() <= '9') { if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808 if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') { + d = i64; useDouble = true; break; } @@ -814,6 +839,7 @@ class GenericReader { while (s.Peek() >= '0' && s.Peek() <= '9') { if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615 if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') { + d = i64; useDouble = true; break; } @@ -823,9 +849,18 @@ class GenericReader { // Force double for big integer if (useDouble) { - while (s.Peek() >= '0' && s.Peek() <= '9') - s.Take(); - useStrtod = true; + if (parseFlags & kParseFullPrecisionFlag) { + while (s.Peek() >= '0' && s.Peek() <= '9') + s.Take(); + useStrtod = true; + } + else { + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + d = d * 10 + (s.Take() - '0'); + } + } } // Parse frac = decimal-point 1*DIGIT @@ -834,27 +869,28 @@ class GenericReader { s.Take(); if (!useDouble) { - if (!use64bit) { - i64 = i; - use64bit = true; - } - + d = use64bit ? i64 : i; + useDouble = true; + while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) { - useStrtod = true; + if (d >= 9007199254740991.0) { + if (parseFlags & kParseFullPrecisionFlag) + useStrtod = true; break; } else { - i64 = i64 * 10 + static_cast(s.Take() - '0'); + d = d * 10.0 + static_cast(s.Take() - '0'); --expFrac; } } } - useDouble = true; - while (s.Peek() >= '0' && s.Peek() <= '9') { - s.Take(); + //s.Take(); + if (parseFlags & kParseFullPrecisionFlag) + s.Take(); + else + d = d * 10 + (s.Take() - '0'); --expFrac; } @@ -865,7 +901,10 @@ class GenericReader { // Parse exp = e [ minus / plus ] 1*DIGIT int exp = 0; if (s.Peek() == 'e' || s.Peek() == 'E') { - useDouble = true; + if (!useDouble) { + d = use64bit ? i64 : i; + useDouble = true; + } s.Take(); bool expMinus = false; @@ -893,43 +932,48 @@ class GenericReader { // Finish parsing, call event according to the type of number. bool cont = true; - - // Pop stack no matter if it will be used or not. - const char* str = s.Pop(); + const char* str = s.Pop(); // Pop stack no matter if it will be used or not. if (useDouble) { int p = exp + expFrac; - double d; - double significand = use64bit ? i64 : i; - - // Use fast path for string-to-double conversion if possible - // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - if (!useStrtod && p > 22) { - if (p < 22 + 16) { - // Fast Path Cases In Disguise - significand *= internal::Pow10(p - 22); - p = 22; + if (parseFlags & kParseFullPrecisionFlag) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (!useStrtod && p > 22) { + if (p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + else + useStrtod = true; } - else - useStrtod = true; - } - if (!useStrtod && p >= -22 && significand <= 9007199254740991.0) { // 2^53 - 1 - if (p >= 0) - d = significand * internal::Pow10(p); - else - d = significand / internal::Pow10(-p); - - if (minus) - d = -d; + if (!useStrtod && p >= -22 && d <= 9007199254740991.0) { // 2^53 - 1 + d = StrtodFastPath(d, p); + if (minus) + d = -d; + } + else { + char* end = 0; + d = strtod(str, &end); + RAPIDJSON_ASSERT(*end == '\0'); // Should have consumed the whole string. + + if (d == HUGE_VAL || d == -HUGE_VAL) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + } } else { - char* end = 0; - d = strtod(str, &end); - RAPIDJSON_ASSERT(*end == '\0'); // Should have consumed the whole string. + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = StrtodFastPath(d, exp); + d = StrtodFastPath(d, expFrac); + } + else + d = StrtodFastPath(d, p); - if (d == HUGE_VAL || d == -HUGE_VAL) - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + if (minus) + d = -d; } cont = handler.Double(d); } @@ -1247,7 +1291,7 @@ class GenericReader { } case IterativeParsingMemberKeyState: - ParseString(is, handler); + ParseString(is, handler, true); if (HasParseError()) return IterativeParsingErrorState; else @@ -1410,6 +1454,10 @@ typedef GenericReader, UTF8<> > Reader; } // namespace rapidjson +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + #ifdef _MSC_VER RAPIDJSON_DIAG_POP #endif diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 73e4aff59..fb6601ef9 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -131,6 +131,8 @@ class Writer { return WriteStartObject(); } + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + bool EndObject(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); @@ -165,6 +167,7 @@ class Writer { //! Simpler but slower overload. bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } //@} diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index bbfbae5b1..a82aacfa8 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -103,8 +103,8 @@ struct ParseDoubleHandler : BaseReaderHandler, ParseDoubleHandler> { double actual_; }; -TEST(Reader, ParseNumberHandler) { -#define TEST_NUMBER(Handler, str, x) \ +TEST(Reader, ParseNumber_Integer) { +#define TEST_INTEGER(Handler, str, x) \ { \ StringStream s(str); \ Handler h; \ @@ -114,67 +114,19 @@ TEST(Reader, ParseNumberHandler) { EXPECT_EQ(double(x), h.actual_); \ } -#define TEST_DOUBLE(str, x) \ - { \ - StringStream s(str); \ - ParseDoubleHandler h; \ - Reader reader; \ - reader.Parse(s, h); \ - EXPECT_EQ(1u, h.step_); \ - EXPECT_EQ(x, h.actual_); \ - if (x != h.actual_) \ - printf(" Actual: %.17g\nExpected: %.17g\n", h.actual_, x);\ - } + TEST_INTEGER(ParseUintHandler, "0", 0); + TEST_INTEGER(ParseUintHandler, "123", 123); + TEST_INTEGER(ParseUintHandler, "2147483648", 2147483648u); // 2^31 - 1 (cannot be stored in int) + TEST_INTEGER(ParseUintHandler, "4294967295", 4294967295u); - TEST_NUMBER(ParseUintHandler, "0", 0); - TEST_NUMBER(ParseUintHandler, "123", 123); - TEST_NUMBER(ParseUintHandler, "2147483648", 2147483648u); // 2^31 - 1 (cannot be stored in int) - TEST_NUMBER(ParseUintHandler, "4294967295", 4294967295u); - - TEST_NUMBER(ParseIntHandler, "-123", -123); - TEST_NUMBER(ParseIntHandler, "-2147483648", -2147483648LL); // -2^31 (min of int) - - TEST_NUMBER(ParseUint64Handler, "4294967296", 4294967296ULL); // 2^32 (max of unsigned + 1, force to use uint64_t) - TEST_NUMBER(ParseUint64Handler, "18446744073709551615", 18446744073709551615ULL); // 2^64 - 1 (max of uint64_t) - - TEST_NUMBER(ParseInt64Handler, "-2147483649", -2147483649LL); // -2^31 -1 (min of int - 1, force to use int64_t) - TEST_NUMBER(ParseInt64Handler, "-9223372036854775808", (-9223372036854775807LL - 1)); // -2^63 (min of int64_t) - - TEST_DOUBLE("0.0", 0.0); - TEST_DOUBLE("1.0", 1.0); - TEST_DOUBLE("-1.0", -1.0); - TEST_DOUBLE("1.5", 1.5); - TEST_DOUBLE("-1.5", -1.5); - TEST_DOUBLE("3.1416", 3.1416); - TEST_DOUBLE("1E10", 1E10); - TEST_DOUBLE("1e10", 1e10); - TEST_DOUBLE("1E+10", 1E+10); - TEST_DOUBLE("1E-10", 1E-10); - TEST_DOUBLE("-1E10", -1E10); - TEST_DOUBLE("-1e10", -1e10); - TEST_DOUBLE("-1E+10", -1E+10); - TEST_DOUBLE("-1E-10", -1E-10); - TEST_DOUBLE("1.234E+10", 1.234E+10); - TEST_DOUBLE("1.234E-10", 1.234E-10); - TEST_DOUBLE("1.79769e+308", 1.79769e+308); - TEST_DOUBLE("2.22507e-308", 2.22507e-308); - TEST_DOUBLE("-1.79769e+308", -1.79769e+308); - TEST_DOUBLE("-2.22507e-308", -2.22507e-308); - TEST_DOUBLE("4.9406564584124654e-324", 4.9406564584124654e-324); // minimum denormal - TEST_DOUBLE("1e-10000", 0.0); // must underflow - TEST_DOUBLE("18446744073709551616", 18446744073709551616.0); // 2^64 (max of uint64_t + 1, force to use double) - TEST_DOUBLE("-9223372036854775809", -9223372036854775809.0); // -2^63 - 1(min of int64_t + 1, force to use double) - TEST_DOUBLE("0.9868011474609375", 0.9868011474609375); // https://github.com/miloyip/rapidjson/issues/120 - TEST_DOUBLE("123e34", 123e34); // Fast Path Cases In Disguise + TEST_INTEGER(ParseIntHandler, "-123", -123); + TEST_INTEGER(ParseIntHandler, "-2147483648", -2147483648LL); // -2^31 (min of int) - { - char n1e308[310]; // '1' followed by 308 '0' - n1e308[0] = '1'; - for (int i = 1; i < 309; i++) - n1e308[i] = '0'; - n1e308[309] = '\0'; - TEST_DOUBLE(n1e308, 1E308); - } + TEST_INTEGER(ParseUint64Handler, "4294967296", 4294967296ULL); // 2^32 (max of unsigned + 1, force to use uint64_t) + TEST_INTEGER(ParseUint64Handler, "18446744073709551615", 18446744073709551615ULL); // 2^64 - 1 (max of uint64_t) + + TEST_INTEGER(ParseInt64Handler, "-2147483649", -2147483649LL); // -2^31 -1 (min of int - 1, force to use int64_t) + TEST_INTEGER(ParseInt64Handler, "-9223372036854775808", (-9223372036854775807LL - 1)); // -2^63 (min of int64_t) // Random test for uint32_t/int32_t { @@ -189,11 +141,11 @@ TEST(Reader, ParseNumberHandler) { char buffer[32]; *internal::u32toa(u.u, buffer) = '\0'; - TEST_NUMBER(ParseUintHandler, buffer, u.u); + TEST_INTEGER(ParseUintHandler, buffer, u.u); if (u.i < 0) { *internal::i32toa(u.i, buffer) = '\0'; - TEST_NUMBER(ParseIntHandler, buffer, u.i); + TEST_INTEGER(ParseIntHandler, buffer, u.i); } } } @@ -213,41 +165,109 @@ TEST(Reader, ParseNumberHandler) { char buffer[32]; if (u.u >= 4294967296ULL) { *internal::u64toa(u.u, buffer) = '\0'; - TEST_NUMBER(ParseUint64Handler, buffer, u.u); + TEST_INTEGER(ParseUint64Handler, buffer, u.u); } if (u.i <= -2147483649LL) { *internal::i64toa(u.i, buffer) = '\0'; - TEST_NUMBER(ParseInt64Handler, buffer, u.i); + TEST_INTEGER(ParseInt64Handler, buffer, u.i); } } } +#undef TEST_INTEGER +} - // Random test for double - { - union { - double d; - uint64_t u; - }u; - Random r; +template +static void TestParseDouble() { +#define TEST_DOUBLE(fullPrecision, str, x) \ + { \ + StringStream s(str); \ + ParseDoubleHandler h; \ + Reader reader; \ + reader.Parse(s, h); \ + EXPECT_EQ(1u, h.step_); \ + if (fullPrecision) { \ + EXPECT_EQ(x, h.actual_); \ + if (x != h.actual_) \ + printf(" String: %s\n Actual: %.17g\nExpected: %.17g\n", str, h.actual_, x); \ + } \ + else \ + EXPECT_DOUBLE_EQ(x, h.actual_); \ + } - for (unsigned i = 0; i < 100000; i++) { - do { - // Need to call r() in two statements for cross-platform coherent sequence. - u.u = uint64_t(r()) << 32; - u.u |= uint64_t(r()); - } while (isnan(u.d) || isinf(u.d)); +TEST_DOUBLE(fullPrecision, "0.0", 0.0); +TEST_DOUBLE(fullPrecision, "1.0", 1.0); +TEST_DOUBLE(fullPrecision, "-1.0", -1.0); +TEST_DOUBLE(fullPrecision, "1.5", 1.5); +TEST_DOUBLE(fullPrecision, "-1.5", -1.5); +TEST_DOUBLE(fullPrecision, "3.1416", 3.1416); +TEST_DOUBLE(fullPrecision, "1E10", 1E10); +TEST_DOUBLE(fullPrecision, "1e10", 1e10); +TEST_DOUBLE(fullPrecision, "1E+10", 1E+10); +TEST_DOUBLE(fullPrecision, "1E-10", 1E-10); +TEST_DOUBLE(fullPrecision, "-1E10", -1E10); +TEST_DOUBLE(fullPrecision, "-1e10", -1e10); +TEST_DOUBLE(fullPrecision, "-1E+10", -1E+10); +TEST_DOUBLE(fullPrecision, "-1E-10", -1E-10); +TEST_DOUBLE(fullPrecision, "1.234E+10", 1.234E+10); +TEST_DOUBLE(fullPrecision, "1.234E-10", 1.234E-10); +TEST_DOUBLE(fullPrecision, "1.79769e+308", 1.79769e+308); +TEST_DOUBLE(fullPrecision, "2.22507e-308", 2.22507e-308); +TEST_DOUBLE(fullPrecision, "-1.79769e+308", -1.79769e+308); +TEST_DOUBLE(fullPrecision, "-2.22507e-308", -2.22507e-308); +TEST_DOUBLE(fullPrecision, "4.9406564584124654e-324", 4.9406564584124654e-324); // minimum denormal +TEST_DOUBLE(fullPrecision, "1e-10000", 0.0); // must underflow +TEST_DOUBLE(fullPrecision, "18446744073709551616", 18446744073709551616.0); // 2^64 (max of uint64_t + 1, force to use double) +TEST_DOUBLE(fullPrecision, "-9223372036854775809", -9223372036854775809.0); // -2^63 - 1(min of int64_t + 1, force to use double) +TEST_DOUBLE(fullPrecision, "0.9868011474609375", 0.9868011474609375); // https://github.com/miloyip/rapidjson/issues/120 +TEST_DOUBLE(fullPrecision, "123e34", 123e34); // Fast Path Cases In Disguise + +{ + char n1e308[310]; // '1' followed by 308 '0' + n1e308[0] = '1'; + for (int i = 1; i < 309; i++) + n1e308[i] = '0'; + n1e308[309] = '\0'; + TEST_DOUBLE(fullPrecision, n1e308, 1E308); +} - char buffer[32]; - *internal::dtoa(u.d, buffer) = '\0'; - TEST_DOUBLE(buffer, u.d); - } +// Random test for double +{ + union { + double d; + uint64_t u; + }u; + Random r; + + for (unsigned i = 0; i < 100000; i++) { + do { + // Need to call r() in two statements for cross-platform coherent sequence. + u.u = uint64_t(r()) << 32; + u.u |= uint64_t(r()); + } while (std::isnan(u.d) || std::isinf(u.d) +#ifdef _MSC_VER + // VC's strtod() has problem with denormal numbers + || !std::isnormal(u.d) +#endif + ); + + char buffer[32]; + *internal::dtoa(u.d, buffer) = '\0'; + TEST_DOUBLE(fullPrecision, buffer, u.d); } +} -#undef TEST_NUMBER #undef TEST_DOUBLE } +TEST(Reader, ParseNumber_NormalPrecisionDouble) { + TestParseDouble(); +} + +TEST(Reader, ParseNumber_FullPrecisionDouble) { + TestParseDouble(); +} + TEST(Reader, ParseNumber_Error) { #define TEST_NUMBER_ERROR(errorCode, str) \ { \ @@ -894,9 +914,10 @@ struct IterativeParsingReaderHandler { const static int LOG_DOUBLE = -7; const static int LOG_STRING = -8; const static int LOG_STARTOBJECT = -9; - const static int LOG_ENDOBJECT = -10; - const static int LOG_STARTARRAY = -11; - const static int LOG_ENDARRAY = -12; + const static int LOG_KEY = -10; + const static int LOG_ENDOBJECT = -11; + const static int LOG_STARTARRAY = -12; + const static int LOG_ENDARRAY = -13; const static size_t LogCapacity = 256; int Logs[LogCapacity]; @@ -923,6 +944,8 @@ struct IterativeParsingReaderHandler { bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTOBJECT; return true; } + bool Key (const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_KEY; return true; } + bool EndObject(SizeType c) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_ENDOBJECT; @@ -955,7 +978,7 @@ TEST(Reader, IterativeParsing_General) { handler.LOG_STARTARRAY, handler.LOG_INT, handler.LOG_STARTOBJECT, - handler.LOG_STRING, + handler.LOG_KEY, handler.LOG_STARTARRAY, handler.LOG_INT, handler.LOG_INT, @@ -993,7 +1016,7 @@ TEST(Reader, IterativeParsing_Count) { handler.LOG_STARTOBJECT, handler.LOG_ENDOBJECT, 0, handler.LOG_STARTOBJECT, - handler.LOG_STRING, + handler.LOG_KEY, handler.LOG_INT, handler.LOG_ENDOBJECT, 1, handler.LOG_STARTARRAY, diff --git a/test/unittest/unittest.h b/test/unittest/unittest.h index 861bb3b36..7dfcca219 100644 --- a/test/unittest/unittest.h +++ b/test/unittest/unittest.h @@ -89,15 +89,9 @@ inline FILE* TempFile(char *filename) { #pragma warning(disable : 4127) #endif -class AssertException : public std::exception { +class AssertException : public std::logic_error { public: - AssertException(const char* w) : what_(w) {} - AssertException(const AssertException& other) : what_(other.what_) {} - AssertException& operator=(const AssertException& rhs) { what_ = rhs.what_; return *this; } - virtual const char* what() const throw() { return what_; } - -private: - const char* what_; + AssertException(const char* w) : std::logic_error(w) {} }; #define RAPIDJSON_ASSERT(x) if (!(x)) throw AssertException(RAPIDJSON_STRINGIFY(x)) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index a234e1f1a..c40df876e 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -24,7 +24,7 @@ using namespace rapidjson; -TEST(Value, default_constructor) { +TEST(Value, DefaultConstructor) { Value x; EXPECT_EQ(kNullType, x.GetType()); EXPECT_TRUE(x.IsNull()); @@ -38,7 +38,32 @@ TEST(Value, default_constructor) { // Value y = x; //} -TEST(Value, assignment_operator) { +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +TEST(Value, MoveConstructor) { + typedef GenericValue, CrtAllocator> Value; + Value::AllocatorType allocator; + + Value x((Value(kArrayType))); + x.Reserve(4u, allocator); + x.PushBack(1, allocator).PushBack(2, allocator).PushBack(3, allocator).PushBack(4, allocator); + EXPECT_TRUE(x.IsArray()); + EXPECT_EQ(4u, x.Size()); + + // Value y(x); // should not compile + Value y(std::move(x)); + EXPECT_TRUE(x.IsNull()); + EXPECT_TRUE(y.IsArray()); + EXPECT_EQ(4u, y.Size()); + + // Value z = y; // should not compile + Value z = std::move(y); + EXPECT_TRUE(y.IsNull()); + EXPECT_TRUE(z.IsArray()); + EXPECT_EQ(4u, z.Size()); +} +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +TEST(Value, AssignmentOperator) { Value x(1234); Value y; y = x; @@ -63,6 +88,22 @@ TEST(Value, assignment_operator) { y = StringRef(mstr); EXPECT_TRUE(y.IsString()); EXPECT_EQ(y.GetString(),mstr); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + // C++11 move assignment + x = Value("World"); + EXPECT_TRUE(x.IsString()); + EXPECT_STREQ("World", x.GetString()); + + x = std::move(y); + EXPECT_TRUE(y.IsNull()); + EXPECT_TRUE(x.IsString()); + EXPECT_EQ(x.GetString(), mstr); + + y = std::move(Value().SetInt(1234)); + EXPECT_TRUE(y.IsInt()); + EXPECT_EQ(1234, y); +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS } template @@ -81,7 +122,7 @@ void TestUnequal(const A& a, const B& b) { EXPECT_TRUE (b != a); } -TEST(Value, equalto_operator) { +TEST(Value, EqualtoOperator) { Value::AllocatorType allocator; Value x(kObjectType); x.AddMember("hello", "world", allocator) @@ -105,16 +146,33 @@ TEST(Value, equalto_operator) { TestEqual(x["i"], 123); TestEqual(x["pi"], 3.14); - // Test operator==() - Value y; - y.CopyFrom(x, allocator); + // Test operator==() (including different allocators) + CrtAllocator crtAllocator; + GenericValue, CrtAllocator> y; + GenericDocument, CrtAllocator> z(&crtAllocator); + y.CopyFrom(x, crtAllocator); + z.CopyFrom(y, z.GetAllocator()); TestEqual(x, y); + TestEqual(y, z); + TestEqual(z, x); // Swapping member order should be fine. - y.RemoveMember("t"); + EXPECT_TRUE(y.RemoveMember("t")); TestUnequal(x, y); - y.AddMember("t", Value(true).Move(), allocator); + TestUnequal(z, y); + EXPECT_TRUE(z.RemoveMember("t")); + TestUnequal(x, z); + TestEqual(y, z); + y.AddMember("t", true, crtAllocator); + z.AddMember("t", true, z.GetAllocator()); TestEqual(x, y); + TestEqual(y, z); + TestEqual(z, x); + + // Issue #129: compare Uint64 + x.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0)); + y.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)); + TestUnequal(x, y); } template @@ -538,7 +596,7 @@ TEST(Value, String) { EXPECT_STREQ("World", w.GetString()); EXPECT_EQ(5u, w.GetStringLength()); -#ifdef RAPIDJSON_HAS_STDSTRING +#if RAPIDJSON_HAS_STDSTRING { std::string str = "Hello World"; str[5] = '\0'; @@ -626,6 +684,21 @@ TEST(Value, Array) { EXPECT_TRUE(y[4u].IsString()); EXPECT_STREQ("foo", y[4u].GetString()); +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + // PushBack(GenericValue&&, Allocator&); + { + Value y(kArrayType); + y.PushBack(Value(true), allocator); + y.PushBack(std::move(Value(kArrayType).PushBack(Value(1), allocator).PushBack("foo", allocator)), allocator); + EXPECT_EQ(2u, y.Size()); + EXPECT_TRUE(y[0u].IsTrue()); + EXPECT_TRUE(y[1u].IsArray()); + EXPECT_EQ(2u, y[1u].Size()); + EXPECT_TRUE(y[1u][0u].IsInt()); + EXPECT_TRUE(y[1u][1u].IsString()); + } +#endif + // iterator Value::ValueIterator itr = x.Begin(); EXPECT_TRUE(itr != x.End()); @@ -734,7 +807,6 @@ TEST(Value, Array) { } // Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed. -#if 0 // http://en.wikipedia.org/wiki/Erase-remove_idiom x.Clear(); for (int i = 0; i < 10; i++) @@ -743,11 +815,11 @@ TEST(Value, Array) { else x.PushBack(Value(kNullType).Move(), allocator); - x.Erase(std::remove(x.Begin(), x.End(), Value(kNullType)), x.End()); + const Value null(kNullType); + x.Erase(std::remove(x.Begin(), x.End(), null), x.End()); EXPECT_EQ(5u, x.Size()); for (int i = 0; i < 5; i++) EXPECT_EQ(i * 2, x[i]); -#endif // SetArray() Value z; @@ -801,6 +873,22 @@ TEST(Value, Object) { EXPECT_EQ(8u, o.MemberCount()); } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + // AddMember(GenericValue&&, ...) variants + { + Value o(kObjectType); + o.AddMember(Value("true"), Value(true), allocator); + o.AddMember(Value("false"), Value(false).Move(), allocator); // value is lvalue ref + o.AddMember(Value("int").Move(), Value(-1), allocator); // name is lvalue ref + o.AddMember("uint", std::move(Value().SetUint(1u)), allocator); // name is literal, value is rvalue + EXPECT_TRUE(o["true"].GetBool()); + EXPECT_FALSE(o["false"].GetBool()); + EXPECT_EQ(-1, o["int"].GetInt()); + EXPECT_EQ(1u, o["uint"].GetUint()); + EXPECT_EQ(4u, o.MemberCount()); + } +#endif + // Tests a member with null character Value name; const Value C0D("C\0D", 3); @@ -818,10 +906,18 @@ TEST(Value, Object) { EXPECT_TRUE(x.HasMember(name)); EXPECT_TRUE(y.HasMember(name)); + GenericValue, CrtAllocator> othername("A"); + EXPECT_TRUE(x.HasMember(othername)); + EXPECT_TRUE(y.HasMember(othername)); + othername.SetString("C\0D"); + EXPECT_TRUE(x.HasMember(othername)); + EXPECT_TRUE(y.HasMember(othername)); + // operator[] EXPECT_STREQ("Apple", x["A"].GetString()); EXPECT_STREQ("Banana", x["B"].GetString()); EXPECT_STREQ("CherryD", x[C0D].GetString()); + EXPECT_STREQ("CherryD", x[othername].GetString()); // const operator[] EXPECT_STREQ("Apple", y["A"].GetString()); @@ -892,7 +988,7 @@ TEST(Value, Object) { x.RemoveMember("B"); EXPECT_FALSE(x.HasMember("B")); - x.RemoveMember(name); + x.RemoveMember(othername); EXPECT_FALSE(x.HasMember(name)); EXPECT_TRUE(x.MemberBegin() == x.MemberEnd()); @@ -905,11 +1001,14 @@ TEST(Value, Object) { for (int i = 0; i < 10; i++) x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator); + // MemberCount, iterator difference + EXPECT_EQ(x.MemberCount(), SizeType(x.MemberEnd() - x.MemberBegin())); + // Erase the first itr = x.EraseMember(x.MemberBegin()); EXPECT_FALSE(x.HasMember(keys[0])); EXPECT_EQ(x.MemberBegin(), itr); - EXPECT_EQ(9, x.MemberEnd() - x.MemberBegin()); + EXPECT_EQ(9u, x.MemberCount()); for (; itr != x.MemberEnd(); ++itr) { int i = (itr - x.MemberBegin()) + 1; EXPECT_STREQ(itr->name.GetString(), keys[i]); @@ -920,7 +1019,7 @@ TEST(Value, Object) { itr = x.EraseMember(x.MemberEnd() - 1); EXPECT_FALSE(x.HasMember(keys[9])); EXPECT_EQ(x.MemberEnd(), itr); - EXPECT_EQ(8, x.MemberEnd() - x.MemberBegin()); + EXPECT_EQ(8u, x.MemberCount()); for (; itr != x.MemberEnd(); ++itr) { int i = (itr - x.MemberBegin()) + 1; EXPECT_STREQ(itr->name.GetString(), keys[i]); @@ -931,7 +1030,7 @@ TEST(Value, Object) { itr = x.EraseMember(x.MemberBegin() + 4); EXPECT_FALSE(x.HasMember(keys[5])); EXPECT_EQ(x.MemberBegin() + 4, itr); - EXPECT_EQ(7, x.MemberEnd() - x.MemberBegin()); + EXPECT_EQ(7u, x.MemberCount()); for (; itr != x.MemberEnd(); ++itr) { int i = (itr - x.MemberBegin()); i += (i<4) ? 1 : 2; @@ -955,7 +1054,7 @@ TEST(Value, Object) { EXPECT_EQ(x.MemberBegin() + first, itr); size_t removeCount = last - first; - EXPECT_EQ(n - removeCount, size_t(x.MemberEnd() - x.MemberBegin())); + EXPECT_EQ(n - removeCount, x.MemberCount()); for (unsigned i = 0; i < first; i++) EXPECT_EQ(i, x[keys[i]][0u].GetUint()); for (unsigned i = first; i < n - removeCount; i++) @@ -1059,3 +1158,23 @@ TEST(Document, CrtAllocator) { V a(kArrayType); a.PushBack(1, allocator); // Should not call destructor on uninitialized Value of newly allocated elements. } + +static void TestShortStringOptimization(const char* str) { + const rapidjson::SizeType len = (rapidjson::SizeType)strlen(str); + + rapidjson::Document doc; + rapidjson::Value val; + val.SetString(str, len, doc.GetAllocator()); + + EXPECT_EQ(val.GetStringLength(), len); + EXPECT_STREQ(val.GetString(), str); +} + +TEST(Value, AllocateShortString) { + TestShortStringOptimization(""); // edge case: empty string + TestShortStringOptimization("12345678"); // regular case for short strings: 8 chars + TestShortStringOptimization("12345678901"); // edge case: 11 chars in 32-bit mode (=> short string) + TestShortStringOptimization("123456789012"); // edge case: 12 chars in 32-bit mode (=> regular string) + TestShortStringOptimization("123456789012345"); // edge case: 15 chars in 64-bit mode (=> short string) + TestShortStringOptimization("1234567890123456"); // edge case: 16 chars in 64-bit mode (=> regular string) +}