
This patch fixes the issue where parsing NaN or Inf values with fractional or exponent parts would return incorrect results (e.g., "NaN.2e2" would be parsed as 20). Before this patch, the parser would continue to process the fractional and exponent parts even after successfully parsing a valid NaN or Inf, which could lead to parsing errors. This patch adds a check for such cases to skips the parsing of the fractional and exponent parts after completing the NaN and Inf parsing.
2374 lines
96 KiB
C++
2374 lines
96 KiB
C++
// Tencent is pleased to support the open source community by making RapidJSON available.
|
|
//
|
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
|
// in compliance with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://opensource.org/licenses/MIT
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software distributed
|
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
#include "unittest.h"
|
|
|
|
#include "rapidjson/reader.h"
|
|
#include "rapidjson/internal/dtoa.h"
|
|
#include "rapidjson/internal/itoa.h"
|
|
#include "rapidjson/memorystream.h"
|
|
|
|
#include <limits>
|
|
|
|
using namespace rapidjson;
|
|
|
|
RAPIDJSON_DIAG_PUSH
|
|
#ifdef __GNUC__
|
|
RAPIDJSON_DIAG_OFF(effc++)
|
|
RAPIDJSON_DIAG_OFF(float-equal)
|
|
RAPIDJSON_DIAG_OFF(missing-noreturn)
|
|
#if __GNUC__ >= 7
|
|
RAPIDJSON_DIAG_OFF(dangling-else)
|
|
#endif
|
|
#endif // __GNUC__
|
|
|
|
#ifdef __clang__
|
|
RAPIDJSON_DIAG_OFF(variadic-macros)
|
|
RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
|
|
#endif
|
|
|
|
template<bool expect>
|
|
struct ParseBoolHandler : BaseReaderHandler<UTF8<>, ParseBoolHandler<expect> > {
|
|
ParseBoolHandler() : step_(0) {}
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
// gcc 4.8.x generates warning in EXPECT_EQ(bool, bool) on this gtest version.
|
|
// Workaround with EXPECT_TRUE().
|
|
bool Bool(bool b) { /*EXPECT_EQ(expect, b); */EXPECT_TRUE(expect == b); ++step_; return true; }
|
|
|
|
unsigned step_;
|
|
};
|
|
|
|
TEST(Reader, ParseTrue) {
|
|
StringStream s("true");
|
|
ParseBoolHandler<true> h;
|
|
Reader reader;
|
|
reader.Parse(s, h);
|
|
EXPECT_EQ(1u, h.step_);
|
|
}
|
|
|
|
TEST(Reader, ParseFalse) {
|
|
StringStream s("false");
|
|
ParseBoolHandler<false> h;
|
|
Reader reader;
|
|
reader.Parse(s, h);
|
|
EXPECT_EQ(1u, h.step_);
|
|
}
|
|
|
|
struct ParseIntHandler : BaseReaderHandler<UTF8<>, ParseIntHandler> {
|
|
ParseIntHandler() : step_(0), actual_() {}
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool Int(int i) { actual_ = i; step_++; return true; }
|
|
|
|
unsigned step_;
|
|
int actual_;
|
|
};
|
|
|
|
struct ParseUintHandler : BaseReaderHandler<UTF8<>, ParseUintHandler> {
|
|
ParseUintHandler() : step_(0), actual_() {}
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool Uint(unsigned i) { actual_ = i; step_++; return true; }
|
|
|
|
unsigned step_;
|
|
unsigned actual_;
|
|
};
|
|
|
|
struct ParseInt64Handler : BaseReaderHandler<UTF8<>, ParseInt64Handler> {
|
|
ParseInt64Handler() : step_(0), actual_() {}
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool Int64(int64_t i) { actual_ = i; step_++; return true; }
|
|
|
|
unsigned step_;
|
|
int64_t actual_;
|
|
};
|
|
|
|
struct ParseUint64Handler : BaseReaderHandler<UTF8<>, ParseUint64Handler> {
|
|
ParseUint64Handler() : step_(0), actual_() {}
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool Uint64(uint64_t i) { actual_ = i; step_++; return true; }
|
|
|
|
unsigned step_;
|
|
uint64_t actual_;
|
|
};
|
|
|
|
struct ParseDoubleHandler : BaseReaderHandler<UTF8<>, ParseDoubleHandler> {
|
|
ParseDoubleHandler() : step_(0), actual_() {}
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool Double(double d) { actual_ = d; step_++; return true; }
|
|
|
|
unsigned step_;
|
|
double actual_;
|
|
};
|
|
|
|
TEST(Reader, ParseNumber_Integer) {
|
|
#define TEST_INTEGER(Handler, str, x) \
|
|
{ \
|
|
StringStream s(str); \
|
|
Handler h; \
|
|
Reader reader; \
|
|
reader.Parse(s, h); \
|
|
EXPECT_EQ(1u, h.step_); \
|
|
EXPECT_EQ(x, h.actual_); \
|
|
}
|
|
|
|
TEST_INTEGER(ParseUintHandler, "0", 0u);
|
|
TEST_INTEGER(ParseUintHandler, "123", 123u);
|
|
TEST_INTEGER(ParseUintHandler, "2147483648", 2147483648u); // 2^31 - 1 (cannot be stored in int)
|
|
TEST_INTEGER(ParseUintHandler, "4294967295", 4294967295u);
|
|
|
|
TEST_INTEGER(ParseIntHandler, "-123", -123);
|
|
TEST_INTEGER(ParseIntHandler, "-2147483648", static_cast<int32_t>(0x80000000)); // -2^31 (min of int)
|
|
|
|
TEST_INTEGER(ParseUint64Handler, "4294967296", RAPIDJSON_UINT64_C2(1, 0)); // 2^32 (max of unsigned + 1, force to use uint64_t)
|
|
TEST_INTEGER(ParseUint64Handler, "18446744073709551615", RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)); // 2^64 - 1 (max of uint64_t)
|
|
|
|
TEST_INTEGER(ParseInt64Handler, "-2147483649", static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x7FFFFFFF))); // -2^31 -1 (min of int - 1, force to use int64_t)
|
|
TEST_INTEGER(ParseInt64Handler, "-9223372036854775808", static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))); // -2^63 (min of int64_t)
|
|
|
|
// Random test for uint32_t/int32_t
|
|
{
|
|
union {
|
|
uint32_t u;
|
|
int32_t i;
|
|
}u;
|
|
Random r;
|
|
|
|
for (unsigned i = 0; i < 100000; i++) {
|
|
u.u = r();
|
|
|
|
char buffer[32];
|
|
*internal::u32toa(u.u, buffer) = '\0';
|
|
TEST_INTEGER(ParseUintHandler, buffer, u.u);
|
|
|
|
if (u.i < 0) {
|
|
*internal::i32toa(u.i, buffer) = '\0';
|
|
TEST_INTEGER(ParseIntHandler, buffer, u.i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Random test for uint64_t/int64_t
|
|
{
|
|
union {
|
|
uint64_t u;
|
|
int64_t i;
|
|
}u;
|
|
Random r;
|
|
|
|
for (unsigned i = 0; i < 100000; i++) {
|
|
u.u = uint64_t(r()) << 32;
|
|
u.u |= r();
|
|
|
|
char buffer[32];
|
|
if (u.u > uint64_t(4294967295u)) {
|
|
*internal::u64toa(u.u, buffer) = '\0';
|
|
TEST_INTEGER(ParseUint64Handler, buffer, u.u);
|
|
}
|
|
|
|
if (u.i < -int64_t(2147483648u)) {
|
|
*internal::i64toa(u.i, buffer) = '\0';
|
|
TEST_INTEGER(ParseInt64Handler, buffer, u.i);
|
|
}
|
|
}
|
|
}
|
|
#undef TEST_INTEGER
|
|
}
|
|
|
|
template<bool fullPrecision>
|
|
static void TestParseDouble() {
|
|
#define TEST_DOUBLE(fullPrecision, str, x) \
|
|
{ \
|
|
StringStream s(str); \
|
|
ParseDoubleHandler h; \
|
|
Reader reader; \
|
|
ASSERT_EQ(kParseErrorNone, reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(s, h).Code()); \
|
|
EXPECT_EQ(1u, h.step_); \
|
|
internal::Double e(x), a(h.actual_); \
|
|
if (fullPrecision) { \
|
|
EXPECT_EQ(e.Uint64Value(), a.Uint64Value()); \
|
|
if (e.Uint64Value() != a.Uint64Value()) \
|
|
printf(" String: %s\n Actual: %.17g\nExpected: %.17g\n", str, h.actual_, x); \
|
|
} \
|
|
else { \
|
|
EXPECT_EQ(e.Sign(), a.Sign()); /* for 0.0 != -0.0 */ \
|
|
EXPECT_DOUBLE_EQ(x, h.actual_); \
|
|
} \
|
|
}
|
|
|
|
TEST_DOUBLE(fullPrecision, "0.0", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "-0.0", -0.0); // For checking issue #289
|
|
TEST_DOUBLE(fullPrecision, "0e100", 0.0); // For checking issue #1249
|
|
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, "2.2250738585072009e-308", 2.2250738585072009e-308); // Max subnormal double
|
|
TEST_DOUBLE(fullPrecision, "2.2250738585072014e-308", 2.2250738585072014e-308); // Min normal positive double
|
|
TEST_DOUBLE(fullPrecision, "1.7976931348623157e+308", 1.7976931348623157e+308); // Max double
|
|
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/Tencent/rapidjson/issues/120
|
|
TEST_DOUBLE(fullPrecision, "123e34", 123e34); // Fast Path Cases In Disguise
|
|
TEST_DOUBLE(fullPrecision, "45913141877270640000.0", 45913141877270640000.0);
|
|
TEST_DOUBLE(fullPrecision, "2.2250738585072011e-308", 2.2250738585072011e-308); // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
|
|
TEST_DOUBLE(fullPrecision, "1e-00011111111111", 0.0); // Issue #313
|
|
TEST_DOUBLE(fullPrecision, "-1e-00011111111111", -0.0);
|
|
TEST_DOUBLE(fullPrecision, "1e-214748363", 0.0); // Maximum supported negative exponent
|
|
TEST_DOUBLE(fullPrecision, "1e-214748364", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "1e-21474836311", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "1.00000000001e-2147483638", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "0.017976931348623157e+310", 1.7976931348623157e+308); // Max double in another form
|
|
TEST_DOUBLE(fullPrecision, "128.74836467836484838364836483643636483648e-336", 0.0); // Issue #1251
|
|
|
|
// Since
|
|
// abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... x 10^-324
|
|
// abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... x 10 ^ -324
|
|
// So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e-308
|
|
TEST_DOUBLE(fullPrecision, "2.2250738585072012e-308", 2.2250738585072014e-308); // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
|
|
|
|
// More closer to normal/subnormal boundary
|
|
// boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... x 10^-308
|
|
TEST_DOUBLE(fullPrecision, "2.22507385850720113605740979670913197593481954635164564e-308", 2.2250738585072009e-308);
|
|
TEST_DOUBLE(fullPrecision, "2.22507385850720113605740979670913197593481954635164565e-308", 2.2250738585072014e-308);
|
|
|
|
// 1.0 is in (1.0 - 2^-54, 1.0 + 2^-53)
|
|
// 1.0 - 2^-54 = 0.999999999999999944488848768742172978818416595458984375
|
|
TEST_DOUBLE(fullPrecision, "0.999999999999999944488848768742172978818416595458984375", 1.0); // round to even
|
|
TEST_DOUBLE(fullPrecision, "0.999999999999999944488848768742172978818416595458984374", 0.99999999999999989); // previous double
|
|
TEST_DOUBLE(fullPrecision, "0.999999999999999944488848768742172978818416595458984376", 1.0); // next double
|
|
// 1.0 + 2^-53 = 1.00000000000000011102230246251565404236316680908203125
|
|
TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203125", 1.0); // round to even
|
|
TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203124", 1.0); // previous 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'
|
|
n1e308[0] = '1';
|
|
for (int i = 1; i < 309; i++)
|
|
n1e308[i] = '0';
|
|
n1e308[309] = '\0';
|
|
TEST_DOUBLE(fullPrecision, n1e308, 1E308);
|
|
}
|
|
|
|
// Cover trimming
|
|
TEST_DOUBLE(fullPrecision,
|
|
"2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508"
|
|
"7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012"
|
|
"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++) {
|
|
// Need to call r() in two statements for cross-platform coherent sequence.
|
|
uint64_t u = (exp << 52) | uint64_t(r() & 0x000FFFFF) << 32;
|
|
u |= uint64_t(r());
|
|
internal::Double d = internal::Double(u);
|
|
|
|
char buffer[32];
|
|
*internal::dtoa(d.Value(), buffer) = '\0';
|
|
|
|
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: %s\n Actual: %.17g\nExpected: %.17g\n", buffer, h.actual_, d.Value());
|
|
}
|
|
else {
|
|
EXPECT_EQ(d.Sign(), a.Sign()); // for 0.0 != -0.0
|
|
EXPECT_DOUBLE_EQ(d.Value(), h.actual_);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Issue #340
|
|
TEST_DOUBLE(fullPrecision, "7.450580596923828e-9", 7.450580596923828e-9);
|
|
{
|
|
internal::Double d(1.0);
|
|
for (int i = 0; i < 324; i++) {
|
|
char buffer[32];
|
|
*internal::dtoa(d.Value(), buffer) = '\0';
|
|
|
|
StringStream s(buffer);
|
|
ParseDoubleHandler h;
|
|
Reader reader;
|
|
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: %s\n Actual: %.17g\nExpected: %.17g\n", buffer, h.actual_, d.Value());
|
|
}
|
|
else {
|
|
EXPECT_EQ(d.Sign(), a.Sign()); // for 0.0 != -0.0
|
|
EXPECT_DOUBLE_EQ(d.Value(), h.actual_);
|
|
}
|
|
|
|
|
|
d = d.Value() * 0.5;
|
|
}
|
|
}
|
|
|
|
// Issue 1249
|
|
TEST_DOUBLE(fullPrecision, "0e100", 0.0);
|
|
|
|
// Issue 1251
|
|
TEST_DOUBLE(fullPrecision, "128.74836467836484838364836483643636483648e-336", 0.0);
|
|
|
|
// Issue 1256
|
|
TEST_DOUBLE(fullPrecision,
|
|
"6223372036854775296.1701512723685473547372536854755293372036854685477"
|
|
"529752233737201701512337200972013723685473123372036872036854236854737"
|
|
"247372368372367752975258547752975254729752547372368737201701512354737"
|
|
"83723677529752585477247372368372368547354737253685475529752",
|
|
6223372036854775808.0);
|
|
|
|
#if 0
|
|
// Test (length + exponent) overflow
|
|
TEST_DOUBLE(fullPrecision, "0e+2147483647", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "0e-2147483648", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "1e-2147483648", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "0e+9223372036854775807", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "0e-9223372036854775808", 0.0);
|
|
#endif
|
|
|
|
if (fullPrecision)
|
|
{
|
|
TEST_DOUBLE(fullPrecision, "1e-325", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "1e-324", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "2e-324", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "2.4703282292062327e-324", 0.0);
|
|
TEST_DOUBLE(fullPrecision, "2.4703282292062328e-324", 5e-324);
|
|
TEST_DOUBLE(fullPrecision, "2.48e-324",5e-324);
|
|
TEST_DOUBLE(fullPrecision, "2.5e-324", 5e-324);
|
|
|
|
// Slightly above max-normal
|
|
TEST_DOUBLE(fullPrecision, "1.7976931348623158e+308", 1.7976931348623158e+308);
|
|
|
|
TEST_DOUBLE(fullPrecision,
|
|
"17976931348623157081452742373170435679807056752584499659891747680315726"
|
|
"07800285387605895586327668781715404589535143824642343213268894641827684"
|
|
"67546703537516986049910576551282076245490090389328944075868508455133942"
|
|
"30458323690322294816580855933212334827479782620414472316873817718091929"
|
|
"9881250404026184124858368",
|
|
(std::numeric_limits<double>::max)());
|
|
|
|
TEST_DOUBLE(fullPrecision,
|
|
"243546080556034731077856379609316893158278902575447060151047"
|
|
"212703405344938119816206067372775299130836050315842578309818"
|
|
"316450894337978612745889730079163798234256495613858256849283"
|
|
"467066859489192118352020514036083287319232435355752493038825"
|
|
"828481044358810649108367633313557305310641892225870327827273"
|
|
"41408256.000000",
|
|
2.4354608055603473e+307);
|
|
// 9007199254740991 * 2^971 (max normal)
|
|
TEST_DOUBLE(fullPrecision,
|
|
"1.797693134862315708145274237317043567980705675258449965989174768031572607800285"
|
|
"38760589558632766878171540458953514382464234321326889464182768467546703537516986"
|
|
"04991057655128207624549009038932894407586850845513394230458323690322294816580855"
|
|
"9332123348274797826204144723168738177180919299881250404026184124858368e+308",
|
|
1.797693134862315708e+308 // 0x1.fffffffffffffp1023
|
|
);
|
|
#if 0
|
|
// TODO:
|
|
// Should work at least in full-precision mode...
|
|
TEST_DOUBLE(fullPrecision,
|
|
"0.00000000000000000000000000000000000000000000000000000000000"
|
|
"0000000000000000000000000000000000000000000000000000000000000"
|
|
"0000000000000000000000000000000000000000000000000000000000000"
|
|
"0000000000000000000000000000000000000000000000000000000000000"
|
|
"0000000000000000000000000000000000000000000000000000000000000"
|
|
"0000000000000000000024703282292062327208828439643411068618252"
|
|
"9901307162382212792841250337753635104375932649918180817996189"
|
|
"8982823477228588654633283551779698981993873980053909390631503"
|
|
"5659515570226392290858392449105184435931802849936536152500319"
|
|
"3704576782492193656236698636584807570015857692699037063119282"
|
|
"7955855133292783433840935197801553124659726357957462276646527"
|
|
"2827220056374006485499977096599470454020828166226237857393450"
|
|
"7363390079677619305775067401763246736009689513405355374585166"
|
|
"6113422376667860416215968046191446729184030053005753084904876"
|
|
"5391711386591646239524912623653881879636239373280423891018672"
|
|
"3484976682350898633885879256283027559956575244555072551893136"
|
|
"9083625477918694866799496832404970582102851318545139621383772"
|
|
"2826145437693412532098591327667236328125",
|
|
0.0);
|
|
#endif
|
|
// 9007199254740991 * 2^-1074 = (2^53 - 1) * 2^-1074
|
|
TEST_DOUBLE(fullPrecision,
|
|
"4.450147717014402272114819593418263951869639092703291296046852219449644444042153"
|
|
"89103305904781627017582829831782607924221374017287738918929105531441481564124348"
|
|
"67599762821265346585071045737627442980259622449029037796981144446145705102663115"
|
|
"10031828794952795966823603998647925096578034214163701381261333311989876551545144"
|
|
"03152612538132666529513060001849177663286607555958373922409899478075565940981010"
|
|
"21612198814605258742579179000071675999344145086087205681577915435923018910334964"
|
|
"86942061405218289243144579760516365090360651414037721744226256159024466852576737"
|
|
"24464300755133324500796506867194913776884780053099639677097589658441378944337966"
|
|
"21993967316936280457084866613206797017728916080020698679408551343728867675409720"
|
|
"757232455434770912461317493580281734466552734375e-308",
|
|
4.450147717014402272e-308 // 0x1.fffffffffffffp-1022
|
|
);
|
|
// 9007199254740990 * 2^-1074
|
|
TEST_DOUBLE(fullPrecision,
|
|
"4.450147717014401778049173752171719775300846224481918930987049605124880018456471"
|
|
"39035755177760751831052846195619008686241717547743167145836439860405887584484471"
|
|
"19639655002484083577939142623582164522087943959208000909794783876158397872163051"
|
|
"22622675229968408654350206725478309956546318828765627255022767720818849892988457"
|
|
"26333908582101604036318532842699932130356061901518261174396928478121372742040102"
|
|
"17446565569357687263889031732270082446958029584739170416643195242132750803227473"
|
|
"16608838720742955671061336566907126801014814608027120593609275183716632624844904"
|
|
"31985250929886016737037234388448352929102742708402644340627409931664203093081360"
|
|
"70794835812045179006047003875039546061891526346421705014598610179523165038319441"
|
|
"51446491086954182492263498716056346893310546875e-308",
|
|
4.450147717014401778e-308 // 0x1.ffffffffffffep-1022
|
|
);
|
|
// half way between the two numbers above.
|
|
// round to nearest even.
|
|
TEST_DOUBLE(fullPrecision,
|
|
"4.450147717014402025081996672794991863585242658592605113516950912287262231249312"
|
|
"64069530541271189424317838013700808305231545782515453032382772695923684574304409"
|
|
"93619708911874715081505094180604803751173783204118519353387964161152051487413083"
|
|
"16327252012460602310586905362063117526562176521464664318142050516404363222266800"
|
|
"64743260560117135282915796422274554896821334728738317548403413978098469341510556"
|
|
"19529382191981473003234105366170879223151087335413188049110555339027884856781219"
|
|
"01775450062980622457102958163711745945687733011032421168917765671370549738710820"
|
|
"78224775842509670618916870627821633352993761380751142008862499795052791018709663"
|
|
"46394401564490729731565935244123171539810221213221201847003580761626016356864581"
|
|
"1358486831521563686919762403704226016998291015625e-308",
|
|
4.450147717014401778e-308 // 0x1.ffffffffffffep-1022
|
|
);
|
|
TEST_DOUBLE(fullPrecision,
|
|
"4.450147717014402025081996672794991863585242658592605113516950912287262231249312"
|
|
"64069530541271189424317838013700808305231545782515453032382772695923684574304409"
|
|
"93619708911874715081505094180604803751173783204118519353387964161152051487413083"
|
|
"16327252012460602310586905362063117526562176521464664318142050516404363222266800"
|
|
"64743260560117135282915796422274554896821334728738317548403413978098469341510556"
|
|
"19529382191981473003234105366170879223151087335413188049110555339027884856781219"
|
|
"01775450062980622457102958163711745945687733011032421168917765671370549738710820"
|
|
"78224775842509670618916870627821633352993761380751142008862499795052791018709663"
|
|
"46394401564490729731565935244123171539810221213221201847003580761626016356864581"
|
|
"13584868315215636869197624037042260169982910156250000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000e-308",
|
|
4.450147717014401778e-308 // 0x1.ffffffffffffep-1022
|
|
);
|
|
#if 0
|
|
// ... round up
|
|
// TODO:
|
|
// Should work at least in full-precision mode...
|
|
TEST_DOUBLE(fullPrecision,
|
|
"4.450147717014402025081996672794991863585242658592605113516950912287262231249312"
|
|
"64069530541271189424317838013700808305231545782515453032382772695923684574304409"
|
|
"93619708911874715081505094180604803751173783204118519353387964161152051487413083"
|
|
"16327252012460602310586905362063117526562176521464664318142050516404363222266800"
|
|
"64743260560117135282915796422274554896821334728738317548403413978098469341510556"
|
|
"19529382191981473003234105366170879223151087335413188049110555339027884856781219"
|
|
"01775450062980622457102958163711745945687733011032421168917765671370549738710820"
|
|
"78224775842509670618916870627821633352993761380751142008862499795052791018709663"
|
|
"46394401564490729731565935244123171539810221213221201847003580761626016356864581"
|
|
"13584868315215636869197624037042260169982910156250000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000001e-308",
|
|
4.450147717014402272e-308 // 0x1.fffffffffffffp-1022
|
|
);
|
|
#endif
|
|
// ... round down
|
|
TEST_DOUBLE(fullPrecision,
|
|
"4.450147717014402025081996672794991863585242658592605113516950912287262231249312"
|
|
"64069530541271189424317838013700808305231545782515453032382772695923684574304409"
|
|
"93619708911874715081505094180604803751173783204118519353387964161152051487413083"
|
|
"16327252012460602310586905362063117526562176521464664318142050516404363222266800"
|
|
"64743260560117135282915796422274554896821334728738317548403413978098469341510556"
|
|
"19529382191981473003234105366170879223151087335413188049110555339027884856781219"
|
|
"01775450062980622457102958163711745945687733011032421168917765671370549738710820"
|
|
"78224775842509670618916870627821633352993761380751142008862499795052791018709663"
|
|
"46394401564490729731565935244123171539810221213221201847003580761626016356864581"
|
|
"13584868315215636869197624037042260169982910156249999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999e-308",
|
|
4.450147717014401778e-308 // 0x1.ffffffffffffep-1022
|
|
);
|
|
// Slightly below half way between max-normal and infinity.
|
|
// Should round down.
|
|
TEST_DOUBLE(fullPrecision,
|
|
"1.797693134862315807937289714053034150799341327100378269361737789804449682927647"
|
|
"50946649017977587207096330286416692887910946555547851940402630657488671505820681"
|
|
"90890200070838367627385484581771153176447573027006985557136695962284291481986083"
|
|
"49364752927190741684443655107043427115596995080930428801779041744977919999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999999999999999999999999999999999999999999e+308",
|
|
1.797693134862315708e+308 // 0x1.fffffffffffffp1023
|
|
);
|
|
}
|
|
|
|
#undef TEST_DOUBLE
|
|
}
|
|
|
|
TEST(Reader, ParseNumber_NormalPrecisionDouble) {
|
|
TestParseDouble<false>();
|
|
}
|
|
|
|
TEST(Reader, ParseNumber_FullPrecisionDouble) {
|
|
TestParseDouble<true>();
|
|
}
|
|
|
|
TEST(Reader, ParseNumber_NormalPrecisionError) {
|
|
static unsigned count = 1000000;
|
|
Random r;
|
|
|
|
double ulpSum = 0.0;
|
|
double ulpMax = 0.0;
|
|
for (unsigned i = 0; i < count; i++) {
|
|
internal::Double e, a;
|
|
do {
|
|
// Need to call r() in two statements for cross-platform coherent sequence.
|
|
uint64_t u = uint64_t(r()) << 32;
|
|
u |= uint64_t(r());
|
|
e = u;
|
|
} while (e.IsNan() || e.IsInf() || !e.IsNormal());
|
|
|
|
char buffer[32];
|
|
*internal::dtoa(e.Value(), buffer) = '\0';
|
|
|
|
StringStream s(buffer);
|
|
ParseDoubleHandler h;
|
|
Reader reader;
|
|
ASSERT_EQ(kParseErrorNone, reader.Parse(s, h).Code());
|
|
EXPECT_EQ(1u, h.step_);
|
|
|
|
a = h.actual_;
|
|
uint64_t bias1 = e.ToBias();
|
|
uint64_t bias2 = a.ToBias();
|
|
double ulp = static_cast<double>(bias1 >= bias2 ? bias1 - bias2 : bias2 - bias1);
|
|
ulpMax = (std::max)(ulpMax, ulp);
|
|
ulpSum += ulp;
|
|
}
|
|
printf("ULP Average = %g, Max = %g \n", ulpSum / count, ulpMax);
|
|
}
|
|
|
|
template<bool fullPrecision>
|
|
static void TestParseNumberError() {
|
|
#define TEST_NUMBER_ERROR(errorCode, str, errorOffset, streamPos) \
|
|
{ \
|
|
char buffer[2048]; \
|
|
ASSERT_LT(std::strlen(str), 2048u); \
|
|
sprintf(buffer, "%s", str); \
|
|
InsituStringStream s(buffer); \
|
|
BaseReaderHandler<> h; \
|
|
Reader reader; \
|
|
EXPECT_FALSE(reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(s, h)); \
|
|
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
|
|
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
|
|
EXPECT_EQ(streamPos, s.Tell());\
|
|
}
|
|
|
|
// Number too big to be stored in double.
|
|
{
|
|
char n1e309[311]; // '1' followed by 309 '0'
|
|
n1e309[0] = '1';
|
|
for (int i = 1; i < 310; i++)
|
|
n1e309[i] = '0';
|
|
n1e309[310] = '\0';
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, n1e309, 0u, 310u);
|
|
}
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0u, 5u);
|
|
|
|
// Miss fraction part in number.
|
|
TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.", 2u, 2u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a", 2u, 2u);
|
|
|
|
// Miss exponent in number.
|
|
TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e", 2u, 2u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_", 2u, 2u);
|
|
|
|
// Issue 849
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1.8e308", 0u, 7u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "5e308", 0u, 5u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0u, 5u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1.0e310", 0u, 7u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1.00e310", 0u, 8u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "-1.8e308", 0u, 8u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "-1e309", 0u, 6u);
|
|
|
|
// Issue 1253
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "2e308", 0u, 5u);
|
|
|
|
// Issue 1259
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
|
|
"88474320368547737236837236775298547354737253685475547552933720368546854775297525"
|
|
"29337203685468547770151233720097201372368547312337203687203685423685123372036872"
|
|
"03685473724737236837236775297525854775297525472975254737236873720170151235473783"
|
|
"7236737247372368772473723683723456789012E66", 0u, 283u);
|
|
|
|
#if 0
|
|
// Test (length + exponent) overflow
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+2147483647", 0u, 13u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+9223372036854775807", 0u, 22u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+10000", 0u, 8u);
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+50000", 0u, 8u);
|
|
#endif
|
|
|
|
// 9007199254740992 * 2^971 ("infinity")
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
|
|
"1.797693134862315907729305190789024733617976978942306572734300811577326758055009"
|
|
"63132708477322407536021120113879871393357658789768814416622492847430639474124377"
|
|
"76789342486548527630221960124609411945308295208500576883815068234246288147391311"
|
|
"0540827237163350510684586298239947245938479716304835356329624224137216e+308", 0u, 315u);
|
|
|
|
// TODO:
|
|
// These tests (currently) fail in normal-precision mode
|
|
if (fullPrecision)
|
|
{
|
|
// Half way between max-normal and infinity
|
|
// Should round to infinity in nearest-even mode.
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
|
|
"1.797693134862315807937289714053034150799341327100378269361737789804449682927647"
|
|
"50946649017977587207096330286416692887910946555547851940402630657488671505820681"
|
|
"90890200070838367627385484581771153176447573027006985557136695962284291481986083"
|
|
"49364752927190741684443655107043427115596995080930428801779041744977920000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000e+308", 0u, 1125u);
|
|
// ...round up
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
|
|
"1.797693134862315807937289714053034150799341327100378269361737789804449682927647"
|
|
"50946649017977587207096330286416692887910946555547851940402630657488671505820681"
|
|
"90890200070838367627385484581771153176447573027006985557136695962284291481986083"
|
|
"49364752927190741684443655107043427115596995080930428801779041744977920000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000001e+308", 0u, 1205u);
|
|
}
|
|
|
|
TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
|
|
"10000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
"0000000000000000000000000000000000000000000000000000000000000000000001", 0u, 310u);
|
|
|
|
#undef TEST_NUMBER_ERROR
|
|
}
|
|
|
|
TEST(Reader, ParseNumberError_NormalPrecisionDouble) {
|
|
TestParseNumberError<false>();
|
|
}
|
|
|
|
TEST(Reader, ParseNumberError_FullPrecisionDouble) {
|
|
TestParseNumberError<true>();
|
|
}
|
|
|
|
template <typename Encoding>
|
|
struct ParseStringHandler : BaseReaderHandler<Encoding, ParseStringHandler<Encoding> > {
|
|
ParseStringHandler() : str_(0), length_(0), copy_() {}
|
|
~ParseStringHandler() { EXPECT_TRUE(str_ != 0); if (copy_) free(const_cast<typename Encoding::Ch*>(str_)); }
|
|
|
|
ParseStringHandler(const ParseStringHandler&);
|
|
ParseStringHandler& operator=(const ParseStringHandler&);
|
|
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool String(const typename Encoding::Ch* str, size_t length, bool copy) {
|
|
EXPECT_EQ(0, str_);
|
|
if (copy) {
|
|
str_ = static_cast<typename Encoding::Ch*>(malloc((length + 1) * sizeof(typename Encoding::Ch)));
|
|
memcpy(const_cast<typename Encoding::Ch*>(str_), str, (length + 1) * sizeof(typename Encoding::Ch));
|
|
}
|
|
else
|
|
str_ = str;
|
|
length_ = length;
|
|
copy_ = copy;
|
|
return true;
|
|
}
|
|
|
|
const typename Encoding::Ch* str_;
|
|
size_t length_;
|
|
bool copy_;
|
|
};
|
|
|
|
TEST(Reader, ParseString) {
|
|
#define TEST_STRING(Encoding, e, x) \
|
|
{ \
|
|
Encoding::Ch* buffer = StrDup(x); \
|
|
GenericInsituStringStream<Encoding> is(buffer); \
|
|
ParseStringHandler<Encoding> h; \
|
|
GenericReader<Encoding, Encoding> reader; \
|
|
reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag>(is, h); \
|
|
EXPECT_EQ(0, StrCmp<Encoding::Ch>(e, h.str_)); \
|
|
EXPECT_EQ(StrLen(e), h.length_); \
|
|
free(buffer); \
|
|
GenericStringStream<Encoding> s(x); \
|
|
ParseStringHandler<Encoding> h2; \
|
|
GenericReader<Encoding, Encoding> reader2; \
|
|
reader2.Parse(s, h2); \
|
|
EXPECT_EQ(0, StrCmp<Encoding::Ch>(e, h2.str_)); \
|
|
EXPECT_EQ(StrLen(e), h2.length_); \
|
|
}
|
|
|
|
// String constant L"\xXX" can only specify character code in bytes, which is not endianness-neutral.
|
|
// And old compiler does not support u"" and U"" string literal. So here specify string literal by array of Ch.
|
|
// In addition, GCC 4.8 generates -Wnarrowing warnings when character code >= 128 are assigned to signed integer types.
|
|
// Therefore, utype is added for declaring unsigned array, and then cast it to Encoding::Ch.
|
|
#define ARRAY(...) { __VA_ARGS__ }
|
|
#define TEST_STRINGARRAY(Encoding, utype, array, x) \
|
|
{ \
|
|
static const utype ue[] = array; \
|
|
static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&ue[0]); \
|
|
TEST_STRING(Encoding, e, x); \
|
|
}
|
|
|
|
#define TEST_STRINGARRAY2(Encoding, utype, earray, xarray) \
|
|
{ \
|
|
static const utype ue[] = earray; \
|
|
static const utype xe[] = xarray; \
|
|
static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&ue[0]); \
|
|
static const Encoding::Ch* x = reinterpret_cast<const Encoding::Ch *>(&xe[0]); \
|
|
TEST_STRING(Encoding, e, x); \
|
|
}
|
|
|
|
TEST_STRING(UTF8<>, "", "\"\"");
|
|
TEST_STRING(UTF8<>, "Hello", "\"Hello\"");
|
|
TEST_STRING(UTF8<>, "Hello\nWorld", "\"Hello\\nWorld\"");
|
|
TEST_STRING(UTF8<>, "\"\\/\b\f\n\r\t", "\"\\\"\\\\/\\b\\f\\n\\r\\t\"");
|
|
TEST_STRING(UTF8<>, "\x24", "\"\\u0024\""); // Dollar sign U+0024
|
|
TEST_STRING(UTF8<>, "\xC2\xA2", "\"\\u00A2\""); // Cents sign U+00A2
|
|
TEST_STRING(UTF8<>, "\xE2\x82\xAC", "\"\\u20AC\""); // Euro sign U+20AC
|
|
TEST_STRING(UTF8<>, "\xF0\x9D\x84\x9E", "\"\\uD834\\uDD1E\""); // G clef sign U+1D11E
|
|
|
|
// UTF16
|
|
TEST_STRING(UTF16<>, L"", L"\"\"");
|
|
TEST_STRING(UTF16<>, L"Hello", L"\"Hello\"");
|
|
TEST_STRING(UTF16<>, L"Hello\nWorld", L"\"Hello\\nWorld\"");
|
|
TEST_STRING(UTF16<>, L"\"\\/\b\f\n\r\t", L"\"\\\"\\\\/\\b\\f\\n\\r\\t\"");
|
|
TEST_STRINGARRAY(UTF16<>, wchar_t, ARRAY(0x0024, 0x0000), L"\"\\u0024\"");
|
|
TEST_STRINGARRAY(UTF16<>, wchar_t, ARRAY(0x00A2, 0x0000), L"\"\\u00A2\""); // Cents sign U+00A2
|
|
TEST_STRINGARRAY(UTF16<>, wchar_t, ARRAY(0x20AC, 0x0000), L"\"\\u20AC\""); // Euro sign U+20AC
|
|
TEST_STRINGARRAY(UTF16<>, wchar_t, ARRAY(0xD834, 0xDD1E, 0x0000), L"\"\\uD834\\uDD1E\""); // G clef sign U+1D11E
|
|
|
|
// UTF32
|
|
TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY('\0'), ARRAY('\"', '\"', '\0'));
|
|
TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY('H', 'e', 'l', 'l', 'o', '\0'), ARRAY('\"', 'H', 'e', 'l', 'l', 'o', '\"', '\0'));
|
|
TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY('H', 'e', 'l', 'l', 'o', '\n', 'W', 'o', 'r', 'l', 'd', '\0'), ARRAY('\"', 'H', 'e', 'l', 'l', 'o', '\\', 'n', 'W', 'o', 'r', 'l', 'd', '\"', '\0'));
|
|
TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY('\"', '\\', '/', '\b', '\f', '\n', '\r', '\t', '\0'), ARRAY('\"', '\\', '\"', '\\', '\\', '/', '\\', 'b', '\\', 'f', '\\', 'n', '\\', 'r', '\\', 't', '\"', '\0'));
|
|
TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY(0x00024, 0x0000), ARRAY('\"', '\\', 'u', '0', '0', '2', '4', '\"', '\0'));
|
|
TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY(0x000A2, 0x0000), ARRAY('\"', '\\', 'u', '0', '0', 'A', '2', '\"', '\0')); // Cents sign U+00A2
|
|
TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY(0x020AC, 0x0000), ARRAY('\"', '\\', 'u', '2', '0', 'A', 'C', '\"', '\0')); // Euro sign U+20AC
|
|
TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY(0x1D11E, 0x0000), ARRAY('\"', '\\', 'u', 'D', '8', '3', '4', '\\', 'u', 'D', 'D', '1', 'E', '\"', '\0')); // G clef sign U+1D11E
|
|
|
|
#undef TEST_STRINGARRAY
|
|
#undef ARRAY
|
|
#undef TEST_STRING
|
|
|
|
// Support of null character in string
|
|
{
|
|
StringStream s("\"Hello\\u0000World\"");
|
|
const char e[] = "Hello\0World";
|
|
ParseStringHandler<UTF8<> > h;
|
|
Reader reader;
|
|
reader.Parse(s, h);
|
|
EXPECT_EQ(0, memcmp(e, h.str_, h.length_ + 1));
|
|
EXPECT_EQ(11u, h.length_);
|
|
}
|
|
}
|
|
|
|
TEST(Reader, ParseString_Transcoding) {
|
|
const char* x = "\"Hello\"";
|
|
const wchar_t* e = L"Hello";
|
|
GenericStringStream<UTF8<> > is(x);
|
|
GenericReader<UTF8<>, UTF16<> > reader;
|
|
ParseStringHandler<UTF16<> > h;
|
|
reader.Parse(is, h);
|
|
EXPECT_EQ(0, StrCmp<UTF16<>::Ch>(e, h.str_));
|
|
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) {
|
|
StringStream s("\"Hello\\nWorld\"");
|
|
ParseStringHandler<UTF8<> > h;
|
|
Reader reader;
|
|
reader.Parse(s, h);
|
|
EXPECT_EQ(0, StrCmp("Hello\nWorld", h.str_));
|
|
EXPECT_EQ(11u, h.length_);
|
|
}
|
|
|
|
template <typename Encoding>
|
|
ParseErrorCode TestString(const typename Encoding::Ch* str) {
|
|
GenericStringStream<Encoding> s(str);
|
|
BaseReaderHandler<Encoding> h;
|
|
GenericReader<Encoding, Encoding> reader;
|
|
reader.template Parse<kParseValidateEncodingFlag>(s, h);
|
|
return reader.GetParseErrorCode();
|
|
}
|
|
|
|
TEST(Reader, ParseString_Error) {
|
|
#define TEST_STRING_ERROR(errorCode, str, errorOffset, streamPos)\
|
|
{\
|
|
GenericStringStream<UTF8<> > s(str);\
|
|
BaseReaderHandler<UTF8<> > h;\
|
|
GenericReader<UTF8<> , UTF8<> > reader;\
|
|
reader.Parse<kParseValidateEncodingFlag>(s, h);\
|
|
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
|
|
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
|
|
EXPECT_EQ(streamPos, s.Tell());\
|
|
}
|
|
|
|
#define ARRAY(...) { __VA_ARGS__ }
|
|
#define TEST_STRINGENCODING_ERROR(Encoding, TargetEncoding, utype, array) \
|
|
{ \
|
|
static const utype ue[] = array; \
|
|
static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&ue[0]); \
|
|
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.
|
|
TEST_STRING_ERROR(kParseErrorStringEscapeInvalid, "[\"\\a\"]", 2u, 3u);
|
|
|
|
// Incorrect hex digit after \\u escape in string.
|
|
TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]", 2u, 7u);
|
|
|
|
// Quotation in \\u escape in string (Issue #288)
|
|
TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uaaa\"]", 2u, 7u);
|
|
TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFFF\"]", 2u, 13u);
|
|
|
|
// The surrogate pair in string is invalid.
|
|
TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]", 2u, 8u);
|
|
TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFFFF\"]", 2u, 14u);
|
|
|
|
// Single low surrogate pair in string is invalid.
|
|
TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\udc4d\"]", 2u, 8u);
|
|
|
|
// Missing a closing quotation mark in string.
|
|
TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]", 7u, 7u);
|
|
|
|
// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
|
|
|
|
// 3 Malformed sequences
|
|
|
|
// 3.1 Unexpected continuation bytes
|
|
{
|
|
char e[] = { '[', '\"', 0, '\"', ']', '\0' };
|
|
for (unsigned char c = 0x80u; c <= 0xBFu; c++) {
|
|
e[2] = static_cast<char>(c);
|
|
ParseErrorCode error = TestString<UTF8<> >(e);
|
|
EXPECT_EQ(kParseErrorStringInvalidEncoding, error);
|
|
if (error != kParseErrorStringInvalidEncoding)
|
|
std::cout << static_cast<unsigned>(c) << std::endl;
|
|
}
|
|
}
|
|
|
|
// 3.2 Lonely start characters, 3.5 Impossible bytes
|
|
{
|
|
char e[] = { '[', '\"', 0, ' ', '\"', ']', '\0' };
|
|
for (unsigned c = 0xC0u; c <= 0xFFu; c++) {
|
|
e[2] = static_cast<char>(c);
|
|
unsigned streamPos;
|
|
if (c <= 0xC1u)
|
|
streamPos = 3; // 0xC0 - 0xC1
|
|
else if (c <= 0xDFu)
|
|
streamPos = 4; // 0xC2 - 0xDF
|
|
else if (c <= 0xEFu)
|
|
streamPos = 5; // 0xE0 - 0xEF
|
|
else if (c <= 0xF4u)
|
|
streamPos = 6; // 0xF0 - 0xF4
|
|
else
|
|
streamPos = 3; // 0xF5 - 0xFF
|
|
TEST_STRING_ERROR(kParseErrorStringInvalidEncoding, e, 2u, streamPos);
|
|
}
|
|
}
|
|
|
|
// 4 Overlong sequences
|
|
|
|
// 4.1 Examples of an overlong ASCII character
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0xAFu, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0xAFu, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0'));
|
|
|
|
// 4.2 Maximum overlong sequences
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC1u, 0xBFu, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x9Fu, 0xBFu, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x8Fu, 0xBFu, 0xBFu, '\"', ']', '\0'));
|
|
|
|
// 4.3 Overlong representation of the NUL character
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0x80u, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0x80u, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0x80u, '\"', ']', '\0'));
|
|
|
|
// 5 Illegal code positions
|
|
|
|
// 5.1 Single UTF-16 surrogates
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xA0u, 0x80u, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xADu, 0xBFu, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAEu, 0x80u, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAFu, 0xBFu, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xB0u, 0x80u, '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBEu, 0x80u, '\"', ']', '\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(0x80u), '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x01u), '\"', ']', '\0'));
|
|
TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x1Cu), '\"', ']', '\0'));
|
|
|
|
#undef ARRAY
|
|
#undef TEST_STRINGARRAY_ERROR
|
|
}
|
|
|
|
template <unsigned count>
|
|
struct ParseArrayHandler : BaseReaderHandler<UTF8<>, ParseArrayHandler<count> > {
|
|
ParseArrayHandler() : step_(0) {}
|
|
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool Uint(unsigned i) { EXPECT_EQ(step_, i); step_++; return true; }
|
|
bool StartArray() { EXPECT_EQ(0u, step_); step_++; return true; }
|
|
bool EndArray(SizeType) { step_++; return true; }
|
|
|
|
unsigned step_;
|
|
};
|
|
|
|
TEST(Reader, ParseEmptyArray) {
|
|
char *json = StrDup("[ ] ");
|
|
InsituStringStream s(json);
|
|
ParseArrayHandler<0> h;
|
|
Reader reader;
|
|
reader.Parse(s, h);
|
|
EXPECT_EQ(2u, h.step_);
|
|
free(json);
|
|
}
|
|
|
|
TEST(Reader, ParseArray) {
|
|
char *json = StrDup("[1, 2, 3, 4]");
|
|
InsituStringStream s(json);
|
|
ParseArrayHandler<4> h;
|
|
Reader reader;
|
|
reader.Parse(s, h);
|
|
EXPECT_EQ(6u, h.step_);
|
|
free(json);
|
|
}
|
|
|
|
TEST(Reader, ParseArray_Error) {
|
|
#define TEST_ARRAY_ERROR(errorCode, str, errorOffset) \
|
|
{ \
|
|
unsigned streamPos = errorOffset; \
|
|
char buffer[1001]; \
|
|
strncpy(buffer, str, 1000); \
|
|
InsituStringStream s(buffer); \
|
|
BaseReaderHandler<> h; \
|
|
GenericReader<UTF8<>, UTF8<>, CrtAllocator> reader; \
|
|
EXPECT_FALSE(reader.Parse(s, h)); \
|
|
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
|
|
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
|
|
EXPECT_EQ(streamPos, s.Tell());\
|
|
}
|
|
|
|
// Missing a comma or ']' after an array element.
|
|
TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1", 2u);
|
|
TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}", 2u);
|
|
TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]", 3u);
|
|
|
|
// Array cannot have a trailing comma (without kParseTrailingCommasFlag);
|
|
// a value must follow a comma
|
|
TEST_ARRAY_ERROR(kParseErrorValueInvalid, "[1,]", 3u);
|
|
|
|
#undef TEST_ARRAY_ERROR
|
|
}
|
|
|
|
struct ParseObjectHandler : BaseReaderHandler<UTF8<>, ParseObjectHandler> {
|
|
ParseObjectHandler() : step_(0) {}
|
|
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool Null() { EXPECT_EQ(8u, step_); step_++; return true; }
|
|
bool Bool(bool b) {
|
|
switch(step_) {
|
|
case 4: EXPECT_TRUE(b); step_++; return true;
|
|
case 6: EXPECT_FALSE(b); step_++; return true;
|
|
default: ADD_FAILURE(); return false;
|
|
}
|
|
}
|
|
bool Int(int i) {
|
|
switch(step_) {
|
|
case 10: EXPECT_EQ(123, i); step_++; return true;
|
|
case 15: EXPECT_EQ(1, i); step_++; return true;
|
|
case 16: EXPECT_EQ(2, i); step_++; return true;
|
|
case 17: EXPECT_EQ(3, i); step_++; return true;
|
|
default: ADD_FAILURE(); return false;
|
|
}
|
|
}
|
|
bool Uint(unsigned i) { return Int(static_cast<int>(i)); }
|
|
bool Double(double d) { EXPECT_EQ(12u, step_); EXPECT_DOUBLE_EQ(3.1416, d); step_++; return true; }
|
|
bool String(const char* str, size_t, bool) {
|
|
switch(step_) {
|
|
case 1: EXPECT_STREQ("hello", str); step_++; return true;
|
|
case 2: EXPECT_STREQ("world", str); step_++; return true;
|
|
case 3: EXPECT_STREQ("t", str); step_++; return true;
|
|
case 5: EXPECT_STREQ("f", str); step_++; return true;
|
|
case 7: EXPECT_STREQ("n", str); step_++; return true;
|
|
case 9: EXPECT_STREQ("i", str); step_++; return true;
|
|
case 11: EXPECT_STREQ("pi", str); step_++; return true;
|
|
case 13: EXPECT_STREQ("a", str); step_++; return true;
|
|
default: ADD_FAILURE(); return false;
|
|
}
|
|
}
|
|
bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
|
|
bool EndObject(SizeType memberCount) { EXPECT_EQ(19u, step_); EXPECT_EQ(7u, memberCount); step_++; return true; }
|
|
bool StartArray() { EXPECT_EQ(14u, step_); step_++; return true; }
|
|
bool EndArray(SizeType elementCount) { EXPECT_EQ(18u, step_); EXPECT_EQ(3u, elementCount); step_++; return true; }
|
|
|
|
unsigned step_;
|
|
};
|
|
|
|
TEST(Reader, ParseObject) {
|
|
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ";
|
|
|
|
// Insitu
|
|
{
|
|
char* json2 = StrDup(json);
|
|
InsituStringStream s(json2);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
reader.Parse<kParseInsituFlag>(s, h);
|
|
EXPECT_EQ(20u, h.step_);
|
|
free(json2);
|
|
}
|
|
|
|
// Normal
|
|
{
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
reader.Parse(s, h);
|
|
EXPECT_EQ(20u, h.step_);
|
|
}
|
|
}
|
|
|
|
struct ParseEmptyObjectHandler : BaseReaderHandler<UTF8<>, ParseEmptyObjectHandler> {
|
|
ParseEmptyObjectHandler() : step_(0) {}
|
|
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
|
|
bool EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; return true; }
|
|
|
|
unsigned step_;
|
|
};
|
|
|
|
TEST(Reader, Parse_EmptyObject) {
|
|
StringStream s("{ } ");
|
|
ParseEmptyObjectHandler h;
|
|
Reader reader;
|
|
reader.Parse(s, h);
|
|
EXPECT_EQ(2u, h.step_);
|
|
}
|
|
|
|
struct ParseMultipleRootHandler : BaseReaderHandler<UTF8<>, ParseMultipleRootHandler> {
|
|
ParseMultipleRootHandler() : step_(0) {}
|
|
|
|
bool Default() { ADD_FAILURE(); return false; }
|
|
bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
|
|
bool EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; return true; }
|
|
bool StartArray() { EXPECT_EQ(2u, step_); step_++; return true; }
|
|
bool EndArray(SizeType) { EXPECT_EQ(3u, step_); step_++; return true; }
|
|
|
|
unsigned step_;
|
|
};
|
|
|
|
template <unsigned parseFlags>
|
|
void TestMultipleRoot() {
|
|
StringStream s("{}[] a");
|
|
ParseMultipleRootHandler h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<parseFlags>(s, h));
|
|
EXPECT_EQ(2u, h.step_);
|
|
EXPECT_TRUE(reader.Parse<parseFlags>(s, h));
|
|
EXPECT_EQ(4u, h.step_);
|
|
EXPECT_EQ(' ', s.Take());
|
|
EXPECT_EQ('a', s.Take());
|
|
}
|
|
|
|
TEST(Reader, Parse_MultipleRoot) {
|
|
TestMultipleRoot<kParseStopWhenDoneFlag>();
|
|
}
|
|
|
|
TEST(Reader, ParseIterative_MultipleRoot) {
|
|
TestMultipleRoot<kParseIterativeFlag | kParseStopWhenDoneFlag>();
|
|
}
|
|
|
|
template <unsigned parseFlags>
|
|
void TestInsituMultipleRoot() {
|
|
char* buffer = strdup("{}[] a");
|
|
InsituStringStream s(buffer);
|
|
ParseMultipleRootHandler h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag | parseFlags>(s, h));
|
|
EXPECT_EQ(2u, h.step_);
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag | parseFlags>(s, h));
|
|
EXPECT_EQ(4u, h.step_);
|
|
EXPECT_EQ(' ', s.Take());
|
|
EXPECT_EQ('a', s.Take());
|
|
free(buffer);
|
|
}
|
|
|
|
TEST(Reader, ParseInsitu_MultipleRoot) {
|
|
TestInsituMultipleRoot<kParseStopWhenDoneFlag>();
|
|
}
|
|
|
|
TEST(Reader, ParseInsituIterative_MultipleRoot) {
|
|
TestInsituMultipleRoot<kParseIterativeFlag | kParseStopWhenDoneFlag>();
|
|
}
|
|
|
|
#define TEST_ERROR(errorCode, str, errorOffset) \
|
|
{ \
|
|
unsigned streamPos = errorOffset; \
|
|
char buffer[1001]; \
|
|
strncpy(buffer, str, 1000); \
|
|
InsituStringStream s(buffer); \
|
|
BaseReaderHandler<> h; \
|
|
Reader reader; \
|
|
EXPECT_FALSE(reader.Parse(s, h)); \
|
|
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
|
|
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
|
|
EXPECT_EQ(streamPos, s.Tell());\
|
|
}
|
|
|
|
TEST(Reader, ParseDocument_Error) {
|
|
// The document is empty.
|
|
TEST_ERROR(kParseErrorDocumentEmpty, "", 0u);
|
|
TEST_ERROR(kParseErrorDocumentEmpty, " ", 1u);
|
|
TEST_ERROR(kParseErrorDocumentEmpty, " \n", 2u);
|
|
|
|
// The document root must not follow by other values.
|
|
TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0", 3u);
|
|
TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0", 3u);
|
|
TEST_ERROR(kParseErrorDocumentRootNotSingular, "null []", 5u);
|
|
TEST_ERROR(kParseErrorDocumentRootNotSingular, "0 {}", 2u);
|
|
}
|
|
|
|
TEST(Reader, ParseValue_Error) {
|
|
// Invalid value.
|
|
TEST_ERROR(kParseErrorValueInvalid, "nulL", 3u);
|
|
TEST_ERROR(kParseErrorValueInvalid, "truE", 3u);
|
|
TEST_ERROR(kParseErrorValueInvalid, "falsE", 4u);
|
|
TEST_ERROR(kParseErrorValueInvalid, "a]", 0u);
|
|
TEST_ERROR(kParseErrorValueInvalid, ".1", 0u);
|
|
}
|
|
|
|
TEST(Reader, ParseObject_Error) {
|
|
// Missing a name for object member.
|
|
TEST_ERROR(kParseErrorObjectMissName, "{1}", 1u);
|
|
TEST_ERROR(kParseErrorObjectMissName, "{:1}", 1u);
|
|
TEST_ERROR(kParseErrorObjectMissName, "{null:1}", 1u);
|
|
TEST_ERROR(kParseErrorObjectMissName, "{true:1}", 1u);
|
|
TEST_ERROR(kParseErrorObjectMissName, "{false:1}", 1u);
|
|
TEST_ERROR(kParseErrorObjectMissName, "{1:1}", 1u);
|
|
TEST_ERROR(kParseErrorObjectMissName, "{[]:1}", 1u);
|
|
TEST_ERROR(kParseErrorObjectMissName, "{{}:1}", 1u);
|
|
TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}", 1u);
|
|
|
|
// Missing a colon after a name of object member.
|
|
TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}", 5u);
|
|
TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}", 4u);
|
|
|
|
// Must be a comma or '}' after an object member
|
|
TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]", 6u);
|
|
|
|
// Object cannot have a trailing comma (without kParseTrailingCommasFlag);
|
|
// an object member name must follow a comma
|
|
TEST_ERROR(kParseErrorObjectMissName, "{\"a\":1,}", 7u);
|
|
|
|
// This tests that MemoryStream is checking the length in Peek().
|
|
{
|
|
MemoryStream ms("{\"a\"", 1);
|
|
BaseReaderHandler<> h;
|
|
Reader reader;
|
|
EXPECT_FALSE(reader.Parse<kParseStopWhenDoneFlag>(ms, h));
|
|
EXPECT_EQ(kParseErrorObjectMissName, reader.GetParseErrorCode());
|
|
}
|
|
}
|
|
|
|
#undef TEST_ERROR
|
|
|
|
TEST(Reader, SkipWhitespace) {
|
|
StringStream ss(" A \t\tB\n \n\nC\r\r \rD \t\n\r E");
|
|
const char* expected = "ABCDE";
|
|
for (size_t i = 0; i < 5; i++) {
|
|
SkipWhitespace(ss);
|
|
EXPECT_EQ(expected[i], ss.Take());
|
|
}
|
|
}
|
|
|
|
// Test implementing a stream without copy stream optimization.
|
|
// Clone from GenericStringStream except that copy constructor is disabled.
|
|
template <typename Encoding>
|
|
class CustomStringStream {
|
|
public:
|
|
typedef typename Encoding::Ch Ch;
|
|
|
|
CustomStringStream(const Ch *src) : src_(src), head_(src) {}
|
|
|
|
Ch Peek() const { return *src_; }
|
|
Ch Take() { return *src_++; }
|
|
size_t Tell() const { return static_cast<size_t>(src_ - head_); }
|
|
|
|
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
|
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
|
void Flush() { RAPIDJSON_ASSERT(false); }
|
|
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
|
|
|
private:
|
|
// Prohibit copy constructor & assignment operator.
|
|
CustomStringStream(const CustomStringStream&);
|
|
CustomStringStream& operator=(const CustomStringStream&);
|
|
|
|
const Ch* src_; //!< Current read position.
|
|
const Ch* head_; //!< Original head of the string.
|
|
};
|
|
|
|
// If the following code is compiled, it should generate compilation error as predicted.
|
|
// Because CustomStringStream<> is not copyable via making copy constructor private.
|
|
#if 0
|
|
namespace rapidjson {
|
|
|
|
template <typename Encoding>
|
|
struct StreamTraits<CustomStringStream<Encoding> > {
|
|
enum { copyOptimization = 1 };
|
|
};
|
|
|
|
} // namespace rapidjson
|
|
#endif
|
|
|
|
TEST(Reader, CustomStringStream) {
|
|
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ";
|
|
CustomStringStream<UTF8<char> > s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
reader.Parse(s, h);
|
|
EXPECT_EQ(20u, h.step_);
|
|
}
|
|
|
|
#include <sstream>
|
|
|
|
class IStreamWrapper {
|
|
public:
|
|
typedef char Ch;
|
|
|
|
IStreamWrapper(std::istream& is) : is_(is) {}
|
|
|
|
Ch Peek() const {
|
|
int c = is_.peek();
|
|
return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c);
|
|
}
|
|
|
|
Ch Take() {
|
|
int c = is_.get();
|
|
return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c);
|
|
}
|
|
|
|
size_t Tell() const { return static_cast<size_t>(is_.tellg()); }
|
|
|
|
Ch* PutBegin() { assert(false); return 0; }
|
|
void Put(Ch) { assert(false); }
|
|
void Flush() { assert(false); }
|
|
size_t PutEnd(Ch*) { assert(false); return 0; }
|
|
|
|
private:
|
|
IStreamWrapper(const IStreamWrapper&);
|
|
IStreamWrapper& operator=(const IStreamWrapper&);
|
|
|
|
std::istream& is_;
|
|
};
|
|
|
|
class WIStreamWrapper {
|
|
public:
|
|
typedef wchar_t Ch;
|
|
|
|
WIStreamWrapper(std::wistream& is) : is_(is) {}
|
|
|
|
Ch Peek() const {
|
|
unsigned c = is_.peek();
|
|
return c == std::char_traits<wchar_t>::eof() ? Ch('\0') : static_cast<Ch>(c);
|
|
}
|
|
|
|
Ch Take() {
|
|
unsigned c = is_.get();
|
|
return c == std::char_traits<wchar_t>::eof() ? Ch('\0') : static_cast<Ch>(c);
|
|
}
|
|
|
|
size_t Tell() const { return static_cast<size_t>(is_.tellg()); }
|
|
|
|
Ch* PutBegin() { assert(false); return 0; }
|
|
void Put(Ch) { assert(false); }
|
|
void Flush() { assert(false); }
|
|
size_t PutEnd(Ch*) { assert(false); return 0; }
|
|
|
|
private:
|
|
WIStreamWrapper(const WIStreamWrapper&);
|
|
WIStreamWrapper& operator=(const WIStreamWrapper&);
|
|
|
|
std::wistream& is_;
|
|
};
|
|
|
|
TEST(Reader, Parse_IStreamWrapper_StringStream) {
|
|
const char* json = "[1,2,3,4]";
|
|
|
|
std::stringstream ss(json);
|
|
IStreamWrapper is(ss);
|
|
|
|
Reader reader;
|
|
ParseArrayHandler<4> h;
|
|
reader.Parse(is, h);
|
|
EXPECT_FALSE(reader.HasParseError());
|
|
}
|
|
|
|
// Test iterative parsing.
|
|
|
|
#define TESTERRORHANDLING(text, errorCode, offset)\
|
|
{\
|
|
unsigned streamPos = offset; \
|
|
StringStream json(text); \
|
|
BaseReaderHandler<> handler; \
|
|
Reader reader; \
|
|
reader.Parse<kParseIterativeFlag>(json, handler); \
|
|
EXPECT_TRUE(reader.HasParseError()); \
|
|
EXPECT_EQ(errorCode, reader.GetParseErrorCode()); \
|
|
EXPECT_EQ(offset, reader.GetErrorOffset()); \
|
|
EXPECT_EQ(streamPos, json.Tell()); \
|
|
}
|
|
|
|
TEST(Reader, IterativeParsing_ErrorHandling) {
|
|
TESTERRORHANDLING("{\"a\": a}", kParseErrorValueInvalid, 6u);
|
|
|
|
TESTERRORHANDLING("", kParseErrorDocumentEmpty, 0u);
|
|
TESTERRORHANDLING("{}{}", kParseErrorDocumentRootNotSingular, 2u);
|
|
|
|
TESTERRORHANDLING("{1}", kParseErrorObjectMissName, 1u);
|
|
TESTERRORHANDLING("{\"a\", 1}", kParseErrorObjectMissColon, 4u);
|
|
TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u);
|
|
TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u);
|
|
TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u);
|
|
TESTERRORHANDLING("{\"a: 1", kParseErrorStringMissQuotationMark, 6u);
|
|
TESTERRORHANDLING("{\"a\":}", kParseErrorValueInvalid, 5u);
|
|
TESTERRORHANDLING("{\"a\":]", kParseErrorValueInvalid, 5u);
|
|
TESTERRORHANDLING("[1,2,}", kParseErrorValueInvalid, 5u);
|
|
TESTERRORHANDLING("[}]", kParseErrorValueInvalid, 1u);
|
|
TESTERRORHANDLING("[,]", kParseErrorValueInvalid, 1u);
|
|
TESTERRORHANDLING("[1,,]", kParseErrorValueInvalid, 3u);
|
|
|
|
// Trailing commas are not allowed without kParseTrailingCommasFlag
|
|
TESTERRORHANDLING("{\"a\": 1,}", kParseErrorObjectMissName, 8u);
|
|
TESTERRORHANDLING("[1,2,3,]", kParseErrorValueInvalid, 7u);
|
|
|
|
// Any JSON value can be a valid root element in RFC7159.
|
|
TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 3u);
|
|
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<> >
|
|
struct IterativeParsingReaderHandler {
|
|
typedef typename Encoding::Ch Ch;
|
|
|
|
const static uint32_t LOG_NULL = 0x10000000;
|
|
const static uint32_t LOG_BOOL = 0x20000000;
|
|
const static uint32_t LOG_INT = 0x30000000;
|
|
const static uint32_t LOG_UINT = 0x40000000;
|
|
const static uint32_t LOG_INT64 = 0x50000000;
|
|
const static uint32_t LOG_UINT64 = 0x60000000;
|
|
const static uint32_t LOG_DOUBLE = 0x70000000;
|
|
const static uint32_t LOG_STRING = 0x80000000;
|
|
const static uint32_t LOG_STARTOBJECT = 0x90000000;
|
|
const static uint32_t LOG_KEY = 0xA0000000;
|
|
const static uint32_t LOG_ENDOBJECT = 0xB0000000;
|
|
const static uint32_t LOG_STARTARRAY = 0xC0000000;
|
|
const static uint32_t LOG_ENDARRAY = 0xD0000000;
|
|
|
|
const static size_t LogCapacity = 256;
|
|
uint32_t Logs[LogCapacity];
|
|
size_t LogCount;
|
|
|
|
IterativeParsingReaderHandler() : LogCount(0) {
|
|
}
|
|
|
|
bool Null() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_NULL; return true; }
|
|
|
|
bool Bool(bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_BOOL; return true; }
|
|
|
|
bool Int(int) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_INT; return true; }
|
|
|
|
bool Uint(unsigned) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_INT; return true; }
|
|
|
|
bool Int64(int64_t) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_INT64; return true; }
|
|
|
|
bool Uint64(uint64_t) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_UINT64; return true; }
|
|
|
|
bool Double(double) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_DOUBLE; return true; }
|
|
|
|
bool RawNumber(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STRING; return true; }
|
|
|
|
bool String(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STRING; return true; }
|
|
|
|
bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTOBJECT; return true; }
|
|
|
|
bool Key (const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_KEY; return true; }
|
|
|
|
bool EndObject(SizeType c) {
|
|
RAPIDJSON_ASSERT(LogCount < LogCapacity);
|
|
RAPIDJSON_ASSERT((static_cast<uint32_t>(c) & 0xF0000000) == 0);
|
|
Logs[LogCount++] = LOG_ENDOBJECT | static_cast<uint32_t>(c);
|
|
return true;
|
|
}
|
|
|
|
bool StartArray() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTARRAY; return true; }
|
|
|
|
bool EndArray(SizeType c) {
|
|
RAPIDJSON_ASSERT(LogCount < LogCapacity);
|
|
RAPIDJSON_ASSERT((static_cast<uint32_t>(c) & 0xF0000000) == 0);
|
|
Logs[LogCount++] = LOG_ENDARRAY | static_cast<uint32_t>(c);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
TEST(Reader, IterativeParsing_General) {
|
|
{
|
|
StringStream is("[1, {\"k\": [1, 2]}, null, false, true, \"string\", 1.2]");
|
|
Reader reader;
|
|
IterativeParsingReaderHandler<> handler;
|
|
|
|
ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
|
|
|
|
EXPECT_FALSE(r.IsError());
|
|
EXPECT_FALSE(reader.HasParseError());
|
|
|
|
uint32_t e[] = {
|
|
handler.LOG_STARTARRAY,
|
|
handler.LOG_INT,
|
|
handler.LOG_STARTOBJECT,
|
|
handler.LOG_KEY,
|
|
handler.LOG_STARTARRAY,
|
|
handler.LOG_INT,
|
|
handler.LOG_INT,
|
|
handler.LOG_ENDARRAY | 2,
|
|
handler.LOG_ENDOBJECT | 1,
|
|
handler.LOG_NULL,
|
|
handler.LOG_BOOL,
|
|
handler.LOG_BOOL,
|
|
handler.LOG_STRING,
|
|
handler.LOG_DOUBLE,
|
|
handler.LOG_ENDARRAY | 7
|
|
};
|
|
|
|
EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount);
|
|
|
|
for (size_t i = 0; i < handler.LogCount; ++i) {
|
|
EXPECT_EQ(e[i], handler.Logs[i]) << "i = " << i;
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(Reader, IterativeParsing_Count) {
|
|
{
|
|
StringStream is("[{}, {\"k\": 1}, [1], []]");
|
|
Reader reader;
|
|
IterativeParsingReaderHandler<> handler;
|
|
|
|
ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
|
|
|
|
EXPECT_FALSE(r.IsError());
|
|
EXPECT_FALSE(reader.HasParseError());
|
|
|
|
uint32_t e[] = {
|
|
handler.LOG_STARTARRAY,
|
|
handler.LOG_STARTOBJECT,
|
|
handler.LOG_ENDOBJECT | 0,
|
|
handler.LOG_STARTOBJECT,
|
|
handler.LOG_KEY,
|
|
handler.LOG_INT,
|
|
handler.LOG_ENDOBJECT | 1,
|
|
handler.LOG_STARTARRAY,
|
|
handler.LOG_INT,
|
|
handler.LOG_ENDARRAY | 1,
|
|
handler.LOG_STARTARRAY,
|
|
handler.LOG_ENDARRAY | 0,
|
|
handler.LOG_ENDARRAY | 4
|
|
};
|
|
|
|
EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount);
|
|
|
|
for (size_t i = 0; i < handler.LogCount; ++i) {
|
|
EXPECT_EQ(e[i], handler.Logs[i]) << "i = " << i;
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(Reader, IterativePullParsing_General) {
|
|
{
|
|
IterativeParsingReaderHandler<> handler;
|
|
uint32_t e[] = {
|
|
handler.LOG_STARTARRAY,
|
|
handler.LOG_INT,
|
|
handler.LOG_STARTOBJECT,
|
|
handler.LOG_KEY,
|
|
handler.LOG_STARTARRAY,
|
|
handler.LOG_INT,
|
|
handler.LOG_INT,
|
|
handler.LOG_ENDARRAY | 2,
|
|
handler.LOG_ENDOBJECT | 1,
|
|
handler.LOG_NULL,
|
|
handler.LOG_BOOL,
|
|
handler.LOG_BOOL,
|
|
handler.LOG_STRING,
|
|
handler.LOG_DOUBLE,
|
|
handler.LOG_ENDARRAY | 7
|
|
};
|
|
|
|
StringStream is("[1, {\"k\": [1, 2]}, null, false, true, \"string\", 1.2]");
|
|
Reader reader;
|
|
|
|
reader.IterativeParseInit();
|
|
while (!reader.IterativeParseComplete()) {
|
|
size_t oldLogCount = handler.LogCount;
|
|
EXPECT_TRUE(oldLogCount < sizeof(e) / sizeof(int)) << "overrun";
|
|
|
|
EXPECT_TRUE(reader.IterativeParseNext<kParseDefaultFlags>(is, handler)) << "parse fail";
|
|
EXPECT_EQ(handler.LogCount, oldLogCount + 1) << "handler should be invoked exactly once each time";
|
|
EXPECT_EQ(e[oldLogCount], handler.Logs[oldLogCount]) << "wrong event returned";
|
|
}
|
|
|
|
EXPECT_FALSE(reader.HasParseError());
|
|
EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount) << "handler invoked wrong number of times";
|
|
|
|
// The handler should not be invoked when the JSON has been fully read, but it should not fail
|
|
size_t oldLogCount = handler.LogCount;
|
|
EXPECT_TRUE(reader.IterativeParseNext<kParseDefaultFlags>(is, handler)) << "parse-next past complete is allowed";
|
|
EXPECT_EQ(handler.LogCount, oldLogCount) << "parse-next past complete should not invoke handler";
|
|
EXPECT_FALSE(reader.HasParseError()) << "parse-next past complete should not generate parse error";
|
|
}
|
|
}
|
|
|
|
// Test iterative parsing on kParseErrorTermination.
|
|
struct HandlerTerminateAtStartObject : public IterativeParsingReaderHandler<> {
|
|
bool StartObject() { return false; }
|
|
};
|
|
|
|
struct HandlerTerminateAtStartArray : public IterativeParsingReaderHandler<> {
|
|
bool StartArray() { return false; }
|
|
};
|
|
|
|
struct HandlerTerminateAtEndObject : public IterativeParsingReaderHandler<> {
|
|
bool EndObject(SizeType) { return false; }
|
|
};
|
|
|
|
struct HandlerTerminateAtEndArray : public IterativeParsingReaderHandler<> {
|
|
bool EndArray(SizeType) { return false; }
|
|
};
|
|
|
|
TEST(Reader, IterativeParsing_ShortCircuit) {
|
|
{
|
|
HandlerTerminateAtStartObject handler;
|
|
Reader reader;
|
|
StringStream is("[1, {}]");
|
|
|
|
ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
|
|
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorTermination, r.Code());
|
|
EXPECT_EQ(4u, r.Offset());
|
|
}
|
|
|
|
{
|
|
HandlerTerminateAtStartArray handler;
|
|
Reader reader;
|
|
StringStream is("{\"a\": []}");
|
|
|
|
ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
|
|
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorTermination, r.Code());
|
|
EXPECT_EQ(6u, r.Offset());
|
|
}
|
|
|
|
{
|
|
HandlerTerminateAtEndObject handler;
|
|
Reader reader;
|
|
StringStream is("[1, {}]");
|
|
|
|
ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
|
|
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorTermination, r.Code());
|
|
EXPECT_EQ(5u, r.Offset());
|
|
}
|
|
|
|
{
|
|
HandlerTerminateAtEndArray handler;
|
|
Reader reader;
|
|
StringStream is("{\"a\": []}");
|
|
|
|
ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
|
|
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorTermination, r.Code());
|
|
EXPECT_EQ(7u, r.Offset());
|
|
}
|
|
}
|
|
|
|
// 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 RawNumber(const char*, SizeType, bool) { return e != 7; }
|
|
bool String(const char*, SizeType, bool) { return e != 8; }
|
|
bool StartObject() { return e != 9; }
|
|
bool Key(const char*, SizeType, bool) { return e != 10; }
|
|
bool EndObject(SizeType) { return e != 11; }
|
|
bool StartArray() { return e != 12; }
|
|
bool EndArray(SizeType) { return e != 13; }
|
|
};
|
|
|
|
#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]");
|
|
// RawNumber() is never called
|
|
TEST_TERMINATION(8, "[\"a\"");
|
|
TEST_TERMINATION(9, "[{");
|
|
TEST_TERMINATION(10, "[{\"a\"");
|
|
TEST_TERMINATION(11, "[{}");
|
|
TEST_TERMINATION(11, "[{\"a\":1}"); // non-empty object
|
|
TEST_TERMINATION(12, "{\"a\":[");
|
|
TEST_TERMINATION(13, "{\"a\":[]");
|
|
TEST_TERMINATION(13, "{\"a\":[1]"); // non-empty array
|
|
}
|
|
|
|
TEST(Reader, ParseComments) {
|
|
const char* json =
|
|
"// Here is a one-line comment.\n"
|
|
"{// And here's another one\n"
|
|
" /*And here's an in-line one.*/\"hello\" : \"world\","
|
|
" \"t\" :/* And one with '*' symbol*/true ,"
|
|
"/* A multiline comment\n"
|
|
" goes here*/"
|
|
" \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3]"
|
|
"}/*And the last one to be sure */";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h));
|
|
EXPECT_EQ(20u, h.step_);
|
|
}
|
|
|
|
TEST(Reader, ParseEmptyInlineComment) {
|
|
const char* json = "{/**/\"hello\" : \"world\", \"t\" : true, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h));
|
|
EXPECT_EQ(20u, h.step_);
|
|
}
|
|
|
|
TEST(Reader, ParseEmptyOnelineComment) {
|
|
const char* json = "{//\n\"hello\" : \"world\", \"t\" : true, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h));
|
|
EXPECT_EQ(20u, h.step_);
|
|
}
|
|
|
|
TEST(Reader, ParseMultipleCommentsInARow) {
|
|
const char* json =
|
|
"{/* first comment *//* second */\n"
|
|
"/* third */ /*fourth*/// last one\n"
|
|
"\"hello\" : \"world\", \"t\" : true, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h));
|
|
EXPECT_EQ(20u, h.step_);
|
|
}
|
|
|
|
TEST(Reader, InlineCommentsAreDisabledByDefault) {
|
|
{
|
|
const char* json = "{/* Inline comment. */\"hello\" : \"world\", \"t\" : true, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_FALSE(reader.Parse<kParseDefaultFlags>(s, h));
|
|
}
|
|
|
|
{
|
|
const char* json =
|
|
"{\"hello\" : /* Multiline comment starts here\n"
|
|
" continues here\n"
|
|
" and ends here */\"world\", \"t\" :true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_FALSE(reader.Parse<kParseDefaultFlags>(s, h));
|
|
}
|
|
}
|
|
|
|
TEST(Reader, OnelineCommentsAreDisabledByDefault) {
|
|
const char* json = "{// One-line comment\n\"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_FALSE(reader.Parse<kParseDefaultFlags>(s, h));
|
|
}
|
|
|
|
TEST(Reader, EofAfterOneLineComment) {
|
|
const char* json = "{\"hello\" : \"world\" // EOF is here -->\0 \n}";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h));
|
|
EXPECT_EQ(kParseErrorObjectMissCommaOrCurlyBracket, reader.GetParseErrorCode());
|
|
}
|
|
|
|
TEST(Reader, IncompleteMultilineComment) {
|
|
const char* json = "{\"hello\" : \"world\" /* EOF is here -->\0 */}";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h));
|
|
EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode());
|
|
}
|
|
|
|
TEST(Reader, IncompleteMultilineComment2) {
|
|
const char* json = "{\"hello\" : \"world\" /* *\0 */}";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h));
|
|
EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode());
|
|
}
|
|
|
|
TEST(Reader, UnrecognizedComment) {
|
|
const char* json = "{\"hello\" : \"world\" /! }";
|
|
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h));
|
|
EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode());
|
|
}
|
|
|
|
struct NumbersAsStringsHandler {
|
|
bool Null() { return true; }
|
|
bool Bool(bool) { return true; }
|
|
bool Int(int) { return true; }
|
|
bool Uint(unsigned) { return true; }
|
|
bool Int64(int64_t) { return true; }
|
|
bool Uint64(uint64_t) { return true; }
|
|
bool Double(double) { return true; }
|
|
// 'str' is not null-terminated
|
|
bool RawNumber(const char* str, SizeType length, bool) {
|
|
EXPECT_TRUE(str != 0);
|
|
EXPECT_TRUE(expected_len_ == length);
|
|
EXPECT_TRUE(strncmp(str, expected_, length) == 0);
|
|
return true;
|
|
}
|
|
bool String(const char*, SizeType, bool) { return true; }
|
|
bool StartObject() { return true; }
|
|
bool Key(const char*, SizeType, bool) { return true; }
|
|
bool EndObject(SizeType) { return true; }
|
|
bool StartArray() { return true; }
|
|
bool EndArray(SizeType) { return true; }
|
|
|
|
NumbersAsStringsHandler(const char* expected)
|
|
: expected_(expected)
|
|
, expected_len_(strlen(expected)) {}
|
|
|
|
const char* expected_;
|
|
size_t expected_len_;
|
|
};
|
|
|
|
TEST(Reader, NumbersAsStrings) {
|
|
{
|
|
const char* json = "{ \"pi\": 3.1416 } ";
|
|
StringStream s(json);
|
|
NumbersAsStringsHandler h("3.1416");
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
char* json = StrDup("{ \"pi\": 3.1416 } ");
|
|
InsituStringStream s(json);
|
|
NumbersAsStringsHandler h("3.1416");
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, h));
|
|
free(json);
|
|
}
|
|
{
|
|
const char* json = "{ \"gigabyte\": 1.0e9 } ";
|
|
StringStream s(json);
|
|
NumbersAsStringsHandler h("1.0e9");
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
char* json = StrDup("{ \"gigabyte\": 1.0e9 } ");
|
|
InsituStringStream s(json);
|
|
NumbersAsStringsHandler h("1.0e9");
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, h));
|
|
free(json);
|
|
}
|
|
{
|
|
const char* json = "{ \"pi\": 314.159e-2 } ";
|
|
StringStream s(json);
|
|
NumbersAsStringsHandler h("314.159e-2");
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
char* json = StrDup("{ \"gigabyte\": 314.159e-2 } ");
|
|
InsituStringStream s(json);
|
|
NumbersAsStringsHandler h("314.159e-2");
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, h));
|
|
free(json);
|
|
}
|
|
{
|
|
const char* json = "{ \"negative\": -1.54321 } ";
|
|
StringStream s(json);
|
|
NumbersAsStringsHandler h("-1.54321");
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
char* json = StrDup("{ \"negative\": -1.54321 } ");
|
|
InsituStringStream s(json);
|
|
NumbersAsStringsHandler h("-1.54321");
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, h));
|
|
free(json);
|
|
}
|
|
{
|
|
const char* json = "{ \"pi\": 314.159e-2 } ";
|
|
std::stringstream ss(json);
|
|
IStreamWrapper s(ss);
|
|
NumbersAsStringsHandler h("314.159e-2");
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
char n1e319[321]; // '1' followed by 319 '0'
|
|
n1e319[0] = '1';
|
|
for (int i = 1; i < 320; i++)
|
|
n1e319[i] = '0';
|
|
n1e319[320] = '\0';
|
|
StringStream s(n1e319);
|
|
NumbersAsStringsHandler h(n1e319);
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
}
|
|
|
|
struct NumbersAsStringsHandlerWChar_t {
|
|
bool Null() { return true; }
|
|
bool Bool(bool) { return true; }
|
|
bool Int(int) { return true; }
|
|
bool Uint(unsigned) { return true; }
|
|
bool Int64(int64_t) { return true; }
|
|
bool Uint64(uint64_t) { return true; }
|
|
bool Double(double) { return true; }
|
|
// 'str' is not null-terminated
|
|
bool RawNumber(const wchar_t* str, SizeType length, bool) {
|
|
EXPECT_TRUE(str != 0);
|
|
EXPECT_TRUE(expected_len_ == length);
|
|
EXPECT_TRUE(wcsncmp(str, expected_, length) == 0);
|
|
return true;
|
|
}
|
|
bool String(const wchar_t*, SizeType, bool) { return true; }
|
|
bool StartObject() { return true; }
|
|
bool Key(const wchar_t*, SizeType, bool) { return true; }
|
|
bool EndObject(SizeType) { return true; }
|
|
bool StartArray() { return true; }
|
|
bool EndArray(SizeType) { return true; }
|
|
|
|
NumbersAsStringsHandlerWChar_t(const wchar_t* expected)
|
|
: expected_(expected)
|
|
, expected_len_(wcslen(expected)) {}
|
|
|
|
const wchar_t* expected_;
|
|
size_t expected_len_;
|
|
};
|
|
|
|
TEST(Reader, NumbersAsStringsWChar_t) {
|
|
{
|
|
const wchar_t* json = L"{ \"pi\": 3.1416 } ";
|
|
GenericStringStream<UTF16<> > s(json);
|
|
NumbersAsStringsHandlerWChar_t h(L"3.1416");
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
wchar_t* json = StrDup(L"{ \"pi\": 3.1416 } ");
|
|
GenericInsituStringStream<UTF16<> > s(json);
|
|
NumbersAsStringsHandlerWChar_t h(L"3.1416");
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseNumbersAsStringsFlag>(s, h));
|
|
free(json);
|
|
}
|
|
{
|
|
const wchar_t* json = L"{ \"gigabyte\": 1.0e9 } ";
|
|
GenericStringStream<UTF16<> > s(json);
|
|
NumbersAsStringsHandlerWChar_t h(L"1.0e9");
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
wchar_t* json = StrDup(L"{ \"gigabyte\": 1.0e9 } ");
|
|
GenericInsituStringStream<UTF16<> > s(json);
|
|
NumbersAsStringsHandlerWChar_t h(L"1.0e9");
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseNumbersAsStringsFlag>(s, h));
|
|
free(json);
|
|
}
|
|
{
|
|
const wchar_t* json = L"{ \"pi\": 314.159e-2 } ";
|
|
GenericStringStream<UTF16<> > s(json);
|
|
NumbersAsStringsHandlerWChar_t h(L"314.159e-2");
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
wchar_t* json = StrDup(L"{ \"gigabyte\": 314.159e-2 } ");
|
|
GenericInsituStringStream<UTF16<> > s(json);
|
|
NumbersAsStringsHandlerWChar_t h(L"314.159e-2");
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseNumbersAsStringsFlag>(s, h));
|
|
free(json);
|
|
}
|
|
{
|
|
const wchar_t* json = L"{ \"negative\": -1.54321 } ";
|
|
GenericStringStream<UTF16<> > s(json);
|
|
NumbersAsStringsHandlerWChar_t h(L"-1.54321");
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
wchar_t* json = StrDup(L"{ \"negative\": -1.54321 } ");
|
|
GenericInsituStringStream<UTF16<> > s(json);
|
|
NumbersAsStringsHandlerWChar_t h(L"-1.54321");
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseNumbersAsStringsFlag>(s, h));
|
|
free(json);
|
|
}
|
|
{
|
|
const wchar_t* json = L"{ \"pi\": 314.159e-2 } ";
|
|
std::wstringstream ss(json);
|
|
WIStreamWrapper s(ss);
|
|
NumbersAsStringsHandlerWChar_t h(L"314.159e-2");
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
{
|
|
wchar_t n1e319[321]; // '1' followed by 319 '0'
|
|
n1e319[0] = L'1';
|
|
for(int i = 1; i < 320; i++)
|
|
n1e319[i] = L'0';
|
|
n1e319[320] = L'\0';
|
|
GenericStringStream<UTF16<> > s(n1e319);
|
|
NumbersAsStringsHandlerWChar_t h(n1e319);
|
|
GenericReader<UTF16<>, UTF16<> > reader;
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
|
|
}
|
|
}
|
|
|
|
template <unsigned extraFlags>
|
|
void TestTrailingCommas() {
|
|
{
|
|
StringStream s("[1,2,3,]");
|
|
ParseArrayHandler<3> h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h));
|
|
EXPECT_EQ(5u, h.step_);
|
|
}
|
|
{
|
|
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false,"
|
|
"\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3],}";
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h));
|
|
EXPECT_EQ(20u, h.step_);
|
|
}
|
|
{
|
|
// whitespace around trailing commas
|
|
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false,"
|
|
"\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3\n,\n]\n,\n} ";
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h));
|
|
EXPECT_EQ(20u, h.step_);
|
|
}
|
|
{
|
|
// comments around trailing commas
|
|
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null,"
|
|
"\"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3/*test*/,/*test*/]/*test*/,/*test*/}";
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag|kParseCommentsFlag>(s, h));
|
|
EXPECT_EQ(20u, h.step_);
|
|
}
|
|
}
|
|
|
|
TEST(Reader, TrailingCommas) {
|
|
TestTrailingCommas<kParseNoFlags>();
|
|
}
|
|
|
|
TEST(Reader, TrailingCommasIterative) {
|
|
TestTrailingCommas<kParseIterativeFlag>();
|
|
}
|
|
|
|
template <unsigned extraFlags>
|
|
void TestMultipleTrailingCommaErrors() {
|
|
// only a single trailing comma is allowed.
|
|
{
|
|
StringStream s("[1,2,3,,]");
|
|
ParseArrayHandler<3> h;
|
|
Reader reader;
|
|
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorValueInvalid, r.Code());
|
|
EXPECT_EQ(7u, r.Offset());
|
|
}
|
|
{
|
|
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false,"
|
|
"\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3,],,}";
|
|
StringStream s(json);
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorObjectMissName, r.Code());
|
|
EXPECT_EQ(95u, r.Offset());
|
|
}
|
|
}
|
|
|
|
TEST(Reader, MultipleTrailingCommaErrors) {
|
|
TestMultipleTrailingCommaErrors<kParseNoFlags>();
|
|
}
|
|
|
|
TEST(Reader, MultipleTrailingCommaErrorsIterative) {
|
|
TestMultipleTrailingCommaErrors<kParseIterativeFlag>();
|
|
}
|
|
|
|
template <unsigned extraFlags>
|
|
void TestEmptyExceptForCommaErrors() {
|
|
// not allowed even with trailing commas enabled; the
|
|
// trailing comma must follow a value.
|
|
{
|
|
StringStream s("[,]");
|
|
ParseArrayHandler<3> h;
|
|
Reader reader;
|
|
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorValueInvalid, r.Code());
|
|
EXPECT_EQ(1u, r.Offset());
|
|
}
|
|
{
|
|
StringStream s("{,}");
|
|
ParseObjectHandler h;
|
|
Reader reader;
|
|
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorObjectMissName, r.Code());
|
|
EXPECT_EQ(1u, r.Offset());
|
|
}
|
|
}
|
|
|
|
TEST(Reader, EmptyExceptForCommaErrors) {
|
|
TestEmptyExceptForCommaErrors<kParseNoFlags>();
|
|
}
|
|
|
|
TEST(Reader, EmptyExceptForCommaErrorsIterative) {
|
|
TestEmptyExceptForCommaErrors<kParseIterativeFlag>();
|
|
}
|
|
|
|
template <unsigned extraFlags>
|
|
void TestTrailingCommaHandlerTermination() {
|
|
{
|
|
HandlerTerminateAtEndArray h;
|
|
Reader reader;
|
|
StringStream s("[1,2,3,]");
|
|
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorTermination, r.Code());
|
|
EXPECT_EQ(7u, r.Offset());
|
|
}
|
|
{
|
|
HandlerTerminateAtEndObject h;
|
|
Reader reader;
|
|
StringStream s("{\"t\": true, \"f\": false,}");
|
|
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorTermination, r.Code());
|
|
EXPECT_EQ(23u, r.Offset());
|
|
}
|
|
}
|
|
|
|
TEST(Reader, TrailingCommaHandlerTermination) {
|
|
TestTrailingCommaHandlerTermination<kParseNoFlags>();
|
|
}
|
|
|
|
TEST(Reader, TrailingCommaHandlerTerminationIterative) {
|
|
TestTrailingCommaHandlerTermination<kParseIterativeFlag>();
|
|
}
|
|
|
|
TEST(Reader, ParseNanAndInfinity) {
|
|
#define TEST_NAN_INF(str, x) \
|
|
{ \
|
|
{ \
|
|
StringStream s(str); \
|
|
ParseDoubleHandler h; \
|
|
Reader reader; \
|
|
ASSERT_EQ(kParseErrorNone, reader.Parse<kParseNanAndInfFlag>(s, h).Code()); \
|
|
EXPECT_EQ(1u, h.step_); \
|
|
internal::Double e(x), a(h.actual_); \
|
|
EXPECT_EQ(e.IsNan(), a.IsNan()); \
|
|
EXPECT_EQ(e.IsInf(), a.IsInf()); \
|
|
if (!e.IsNan()) \
|
|
EXPECT_EQ(e.Sign(), a.Sign()); \
|
|
} \
|
|
{ \
|
|
const char* json = "{ \"naninfdouble\": " str " } "; \
|
|
StringStream s(json); \
|
|
NumbersAsStringsHandler h(str); \
|
|
Reader reader; \
|
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag|kParseNanAndInfFlag>(s, h)); \
|
|
} \
|
|
{ \
|
|
char* json = StrDup("{ \"naninfdouble\": " str " } "); \
|
|
InsituStringStream s(json); \
|
|
NumbersAsStringsHandler h(str); \
|
|
Reader reader; \
|
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag|kParseNanAndInfFlag>(s, h)); \
|
|
free(json); \
|
|
} \
|
|
}
|
|
#define TEST_NAN_INF_ERROR(errorCode, str, errorOffset) \
|
|
{ \
|
|
unsigned streamPos = errorOffset; \
|
|
char buffer[1001]; \
|
|
strncpy(buffer, str, 1000); \
|
|
InsituStringStream s(buffer); \
|
|
BaseReaderHandler<> h; \
|
|
Reader reader; \
|
|
EXPECT_FALSE(reader.Parse<kParseNanAndInfFlag>(s, h)); \
|
|
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
|
|
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
|
|
EXPECT_EQ(streamPos, s.Tell());\
|
|
}
|
|
|
|
double nan = std::numeric_limits<double>::quiet_NaN();
|
|
double inf = std::numeric_limits<double>::infinity();
|
|
|
|
TEST_NAN_INF("NaN", nan);
|
|
TEST_NAN_INF("-NaN", nan);
|
|
TEST_NAN_INF("Inf", inf);
|
|
TEST_NAN_INF("Infinity", inf);
|
|
TEST_NAN_INF("-Inf", -inf);
|
|
TEST_NAN_INF("-Infinity", -inf);
|
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NInf", 1u);
|
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NaInf", 2u);
|
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "INan", 1u);
|
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "InNan", 2u);
|
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "nan", 1u);
|
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1u);
|
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1u);
|
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-Infinty", 6u);
|
|
TEST_NAN_INF_ERROR(kParseErrorDocumentRootNotSingular, "NaN.2e2", 3u);
|
|
TEST_NAN_INF_ERROR(kParseErrorDocumentRootNotSingular, "Inf.2", 3u);
|
|
TEST_NAN_INF_ERROR(kParseErrorDocumentRootNotSingular, "-InfE2", 4u);
|
|
|
|
#undef TEST_NAN_INF_ERROR
|
|
#undef TEST_NAN_INF
|
|
}
|
|
|
|
TEST(Reader, EscapedApostrophe) {
|
|
const char json[] = " { \"foo\": \"bar\\'buzz\" } ";
|
|
|
|
BaseReaderHandler<> h;
|
|
|
|
{
|
|
StringStream s(json);
|
|
Reader reader;
|
|
ParseResult r = reader.Parse<kParseNoFlags>(s, h);
|
|
EXPECT_TRUE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorStringEscapeInvalid, r.Code());
|
|
EXPECT_EQ(14u, r.Offset());
|
|
}
|
|
|
|
{
|
|
StringStream s(json);
|
|
Reader reader;
|
|
ParseResult r = reader.Parse<kParseEscapedApostropheFlag>(s, h);
|
|
EXPECT_FALSE(reader.HasParseError());
|
|
EXPECT_EQ(kParseErrorNone, r.Code());
|
|
EXPECT_EQ(0u, r.Offset());
|
|
}
|
|
}
|
|
|
|
RAPIDJSON_DIAG_POP
|