Commit 881c91d6 authored by Milo Yip's avatar Milo Yip

Merge master and implement kParseFullPrecision

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