Welcome to mirror list, hosted at ThFree Co, Russian Federation.

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