Merge pull request #304 from miloyip/issue298_coverage
100% line of code coverage
This commit is contained in:
commit
75ee552f49
@ -21,7 +21,7 @@ before_install:
|
|||||||
- sudo apt-get install -qq cmake valgrind
|
- sudo apt-get install -qq cmake valgrind
|
||||||
- sudo apt-get --no-install-recommends install doxygen # Don't install LaTeX stuffs
|
- sudo apt-get --no-install-recommends install doxygen # Don't install LaTeX stuffs
|
||||||
- if [ "$ARCH" = "x86" ]; then sudo apt-get install -qq g++-multilib libc6-dbg:i386; fi
|
- if [ "$ARCH" = "x86" ]; then sudo apt-get install -qq g++-multilib libc6-dbg:i386; fi
|
||||||
- if [ "$CC" = "gcc" ] && [ "$CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi
|
- if [ "$CC" = "gcc" ] && [ "CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi
|
||||||
|
|
||||||
install: true
|
install: true
|
||||||
|
|
||||||
@ -48,4 +48,4 @@ script:
|
|||||||
- make travis_doc
|
- make travis_doc
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes
|
- coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes -e include/rapidjson/internal/meta.h -e include/rapidjson/error/en.h
|
||||||
|
@ -1459,17 +1459,14 @@ public:
|
|||||||
case kStringType:
|
case kStringType:
|
||||||
return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0);
|
return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0);
|
||||||
|
|
||||||
case kNumberType:
|
default:
|
||||||
|
RAPIDJSON_ASSERT(GetType() == kNumberType);
|
||||||
if (IsInt()) return handler.Int(data_.n.i.i);
|
if (IsInt()) return handler.Int(data_.n.i.i);
|
||||||
else if (IsUint()) return handler.Uint(data_.n.u.u);
|
else if (IsUint()) return handler.Uint(data_.n.u.u);
|
||||||
else if (IsInt64()) return handler.Int64(data_.n.i64);
|
else if (IsInt64()) return handler.Int64(data_.n.i64);
|
||||||
else if (IsUint64()) return handler.Uint64(data_.n.u64);
|
else if (IsUint64()) return handler.Uint64(data_.n.u64);
|
||||||
else return handler.Double(data_.n.d);
|
else return handler.Double(data_.n.d);
|
||||||
|
|
||||||
default:
|
|
||||||
RAPIDJSON_ASSERT(false);
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1753,7 +1750,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
template <unsigned parseFlags, typename InputStream>
|
template <unsigned parseFlags, typename InputStream>
|
||||||
GenericDocument& ParseStream(InputStream& is) {
|
GenericDocument& ParseStream(InputStream& is) {
|
||||||
return ParseStream<parseFlags,Encoding,InputStream>(is);
|
return ParseStream<parseFlags, Encoding, InputStream>(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Parse JSON text from an input stream (with \ref kParseDefaultFlags)
|
//! Parse JSON text from an input stream (with \ref kParseDefaultFlags)
|
||||||
|
@ -109,6 +109,7 @@ public:
|
|||||||
\param type UTF encoding type if it is not detected from the stream.
|
\param type UTF encoding type if it is not detected from the stream.
|
||||||
*/
|
*/
|
||||||
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
|
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
|
||||||
|
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||||
DetectType();
|
DetectType();
|
||||||
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
|
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
|
||||||
takeFunc_ = f[type_];
|
takeFunc_ = f[type_];
|
||||||
@ -177,21 +178,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||||
switch (type_) {
|
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||||
case kUTF8:
|
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||||
// Do nothing
|
|
||||||
break;
|
|
||||||
case kUTF16LE:
|
|
||||||
case kUTF16BE:
|
|
||||||
RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
|
||||||
break;
|
|
||||||
case kUTF32LE:
|
|
||||||
case kUTF32BE:
|
|
||||||
RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
RAPIDJSON_ASSERT(false); // Invalid type
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef Ch (*TakeFunc)(InputByteStream& is);
|
typedef Ch (*TakeFunc)(InputByteStream& is);
|
||||||
@ -220,22 +208,11 @@ public:
|
|||||||
\param putBOM Whether to write BOM at the beginning of the stream.
|
\param putBOM Whether to write BOM at the beginning of the stream.
|
||||||
*/
|
*/
|
||||||
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
|
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
|
||||||
// RUntime check whether the size of character type is sufficient. It only perform checks with assertion.
|
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||||
switch (type_) {
|
|
||||||
case kUTF16LE:
|
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||||
case kUTF16BE:
|
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||||
RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||||
break;
|
|
||||||
case kUTF32LE:
|
|
||||||
case kUTF32BE:
|
|
||||||
RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
|
||||||
break;
|
|
||||||
case kUTF8:
|
|
||||||
// Do nothing
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
RAPIDJSON_ASSERT(false); // Invalid UTFType
|
|
||||||
}
|
|
||||||
|
|
||||||
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
|
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
|
||||||
putFunc_ = f[type_];
|
putFunc_ = f[type_];
|
||||||
|
@ -172,13 +172,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute absolute difference of this and rhs.
|
// Compute absolute difference of this and rhs.
|
||||||
// Return false if this < rhs
|
// Assume this != rhs
|
||||||
bool Difference(const BigInteger& rhs, BigInteger* out) const {
|
bool Difference(const BigInteger& rhs, BigInteger* out) const {
|
||||||
int cmp = Compare(rhs);
|
int cmp = Compare(rhs);
|
||||||
if (cmp == 0) {
|
RAPIDJSON_ASSERT(cmp != 0);
|
||||||
*out = BigInteger(0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const BigInteger *a, *b; // Makes a > b
|
const BigInteger *a, *b; // Makes a > b
|
||||||
bool ret;
|
bool ret;
|
||||||
if (cmp < 0) { a = &rhs; b = this; ret = true; }
|
if (cmp < 0) { a = &rhs; b = this; ret = true; }
|
||||||
@ -269,12 +266,6 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static Type FullAdd(Type a, Type b, bool inCarry, bool* outCarry) {
|
|
||||||
Type c = a + b + (inCarry ? 1 : 0);
|
|
||||||
*outCarry = c < a;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
|
static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
|
||||||
static const size_t kCapacity = kBitCount / sizeof(Type);
|
static const size_t kCapacity = kBitCount / sizeof(Type);
|
||||||
static const size_t kTypeBit = sizeof(Type) * 8;
|
static const size_t kTypeBit = sizeof(Type) * 8;
|
||||||
|
@ -135,25 +135,9 @@ struct DiyFp {
|
|||||||
double d;
|
double d;
|
||||||
uint64_t u64;
|
uint64_t u64;
|
||||||
}u;
|
}u;
|
||||||
uint64_t significand = f;
|
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
|
||||||
int exponent = e;
|
static_cast<uint64_t>(e + kDpExponentBias);
|
||||||
while (significand > kDpHiddenBit + kDpSignificandMask) {
|
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
|
||||||
significand >>= 1;
|
|
||||||
exponent++;
|
|
||||||
}
|
|
||||||
while (exponent > kDpDenormalExponent && (significand & kDpHiddenBit) == 0) {
|
|
||||||
significand <<= 1;
|
|
||||||
exponent--;
|
|
||||||
}
|
|
||||||
if (exponent >= kDpMaxExponent) {
|
|
||||||
u.u64 = kDpExponentMask; // Infinity
|
|
||||||
return u.d;
|
|
||||||
}
|
|
||||||
else if (exponent < kDpDenormalExponent)
|
|
||||||
return 0.0;
|
|
||||||
const uint64_t be = (exponent == kDpDenormalExponent && (significand & kDpHiddenBit) == 0) ? 0 :
|
|
||||||
static_cast<uint64_t>(exponent + kDpExponentBias);
|
|
||||||
u.u64 = (significand & kDpSignificandMask) | (be << kDpSignificandSize);
|
|
||||||
return u.d;
|
return u.d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +50,10 @@ inline unsigned CountDecimalDigit32(uint32_t n) {
|
|||||||
if (n < 1000000) return 6;
|
if (n < 1000000) return 6;
|
||||||
if (n < 10000000) return 7;
|
if (n < 10000000) return 7;
|
||||||
if (n < 100000000) return 8;
|
if (n < 100000000) return 8;
|
||||||
if (n < 1000000000) return 9;
|
// Will not reach 10 digits in DigitGen()
|
||||||
return 10;
|
//if (n < 1000000000) return 9;
|
||||||
|
//return 10;
|
||||||
|
return 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
|
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
|
||||||
@ -60,13 +62,12 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff
|
|||||||
const DiyFp wp_w = Mp - W;
|
const DiyFp wp_w = Mp - W;
|
||||||
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
|
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
|
||||||
uint64_t p2 = Mp.f & (one.f - 1);
|
uint64_t p2 = Mp.f & (one.f - 1);
|
||||||
int kappa = CountDecimalDigit32(p1);
|
int kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
|
||||||
*len = 0;
|
*len = 0;
|
||||||
|
|
||||||
while (kappa > 0) {
|
while (kappa > 0) {
|
||||||
uint32_t d;
|
uint32_t d = 0;
|
||||||
switch (kappa) {
|
switch (kappa) {
|
||||||
case 10: d = p1 / 1000000000; p1 %= 1000000000; break;
|
|
||||||
case 9: d = p1 / 100000000; p1 %= 100000000; break;
|
case 9: d = p1 / 100000000; p1 %= 100000000; break;
|
||||||
case 8: d = p1 / 10000000; p1 %= 10000000; break;
|
case 8: d = p1 / 10000000; p1 %= 10000000; break;
|
||||||
case 7: d = p1 / 1000000; p1 %= 1000000; break;
|
case 7: d = p1 / 1000000; p1 %= 1000000; break;
|
||||||
@ -76,14 +77,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff
|
|||||||
case 3: d = p1 / 100; p1 %= 100; break;
|
case 3: d = p1 / 100; p1 %= 100; break;
|
||||||
case 2: d = p1 / 10; p1 %= 10; break;
|
case 2: d = p1 / 10; p1 %= 10; break;
|
||||||
case 1: d = p1; p1 = 0; break;
|
case 1: d = p1; p1 = 0; break;
|
||||||
default:
|
default:;
|
||||||
#if defined(_MSC_VER)
|
|
||||||
__assume(0);
|
|
||||||
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
|
|
||||||
__builtin_unreachable();
|
|
||||||
#else
|
|
||||||
d = 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
if (d || *len)
|
if (d || *len)
|
||||||
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
|
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
|
||||||
|
@ -34,14 +34,6 @@ public:
|
|||||||
return Double(u + 1).Value();
|
return Double(u + 1).Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
double PreviousPositiveDouble() const {
|
|
||||||
RAPIDJSON_ASSERT(!Sign());
|
|
||||||
if (IsZero())
|
|
||||||
return 0.0;
|
|
||||||
else
|
|
||||||
return Double(u - 1).Value();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sign() const { return (u & kSignMask) != 0; }
|
bool Sign() const { return (u & kSignMask) != 0; }
|
||||||
uint64_t Significand() const { return u & kSignificandMask; }
|
uint64_t Significand() const { return u & kSignificandMask; }
|
||||||
int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; }
|
int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; }
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
#ifndef RAPIDJSON_ITOA_
|
#ifndef RAPIDJSON_ITOA_
|
||||||
#define RAPIDJSON_ITOA_
|
#define RAPIDJSON_ITOA_
|
||||||
|
|
||||||
|
#include "../rapidjson.h"
|
||||||
|
|
||||||
RAPIDJSON_NAMESPACE_BEGIN
|
RAPIDJSON_NAMESPACE_BEGIN
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ inline T Min3(T a, T b, T c) {
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adjustToNegative) {
|
inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
|
||||||
const Double db(b);
|
const Double db(b);
|
||||||
const uint64_t bInt = db.IntegerSignificand();
|
const uint64_t bInt = db.IntegerSignificand();
|
||||||
const int bExp = db.IntegerExponent();
|
const int bExp = db.IntegerExponent();
|
||||||
@ -104,19 +104,9 @@ inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adj
|
|||||||
hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2;
|
hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2;
|
||||||
|
|
||||||
BigInteger delta(0);
|
BigInteger delta(0);
|
||||||
*adjustToNegative = dS.Difference(bS, &delta);
|
dS.Difference(bS, &delta);
|
||||||
|
|
||||||
int cmp = delta.Compare(hS);
|
|
||||||
// If delta is within 1/2 ULP, check for special case when significand is power of two.
|
|
||||||
// In this case, need to compare with 1/2h in the lower bound.
|
|
||||||
if (cmp < 0 && *adjustToNegative && // within and dS < bS
|
|
||||||
db.IsNormal() && (bInt & (bInt - 1)) == 0 && // Power of 2
|
|
||||||
db.Uint64Value() != RAPIDJSON_UINT64_C2(0x00100000, 0x00000000)) // minimum normal number must not do this
|
|
||||||
{
|
|
||||||
delta <<= 1;
|
|
||||||
return delta.Compare(hS);
|
return delta.Compare(hS);
|
||||||
}
|
|
||||||
return cmp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool StrtodFast(double d, int p, double* result) {
|
inline bool StrtodFast(double d, int p, double* result) {
|
||||||
@ -213,24 +203,18 @@ inline double StrtodBigInteger(double approx, const char* decimals, size_t lengt
|
|||||||
const BigInteger dInt(decimals, length);
|
const BigInteger dInt(decimals, length);
|
||||||
const int dExp = (int)decimalPosition - (int)length + exp;
|
const int dExp = (int)decimalPosition - (int)length + exp;
|
||||||
Double a(approx);
|
Double a(approx);
|
||||||
for (int i = 0; i < 10; i++) {
|
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
|
||||||
bool adjustToNegative;
|
|
||||||
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp, &adjustToNegative);
|
|
||||||
if (cmp < 0)
|
if (cmp < 0)
|
||||||
return a.Value(); // within half ULP
|
return a.Value(); // within half ULP
|
||||||
else if (cmp == 0) {
|
else if (cmp == 0) {
|
||||||
// Round towards even
|
// Round towards even
|
||||||
if (a.Significand() & 1)
|
if (a.Significand() & 1)
|
||||||
return adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble();
|
return a.NextPositiveDouble();
|
||||||
else
|
else
|
||||||
return a.Value();
|
return a.Value();
|
||||||
}
|
}
|
||||||
else // adjustment
|
else // adjustment
|
||||||
a = adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble();
|
return a.NextPositiveDouble();
|
||||||
}
|
|
||||||
|
|
||||||
// This should not happen, but in case there is really a bug, break the infinite-loop
|
|
||||||
return a.Value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
||||||
@ -258,7 +242,9 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t
|
|||||||
// Trim right-most digits
|
// Trim right-most digits
|
||||||
const int kMaxDecimalDigit = 780;
|
const int kMaxDecimalDigit = 780;
|
||||||
if ((int)length > kMaxDecimalDigit) {
|
if ((int)length > kMaxDecimalDigit) {
|
||||||
exp += (int(length) - kMaxDecimalDigit);
|
int delta = (int(length) - kMaxDecimalDigit);
|
||||||
|
exp += delta;
|
||||||
|
decimalPosition -= delta;
|
||||||
length = kMaxDecimalDigit;
|
length = kMaxDecimalDigit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,8 +100,9 @@ public:
|
|||||||
Base::os_->Put('\n');
|
Base::os_->Put('\n');
|
||||||
WriteIndent();
|
WriteIndent();
|
||||||
}
|
}
|
||||||
if (!Base::WriteEndObject())
|
bool ret = Base::WriteEndObject();
|
||||||
return false;
|
(void)ret;
|
||||||
|
RAPIDJSON_ASSERT(ret == true);
|
||||||
if (Base::level_stack_.Empty()) // end of json text
|
if (Base::level_stack_.Empty()) // end of json text
|
||||||
Base::os_->Flush();
|
Base::os_->Flush();
|
||||||
return true;
|
return true;
|
||||||
@ -123,8 +124,9 @@ public:
|
|||||||
Base::os_->Put('\n');
|
Base::os_->Put('\n');
|
||||||
WriteIndent();
|
WriteIndent();
|
||||||
}
|
}
|
||||||
if (!Base::WriteEndArray())
|
bool ret = Base::WriteEndArray();
|
||||||
return false;
|
(void)ret;
|
||||||
|
RAPIDJSON_ASSERT(ret == true);
|
||||||
if (Base::level_stack_.Empty()) // end of json text
|
if (Base::level_stack_.Empty()) // end of json text
|
||||||
Base::os_->Flush();
|
Base::os_->Flush();
|
||||||
return true;
|
return true;
|
||||||
|
@ -122,9 +122,9 @@
|
|||||||
|
|
||||||
#ifndef RAPIDJSON_FORCEINLINE
|
#ifndef RAPIDJSON_FORCEINLINE
|
||||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||||
#ifdef _MSC_VER
|
#if defined(_MSC_VER) && !defined(NDEBUG)
|
||||||
#define RAPIDJSON_FORCEINLINE __forceinline
|
#define RAPIDJSON_FORCEINLINE __forceinline
|
||||||
#elif defined(__GNUC__) && __GNUC__ >= 4
|
#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG)
|
||||||
#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
|
#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
|
||||||
#else
|
#else
|
||||||
#define RAPIDJSON_FORCEINLINE
|
#define RAPIDJSON_FORCEINLINE
|
||||||
|
@ -1227,14 +1227,9 @@ private:
|
|||||||
// May return a new state on state pop.
|
// May return a new state on state pop.
|
||||||
template <unsigned parseFlags, typename InputStream, typename Handler>
|
template <unsigned parseFlags, typename InputStream, typename Handler>
|
||||||
RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) {
|
RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) {
|
||||||
|
(void)token;
|
||||||
|
|
||||||
switch (dst) {
|
switch (dst) {
|
||||||
case IterativeParsingStartState:
|
|
||||||
RAPIDJSON_ASSERT(false);
|
|
||||||
return IterativeParsingErrorState;
|
|
||||||
|
|
||||||
case IterativeParsingFinishState:
|
|
||||||
return dst;
|
|
||||||
|
|
||||||
case IterativeParsingErrorState:
|
case IterativeParsingErrorState:
|
||||||
return dst;
|
return dst;
|
||||||
|
|
||||||
@ -1273,12 +1268,9 @@ private:
|
|||||||
return dst;
|
return dst;
|
||||||
|
|
||||||
case IterativeParsingKeyValueDelimiterState:
|
case IterativeParsingKeyValueDelimiterState:
|
||||||
if (token == ColonToken) {
|
RAPIDJSON_ASSERT(token == ColonToken);
|
||||||
is.Take();
|
is.Take();
|
||||||
return dst;
|
return dst;
|
||||||
}
|
|
||||||
else
|
|
||||||
return IterativeParsingErrorState;
|
|
||||||
|
|
||||||
case IterativeParsingMemberValueState:
|
case IterativeParsingMemberValueState:
|
||||||
// Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
|
// Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
|
||||||
@ -1353,17 +1345,25 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case IterativeParsingValueState:
|
default:
|
||||||
|
// This branch is for IterativeParsingValueState actually.
|
||||||
|
// Use `default:` rather than
|
||||||
|
// `case IterativeParsingValueState:` is for code coverage.
|
||||||
|
|
||||||
|
// The IterativeParsingStartState is not enumerated in this switch-case.
|
||||||
|
// It is impossible for that case. And it can be caught by following assertion.
|
||||||
|
|
||||||
|
// The IterativeParsingFinishState is not enumerated in this switch-case either.
|
||||||
|
// It is a "derivative" state which cannot triggered from Predict() directly.
|
||||||
|
// Therefore it cannot happen here. And it can be caught by following assertion.
|
||||||
|
RAPIDJSON_ASSERT(dst == IterativeParsingValueState);
|
||||||
|
|
||||||
// Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
|
// Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
|
||||||
ParseValue<parseFlags>(is, handler);
|
ParseValue<parseFlags>(is, handler);
|
||||||
if (HasParseError()) {
|
if (HasParseError()) {
|
||||||
return IterativeParsingErrorState;
|
return IterativeParsingErrorState;
|
||||||
}
|
}
|
||||||
return IterativeParsingFinishState;
|
return IterativeParsingFinishState;
|
||||||
|
|
||||||
default:
|
|
||||||
RAPIDJSON_ASSERT(false);
|
|
||||||
return IterativeParsingErrorState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +272,8 @@ protected:
|
|||||||
os_->Put(hexDigits[(codepoint >> 4) & 15]);
|
os_->Put(hexDigits[(codepoint >> 4) & 15]);
|
||||||
os_->Put(hexDigits[(codepoint ) & 15]);
|
os_->Put(hexDigits[(codepoint ) & 15]);
|
||||||
}
|
}
|
||||||
else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) {
|
else {
|
||||||
|
RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
|
||||||
// Surrogate pair
|
// Surrogate pair
|
||||||
unsigned s = codepoint - 0x010000;
|
unsigned s = codepoint - 0x010000;
|
||||||
unsigned lead = (s >> 10) + 0xD800;
|
unsigned lead = (s >> 10) + 0xD800;
|
||||||
@ -288,8 +289,6 @@ protected:
|
|||||||
os_->Put(hexDigits[(trail >> 4) & 15]);
|
os_->Put(hexDigits[(trail >> 4) & 15]);
|
||||||
os_->Put(hexDigits[(trail ) & 15]);
|
os_->Put(hexDigits[(trail ) & 15]);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return false; // invalid code point
|
|
||||||
}
|
}
|
||||||
else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) {
|
else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) {
|
||||||
is.Take();
|
is.Take();
|
||||||
@ -303,7 +302,8 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_);
|
if (!Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
os_->Put('\"');
|
os_->Put('\"');
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
set(UNITTEST_SOURCES
|
set(UNITTEST_SOURCES
|
||||||
|
allocatorstest.cpp
|
||||||
bigintegertest.cpp
|
bigintegertest.cpp
|
||||||
documenttest.cpp
|
documenttest.cpp
|
||||||
encodedstreamtest.cpp
|
encodedstreamtest.cpp
|
||||||
encodingstest.cpp
|
encodingstest.cpp
|
||||||
filestreamtest.cpp
|
filestreamtest.cpp
|
||||||
|
itoatest.cpp
|
||||||
jsoncheckertest.cpp
|
jsoncheckertest.cpp
|
||||||
namespacetest.cpp
|
namespacetest.cpp
|
||||||
|
prettywritertest.cpp
|
||||||
readertest.cpp
|
readertest.cpp
|
||||||
|
simdtest.cpp
|
||||||
stringbuffertest.cpp
|
stringbuffertest.cpp
|
||||||
strtodtest.cpp
|
strtodtest.cpp
|
||||||
unittest.cpp
|
unittest.cpp
|
||||||
@ -33,8 +37,9 @@ add_test(NAME unittest
|
|||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
|
||||||
|
|
||||||
if(NOT MSVC)
|
if(NOT MSVC)
|
||||||
|
# Not running SIMD.* unit test cases for Valgrind
|
||||||
add_test(NAME valgrind_unittest
|
add_test(NAME valgrind_unittest
|
||||||
COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest
|
COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest --gtest_filter=-SIMD.*
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
64
test/unittest/allocatorstest.cpp
Normal file
64
test/unittest/allocatorstest.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (C) 2011 Milo Yip
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "unittest.h"
|
||||||
|
|
||||||
|
#include "rapidjson/allocators.h"
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
template <typename Allocator>
|
||||||
|
void TestAllocator(Allocator& a) {
|
||||||
|
uint8_t* p = (uint8_t*)a.Malloc(100);
|
||||||
|
EXPECT_TRUE(p != 0);
|
||||||
|
for (size_t i = 0; i < 100; i++)
|
||||||
|
p[i] = (uint8_t)i;
|
||||||
|
|
||||||
|
// Expand
|
||||||
|
uint8_t* q = (uint8_t*)a.Realloc(p, 100, 200);
|
||||||
|
EXPECT_TRUE(q != 0);
|
||||||
|
for (size_t i = 0; i < 100; i++)
|
||||||
|
EXPECT_EQ(i, q[i]);
|
||||||
|
for (size_t i = 100; i < 200; i++)
|
||||||
|
q[i] = (uint8_t)i;
|
||||||
|
|
||||||
|
// Shrink
|
||||||
|
uint8_t *r = (uint8_t*)a.Realloc(q, 200, 150);
|
||||||
|
EXPECT_TRUE(r != 0);
|
||||||
|
for (size_t i = 0; i < 150; i++)
|
||||||
|
EXPECT_EQ(i, r[i]);
|
||||||
|
|
||||||
|
Allocator::Free(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Allocator, CrtAllocator) {
|
||||||
|
CrtAllocator a;
|
||||||
|
TestAllocator(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Allocator, MemoryPoolAllocator) {
|
||||||
|
MemoryPoolAllocator<> a;
|
||||||
|
TestAllocator(a);
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
EXPECT_TRUE(a.Malloc(i) != 0);
|
||||||
|
EXPECT_LE(a.Size(), a.Capacity());
|
||||||
|
}
|
||||||
|
}
|
@ -28,13 +28,11 @@
|
|||||||
|
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
|
|
||||||
template <typename Allocator, typename StackAllocator>
|
template <typename DocumentType>
|
||||||
void ParseTest() {
|
void ParseCheck(DocumentType& doc) {
|
||||||
typedef GenericDocument<UTF8<>, Allocator, StackAllocator> DocumentType;
|
|
||||||
typedef typename DocumentType::ValueType ValueType;
|
typedef typename DocumentType::ValueType ValueType;
|
||||||
DocumentType doc;
|
|
||||||
|
|
||||||
doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
|
EXPECT_FALSE(doc.HasParseError());
|
||||||
|
|
||||||
EXPECT_TRUE(doc.IsObject());
|
EXPECT_TRUE(doc.IsObject());
|
||||||
|
|
||||||
@ -73,6 +71,28 @@ void ParseTest() {
|
|||||||
EXPECT_EQ(i + 1, a[i].GetUint());
|
EXPECT_EQ(i + 1, a[i].GetUint());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Allocator, typename StackAllocator>
|
||||||
|
void ParseTest() {
|
||||||
|
typedef GenericDocument<UTF8<>, Allocator, StackAllocator> DocumentType;
|
||||||
|
DocumentType doc;
|
||||||
|
|
||||||
|
const char* json = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
|
||||||
|
|
||||||
|
doc.Parse(json);
|
||||||
|
ParseCheck(doc);
|
||||||
|
|
||||||
|
doc.SetNull();
|
||||||
|
StringStream s(json);
|
||||||
|
doc.template ParseStream<0>(s);
|
||||||
|
ParseCheck(doc);
|
||||||
|
|
||||||
|
doc.SetNull();
|
||||||
|
char *buffer = strdup(json);
|
||||||
|
doc.ParseInsitu(buffer);
|
||||||
|
ParseCheck(doc);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Document, Parse) {
|
TEST(Document, Parse) {
|
||||||
ParseTest<MemoryPoolAllocator<>, CrtAllocator>();
|
ParseTest<MemoryPoolAllocator<>, CrtAllocator>();
|
||||||
ParseTest<MemoryPoolAllocator<>, MemoryPoolAllocator<> >();
|
ParseTest<MemoryPoolAllocator<>, MemoryPoolAllocator<> >();
|
||||||
@ -81,14 +101,21 @@ TEST(Document, Parse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static FILE* OpenEncodedFile(const char* filename) {
|
static FILE* OpenEncodedFile(const char* filename) {
|
||||||
|
const char *paths[] = {
|
||||||
|
"encodings/%s",
|
||||||
|
"bin/encodings/%s",
|
||||||
|
"../bin/encodings/%s",
|
||||||
|
"../../bin/encodings/%s",
|
||||||
|
"../../../bin/encodings/%s"
|
||||||
|
};
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
sprintf(buffer, "encodings/%s", filename);
|
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
|
||||||
|
sprintf(buffer, paths[i], filename);
|
||||||
FILE *fp = fopen(buffer, "rb");
|
FILE *fp = fopen(buffer, "rb");
|
||||||
if (!fp) {
|
if (fp)
|
||||||
sprintf(buffer, "../../bin/encodings/%s", filename);
|
|
||||||
fp = fopen(buffer, "rb");
|
|
||||||
}
|
|
||||||
return fp;
|
return fp;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Document, ParseStream_EncodedInputStream) {
|
TEST(Document, ParseStream_EncodedInputStream) {
|
||||||
@ -223,6 +250,10 @@ TEST(Document, UserBuffer) {
|
|||||||
EXPECT_FALSE(doc.HasParseError());
|
EXPECT_FALSE(doc.HasParseError());
|
||||||
EXPECT_LE(valueAllocator.Size(), sizeof(valueBuffer));
|
EXPECT_LE(valueAllocator.Size(), sizeof(valueBuffer));
|
||||||
EXPECT_LE(parseAllocator.Size(), sizeof(parseBuffer));
|
EXPECT_LE(parseAllocator.Size(), sizeof(parseBuffer));
|
||||||
|
|
||||||
|
// Cover MemoryPoolAllocator::Capacity()
|
||||||
|
EXPECT_LE(valueAllocator.Size(), valueAllocator.Capacity());
|
||||||
|
EXPECT_LE(parseAllocator.Size(), parseAllocator.Capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue 226: Value of string type should not point to NULL
|
// Issue 226: Value of string type should not point to NULL
|
||||||
|
@ -47,15 +47,22 @@ private:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
static FILE* Open(const char* filename) {
|
static FILE* Open(const char* filename) {
|
||||||
|
const char *paths[] = {
|
||||||
|
"encodings/%s",
|
||||||
|
"bin/encodings/%s",
|
||||||
|
"../bin/encodings/%s",
|
||||||
|
"../../bin/encodings/%s",
|
||||||
|
"../../../bin/encodings/%s"
|
||||||
|
};
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
sprintf(buffer, "encodings/%s", filename);
|
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
|
||||||
|
sprintf(buffer, paths[i], filename);
|
||||||
FILE *fp = fopen(buffer, "rb");
|
FILE *fp = fopen(buffer, "rb");
|
||||||
if (!fp) {
|
if (fp)
|
||||||
sprintf(buffer, "../../bin/encodings/%s", filename);
|
|
||||||
fp = fopen(buffer, "rb");
|
|
||||||
}
|
|
||||||
return fp;
|
return fp;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static char *ReadFile(const char* filename, bool appendPath, size_t* outLength) {
|
static char *ReadFile(const char* filename, bool appendPath, size_t* outLength) {
|
||||||
FILE *fp = appendPath ? Open(filename) : fopen(filename, "rb");
|
FILE *fp = appendPath ? Open(filename) : fopen(filename, "rb");
|
||||||
@ -112,10 +119,11 @@ protected:
|
|||||||
}
|
}
|
||||||
EXPECT_EQ('\0', s.Peek());
|
EXPECT_EQ('\0', s.Peek());
|
||||||
free(data);
|
free(data);
|
||||||
|
EXPECT_EQ(size, eis.Tell());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestAutoUTFInputStream(const char *filename) {
|
void TestAutoUTFInputStream(const char *filename, bool expectHasBOM) {
|
||||||
// Test FileReadStream
|
// Test FileReadStream
|
||||||
{
|
{
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
@ -123,6 +131,7 @@ protected:
|
|||||||
ASSERT_TRUE(fp != 0);
|
ASSERT_TRUE(fp != 0);
|
||||||
FileReadStream fs(fp, buffer, sizeof(buffer));
|
FileReadStream fs(fp, buffer, sizeof(buffer));
|
||||||
AutoUTFInputStream<unsigned, FileReadStream> eis(fs);
|
AutoUTFInputStream<unsigned, FileReadStream> eis(fs);
|
||||||
|
EXPECT_EQ(expectHasBOM, eis.HasBOM());
|
||||||
StringStream s(json_);
|
StringStream s(json_);
|
||||||
while (eis.Peek() != '\0') {
|
while (eis.Peek() != '\0') {
|
||||||
unsigned expected, actual;
|
unsigned expected, actual;
|
||||||
@ -140,6 +149,7 @@ protected:
|
|||||||
char* data = ReadFile(filename, true, &size);
|
char* data = ReadFile(filename, true, &size);
|
||||||
MemoryStream ms(data, size);
|
MemoryStream ms(data, size);
|
||||||
AutoUTFInputStream<unsigned, MemoryStream> eis(ms);
|
AutoUTFInputStream<unsigned, MemoryStream> eis(ms);
|
||||||
|
EXPECT_EQ(expectHasBOM, eis.HasBOM());
|
||||||
StringStream s(json_);
|
StringStream s(json_);
|
||||||
|
|
||||||
while (eis.Peek() != '\0') {
|
while (eis.Peek() != '\0') {
|
||||||
@ -150,6 +160,7 @@ protected:
|
|||||||
}
|
}
|
||||||
EXPECT_EQ('\0', s.Peek());
|
EXPECT_EQ('\0', s.Peek());
|
||||||
free(data);
|
free(data);
|
||||||
|
EXPECT_EQ(size, eis.Tell());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,16 +268,25 @@ TEST_F(EncodedStreamTest, EncodedInputStream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(EncodedStreamTest, AutoUTFInputStream) {
|
TEST_F(EncodedStreamTest, AutoUTFInputStream) {
|
||||||
TestAutoUTFInputStream("utf8.json");
|
TestAutoUTFInputStream("utf8.json", false);
|
||||||
TestAutoUTFInputStream("utf8bom.json");
|
TestAutoUTFInputStream("utf8bom.json", true);
|
||||||
TestAutoUTFInputStream("utf16le.json");
|
TestAutoUTFInputStream("utf16le.json", false);
|
||||||
TestAutoUTFInputStream("utf16lebom.json");
|
TestAutoUTFInputStream("utf16lebom.json",true);
|
||||||
TestAutoUTFInputStream("utf16be.json");
|
TestAutoUTFInputStream("utf16be.json", false);
|
||||||
TestAutoUTFInputStream("utf16bebom.json");
|
TestAutoUTFInputStream("utf16bebom.json",true);
|
||||||
TestAutoUTFInputStream("utf32le.json");
|
TestAutoUTFInputStream("utf32le.json", false);
|
||||||
TestAutoUTFInputStream("utf32lebom.json");
|
TestAutoUTFInputStream("utf32lebom.json",true);
|
||||||
TestAutoUTFInputStream("utf32be.json");
|
TestAutoUTFInputStream("utf32be.json", false);
|
||||||
TestAutoUTFInputStream("utf32bebom.json");
|
TestAutoUTFInputStream("utf32bebom.json", true);
|
||||||
|
|
||||||
|
{
|
||||||
|
// Auto detection fail, use user defined UTF type
|
||||||
|
const char json[] = "{ }";
|
||||||
|
MemoryStream ms(json, sizeof(json));
|
||||||
|
AutoUTFInputStream<unsigned, MemoryStream> eis(ms, kUTF8);
|
||||||
|
EXPECT_FALSE(eis.HasBOM());
|
||||||
|
EXPECT_EQ(kUTF8, eis.GetType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(EncodedStreamTest, EncodedOutputStream) {
|
TEST_F(EncodedStreamTest, EncodedOutputStream) {
|
||||||
|
@ -30,9 +30,21 @@ public:
|
|||||||
FileStreamTest() : filename_(), json_(), length_() {}
|
FileStreamTest() : filename_(), json_(), length_() {}
|
||||||
|
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
FILE *fp = fopen(filename_ = "data/sample.json", "rb");
|
const char *paths[] = {
|
||||||
if (!fp)
|
"data/sample.json",
|
||||||
fp = fopen(filename_ = "../../bin/data/sample.json", "rb");
|
"bin/data/sample.json",
|
||||||
|
"../bin/data/sample.json",
|
||||||
|
"../../bin/data/sample.json",
|
||||||
|
"../../../bin/data/sample.json"
|
||||||
|
};
|
||||||
|
FILE* fp = 0;
|
||||||
|
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
|
||||||
|
fp = fopen(paths[i], "rb");
|
||||||
|
if (fp) {
|
||||||
|
filename_ = paths[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
ASSERT_TRUE(fp != 0);
|
ASSERT_TRUE(fp != 0);
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
fseek(fp, 0, SEEK_END);
|
||||||
|
164
test/unittest/itoatest.cpp
Normal file
164
test/unittest/itoatest.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// Copyright (C) 2011 Milo Yip
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "unittest.h"
|
||||||
|
#include "rapidjson/internal/itoa.h"
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
RAPIDJSON_DIAG_PUSH
|
||||||
|
RAPIDJSON_DIAG_OFF(type-limits)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace rapidjson::internal;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Traits {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Traits<uint32_t> {
|
||||||
|
enum { kBufferSize = 11 };
|
||||||
|
enum { kMaxDigit = 10 };
|
||||||
|
static uint32_t Negate(uint32_t x) { return x; };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Traits<int32_t> {
|
||||||
|
enum { kBufferSize = 12 };
|
||||||
|
enum { kMaxDigit = 10 };
|
||||||
|
static int32_t Negate(int32_t x) { return -x; };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Traits<uint64_t> {
|
||||||
|
enum { kBufferSize = 21 };
|
||||||
|
enum { kMaxDigit = 20 };
|
||||||
|
static uint64_t Negate(uint64_t x) { return x; };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Traits<int64_t> {
|
||||||
|
enum { kBufferSize = 22 };
|
||||||
|
enum { kMaxDigit = 20 };
|
||||||
|
static int64_t Negate(int64_t x) { return -x; };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void VerifyValue(T value, void(*f)(T, char*), char* (*g)(T, char*)) {
|
||||||
|
char buffer1[Traits<T>::kBufferSize];
|
||||||
|
char buffer2[Traits<T>::kBufferSize];
|
||||||
|
|
||||||
|
f(value, buffer1);
|
||||||
|
*g(value, buffer2) = '\0';
|
||||||
|
|
||||||
|
|
||||||
|
EXPECT_STREQ(buffer1, buffer2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void Verify(void(*f)(T, char*), char* (*g)(T, char*)) {
|
||||||
|
// Boundary cases
|
||||||
|
VerifyValue<T>(0, f, g);
|
||||||
|
VerifyValue<T>(std::numeric_limits<T>::min(), f, g);
|
||||||
|
VerifyValue<T>(std::numeric_limits<T>::max(), f, g);
|
||||||
|
|
||||||
|
// 2^n - 1, 2^n, 10^n - 1, 10^n until overflow
|
||||||
|
for (uint32_t power = 2; power <= 10; power += 8) {
|
||||||
|
T i = 1, last;
|
||||||
|
do {
|
||||||
|
VerifyValue<T>(i - 1, f, g);
|
||||||
|
VerifyValue<T>(i, f, g);
|
||||||
|
if (std::numeric_limits<T>::min() < 0) {
|
||||||
|
VerifyValue<T>(Traits<T>::Negate(i), f, g);
|
||||||
|
VerifyValue<T>(Traits<T>::Negate(i + 1), f, g);
|
||||||
|
}
|
||||||
|
last = i;
|
||||||
|
i *= power;
|
||||||
|
} while (last < i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void u32toa_naive(uint32_t value, char* buffer) {
|
||||||
|
char temp[10];
|
||||||
|
char *p = temp;
|
||||||
|
do {
|
||||||
|
*p++ = char(value % 10) + '0';
|
||||||
|
value /= 10;
|
||||||
|
} while (value > 0);
|
||||||
|
|
||||||
|
do {
|
||||||
|
*buffer++ = *--p;
|
||||||
|
} while (p != temp);
|
||||||
|
|
||||||
|
*buffer = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i32toa_naive(int32_t value, char* buffer) {
|
||||||
|
uint32_t u = static_cast<uint32_t>(value);
|
||||||
|
if (value < 0) {
|
||||||
|
*buffer++ = '-';
|
||||||
|
u = ~u + 1;
|
||||||
|
}
|
||||||
|
u32toa_naive(u, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void u64toa_naive(uint64_t value, char* buffer) {
|
||||||
|
char temp[20];
|
||||||
|
char *p = temp;
|
||||||
|
do {
|
||||||
|
*p++ = char(value % 10) + '0';
|
||||||
|
value /= 10;
|
||||||
|
} while (value > 0);
|
||||||
|
|
||||||
|
do {
|
||||||
|
*buffer++ = *--p;
|
||||||
|
} while (p != temp);
|
||||||
|
|
||||||
|
*buffer = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i64toa_naive(int64_t value, char* buffer) {
|
||||||
|
uint64_t u = static_cast<uint64_t>(value);
|
||||||
|
if (value < 0) {
|
||||||
|
*buffer++ = '-';
|
||||||
|
u = ~u + 1;
|
||||||
|
}
|
||||||
|
u64toa_naive(u, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(itoa, u32toa) {
|
||||||
|
Verify(u32toa_naive, u32toa);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(itoa, i32toa) {
|
||||||
|
Verify(i32toa_naive, i32toa);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(itoa, u64toa) {
|
||||||
|
Verify(u64toa_naive, u64toa);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(itoa, i64toa) {
|
||||||
|
Verify(i64toa_naive, i64toa);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
@ -25,9 +25,22 @@
|
|||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
|
|
||||||
static char* ReadFile(const char* filename, size_t& length) {
|
static char* ReadFile(const char* filename, size_t& length) {
|
||||||
FILE *fp = fopen(filename, "rb");
|
const char *paths[] = {
|
||||||
if (!fp)
|
"jsonchecker/%s",
|
||||||
fp = fopen(filename, "rb");
|
"bin/jsonchecker/%s",
|
||||||
|
"../bin/jsonchecker/%s",
|
||||||
|
"../../bin/jsonchecker/%s",
|
||||||
|
"../../../bin/jsonchecker/%s"
|
||||||
|
};
|
||||||
|
char buffer[1024];
|
||||||
|
FILE *fp = 0;
|
||||||
|
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
|
||||||
|
sprintf(buffer, paths[i], filename);
|
||||||
|
fp = fopen(buffer, "rb");
|
||||||
|
if (fp)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -51,18 +64,14 @@ TEST(JsonChecker, Reader) {
|
|||||||
if (i == 18) // fail18.json is valid in rapidjson, which has no limitation on depth of nesting.
|
if (i == 18) // fail18.json is valid in rapidjson, which has no limitation on depth of nesting.
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sprintf(filename, "jsonchecker/fail%d.json", i);
|
sprintf(filename, "fail%d.json", i);
|
||||||
size_t length;
|
size_t length;
|
||||||
char* json = ReadFile(filename, length);
|
char* json = ReadFile(filename, length);
|
||||||
if (!json) {
|
|
||||||
sprintf(filename, "../../bin/jsonchecker/fail%d.json", i);
|
|
||||||
json = ReadFile(filename, length);
|
|
||||||
if (!json) {
|
if (!json) {
|
||||||
printf("jsonchecker file %s not found", filename);
|
printf("jsonchecker file %s not found", filename);
|
||||||
ADD_FAILURE();
|
ADD_FAILURE();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
|
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
|
||||||
document.Parse((const char*)json);
|
document.Parse((const char*)json);
|
||||||
@ -76,17 +85,13 @@ TEST(JsonChecker, Reader) {
|
|||||||
|
|
||||||
// passX.json
|
// passX.json
|
||||||
for (int i = 1; i <= 3; i++) {
|
for (int i = 1; i <= 3; i++) {
|
||||||
sprintf(filename, "jsonchecker/pass%d.json", i);
|
sprintf(filename, "pass%d.json", i);
|
||||||
size_t length;
|
size_t length;
|
||||||
char* json = ReadFile(filename, length);
|
char* json = ReadFile(filename, length);
|
||||||
if (!json) {
|
|
||||||
sprintf(filename, "../../bin/jsonchecker/pass%d.json", i);
|
|
||||||
json = ReadFile(filename, length);
|
|
||||||
if (!json) {
|
if (!json) {
|
||||||
printf("jsonchecker file %s not found", filename);
|
printf("jsonchecker file %s not found", filename);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
|
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
|
||||||
document.Parse((const char*)json);
|
document.Parse((const char*)json);
|
||||||
|
167
test/unittest/prettywritertest.cpp
Normal file
167
test/unittest/prettywritertest.cpp
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// Copyright (C) 2011 Milo Yip
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "unittest.h"
|
||||||
|
#include "rapidjson/reader.h"
|
||||||
|
#include "rapidjson/prettywriter.h"
|
||||||
|
#include "rapidjson/stringbuffer.h"
|
||||||
|
#include "rapidjson/filewritestream.h"
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,-1],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}";
|
||||||
|
static const char kPrettyJson[] =
|
||||||
|
"{\n"
|
||||||
|
" \"hello\": \"world\",\n"
|
||||||
|
" \"t\": true,\n"
|
||||||
|
" \"f\": false,\n"
|
||||||
|
" \"n\": null,\n"
|
||||||
|
" \"i\": 123,\n"
|
||||||
|
" \"pi\": 3.1416,\n"
|
||||||
|
" \"a\": [\n"
|
||||||
|
" 1,\n"
|
||||||
|
" 2,\n"
|
||||||
|
" 3,\n"
|
||||||
|
" -1\n"
|
||||||
|
" ],\n"
|
||||||
|
" \"u64\": 1234567890123456789,\n"
|
||||||
|
" \"i64\": -1234567890123456789\n"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
TEST(PrettyWriter, Basic) {
|
||||||
|
StringBuffer buffer;
|
||||||
|
PrettyWriter<StringBuffer> writer(buffer);
|
||||||
|
Reader reader;
|
||||||
|
StringStream s(kJson);
|
||||||
|
reader.Parse(s, writer);
|
||||||
|
EXPECT_STREQ(kPrettyJson, buffer.GetString());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PrettyWriter, SetIndent) {
|
||||||
|
StringBuffer buffer;
|
||||||
|
PrettyWriter<StringBuffer> writer(buffer);
|
||||||
|
writer.SetIndent('\t', 1);
|
||||||
|
Reader reader;
|
||||||
|
StringStream s(kJson);
|
||||||
|
reader.Parse(s, writer);
|
||||||
|
EXPECT_STREQ(
|
||||||
|
"{\n"
|
||||||
|
"\t\"hello\": \"world\",\n"
|
||||||
|
"\t\"t\": true,\n"
|
||||||
|
"\t\"f\": false,\n"
|
||||||
|
"\t\"n\": null,\n"
|
||||||
|
"\t\"i\": 123,\n"
|
||||||
|
"\t\"pi\": 3.1416,\n"
|
||||||
|
"\t\"a\": [\n"
|
||||||
|
"\t\t1,\n"
|
||||||
|
"\t\t2,\n"
|
||||||
|
"\t\t3,\n"
|
||||||
|
"\t\t-1\n"
|
||||||
|
"\t],\n"
|
||||||
|
"\t\"u64\": 1234567890123456789,\n"
|
||||||
|
"\t\"i64\": -1234567890123456789\n"
|
||||||
|
"}",
|
||||||
|
buffer.GetString());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PrettyWriter, String) {
|
||||||
|
StringBuffer buffer;
|
||||||
|
PrettyWriter<StringBuffer> writer(buffer);
|
||||||
|
EXPECT_TRUE(writer.StartArray());
|
||||||
|
EXPECT_TRUE(writer.String("Hello\n"));
|
||||||
|
EXPECT_TRUE(writer.EndArray());
|
||||||
|
EXPECT_STREQ("[\n \"Hello\\n\"\n]", buffer.GetString());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if RAPIDJSON_HAS_STDSTRING
|
||||||
|
TEST(PrettyWriter, String_STDSTRING) {
|
||||||
|
StringBuffer buffer;
|
||||||
|
PrettyWriter<StringBuffer> writer(buffer);
|
||||||
|
EXPECT_TRUE(writer.StartArray());
|
||||||
|
EXPECT_TRUE(writer.String(std::string("Hello\n")));
|
||||||
|
EXPECT_TRUE(writer.EndArray());
|
||||||
|
EXPECT_STREQ("[\n \"Hello\\n\"\n]", buffer.GetString());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
class OStreamWrapper {
|
||||||
|
public:
|
||||||
|
typedef char Ch;
|
||||||
|
|
||||||
|
OStreamWrapper(std::ostream& os) : os_(os) {}
|
||||||
|
|
||||||
|
Ch Peek() const { assert(false); return '\0'; }
|
||||||
|
Ch Take() { assert(false); return '\0'; }
|
||||||
|
size_t Tell() const { return 0; }
|
||||||
|
|
||||||
|
Ch* PutBegin() { assert(false); return 0; }
|
||||||
|
void Put(Ch c) { os_.put(c); }
|
||||||
|
void Flush() { os_.flush(); }
|
||||||
|
size_t PutEnd(Ch*) { assert(false); return 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
OStreamWrapper(const OStreamWrapper&);
|
||||||
|
OStreamWrapper& operator=(const OStreamWrapper&);
|
||||||
|
|
||||||
|
std::ostream& os_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// For covering PutN() generic version
|
||||||
|
TEST(PrettyWriter, OStreamWrapper) {
|
||||||
|
StringStream s(kJson);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
OStreamWrapper os(ss);
|
||||||
|
|
||||||
|
PrettyWriter<OStreamWrapper> writer(os);
|
||||||
|
|
||||||
|
Reader reader;
|
||||||
|
reader.Parse(s, writer);
|
||||||
|
|
||||||
|
std::string actual = ss.str();
|
||||||
|
EXPECT_STREQ(kPrettyJson, actual.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// For covering FileWriteStream::PutN()
|
||||||
|
TEST(PrettyWriter, FileWriteStream) {
|
||||||
|
char filename[L_tmpnam];
|
||||||
|
FILE* fp = TempFile(filename);
|
||||||
|
char buffer[16];
|
||||||
|
FileWriteStream os(fp, buffer, sizeof(buffer));
|
||||||
|
PrettyWriter<FileWriteStream> writer(os);
|
||||||
|
Reader reader;
|
||||||
|
StringStream s(kJson);
|
||||||
|
reader.Parse(s, writer);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
fp = fopen(filename, "rb");
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
size_t size = (size_t)ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
char* json = (char*)malloc(size + 1);
|
||||||
|
size_t readLength = fread(json, 1, size, fp);
|
||||||
|
json[readLength] = '\0';
|
||||||
|
fclose(fp);
|
||||||
|
remove(filename);
|
||||||
|
EXPECT_STREQ(kPrettyJson, json);
|
||||||
|
free(json);
|
||||||
|
}
|
@ -254,6 +254,32 @@ static void TestParseDouble() {
|
|||||||
TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203124", 1.0); // previous double
|
TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203124", 1.0); // previous double
|
||||||
TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203126", 1.00000000000000022); // next double
|
TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203126", 1.00000000000000022); // next double
|
||||||
|
|
||||||
|
// Numbers from https://github.com/floitsch/double-conversion/blob/master/test/cctest/test-strtod.cc
|
||||||
|
|
||||||
|
TEST_DOUBLE(fullPrecision, "72057594037927928.0", 72057594037927928.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "72057594037927936.0", 72057594037927936.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "72057594037927932.0", 72057594037927936.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "7205759403792793199999e-5", 72057594037927928.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "7205759403792793200001e-5", 72057594037927936.0);
|
||||||
|
|
||||||
|
TEST_DOUBLE(fullPrecision, "9223372036854774784.0", 9223372036854774784.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "9223372036854775808.0", 9223372036854775808.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "9223372036854775296.0", 9223372036854775808.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "922337203685477529599999e-5", 9223372036854774784.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "922337203685477529600001e-5", 9223372036854775808.0);
|
||||||
|
|
||||||
|
TEST_DOUBLE(fullPrecision, "10141204801825834086073718800384", 10141204801825834086073718800384.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "10141204801825835211973625643008", 10141204801825835211973625643008.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "10141204801825834649023672221696", 10141204801825835211973625643008.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "1014120480182583464902367222169599999e-5", 10141204801825834086073718800384.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "1014120480182583464902367222169600001e-5", 10141204801825835211973625643008.0);
|
||||||
|
|
||||||
|
TEST_DOUBLE(fullPrecision, "5708990770823838890407843763683279797179383808", 5708990770823838890407843763683279797179383808.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "5708990770823839524233143877797980545530986496", 5708990770823839524233143877797980545530986496.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "5708990770823839207320493820740630171355185152", 5708990770823839524233143877797980545530986496.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "5708990770823839207320493820740630171355185151999e-3", 5708990770823838890407843763683279797179383808.0);
|
||||||
|
TEST_DOUBLE(fullPrecision, "5708990770823839207320493820740630171355185152001e-3", 5708990770823839524233143877797980545530986496.0);
|
||||||
|
|
||||||
{
|
{
|
||||||
char n1e308[310]; // '1' followed by 308 '0'
|
char n1e308[310]; // '1' followed by 308 '0'
|
||||||
n1e308[0] = '1';
|
n1e308[0] = '1';
|
||||||
@ -263,28 +289,53 @@ static void TestParseDouble() {
|
|||||||
TEST_DOUBLE(fullPrecision, n1e308, 1E308);
|
TEST_DOUBLE(fullPrecision, n1e308, 1E308);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0 // Very slow
|
// Cover trimming
|
||||||
static const unsigned count = 10000000;
|
TEST_DOUBLE(fullPrecision,
|
||||||
// Random test for double
|
"2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508"
|
||||||
{
|
"7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012"
|
||||||
Random r;
|
"9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306"
|
||||||
|
"6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505"
|
||||||
|
"1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621"
|
||||||
|
"5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844"
|
||||||
|
"2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042"
|
||||||
|
"7567186443383770486037861622771738545623065874679014086723327636718751234567890123456789012345678901"
|
||||||
|
"e-308",
|
||||||
|
2.2250738585072014e-308);
|
||||||
|
|
||||||
|
{
|
||||||
|
static const unsigned count = 100; // Tested with 1000000 locally
|
||||||
|
Random r;
|
||||||
|
Reader reader; // Reusing reader to prevent heap allocation
|
||||||
|
|
||||||
|
// Exhaustively test different exponents with random significant
|
||||||
|
for (uint64_t exp = 0; exp < 2047; exp++) {
|
||||||
|
;
|
||||||
for (unsigned i = 0; i < count; i++) {
|
for (unsigned i = 0; i < count; i++) {
|
||||||
internal::Double d;
|
|
||||||
do {
|
|
||||||
// Need to call r() in two statements for cross-platform coherent sequence.
|
// Need to call r() in two statements for cross-platform coherent sequence.
|
||||||
uint64_t u = uint64_t(r()) << 32;
|
uint64_t u = (exp << 52) | uint64_t(r() & 0x000FFFFF) << 32;
|
||||||
u |= uint64_t(r());
|
u |= uint64_t(r());
|
||||||
d = internal::Double(u);
|
internal::Double d = internal::Double(u);
|
||||||
} while (d.IsNan() || d.IsInf()/* || !d.IsNormal()*/); // Also work for subnormal now
|
|
||||||
|
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
*internal::dtoa(d.Value(), buffer) = '\0';
|
*internal::dtoa(d.Value(), buffer) = '\0';
|
||||||
TEST_DOUBLE(fullPrecision, buffer, d.Value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
StringStream s(buffer);
|
||||||
|
ParseDoubleHandler h;
|
||||||
|
ASSERT_EQ(kParseErrorNone, reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(s, h).Code());
|
||||||
|
EXPECT_EQ(1u, h.step_);
|
||||||
|
internal::Double a(h.actual_);
|
||||||
|
if (fullPrecision) {
|
||||||
|
EXPECT_EQ(d.Uint64Value(), a.Uint64Value());
|
||||||
|
if (d.Uint64Value() != a.Uint64Value())
|
||||||
|
printf(" String: %sn Actual: %.17gnExpected: %.17gn", buffer, h.actual_, d.Value());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
EXPECT_EQ(d.Sign(), a.Sign()); /* for 0.0 != -0.0 */
|
||||||
|
EXPECT_DOUBLE_EQ(d.Value(), h.actual_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#undef TEST_DOUBLE
|
#undef TEST_DOUBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,6 +538,17 @@ TEST(Reader, ParseString_Transcoding) {
|
|||||||
EXPECT_EQ(StrLen(e), h.length_);
|
EXPECT_EQ(StrLen(e), h.length_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Reader, ParseString_TranscodingWithValidation) {
|
||||||
|
const char* x = "\"Hello\"";
|
||||||
|
const wchar_t* e = L"Hello";
|
||||||
|
GenericStringStream<UTF8<> > is(x);
|
||||||
|
GenericReader<UTF8<>, UTF16<> > reader;
|
||||||
|
ParseStringHandler<UTF16<> > h;
|
||||||
|
reader.Parse<kParseValidateEncodingFlag>(is, h);
|
||||||
|
EXPECT_EQ(0, StrCmp<UTF16<>::Ch>(e, h.str_));
|
||||||
|
EXPECT_EQ(StrLen(e), h.length_);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Reader, ParseString_NonDestructive) {
|
TEST(Reader, ParseString_NonDestructive) {
|
||||||
StringStream s("\"Hello\\nWorld\"");
|
StringStream s("\"Hello\\nWorld\"");
|
||||||
ParseStringHandler<UTF8<> > h;
|
ParseStringHandler<UTF8<> > h;
|
||||||
@ -496,24 +558,31 @@ TEST(Reader, ParseString_NonDestructive) {
|
|||||||
EXPECT_EQ(11u, h.length_);
|
EXPECT_EQ(11u, h.length_);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseErrorCode TestString(const char* str) {
|
template <typename Encoding>
|
||||||
StringStream s(str);
|
ParseErrorCode TestString(const typename Encoding::Ch* str) {
|
||||||
BaseReaderHandler<> h;
|
GenericStringStream<Encoding> s(str);
|
||||||
Reader reader;
|
BaseReaderHandler<Encoding> h;
|
||||||
reader.Parse<kParseValidateEncodingFlag>(s, h);
|
GenericReader<Encoding, Encoding> reader;
|
||||||
|
reader.template Parse<kParseValidateEncodingFlag>(s, h);
|
||||||
return reader.GetParseErrorCode();
|
return reader.GetParseErrorCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Reader, ParseString_Error) {
|
TEST(Reader, ParseString_Error) {
|
||||||
#define TEST_STRING_ERROR(errorCode, str)\
|
#define TEST_STRING_ERROR(errorCode, str)\
|
||||||
EXPECT_EQ(errorCode, TestString(str))
|
EXPECT_EQ(errorCode, TestString<UTF8<> >(str))
|
||||||
|
|
||||||
#define ARRAY(...) { __VA_ARGS__ }
|
#define ARRAY(...) { __VA_ARGS__ }
|
||||||
#define TEST_STRINGENCODING_ERROR(Encoding, utype, array) \
|
#define TEST_STRINGENCODING_ERROR(Encoding, TargetEncoding, utype, array) \
|
||||||
{ \
|
{ \
|
||||||
static const utype ue[] = array; \
|
static const utype ue[] = array; \
|
||||||
static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&ue[0]); \
|
static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&ue[0]); \
|
||||||
EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString(e));\
|
EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString<Encoding>(e));\
|
||||||
|
/* decode error */\
|
||||||
|
GenericStringStream<Encoding> s(e);\
|
||||||
|
BaseReaderHandler<TargetEncoding> h;\
|
||||||
|
GenericReader<Encoding, TargetEncoding> reader;\
|
||||||
|
reader.Parse(s, h);\
|
||||||
|
EXPECT_EQ(kParseErrorStringInvalidEncoding, reader.GetParseErrorCode());\
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid escape character in string.
|
// Invalid escape character in string.
|
||||||
@ -542,7 +611,7 @@ TEST(Reader, ParseString_Error) {
|
|||||||
char e[] = { '[', '\"', 0, '\"', ']', '\0' };
|
char e[] = { '[', '\"', 0, '\"', ']', '\0' };
|
||||||
for (unsigned char c = 0x80u; c <= 0xBFu; c++) {
|
for (unsigned char c = 0x80u; c <= 0xBFu; c++) {
|
||||||
e[2] = c;
|
e[2] = c;
|
||||||
ParseErrorCode error = TestString(e);
|
ParseErrorCode error = TestString<UTF8<> >(e);
|
||||||
EXPECT_EQ(kParseErrorStringInvalidEncoding, error);
|
EXPECT_EQ(kParseErrorStringInvalidEncoding, error);
|
||||||
if (error != kParseErrorStringInvalidEncoding)
|
if (error != kParseErrorStringInvalidEncoding)
|
||||||
std::cout << (unsigned)(unsigned char)c << std::endl;
|
std::cout << (unsigned)(unsigned char)c << std::endl;
|
||||||
@ -561,30 +630,40 @@ TEST(Reader, ParseString_Error) {
|
|||||||
// 4 Overlong sequences
|
// 4 Overlong sequences
|
||||||
|
|
||||||
// 4.1 Examples of an overlong ASCII character
|
// 4.1 Examples of an overlong ASCII character
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0xAFu, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0xAFu, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0xAFu, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0xAFu, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0'));
|
||||||
|
|
||||||
// 4.2 Maximum overlong sequences
|
// 4.2 Maximum overlong sequences
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC1u, 0xBFu, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC1u, 0xBFu, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x9Fu, 0xBFu, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x9Fu, 0xBFu, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x8Fu, 0xBFu, 0xBFu, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x8Fu, 0xBFu, 0xBFu, '\"', ']', '\0'));
|
||||||
|
|
||||||
// 4.3 Overlong representation of the NUL character
|
// 4.3 Overlong representation of the NUL character
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0x80u, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0x80u, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0x80u, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0x80u, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0x80u, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0x80u, '\"', ']', '\0'));
|
||||||
|
|
||||||
// 5 Illegal code positions
|
// 5 Illegal code positions
|
||||||
|
|
||||||
// 5.1 Single UTF-16 surrogates
|
// 5.1 Single UTF-16 surrogates
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xA0u, 0x80u, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xA0u, 0x80u, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xADu, 0xBFu, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xADu, 0xBFu, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAEu, 0x80u, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAEu, 0x80u, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAFu, 0xBFu, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAFu, 0xBFu, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xB0u, 0x80u, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xB0u, 0x80u, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBEu, 0x80u, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBEu, 0x80u, '\"', ']', '\0'));
|
||||||
TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBFu, 0xBFu, '\"', ']', '\0'));
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBFu, 0xBFu, '\"', ']', '\0'));
|
||||||
|
|
||||||
|
// Malform UTF-16 sequences
|
||||||
|
TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xDC00, 0xDC00, '\"', ']', '\0'));
|
||||||
|
TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xD800, 0xD800, '\"', ']', '\0'));
|
||||||
|
|
||||||
|
// Malform UTF-32 sequence
|
||||||
|
TEST_STRINGENCODING_ERROR(UTF32<>, UTF8<>, unsigned, ARRAY('[', '\"', 0x110000, '\"', ']', '\0'));
|
||||||
|
|
||||||
|
// Malform ASCII sequence
|
||||||
|
TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x80), '\"', ']', '\0'));
|
||||||
|
|
||||||
#undef ARRAY
|
#undef ARRAY
|
||||||
#undef TEST_STRINGARRAY_ERROR
|
#undef TEST_STRINGARRAY_ERROR
|
||||||
@ -974,6 +1053,17 @@ TEST(Reader, IterativeParsing_ErrorHandling) {
|
|||||||
TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u);
|
TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u);
|
||||||
TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u);
|
TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u);
|
||||||
TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u);
|
TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u);
|
||||||
|
TESTERRORHANDLING("{\"a: 1", kParseErrorStringMissQuotationMark, 5u);
|
||||||
|
|
||||||
|
// Any JSON value can be a valid root element in RFC7159.
|
||||||
|
TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 2u);
|
||||||
|
TESTERRORHANDLING("truE", kParseErrorValueInvalid, 3u);
|
||||||
|
TESTERRORHANDLING("False", kParseErrorValueInvalid, 0u);
|
||||||
|
TESTERRORHANDLING("true, false", kParseErrorDocumentRootNotSingular, 4u);
|
||||||
|
TESTERRORHANDLING("false, false", kParseErrorDocumentRootNotSingular, 5u);
|
||||||
|
TESTERRORHANDLING("nulL", kParseErrorValueInvalid, 3u);
|
||||||
|
TESTERRORHANDLING("null , null", kParseErrorDocumentRootNotSingular, 5u);
|
||||||
|
TESTERRORHANDLING("1a", kParseErrorDocumentRootNotSingular, 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Encoding = UTF8<> >
|
template<typename Encoding = UTF8<> >
|
||||||
@ -1177,6 +1267,59 @@ TEST(Reader, IterativeParsing_ShortCircuit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For covering BaseReaderHandler default functions
|
||||||
|
TEST(Reader, BaseReaderHandler_Default) {
|
||||||
|
BaseReaderHandler<> h;
|
||||||
|
Reader reader;
|
||||||
|
StringStream is("[null, true, -1, 1, -1234567890123456789, 1234567890123456789, 3.14, \"s\", { \"a\" : 1 }]");
|
||||||
|
EXPECT_TRUE(reader.Parse(is, h));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int e>
|
||||||
|
struct TerminateHandler {
|
||||||
|
bool Null() { return e != 0; }
|
||||||
|
bool Bool(bool) { return e != 1; }
|
||||||
|
bool Int(int) { return e != 2; }
|
||||||
|
bool Uint(unsigned) { return e != 3; }
|
||||||
|
bool Int64(int64_t) { return e != 4; }
|
||||||
|
bool Uint64(uint64_t) { return e != 5; }
|
||||||
|
bool Double(double) { return e != 6; }
|
||||||
|
bool String(const char*, SizeType, bool) { return e != 7; }
|
||||||
|
bool StartObject() { return e != 8; }
|
||||||
|
bool Key(const char*, SizeType, bool) { return e != 9; }
|
||||||
|
bool EndObject(SizeType) { return e != 10; }
|
||||||
|
bool StartArray() { return e != 11; }
|
||||||
|
bool EndArray(SizeType) { return e != 12; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TEST_TERMINATION(e, json)\
|
||||||
|
{\
|
||||||
|
Reader reader;\
|
||||||
|
TerminateHandler<e> h;\
|
||||||
|
StringStream is(json);\
|
||||||
|
EXPECT_FALSE(reader.Parse(is, h));\
|
||||||
|
EXPECT_EQ(kParseErrorTermination, reader.GetParseErrorCode());\
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Reader, ParseTerminationByHandler) {
|
||||||
|
TEST_TERMINATION(0, "[null");
|
||||||
|
TEST_TERMINATION(1, "[true");
|
||||||
|
TEST_TERMINATION(1, "[false");
|
||||||
|
TEST_TERMINATION(2, "[-1");
|
||||||
|
TEST_TERMINATION(3, "[1");
|
||||||
|
TEST_TERMINATION(4, "[-1234567890123456789");
|
||||||
|
TEST_TERMINATION(5, "[1234567890123456789");
|
||||||
|
TEST_TERMINATION(6, "[0.5]");
|
||||||
|
TEST_TERMINATION(7, "[\"a\"");
|
||||||
|
TEST_TERMINATION(8, "[{");
|
||||||
|
TEST_TERMINATION(9, "[{\"a\"");
|
||||||
|
TEST_TERMINATION(10, "[{}");
|
||||||
|
TEST_TERMINATION(10, "[{\"a\":1}"); // non-empty object
|
||||||
|
TEST_TERMINATION(11, "{\"a\":[");
|
||||||
|
TEST_TERMINATION(12, "{\"a\":[]");
|
||||||
|
TEST_TERMINATION(12, "{\"a\":[1]"); // non-empty array
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
RAPIDJSON_DIAG_POP
|
RAPIDJSON_DIAG_POP
|
||||||
#endif
|
#endif
|
||||||
|
74
test/unittest/simdtest.cpp
Normal file
74
test/unittest/simdtest.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (C) 2011 Milo Yip
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
// Since Travis CI installs old Valgrind 3.7.0, which fails with some SSE4.2
|
||||||
|
// The unit tests prefix with SIMD should be skipped by Valgrind test
|
||||||
|
|
||||||
|
// __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler.
|
||||||
|
// We use -march=native with gmake to enable -msse2 and -msse4.2, if supported.
|
||||||
|
#if defined(__SSE4_2__)
|
||||||
|
# define RAPIDJSON_SSE42
|
||||||
|
#elif defined(__SSE2__)
|
||||||
|
# define RAPIDJSON_SSE2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RAPIDJSON_NAMESPACE rapidjson_simd
|
||||||
|
|
||||||
|
#include "unittest.h"
|
||||||
|
|
||||||
|
#include "rapidjson/reader.h"
|
||||||
|
|
||||||
|
using namespace rapidjson_simd;
|
||||||
|
|
||||||
|
#ifdef RAPIDJSON_SSE2
|
||||||
|
#define SIMD_SUFFIX(name) name##_SSE2
|
||||||
|
#elif defined(RAPIDJSON_SSE42)
|
||||||
|
#define SIMD_SUFFIX(name) name##_SSE42
|
||||||
|
#else
|
||||||
|
#define SIMD_SUFFIX(name) name
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename StreamType>
|
||||||
|
void TestSkipWhitespace() {
|
||||||
|
for (int step = 1; step < 32; step++) {
|
||||||
|
char buffer[1025];
|
||||||
|
for (size_t i = 0; i < 1024; i++)
|
||||||
|
buffer[i] = " \t\r\n"[i % 4];
|
||||||
|
for (size_t i = 0; i < 1024; i += step)
|
||||||
|
buffer[i] = 'X';
|
||||||
|
buffer[1024] = '\0';
|
||||||
|
|
||||||
|
StreamType s(buffer);
|
||||||
|
size_t i = 0;
|
||||||
|
for (;;) {
|
||||||
|
SkipWhitespace(s);
|
||||||
|
if (s.Peek() == '\0')
|
||||||
|
break;
|
||||||
|
EXPECT_EQ(i, s.Tell());
|
||||||
|
EXPECT_EQ('X', s.Take());
|
||||||
|
i += step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) {
|
||||||
|
TestSkipWhitespace<StringStream>();
|
||||||
|
TestSkipWhitespace<InsituStringStream>();
|
||||||
|
}
|
@ -54,6 +54,10 @@ TEST(StringBuffer, Push) {
|
|||||||
buffer.Push(5);
|
buffer.Push(5);
|
||||||
|
|
||||||
EXPECT_EQ(5u, buffer.GetSize());
|
EXPECT_EQ(5u, buffer.GetSize());
|
||||||
|
|
||||||
|
// Causes sudden expansion to make the stack's capacity equal to size
|
||||||
|
buffer.Push(65536u);
|
||||||
|
EXPECT_EQ(5u + 65536u, buffer.GetSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StringBuffer, Pop) {
|
TEST(StringBuffer, Pop) {
|
||||||
|
@ -203,12 +203,28 @@ TEST(Value, EqualtoOperator) {
|
|||||||
EXPECT_TRUE(z.RemoveMember("t"));
|
EXPECT_TRUE(z.RemoveMember("t"));
|
||||||
TestUnequal(x, z);
|
TestUnequal(x, z);
|
||||||
TestEqual(y, z);
|
TestEqual(y, z);
|
||||||
y.AddMember("t", true, crtAllocator);
|
y.AddMember("t", false, crtAllocator);
|
||||||
z.AddMember("t", true, z.GetAllocator());
|
z.AddMember("t", false, z.GetAllocator());
|
||||||
|
TestUnequal(x, y);
|
||||||
|
TestUnequal(z, x);
|
||||||
|
y["t"] = true;
|
||||||
|
z["t"] = true;
|
||||||
TestEqual(x, y);
|
TestEqual(x, y);
|
||||||
TestEqual(y, z);
|
TestEqual(y, z);
|
||||||
TestEqual(z, x);
|
TestEqual(z, x);
|
||||||
|
|
||||||
|
// Swapping element order is not OK
|
||||||
|
x["a"][0].Swap(x["a"][1]);
|
||||||
|
TestUnequal(x, y);
|
||||||
|
x["a"][0].Swap(x["a"][1]);
|
||||||
|
TestEqual(x, y);
|
||||||
|
|
||||||
|
// Array of different size
|
||||||
|
x["a"].PushBack(4, allocator);
|
||||||
|
TestUnequal(x, y);
|
||||||
|
x["a"].PopBack();
|
||||||
|
TestEqual(x, y);
|
||||||
|
|
||||||
// Issue #129: compare Uint64
|
// Issue #129: compare Uint64
|
||||||
x.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0));
|
x.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0));
|
||||||
y.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF));
|
y.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF));
|
||||||
@ -229,6 +245,13 @@ void TestCopyFrom() {
|
|||||||
EXPECT_STREQ(v1.GetString(), v2.GetString());
|
EXPECT_STREQ(v1.GetString(), v2.GetString());
|
||||||
EXPECT_EQ(v1.GetString(), v2.GetString()); // string NOT copied
|
EXPECT_EQ(v1.GetString(), v2.GetString()); // string NOT copied
|
||||||
|
|
||||||
|
v1.SetString("bar", a); // copy string
|
||||||
|
v2.CopyFrom(v1, a);
|
||||||
|
EXPECT_TRUE(v1.GetType() == v2.GetType());
|
||||||
|
EXPECT_STREQ(v1.GetString(), v2.GetString());
|
||||||
|
EXPECT_NE(v1.GetString(), v2.GetString()); // string copied
|
||||||
|
|
||||||
|
|
||||||
v1.SetArray().PushBack(1234, a);
|
v1.SetArray().PushBack(1234, a);
|
||||||
v2.CopyFrom(v1, a);
|
v2.CopyFrom(v1, a);
|
||||||
EXPECT_TRUE(v2.IsArray());
|
EXPECT_TRUE(v2.IsArray());
|
||||||
@ -462,10 +485,19 @@ TEST(Value, Int64) {
|
|||||||
z.SetInt64(2147483648LL); // 2^31, cannot cast as int
|
z.SetInt64(2147483648LL); // 2^31, cannot cast as int
|
||||||
EXPECT_FALSE(z.IsInt());
|
EXPECT_FALSE(z.IsInt());
|
||||||
EXPECT_TRUE(z.IsUint());
|
EXPECT_TRUE(z.IsUint());
|
||||||
|
EXPECT_NEAR(2147483648.0, z.GetDouble(), 0.0);
|
||||||
|
|
||||||
z.SetInt64(4294967296LL); // 2^32, cannot cast as uint
|
z.SetInt64(4294967296LL); // 2^32, cannot cast as uint
|
||||||
EXPECT_FALSE(z.IsInt());
|
EXPECT_FALSE(z.IsInt());
|
||||||
EXPECT_FALSE(z.IsUint());
|
EXPECT_FALSE(z.IsUint());
|
||||||
|
EXPECT_NEAR(4294967296.0, z.GetDouble(), 0.0);
|
||||||
|
|
||||||
|
z.SetInt64(-2147483649LL); // -2^31-1, cannot cast as int
|
||||||
|
EXPECT_FALSE(z.IsInt());
|
||||||
|
EXPECT_NEAR(-2147483649.0, z.GetDouble(), 0.0);
|
||||||
|
|
||||||
|
z.SetInt64(static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 00000000)));
|
||||||
|
EXPECT_DOUBLE_EQ(-9223372036854775808.0, z.GetDouble());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Value, Uint64) {
|
TEST(Value, Uint64) {
|
||||||
@ -508,9 +540,8 @@ TEST(Value, Uint64) {
|
|||||||
|
|
||||||
z.SetUint64(9223372036854775808uLL); // 2^63 cannot cast as int64
|
z.SetUint64(9223372036854775808uLL); // 2^63 cannot cast as int64
|
||||||
EXPECT_FALSE(z.IsInt64());
|
EXPECT_FALSE(z.IsInt64());
|
||||||
|
EXPECT_EQ(9223372036854775808uLL, z.GetUint64()); // Issue 48
|
||||||
// Issue 48
|
EXPECT_DOUBLE_EQ(9223372036854775808.0, z.GetDouble());
|
||||||
EXPECT_EQ(9223372036854775808uLL, z.GetUint64());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Value, Double) {
|
TEST(Value, Double) {
|
||||||
@ -977,6 +1008,7 @@ TEST(Value, Object) {
|
|||||||
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());
|
EXPECT_STREQ("CherryD", x[othername].GetString());
|
||||||
|
EXPECT_THROW(x["nonexist"], AssertException);
|
||||||
|
|
||||||
// const operator[]
|
// const operator[]
|
||||||
EXPECT_STREQ("Apple", y["A"].GetString());
|
EXPECT_STREQ("Apple", y["A"].GetString());
|
||||||
@ -1041,13 +1073,15 @@ TEST(Value, Object) {
|
|||||||
EXPECT_FALSE(citr >= itr);
|
EXPECT_FALSE(citr >= itr);
|
||||||
|
|
||||||
// RemoveMember()
|
// RemoveMember()
|
||||||
x.RemoveMember("A");
|
EXPECT_TRUE(x.RemoveMember("A"));
|
||||||
EXPECT_FALSE(x.HasMember("A"));
|
EXPECT_FALSE(x.HasMember("A"));
|
||||||
|
|
||||||
x.RemoveMember("B");
|
EXPECT_TRUE(x.RemoveMember("B"));
|
||||||
EXPECT_FALSE(x.HasMember("B"));
|
EXPECT_FALSE(x.HasMember("B"));
|
||||||
|
|
||||||
x.RemoveMember(othername);
|
EXPECT_FALSE(x.RemoveMember("nonexist"));
|
||||||
|
|
||||||
|
EXPECT_TRUE(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());
|
||||||
@ -1237,3 +1271,46 @@ TEST(Value, AllocateShortString) {
|
|||||||
TestShortStringOptimization("123456789012345"); // edge case: 15 chars in 64-bit mode (=> short 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)
|
TestShortStringOptimization("1234567890123456"); // edge case: 16 chars in 64-bit mode (=> regular string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int e>
|
||||||
|
struct TerminateHandler {
|
||||||
|
bool Null() { return e != 0; }
|
||||||
|
bool Bool(bool) { return e != 1; }
|
||||||
|
bool Int(int) { return e != 2; }
|
||||||
|
bool Uint(unsigned) { return e != 3; }
|
||||||
|
bool Int64(int64_t) { return e != 4; }
|
||||||
|
bool Uint64(uint64_t) { return e != 5; }
|
||||||
|
bool Double(double) { return e != 6; }
|
||||||
|
bool String(const char*, SizeType, bool) { return e != 7; }
|
||||||
|
bool StartObject() { return e != 8; }
|
||||||
|
bool Key(const char*, SizeType, bool) { return e != 9; }
|
||||||
|
bool EndObject(SizeType) { return e != 10; }
|
||||||
|
bool StartArray() { return e != 11; }
|
||||||
|
bool EndArray(SizeType) { return e != 12; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TEST_TERMINATION(e, json)\
|
||||||
|
{\
|
||||||
|
Document d; \
|
||||||
|
EXPECT_FALSE(d.Parse(json).HasParseError()); \
|
||||||
|
Reader reader; \
|
||||||
|
TerminateHandler<e> h;\
|
||||||
|
EXPECT_FALSE(d.Accept(h));\
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Value, AcceptTerminationByHandler) {
|
||||||
|
TEST_TERMINATION(0, "[null]");
|
||||||
|
TEST_TERMINATION(1, "[true]");
|
||||||
|
TEST_TERMINATION(1, "[false]");
|
||||||
|
TEST_TERMINATION(2, "[-1]");
|
||||||
|
TEST_TERMINATION(3, "[2147483648]");
|
||||||
|
TEST_TERMINATION(4, "[-1234567890123456789]");
|
||||||
|
TEST_TERMINATION(5, "[9223372036854775808]");
|
||||||
|
TEST_TERMINATION(6, "[0.5]");
|
||||||
|
TEST_TERMINATION(7, "[\"a\"]");
|
||||||
|
TEST_TERMINATION(8, "[{}]");
|
||||||
|
TEST_TERMINATION(9, "[{\"a\":1}]");
|
||||||
|
TEST_TERMINATION(10, "[{}]");
|
||||||
|
TEST_TERMINATION(11, "{\"a\":[]}");
|
||||||
|
TEST_TERMINATION(12, "{\"a\":[]}");
|
||||||
|
}
|
||||||
|
@ -46,7 +46,7 @@ TEST(Writer, Compact) {
|
|||||||
StringBuffer buffer; \
|
StringBuffer buffer; \
|
||||||
Writer<StringBuffer> writer(buffer); \
|
Writer<StringBuffer> writer(buffer); \
|
||||||
Reader reader; \
|
Reader reader; \
|
||||||
reader.Parse<0>(s, writer); \
|
reader.Parse<kParseFullPrecisionFlag>(s, writer); \
|
||||||
EXPECT_STREQ(json, buffer.GetString()); \
|
EXPECT_STREQ(json, buffer.GetString()); \
|
||||||
EXPECT_TRUE(writer.IsComplete()); \
|
EXPECT_TRUE(writer.IsComplete()); \
|
||||||
}
|
}
|
||||||
@ -89,11 +89,28 @@ TEST(Writer, String) {
|
|||||||
TEST_ROUNDTRIP("[\"Hello\"]");
|
TEST_ROUNDTRIP("[\"Hello\"]");
|
||||||
TEST_ROUNDTRIP("[\"Hello\\u0000World\"]");
|
TEST_ROUNDTRIP("[\"Hello\\u0000World\"]");
|
||||||
TEST_ROUNDTRIP("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]");
|
TEST_ROUNDTRIP("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]");
|
||||||
|
|
||||||
|
#if RAPIDJSON_HAS_STDSTRING
|
||||||
|
{
|
||||||
|
StringBuffer buffer;
|
||||||
|
Writer<StringBuffer> writer(buffer);
|
||||||
|
writer.String(std::string("Hello\n"));
|
||||||
|
EXPECT_STREQ("\"Hello\\n\"", buffer.GetString());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Writer, Double) {
|
TEST(Writer, Double) {
|
||||||
TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]");
|
TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]");
|
||||||
TEST_ROUNDTRIP("[-0.0]"); // Issue #289
|
TEST_ROUNDTRIP("0.0");
|
||||||
|
TEST_ROUNDTRIP("-0.0"); // Issue #289
|
||||||
|
TEST_ROUNDTRIP("1e30");
|
||||||
|
TEST_ROUNDTRIP("1.0");
|
||||||
|
TEST_ROUNDTRIP("5e-324"); // Min subnormal positive double
|
||||||
|
TEST_ROUNDTRIP("2.225073858507201e-308"); // Max subnormal positive double
|
||||||
|
TEST_ROUNDTRIP("2.2250738585072014e-308"); // Min normal positive double
|
||||||
|
TEST_ROUNDTRIP("1.7976931348623157e308"); // Max double
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Writer, Transcode) {
|
TEST(Writer, Transcode) {
|
||||||
@ -152,7 +169,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST(Writer, OStreamWrapper) {
|
TEST(Writer, OStreamWrapper) {
|
||||||
StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ");
|
StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3], \"u64\": 1234567890123456789, \"i64\":-1234567890123456789 } ");
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
OStreamWrapper os(ss);
|
OStreamWrapper os(ss);
|
||||||
@ -163,7 +180,7 @@ TEST(Writer, OStreamWrapper) {
|
|||||||
reader.Parse<0>(s, writer);
|
reader.Parse<0>(s, writer);
|
||||||
|
|
||||||
std::string actual = ss.str();
|
std::string actual = ss.str();
|
||||||
EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", actual.c_str());
|
EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}", actual.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Writer, AssertRootMayBeAnyValue) {
|
TEST(Writer, AssertRootMayBeAnyValue) {
|
||||||
@ -306,3 +323,61 @@ TEST(Writer, RootValueIsComplete) {
|
|||||||
T(writer.String(""));
|
T(writer.String(""));
|
||||||
#undef T
|
#undef T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Writer, InvalidEncoding) {
|
||||||
|
// Fail in decoding invalid UTF-8 sequence http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
|
||||||
|
{
|
||||||
|
GenericStringBuffer<UTF16<> > buffer;
|
||||||
|
Writer<GenericStringBuffer<UTF16<> >, UTF8<>, UTF16<> > writer(buffer);
|
||||||
|
writer.StartArray();
|
||||||
|
EXPECT_FALSE(writer.String("\xfe"));
|
||||||
|
EXPECT_FALSE(writer.String("\xff"));
|
||||||
|
EXPECT_FALSE(writer.String("\xfe\xfe\xff\xff"));
|
||||||
|
writer.EndArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail in encoding
|
||||||
|
{
|
||||||
|
StringBuffer buffer;
|
||||||
|
Writer<StringBuffer, UTF32<> > writer(buffer);
|
||||||
|
static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF
|
||||||
|
EXPECT_FALSE(writer.String(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail in unicode escaping in ASCII output
|
||||||
|
{
|
||||||
|
StringBuffer buffer;
|
||||||
|
Writer<StringBuffer, UTF32<>, ASCII<> > writer(buffer);
|
||||||
|
static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF
|
||||||
|
EXPECT_FALSE(writer.String(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Writer, InvalidEventSequence) {
|
||||||
|
// {]
|
||||||
|
{
|
||||||
|
StringBuffer buffer;
|
||||||
|
Writer<StringBuffer> writer(buffer);
|
||||||
|
writer.StartObject();
|
||||||
|
EXPECT_THROW(writer.EndArray(), AssertException);
|
||||||
|
EXPECT_FALSE(writer.IsComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
// [}
|
||||||
|
{
|
||||||
|
StringBuffer buffer;
|
||||||
|
Writer<StringBuffer> writer(buffer);
|
||||||
|
writer.StartArray();
|
||||||
|
EXPECT_THROW(writer.EndObject(), AssertException);
|
||||||
|
EXPECT_FALSE(writer.IsComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
// { 1:
|
||||||
|
{
|
||||||
|
StringBuffer buffer;
|
||||||
|
Writer<StringBuffer> writer(buffer);
|
||||||
|
writer.StartObject();
|
||||||
|
EXPECT_THROW(writer.Int(1), AssertException);
|
||||||
|
EXPECT_FALSE(writer.IsComplete());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user