mirror of
				https://github.com/RetroDECK/ES-DE.git
				synced 2025-04-10 19:15:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2371 lines
		
	
	
		
			96 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2371 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);
 | |
| 
 | |
| #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
 | 
