summaryrefslogtreecommitdiffstats
path: root/src/rapidjson/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/rapidjson/test')
-rw-r--r--src/rapidjson/test/CMakeLists.txt20
-rw-r--r--src/rapidjson/test/perftest/CMakeLists.txt26
-rw-r--r--src/rapidjson/test/perftest/misctest.cpp974
-rw-r--r--src/rapidjson/test/perftest/perftest.cpp24
-rw-r--r--src/rapidjson/test/perftest/perftest.h182
-rw-r--r--src/rapidjson/test/perftest/platformtest.cpp166
-rw-r--r--src/rapidjson/test/perftest/rapidjsontest.cpp441
-rw-r--r--src/rapidjson/test/perftest/schematest.cpp216
-rw-r--r--src/rapidjson/test/unittest/CMakeLists.txt92
-rw-r--r--src/rapidjson/test/unittest/allocatorstest.cpp102
-rw-r--r--src/rapidjson/test/unittest/bigintegertest.cpp133
-rw-r--r--src/rapidjson/test/unittest/documenttest.cpp652
-rw-r--r--src/rapidjson/test/unittest/dtoatest.cpp98
-rw-r--r--src/rapidjson/test/unittest/encodedstreamtest.cpp313
-rw-r--r--src/rapidjson/test/unittest/encodingstest.cpp451
-rw-r--r--src/rapidjson/test/unittest/filestreamtest.cpp112
-rw-r--r--src/rapidjson/test/unittest/fwdtest.cpp227
-rw-r--r--src/rapidjson/test/unittest/istreamwrappertest.cpp181
-rw-r--r--src/rapidjson/test/unittest/itoatest.cpp160
-rw-r--r--src/rapidjson/test/unittest/jsoncheckertest.cpp99
-rw-r--r--src/rapidjson/test/unittest/namespacetest.cpp70
-rw-r--r--src/rapidjson/test/unittest/ostreamwrappertest.cpp91
-rw-r--r--src/rapidjson/test/unittest/pointertest.cpp1524
-rw-r--r--src/rapidjson/test/unittest/prettywritertest.cpp203
-rw-r--r--src/rapidjson/test/unittest/readertest.cpp1844
-rw-r--r--src/rapidjson/test/unittest/regextest.cpp592
-rw-r--r--src/rapidjson/test/unittest/schematest.cpp1313
-rw-r--r--src/rapidjson/test/unittest/simdtest.cpp215
-rw-r--r--src/rapidjson/test/unittest/strfunctest.cpp30
-rw-r--r--src/rapidjson/test/unittest/stringbuffertest.cpp170
-rw-r--r--src/rapidjson/test/unittest/strtodtest.cpp132
-rw-r--r--src/rapidjson/test/unittest/unittest.cpp51
-rw-r--r--src/rapidjson/test/unittest/unittest.h135
-rw-r--r--src/rapidjson/test/unittest/valuetest.cpp1792
-rw-r--r--src/rapidjson/test/unittest/writertest.cpp497
35 files changed, 13328 insertions, 0 deletions
diff --git a/src/rapidjson/test/CMakeLists.txt b/src/rapidjson/test/CMakeLists.txt
new file mode 100644
index 000000000..11c1b04c7
--- /dev/null
+++ b/src/rapidjson/test/CMakeLists.txt
@@ -0,0 +1,20 @@
+find_package(GTestSrc)
+
+IF(GTESTSRC_FOUND)
+ enable_testing()
+
+ if (WIN32 AND (NOT CYGWIN) AND (NOT MINGW))
+ set(gtest_disable_pthreads ON)
+ set(gtest_force_shared_crt ON)
+ endif()
+
+ add_subdirectory(${GTEST_SOURCE_DIR} ${CMAKE_BINARY_DIR}/googletest)
+ include_directories(SYSTEM ${GTEST_INCLUDE_DIR})
+
+ set(TEST_LIBRARIES gtest gtest_main)
+
+ add_custom_target(tests ALL)
+ add_subdirectory(perftest)
+ add_subdirectory(unittest)
+
+ENDIF(GTESTSRC_FOUND)
diff --git a/src/rapidjson/test/perftest/CMakeLists.txt b/src/rapidjson/test/perftest/CMakeLists.txt
new file mode 100644
index 000000000..c33aae469
--- /dev/null
+++ b/src/rapidjson/test/perftest/CMakeLists.txt
@@ -0,0 +1,26 @@
+set(PERFTEST_SOURCES
+ misctest.cpp
+ perftest.cpp
+ platformtest.cpp
+ rapidjsontest.cpp
+ schematest.cpp)
+
+add_executable(perftest ${PERFTEST_SOURCES})
+target_link_libraries(perftest ${TEST_LIBRARIES})
+
+add_dependencies(tests perftest)
+
+find_program(CCACHE_FOUND ccache)
+if(CCACHE_FOUND)
+ set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
+ set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
+ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -fcolor-diagnostics")
+ endif()
+endif(CCACHE_FOUND)
+
+IF(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
+add_test(NAME perftest
+ COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/perftest
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
+ENDIF()
diff --git a/src/rapidjson/test/perftest/misctest.cpp b/src/rapidjson/test/perftest/misctest.cpp
new file mode 100644
index 000000000..aac847784
--- /dev/null
+++ b/src/rapidjson/test/perftest/misctest.cpp
@@ -0,0 +1,974 @@
+// 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. All rights reserved.
+//
+// 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 "perftest.h"
+
+#if TEST_MISC
+
+#define __STDC_FORMAT_MACROS
+#include "rapidjson/stringbuffer.h"
+
+#define protected public
+#include "rapidjson/writer.h"
+#undef private
+
+class Misc : public PerfTest {
+};
+
+// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 12
+
+static const unsigned char utf8d[] = {
+ // The first part of the table maps bytes to character classes that
+ // to reduce the size of the transition table and create bitmasks.
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+ // The second part is a transition table that maps a combination
+ // of a state of the automaton and a character class to a state.
+ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+ 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+ 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+ 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+ 12,36,12,12,12,12,12,12,12,12,12,12,
+};
+
+static unsigned inline decode(unsigned* state, unsigned* codep, unsigned byte) {
+ unsigned type = utf8d[byte];
+
+ *codep = (*state != UTF8_ACCEPT) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xff >> type) & (byte);
+
+ *state = utf8d[256 + *state + type];
+ return *state;
+}
+
+static bool IsUTF8(unsigned char* s) {
+ unsigned codepoint, state = 0;
+
+ while (*s)
+ decode(&state, &codepoint, *s++);
+
+ return state == UTF8_ACCEPT;
+}
+
+TEST_F(Misc, Hoehrmann_IsUTF8) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ EXPECT_TRUE(IsUTF8((unsigned char*)json_));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CountDecimalDigit: Count number of decimal places
+
+inline unsigned CountDecimalDigit_naive(unsigned n) {
+ unsigned count = 1;
+ while (n >= 10) {
+ n /= 10;
+ count++;
+ }
+ return count;
+}
+
+inline unsigned CountDecimalDigit_enroll4(unsigned n) {
+ unsigned count = 1;
+ while (n >= 10000) {
+ n /= 10000u;
+ count += 4;
+ }
+ if (n < 10) return count;
+ if (n < 100) return count + 1;
+ if (n < 1000) return count + 2;
+ return count + 3;
+}
+
+inline unsigned CountDecimalDigit64_enroll4(uint64_t n) {
+ unsigned count = 1;
+ while (n >= 10000) {
+ n /= 10000u;
+ count += 4;
+ }
+ if (n < 10) return count;
+ if (n < 100) return count + 1;
+ if (n < 1000) return count + 2;
+ return count + 3;
+}
+
+inline unsigned CountDecimalDigit_fast(unsigned n) {
+ static const uint32_t powers_of_10[] = {
+ 0,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000
+ };
+
+#if defined(_M_IX86) || defined(_M_X64)
+ unsigned long i = 0;
+ _BitScanReverse(&i, n | 1);
+ uint32_t t = (i + 1) * 1233 >> 12;
+#elif defined(__GNUC__)
+ uint32_t t = (32 - __builtin_clz(n | 1)) * 1233 >> 12;
+#else
+#error
+#endif
+ return t - (n < powers_of_10[t]) + 1;
+}
+
+inline unsigned CountDecimalDigit64_fast(uint64_t n) {
+ static const uint64_t powers_of_10[] = {
+ 0,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000,
+ 10000000000,
+ 100000000000,
+ 1000000000000,
+ 10000000000000,
+ 100000000000000,
+ 1000000000000000,
+ 10000000000000000,
+ 100000000000000000,
+ 1000000000000000000,
+ 10000000000000000000U
+ };
+
+#if defined(_M_IX86)
+ uint64_t m = n | 1;
+ unsigned long i = 0;
+ if (_BitScanReverse(&i, m >> 32))
+ i += 32;
+ else
+ _BitScanReverse(&i, m & 0xFFFFFFFF);
+ uint32_t t = (i + 1) * 1233 >> 12;
+#elif defined(_M_X64)
+ unsigned long i = 0;
+ _BitScanReverse64(&i, n | 1);
+ uint32_t t = (i + 1) * 1233 >> 12;
+#elif defined(__GNUC__)
+ uint32_t t = (64 - __builtin_clzll(n | 1)) * 1233 >> 12;
+#else
+#error
+#endif
+
+ return t - (n < powers_of_10[t]) + 1;
+}
+
+#if 0
+// Exhaustive, very slow
+TEST_F(Misc, CountDecimalDigit_Verify) {
+ unsigned i = 0;
+ do {
+ if (i % (65536 * 256) == 0)
+ printf("%u\n", i);
+ ASSERT_EQ(CountDecimalDigit_enroll4(i), CountDecimalDigit_fast(i));
+ i++;
+ } while (i != 0);
+}
+
+static const unsigned kDigits10Trial = 1000000000u;
+TEST_F(Misc, CountDecimalDigit_naive) {
+ unsigned sum = 0;
+ for (unsigned i = 0; i < kDigits10Trial; i++)
+ sum += CountDecimalDigit_naive(i);
+ printf("%u\n", sum);
+}
+
+TEST_F(Misc, CountDecimalDigit_enroll4) {
+ unsigned sum = 0;
+ for (unsigned i = 0; i < kDigits10Trial; i++)
+ sum += CountDecimalDigit_enroll4(i);
+ printf("%u\n", sum);
+}
+
+TEST_F(Misc, CountDecimalDigit_fast) {
+ unsigned sum = 0;
+ for (unsigned i = 0; i < kDigits10Trial; i++)
+ sum += CountDecimalDigit_fast(i);
+ printf("%u\n", sum);
+}
+#endif
+
+TEST_F(Misc, CountDecimalDigit64_VerifyFast) {
+ uint64_t i = 1, j;
+ do {
+ //printf("%" PRIu64 "\n", i);
+ ASSERT_EQ(CountDecimalDigit64_enroll4(i), CountDecimalDigit64_fast(i));
+ j = i;
+ i *= 3;
+ } while (j < i);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// integer-to-string conversion
+
+// https://gist.github.com/anonymous/7179097
+static const int randval[] ={
+ 936116, 369532, 453755, -72860, 209713, 268347, 435278, -360266, -416287, -182064,
+ -644712, 944969, 640463, -366588, 471577, -69401, -744294, -505829, 923883, 831785,
+ -601136, -636767, -437054, 591718, 100758, 231907, -719038, 973540, -605220, 506659,
+ -871653, 462533, 764843, -919138, 404305, -630931, -288711, -751454, -173726, -718208,
+ 432689, -281157, 360737, 659827, 19174, -376450, 769984, -858198, 439127, 734703,
+ -683426, 7, 386135, 186997, -643900, -744422, -604708, -629545, 42313, -933592,
+ -635566, 182308, 439024, -367219, -73924, -516649, 421935, -470515, 413507, -78952,
+ -427917, -561158, 737176, 94538, 572322, 405217, 709266, -357278, -908099, -425447,
+ 601119, 750712, -862285, -177869, 900102, 384877, 157859, -641680, 503738, -702558,
+ 278225, 463290, 268378, -212840, 580090, 347346, -473985, -950968, -114547, -839893,
+ -738032, -789424, 409540, 493495, 432099, 119755, 905004, -174834, 338266, 234298,
+ 74641, -965136, -754593, 685273, 466924, 920560, 385062, 796402, -67229, 994864,
+ 376974, 299869, -647540, -128724, 469890, -163167, -547803, -743363, 486463, -621028,
+ 612288, 27459, -514224, 126342, -66612, 803409, -777155, -336453, -284002, 472451,
+ 342390, -163630, 908356, -456147, -825607, 268092, -974715, 287227, 227890, -524101,
+ 616370, -782456, 922098, -624001, -813690, 171605, -192962, 796151, 707183, -95696,
+ -23163, -721260, 508892, 430715, 791331, 482048, -996102, 863274, 275406, -8279,
+ -556239, -902076, 268647, -818565, 260069, -798232, -172924, -566311, -806503, -885992,
+ 813969, -78468, 956632, 304288, 494867, -508784, 381751, 151264, 762953, 76352,
+ 594902, 375424, 271700, -743062, 390176, 924237, 772574, 676610, 435752, -153847,
+ 3959, -971937, -294181, -538049, -344620, -170136, 19120, -703157, 868152, -657961,
+ -818631, 219015, -872729, -940001, -956570, 880727, -345910, 942913, -942271, -788115,
+ 225294, 701108, -517736, -416071, 281940, 488730, 942698, 711494, 838382, -892302,
+ -533028, 103052, 528823, 901515, 949577, 159364, 718227, -241814, -733661, -462928,
+ -495829, 165170, 513580, -629188, -509571, -459083, 198437, 77198, -644612, 811276,
+ -422298, -860842, -52584, 920369, 686424, -530667, -243476, 49763, 345866, -411960,
+ -114863, 470810, -302860, 683007, -509080, 2, -174981, -772163, -48697, 447770,
+ -268246, 213268, 269215, 78810, -236340, -639140, -864323, 505113, -986569, -325215,
+ 541859, 163070, -819998, -645161, -583336, 573414, 696417, -132375, 3, -294501,
+ 320435, 682591, 840008, 351740, 426951, 609354, 898154, -943254, 227321, -859793,
+ -727993, 44137, -497965, -782239, 14955, -746080, -243366, 9837, -233083, 606507,
+ -995864, -615287, -994307, 602715, 770771, -315040, 610860, 446102, -307120, 710728,
+ -590392, -230474, -762625, -637525, 134963, -202700, -766902, -985541, 218163, 682009,
+ 926051, 525156, -61195, 403211, -810098, 245539, -431733, 179998, -806533, 745943,
+ 447597, 131973, -187130, 826019, 286107, -937230, -577419, 20254, 681802, -340500,
+ 323080, 266283, -667617, 309656, 416386, 611863, 759991, -534257, 523112, -634892,
+ -169913, -204905, -909867, -882185, -944908, 741811, -717675, 967007, -317396, 407230,
+ -412805, 792905, 994873, 744793, -456797, 713493, 355232, 116900, -945199, 880539,
+ 342505, -580824, -262273, 982968, -349497, -735488, 311767, -455191, 570918, 389734,
+ -958386, 10262, -99267, 155481, 304210, 204724, 704367, -144893, -233664, -671441,
+ 896849, 408613, 762236, 322697, 981321, 688476, 13663, -970704, -379507, 896412,
+ 977084, 348869, 875948, 341348, 318710, 512081, 6163, 669044, 833295, 811883,
+ 708756, -802534, -536057, 608413, -389625, -694603, 541106, -110037, 720322, -540581,
+ 645420, 32980, 62442, 510157, -981870, -87093, -325960, -500494, -718291, -67889,
+ 991501, 374804, 769026, -978869, 294747, 714623, 413327, -199164, 671368, 804789,
+ -362507, 798196, -170790, -568895, -869379, 62020, -316693, -837793, 644994, -39341,
+ -417504, -243068, -957756, 99072, 622234, -739992, 225668, 8863, -505910, 82483,
+ -559244, 241572, 1315, -36175, -54990, 376813, -11, 162647, -688204, -486163,
+ -54934, -197470, 744223, -762707, 732540, 996618, 351561, -445933, -898491, 486531,
+ 456151, 15276, 290186, -817110, -52995, 313046, -452533, -96267, 94470, -500176,
+ -818026, -398071, -810548, -143325, -819741, 1338, -897676, -101577, -855445, 37309,
+ 285742, 953804, -777927, -926962, -811217, -936744, -952245, -802300, -490188, -964953,
+ -552279, 329142, -570048, -505756, 682898, -381089, -14352, 175138, 152390, -582268,
+ -485137, 717035, 805329, 239572, -730409, 209643, -184403, -385864, 675086, 819648,
+ 629058, -527109, -488666, -171981, 532788, 552441, 174666, 984921, 766514, 758787,
+ 716309, 338801, -978004, -412163, 876079, -734212, 789557, -160491, -522719, 56644,
+ -991, -286038, -53983, 663740, 809812, 919889, -717502, -137704, 220511, 184396,
+ -825740, -588447, 430870, 124309, 135956, 558662, -307087, -788055, -451328, 812260,
+ 931601, 324347, -482989, -117858, -278861, 189068, -172774, 929057, 293787, 198161,
+ -342386, -47173, 906555, -759955, -12779, 777604, -97869, 899320, 927486, -25284,
+ -848550, 259450, -485856, -17820, 88, 171400, 235492, -326783, -340793, 886886,
+ 112428, -246280, 5979, 648444, -114982, 991013, -56489, -9497, 419706, 632820,
+ -341664, 393926, -848977, -22538, 257307, 773731, -905319, 491153, 734883, -868212,
+ -951053, 644458, -580758, 764735, 584316, 297077, 28852, -397710, -953669, 201772,
+ 879050, -198237, -588468, 448102, -116837, 770007, -231812, 642906, -582166, -885828,
+ 9, 305082, -996577, 303559, 75008, -772956, -447960, 599825, -295552, 870739,
+ -386278, -950300, 485359, -457081, 629461, -850276, 550496, -451755, -620841, -11766,
+ -950137, 832337, 28711, -273398, -507197, 91921, -271360, -705991, -753220, -388968,
+ 967945, 340434, -320883, -662793, -554617, -574568, 477946, -6148, -129519, 689217,
+ 920020, -656315, -974523, -212525, 80921, -612532, 645096, 545655, 655713, -591631,
+ -307385, -816688, -618823, -113713, 526430, 673063, 735916, -809095, -850417, 639004,
+ 432281, -388185, 270708, 860146, -39902, -786157, -258180, -246169, -966720, -264957,
+ 548072, -306010, -57367, -635665, 933824, 70553, -989936, -488741, 72411, -452509,
+ 529831, 956277, 449019, -577850, -360986, -803418, 48833, 296073, 203430, 609591,
+ 715483, 470964, 658106, -718254, -96424, 790163, 334739, 181070, -373578, 5,
+ -435088, 329841, 330939, -256602, 394355, 912412, 231910, 927278, -661933, 788539,
+ -769664, -893274, -96856, 298205, 901043, -608122, -527430, 183618, -553963, -35246,
+ -393924, 948832, -483198, 594501, 35460, -407007, 93494, -336881, -634072, 984205,
+ -812161, 944664, -31062, 753872, 823933, -69566, 50445, 290147, 85134, 34706,
+ 551902, 405202, -991246, -84642, 154341, 316432, -695101, -651588, -5030, 137564,
+ -294665, 332541, 528307, -90572, -344923, 523766, -758498, -968047, 339028, 494578,
+ 593129, -725773, 31834, -718406, -208638, 159665, -2043, 673344, -442767, 75816,
+ 755442, 769257, -158730, -410272, 691688, 589550, -878398, -184121, 460679, 346312,
+ 294163, -544602, 653308, 254167, -276979, 52073, -892684, 887653, -41222, 983065,
+ -68258, -408799, -99069, -674069, -863635, -32890, 622757, -743862, 40872, -4837,
+ -967228, 522370, -903951, -818669, 524459, 514702, 925801, 20007, -299229, 579348,
+ 626021, 430089, 348139, -562692, -607728, -130606, -928451, -424793, -458647, -448892,
+ -312230, 143337, 109746, 880042, -339658, -785614, 938995, 540916, 118429, 661351,
+ -402967, 404729, -40918, -976535, 743230, 713110, 440182, -381314, -499252, 74613,
+ 193652, 912717, 491323, 583633, 324691, 459397, 281253, 195540, -2764, -888651,
+ 892449, 132663, -478373, -430002, -314551, 527826, 247165, 557966, 554778, 481531,
+ -946634, 431685, -769059, -348371, 174046, 184597, -354867, 584422, 227390, -850397,
+ -542924, -849093, -737769, 325359, 736314, 269101, 767940, 674809, 81413, -447458,
+ 445076, 189072, 906218, 502688, -718476, -863827, -731381, 100660, 623249, 710008,
+ 572060, 922203, 685740, 55096, 263394, -243695, -353910, -516788, 388471, 455165,
+ 844103, -643772, 363976, 268875, -899450, 104470, 104029, -238874, -274659, 732969,
+ -676443, 953291, -916289, -861849, -242344, 958083, -479593, -970395, 799831, 277841,
+ -243236, -283462, -201510, 166263, -259105, -575706, 878926, 891064, 895297, 655262,
+ -34807, -809833, -89281, 342585, 554920, 1, 902141, -333425, 139703, 852318,
+ -618438, 329498, -932596, -692836, -513372, 733656, -523411, 85779, 500478, -682697,
+ -502836, 138776, 156341, -420037, -557964, -556378, 710993, -50383, -877159, 916334,
+ 132996, 583516, -603392, -111615, -12288, -780214, 476780, 123327, 137607, 519956,
+ 745837, 17358, -158581, -53490
+};
+static const size_t randvalCount = sizeof(randval) / sizeof(randval[0]);
+static const size_t kItoaTrialCount = 10000;
+
+static const char digits[201] =
+"0001020304050607080910111213141516171819"
+"2021222324252627282930313233343536373839"
+"4041424344454647484950515253545556575859"
+"6061626364656667686970717273747576777879"
+"8081828384858687888990919293949596979899";
+
+// Prevent code being optimized out
+//#define OUTPUT_LENGTH(length) printf("", length)
+#define OUTPUT_LENGTH(length) printf("%u\n", (unsigned)length)
+
+template<typename OutputStream>
+class Writer1 {
+public:
+ Writer1() : os_() {}
+ Writer1(OutputStream& os) : os_(&os) {}
+
+ void Reset(OutputStream& os) {
+ os_ = &os;
+ }
+
+ bool WriteInt(int i) {
+ if (i < 0) {
+ os_->Put('-');
+ i = -i;
+ }
+ return WriteUint((unsigned)i);
+ }
+
+ bool WriteUint(unsigned u) {
+ char buffer[10];
+ char *p = buffer;
+ do {
+ *p++ = char(u % 10) + '0';
+ u /= 10;
+ } while (u > 0);
+
+ do {
+ --p;
+ os_->Put(*p);
+ } while (p != buffer);
+ return true;
+ }
+
+ bool WriteInt64(int64_t i64) {
+ if (i64 < 0) {
+ os_->Put('-');
+ i64 = -i64;
+ }
+ WriteUint64((uint64_t)i64);
+ return true;
+ }
+
+ bool WriteUint64(uint64_t u64) {
+ char buffer[20];
+ char *p = buffer;
+ do {
+ *p++ = char(u64 % 10) + '0';
+ u64 /= 10;
+ } while (u64 > 0);
+
+ do {
+ --p;
+ os_->Put(*p);
+ } while (p != buffer);
+ return true;
+ }
+
+private:
+ OutputStream* os_;
+};
+
+template<>
+bool Writer1<rapidjson::StringBuffer>::WriteUint(unsigned u) {
+ char buffer[10];
+ char* p = buffer;
+ do {
+ *p++ = char(u % 10) + '0';
+ u /= 10;
+ } while (u > 0);
+
+ char* d = os_->Push(p - buffer);
+ do {
+ --p;
+ *d++ = *p;
+ } while (p != buffer);
+ return true;
+}
+
+// Using digits LUT to reduce divsion/modulo
+template<typename OutputStream>
+class Writer2 {
+public:
+ Writer2() : os_() {}
+ Writer2(OutputStream& os) : os_(&os) {}
+
+ void Reset(OutputStream& os) {
+ os_ = &os;
+ }
+
+ bool WriteInt(int i) {
+ if (i < 0) {
+ os_->Put('-');
+ i = -i;
+ }
+ return WriteUint((unsigned)i);
+ }
+
+ bool WriteUint(unsigned u) {
+ char buffer[10];
+ char* p = buffer;
+ while (u >= 100) {
+ const unsigned i = (u % 100) << 1;
+ u /= 100;
+ *p++ = digits[i + 1];
+ *p++ = digits[i];
+ }
+ if (u < 10)
+ *p++ = char(u) + '0';
+ else {
+ const unsigned i = u << 1;
+ *p++ = digits[i + 1];
+ *p++ = digits[i];
+ }
+
+ do {
+ --p;
+ os_->Put(*p);
+ } while (p != buffer);
+ return true;
+ }
+
+ bool WriteInt64(int64_t i64) {
+ if (i64 < 0) {
+ os_->Put('-');
+ i64 = -i64;
+ }
+ WriteUint64((uint64_t)i64);
+ return true;
+ }
+
+ bool WriteUint64(uint64_t u64) {
+ char buffer[20];
+ char* p = buffer;
+ while (u64 >= 100) {
+ const unsigned i = static_cast<unsigned>(u64 % 100) << 1;
+ u64 /= 100;
+ *p++ = digits[i + 1];
+ *p++ = digits[i];
+ }
+ if (u64 < 10)
+ *p++ = char(u64) + '0';
+ else {
+ const unsigned i = static_cast<unsigned>(u64) << 1;
+ *p++ = digits[i + 1];
+ *p++ = digits[i];
+ }
+
+ do {
+ --p;
+ os_->Put(*p);
+ } while (p != buffer);
+ return true;
+ }
+
+private:
+ OutputStream* os_;
+};
+
+// First pass to count digits
+template<typename OutputStream>
+class Writer3 {
+public:
+ Writer3() : os_() {}
+ Writer3(OutputStream& os) : os_(&os) {}
+
+ void Reset(OutputStream& os) {
+ os_ = &os;
+ }
+
+ bool WriteInt(int i) {
+ if (i < 0) {
+ os_->Put('-');
+ i = -i;
+ }
+ return WriteUint((unsigned)i);
+ }
+
+ bool WriteUint(unsigned u) {
+ char buffer[10];
+ char *p = buffer;
+ do {
+ *p++ = char(u % 10) + '0';
+ u /= 10;
+ } while (u > 0);
+
+ do {
+ --p;
+ os_->Put(*p);
+ } while (p != buffer);
+ return true;
+ }
+
+ bool WriteInt64(int64_t i64) {
+ if (i64 < 0) {
+ os_->Put('-');
+ i64 = -i64;
+ }
+ WriteUint64((uint64_t)i64);
+ return true;
+ }
+
+ bool WriteUint64(uint64_t u64) {
+ char buffer[20];
+ char *p = buffer;
+ do {
+ *p++ = char(u64 % 10) + '0';
+ u64 /= 10;
+ } while (u64 > 0);
+
+ do {
+ --p;
+ os_->Put(*p);
+ } while (p != buffer);
+ return true;
+ }
+
+private:
+ void WriteUintReverse(char* d, unsigned u) {
+ do {
+ *--d = char(u % 10) + '0';
+ u /= 10;
+ } while (u > 0);
+ }
+
+ void WriteUint64Reverse(char* d, uint64_t u) {
+ do {
+ *--d = char(u % 10) + '0';
+ u /= 10;
+ } while (u > 0);
+ }
+
+ OutputStream* os_;
+};
+
+template<>
+inline bool Writer3<rapidjson::StringBuffer>::WriteUint(unsigned u) {
+ unsigned digit = CountDecimalDigit_fast(u);
+ WriteUintReverse(os_->Push(digit) + digit, u);
+ return true;
+}
+
+template<>
+inline bool Writer3<rapidjson::InsituStringStream>::WriteUint(unsigned u) {
+ unsigned digit = CountDecimalDigit_fast(u);
+ WriteUintReverse(os_->Push(digit) + digit, u);
+ return true;
+}
+
+template<>
+inline bool Writer3<rapidjson::StringBuffer>::WriteUint64(uint64_t u) {
+ unsigned digit = CountDecimalDigit64_fast(u);
+ WriteUint64Reverse(os_->Push(digit) + digit, u);
+ return true;
+}
+
+template<>
+inline bool Writer3<rapidjson::InsituStringStream>::WriteUint64(uint64_t u) {
+ unsigned digit = CountDecimalDigit64_fast(u);
+ WriteUint64Reverse(os_->Push(digit) + digit, u);
+ return true;
+}
+
+// Using digits LUT to reduce divsion/modulo, two passes
+template<typename OutputStream>
+class Writer4 {
+public:
+ Writer4() : os_() {}
+ Writer4(OutputStream& os) : os_(&os) {}
+
+ void Reset(OutputStream& os) {
+ os_ = &os;
+ }
+
+ bool WriteInt(int i) {
+ if (i < 0) {
+ os_->Put('-');
+ i = -i;
+ }
+ return WriteUint((unsigned)i);
+ }
+
+ bool WriteUint(unsigned u) {
+ char buffer[10];
+ char* p = buffer;
+ while (u >= 100) {
+ const unsigned i = (u % 100) << 1;
+ u /= 100;
+ *p++ = digits[i + 1];
+ *p++ = digits[i];
+ }
+ if (u < 10)
+ *p++ = char(u) + '0';
+ else {
+ const unsigned i = u << 1;
+ *p++ = digits[i + 1];
+ *p++ = digits[i];
+ }
+
+ do {
+ --p;
+ os_->Put(*p);
+ } while (p != buffer);
+ return true;
+ }
+
+ bool WriteInt64(int64_t i64) {
+ if (i64 < 0) {
+ os_->Put('-');
+ i64 = -i64;
+ }
+ WriteUint64((uint64_t)i64);
+ return true;
+ }
+
+ bool WriteUint64(uint64_t u64) {
+ char buffer[20];
+ char* p = buffer;
+ while (u64 >= 100) {
+ const unsigned i = static_cast<unsigned>(u64 % 100) << 1;
+ u64 /= 100;
+ *p++ = digits[i + 1];
+ *p++ = digits[i];
+ }
+ if (u64 < 10)
+ *p++ = char(u64) + '0';
+ else {
+ const unsigned i = static_cast<unsigned>(u64) << 1;
+ *p++ = digits[i + 1];
+ *p++ = digits[i];
+ }
+
+ do {
+ --p;
+ os_->Put(*p);
+ } while (p != buffer);
+ return true;
+ }
+
+private:
+ void WriteUintReverse(char* d, unsigned u) {
+ while (u >= 100) {
+ const unsigned i = (u % 100) << 1;
+ u /= 100;
+ *--d = digits[i + 1];
+ *--d = digits[i];
+ }
+ if (u < 10) {
+ *--d = char(u) + '0';
+ }
+ else {
+ const unsigned i = u << 1;
+ *--d = digits[i + 1];
+ *--d = digits[i];
+ }
+ }
+
+ void WriteUint64Reverse(char* d, uint64_t u) {
+ while (u >= 100) {
+ const unsigned i = (u % 100) << 1;
+ u /= 100;
+ *--d = digits[i + 1];
+ *--d = digits[i];
+ }
+ if (u < 10) {
+ *--d = char(u) + '0';
+ }
+ else {
+ const unsigned i = u << 1;
+ *--d = digits[i + 1];
+ *--d = digits[i];
+ }
+ }
+
+ OutputStream* os_;
+};
+
+template<>
+inline bool Writer4<rapidjson::StringBuffer>::WriteUint(unsigned u) {
+ unsigned digit = CountDecimalDigit_fast(u);
+ WriteUintReverse(os_->Push(digit) + digit, u);
+ return true;
+}
+
+template<>
+inline bool Writer4<rapidjson::InsituStringStream>::WriteUint(unsigned u) {
+ unsigned digit = CountDecimalDigit_fast(u);
+ WriteUintReverse(os_->Push(digit) + digit, u);
+ return true;
+}
+
+template<>
+inline bool Writer4<rapidjson::StringBuffer>::WriteUint64(uint64_t u) {
+ unsigned digit = CountDecimalDigit64_fast(u);
+ WriteUint64Reverse(os_->Push(digit) + digit, u);
+ return true;
+}
+
+template<>
+inline bool Writer4<rapidjson::InsituStringStream>::WriteUint64(uint64_t u) {
+ unsigned digit = CountDecimalDigit64_fast(u);
+ WriteUint64Reverse(os_->Push(digit) + digit, u);
+ return true;
+}
+
+template <typename Writer>
+void itoa_Writer_StringBufferVerify() {
+ rapidjson::StringBuffer sb;
+ Writer writer(sb);
+ for (size_t j = 0; j < randvalCount; j++) {
+ char buffer[32];
+ sprintf(buffer, "%d", randval[j]);
+ writer.WriteInt(randval[j]);
+ ASSERT_STREQ(buffer, sb.GetString());
+ sb.Clear();
+ }
+}
+
+template <typename Writer>
+void itoa_Writer_InsituStringStreamVerify() {
+ Writer writer;
+ for (size_t j = 0; j < randvalCount; j++) {
+ char buffer[32];
+ sprintf(buffer, "%d", randval[j]);
+ char buffer2[32];
+ rapidjson::InsituStringStream ss(buffer2);
+ writer.Reset(ss);
+ char* begin = ss.PutBegin();
+ writer.WriteInt(randval[j]);
+ ss.Put('\0');
+ ss.PutEnd(begin);
+ ASSERT_STREQ(buffer, buffer2);
+ }
+}
+
+template <typename Writer>
+void itoa_Writer_StringBuffer() {
+ size_t length = 0;
+
+ rapidjson::StringBuffer sb;
+ Writer writer(sb);
+
+ for (size_t i = 0; i < kItoaTrialCount; i++) {
+ for (size_t j = 0; j < randvalCount; j++) {
+ writer.WriteInt(randval[j]);
+ length += sb.GetSize();
+ sb.Clear();
+ }
+ }
+ OUTPUT_LENGTH(length);
+}
+
+template <typename Writer>
+void itoa_Writer_InsituStringStream() {
+ size_t length = 0;
+
+ char buffer[32];
+ Writer writer;
+ for (size_t i = 0; i < kItoaTrialCount; i++) {
+ for (size_t j = 0; j < randvalCount; j++) {
+ rapidjson::InsituStringStream ss(buffer);
+ writer.Reset(ss);
+ char* begin = ss.PutBegin();
+ writer.WriteInt(randval[j]);
+ length += ss.PutEnd(begin);
+ }
+ }
+ OUTPUT_LENGTH(length);
+};
+
+template <typename Writer>
+void itoa64_Writer_StringBufferVerify() {
+ rapidjson::StringBuffer sb;
+ Writer writer(sb);
+ for (size_t j = 0; j < randvalCount; j++) {
+ char buffer[32];
+ int64_t x = randval[j] * randval[j];
+ sprintf(buffer, "%" PRIi64, x);
+ writer.WriteInt64(x);
+ ASSERT_STREQ(buffer, sb.GetString());
+ sb.Clear();
+ }
+}
+
+template <typename Writer>
+void itoa64_Writer_InsituStringStreamVerify() {
+ Writer writer;
+ for (size_t j = 0; j < randvalCount; j++) {
+ char buffer[32];
+ int64_t x = randval[j] * randval[j];
+ sprintf(buffer, "%" PRIi64, x);
+ char buffer2[32];
+ rapidjson::InsituStringStream ss(buffer2);
+ writer.Reset(ss);
+ char* begin = ss.PutBegin();
+ writer.WriteInt64(x);
+ ss.Put('\0');
+ ss.PutEnd(begin);
+ ASSERT_STREQ(buffer, buffer2);
+ }
+}
+
+template <typename Writer>
+void itoa64_Writer_StringBuffer() {
+ size_t length = 0;
+
+ rapidjson::StringBuffer sb;
+ Writer writer(sb);
+
+ for (size_t i = 0; i < kItoaTrialCount; i++) {
+ for (size_t j = 0; j < randvalCount; j++) {
+ writer.WriteInt64(randval[j] * randval[j]);
+ length += sb.GetSize();
+ sb.Clear();
+ }
+ }
+ OUTPUT_LENGTH(length);
+}
+
+template <typename Writer>
+void itoa64_Writer_InsituStringStream() {
+ size_t length = 0;
+
+ char buffer[32];
+ Writer writer;
+ for (size_t i = 0; i < kItoaTrialCount; i++) {
+ for (size_t j = 0; j < randvalCount; j++) {
+ rapidjson::InsituStringStream ss(buffer);
+ writer.Reset(ss);
+ char* begin = ss.PutBegin();
+ writer.WriteInt64(randval[j] * randval[j]);
+ length += ss.PutEnd(begin);
+ }
+ }
+ OUTPUT_LENGTH(length);
+};
+
+// Full specialization for InsituStringStream to prevent memory copying
+// (normally we will not use InsituStringStream for writing, just for testing)
+
+namespace rapidjson {
+
+template<>
+bool rapidjson::Writer<InsituStringStream>::WriteInt(int i) {
+ char *buffer = os_->Push(11);
+ const char* end = internal::i32toa(i, buffer);
+ os_->Pop(11 - (end - buffer));
+ return true;
+}
+
+template<>
+bool Writer<InsituStringStream>::WriteUint(unsigned u) {
+ char *buffer = os_->Push(10);
+ const char* end = internal::u32toa(u, buffer);
+ os_->Pop(10 - (end - buffer));
+ return true;
+}
+
+template<>
+bool Writer<InsituStringStream>::WriteInt64(int64_t i64) {
+ char *buffer = os_->Push(21);
+ const char* end = internal::i64toa(i64, buffer);
+ os_->Pop(21 - (end - buffer));
+ return true;
+}
+
+template<>
+bool Writer<InsituStringStream>::WriteUint64(uint64_t u) {
+ char *buffer = os_->Push(20);
+ const char* end = internal::u64toa(u, buffer);
+ os_->Pop(20 - (end - buffer));
+ return true;
+}
+
+} // namespace rapidjson
+
+TEST_F(Misc, itoa_Writer_StringBufferVerify) { itoa_Writer_StringBufferVerify<rapidjson::Writer<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer1_StringBufferVerify) { itoa_Writer_StringBufferVerify<Writer1<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer2_StringBufferVerify) { itoa_Writer_StringBufferVerify<Writer2<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer3_StringBufferVerify) { itoa_Writer_StringBufferVerify<Writer3<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer4_StringBufferVerify) { itoa_Writer_StringBufferVerify<Writer4<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify<rapidjson::Writer<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa_Writer1_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify<Writer1<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa_Writer2_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify<Writer2<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa_Writer3_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify<Writer3<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa_Writer4_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify<Writer4<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa_Writer_StringBuffer) { itoa_Writer_StringBuffer<rapidjson::Writer<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer1_StringBuffer) { itoa_Writer_StringBuffer<Writer1<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer2_StringBuffer) { itoa_Writer_StringBuffer<Writer2<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer3_StringBuffer) { itoa_Writer_StringBuffer<Writer3<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer4_StringBuffer) { itoa_Writer_StringBuffer<Writer4<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa_Writer_InsituStringStream) { itoa_Writer_InsituStringStream<rapidjson::Writer<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa_Writer1_InsituStringStream) { itoa_Writer_InsituStringStream<Writer1<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa_Writer2_InsituStringStream) { itoa_Writer_InsituStringStream<Writer2<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa_Writer3_InsituStringStream) { itoa_Writer_InsituStringStream<Writer3<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa_Writer4_InsituStringStream) { itoa_Writer_InsituStringStream<Writer4<rapidjson::InsituStringStream> >(); }
+
+TEST_F(Misc, itoa64_Writer_StringBufferVerify) { itoa64_Writer_StringBufferVerify<rapidjson::Writer<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer1_StringBufferVerify) { itoa64_Writer_StringBufferVerify<Writer1<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer2_StringBufferVerify) { itoa64_Writer_StringBufferVerify<Writer2<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer3_StringBufferVerify) { itoa64_Writer_StringBufferVerify<Writer3<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer4_StringBufferVerify) { itoa64_Writer_StringBufferVerify<Writer4<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify<rapidjson::Writer<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa64_Writer1_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify<Writer1<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa64_Writer2_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify<Writer2<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa64_Writer3_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify<Writer3<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa64_Writer4_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify<Writer4<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa64_Writer_StringBuffer) { itoa64_Writer_StringBuffer<rapidjson::Writer<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer1_StringBuffer) { itoa64_Writer_StringBuffer<Writer1<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer2_StringBuffer) { itoa64_Writer_StringBuffer<Writer2<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer3_StringBuffer) { itoa64_Writer_StringBuffer<Writer3<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer4_StringBuffer) { itoa64_Writer_StringBuffer<Writer4<rapidjson::StringBuffer> >(); }
+TEST_F(Misc, itoa64_Writer_InsituStringStream) { itoa64_Writer_InsituStringStream<rapidjson::Writer<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa64_Writer1_InsituStringStream) { itoa64_Writer_InsituStringStream<Writer1<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa64_Writer2_InsituStringStream) { itoa64_Writer_InsituStringStream<Writer2<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa64_Writer3_InsituStringStream) { itoa64_Writer_InsituStringStream<Writer3<rapidjson::InsituStringStream> >(); }
+TEST_F(Misc, itoa64_Writer4_InsituStringStream) { itoa64_Writer_InsituStringStream<Writer4<rapidjson::InsituStringStream> >(); }
+
+#endif // TEST_MISC
diff --git a/src/rapidjson/test/perftest/perftest.cpp b/src/rapidjson/test/perftest/perftest.cpp
new file mode 100644
index 000000000..4e79f1f51
--- /dev/null
+++ b/src/rapidjson/test/perftest/perftest.cpp
@@ -0,0 +1,24 @@
+// 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. All rights reserved.
+//
+// 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 "perftest.h"
+
+int main(int argc, char **argv) {
+#if _MSC_VER
+ _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
+ //void *testWhetherMemoryLeakDetectionWorks = malloc(1);
+#endif
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/rapidjson/test/perftest/perftest.h b/src/rapidjson/test/perftest/perftest.h
new file mode 100644
index 000000000..b098e4147
--- /dev/null
+++ b/src/rapidjson/test/perftest/perftest.h
@@ -0,0 +1,182 @@
+// 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. All rights reserved.
+//
+// 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.
+
+#ifndef PERFTEST_H_
+#define PERFTEST_H_
+
+#define TEST_RAPIDJSON 1
+#define TEST_PLATFORM 0
+#define TEST_MISC 0
+
+#define TEST_VERSION_CODE(x,y,z) \
+ (((x)*100000) + ((y)*100) + (z))
+
+// __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler.
+// We use -march=native with gmake to enable -msse2 and -msse4.2, if supported.
+#if defined(__SSE4_2__)
+# define RAPIDJSON_SSE42
+#elif defined(__SSE2__)
+# define RAPIDJSON_SSE2
+#endif
+
+#define RAPIDJSON_HAS_STDSTRING 1
+
+////////////////////////////////////////////////////////////////////////////////
+// Google Test
+
+#ifdef __cplusplus
+
+// gtest indirectly included inttypes.h, without __STDC_CONSTANT_MACROS.
+#ifndef __STDC_CONSTANT_MACROS
+# define __STDC_CONSTANT_MACROS 1 // required by C++ standard
+#endif
+
+#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+#if defined(__clang__) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#pragma GCC diagnostic push
+#endif
+#pragma GCC diagnostic ignored "-Weffc++"
+#endif
+
+#include "gtest/gtest.h"
+
+#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#pragma GCC diagnostic pop
+#endif
+
+#ifdef _MSC_VER
+#define _CRTDBG_MAP_ALLOC
+#include <crtdbg.h>
+#pragma warning(disable : 4996) // 'function': was declared deprecated
+#endif
+
+//! Base class for all performance tests
+class PerfTest : public ::testing::Test {
+public:
+ PerfTest() : filename_(), json_(), length_(), whitespace_(), whitespace_length_() {}
+
+ virtual void SetUp() {
+ {
+ const char *paths[] = {
+ "data/sample.json",
+ "bin/data/sample.json",
+ "../bin/data/sample.json",
+ "../../bin/data/sample.json",
+ "../../../bin/data/sample.json"
+ };
+
+ FILE *fp = 0;
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ fp = fopen(filename_ = paths[i], "rb");
+ if (fp)
+ break;
+ }
+ ASSERT_TRUE(fp != 0);
+
+ fseek(fp, 0, SEEK_END);
+ length_ = (size_t)ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ json_ = (char*)malloc(length_ + 1);
+ ASSERT_EQ(length_, fread(json_, 1, length_, fp));
+ json_[length_] = '\0';
+ fclose(fp);
+ }
+
+ // whitespace test
+ {
+ whitespace_length_ = 1024 * 1024;
+ whitespace_ = (char *)malloc(whitespace_length_ + 4);
+ char *p = whitespace_;
+ for (size_t i = 0; i < whitespace_length_; i += 4) {
+ *p++ = ' ';
+ *p++ = '\n';
+ *p++ = '\r';
+ *p++ = '\t';
+ }
+ *p++ = '[';
+ *p++ = '0';
+ *p++ = ']';
+ *p++ = '\0';
+ }
+
+ // types test
+ {
+ const char *typespaths[] = {
+ "data/types",
+ "bin/types",
+ "../bin/types",
+ "../../bin/types/",
+ "../../../bin/types"
+ };
+
+ const char* typesfilenames[] = {
+ "booleans.json",
+ "floats.json",
+ "guids.json",
+ "integers.json",
+ "mixed.json",
+ "nulls.json",
+ "paragraphs.json"
+ };
+
+ for (size_t j = 0; j < sizeof(typesfilenames) / sizeof(typesfilenames[0]); j++) {
+ types_[j] = 0;
+ for (size_t i = 0; i < sizeof(typespaths) / sizeof(typespaths[0]); i++) {
+ char filename[256];
+ sprintf(filename, "%s/%s", typespaths[i], typesfilenames[j]);
+ if (FILE* fp = fopen(filename, "rb")) {
+ fseek(fp, 0, SEEK_END);
+ typesLength_[j] = (size_t)ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ types_[j] = (char*)malloc(typesLength_[j] + 1);
+ ASSERT_EQ(typesLength_[j], fread(types_[j], 1, typesLength_[j], fp));
+ types_[j][typesLength_[j]] = '\0';
+ fclose(fp);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ virtual void TearDown() {
+ free(json_);
+ free(whitespace_);
+ json_ = 0;
+ whitespace_ = 0;
+ for (size_t i = 0; i < 7; i++) {
+ free(types_[i]);
+ types_[i] = 0;
+ }
+ }
+
+private:
+ PerfTest(const PerfTest&);
+ PerfTest& operator=(const PerfTest&);
+
+protected:
+ const char* filename_;
+ char *json_;
+ size_t length_;
+ char *whitespace_;
+ size_t whitespace_length_;
+ char *types_[7];
+ size_t typesLength_[7];
+
+ static const size_t kTrialCount = 1000;
+};
+
+#endif // __cplusplus
+
+#endif // PERFTEST_H_
diff --git a/src/rapidjson/test/perftest/platformtest.cpp b/src/rapidjson/test/perftest/platformtest.cpp
new file mode 100644
index 000000000..bb905ca73
--- /dev/null
+++ b/src/rapidjson/test/perftest/platformtest.cpp
@@ -0,0 +1,166 @@
+// 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. All rights reserved.
+//
+// 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 "perftest.h"
+
+// This file is for giving the performance characteristics of the platform (compiler/OS/CPU).
+
+#if TEST_PLATFORM
+
+#include <cmath>
+#include <fcntl.h>
+
+// Windows
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+// UNIX
+#if defined(unix) || defined(__unix__) || defined(__unix)
+#include <unistd.h>
+#ifdef _POSIX_MAPPED_FILES
+#include <sys/mman.h>
+#endif
+#endif
+
+class Platform : public PerfTest {
+public:
+ virtual void SetUp() {
+ PerfTest::SetUp();
+
+ // temp buffer for testing
+ temp_ = (char *)malloc(length_ + 1);
+ memcpy(temp_, json_, length_);
+ checkSum_ = CheckSum();
+ }
+
+ char CheckSum() {
+ char c = 0;
+ for (size_t i = 0; i < length_; ++i)
+ c += temp_[i];
+ return c;
+ }
+
+ virtual void TearDown() {
+ PerfTest::TearDown();
+ free(temp_);
+ }
+
+protected:
+ char *temp_;
+ char checkSum_;
+};
+
+TEST_F(Platform, CheckSum) {
+ for (int i = 0; i < kTrialCount; i++)
+ EXPECT_EQ(checkSum_, CheckSum());
+}
+
+TEST_F(Platform, strlen) {
+ for (int i = 0; i < kTrialCount; i++) {
+ size_t l = strlen(json_);
+ EXPECT_EQ(length_, l);
+ }
+}
+
+TEST_F(Platform, memcmp) {
+ for (int i = 0; i < kTrialCount; i++) {
+ EXPECT_EQ(0, memcmp(temp_, json_, length_));
+ }
+}
+
+TEST_F(Platform, pow) {
+ double sum = 0;
+ for (int i = 0; i < kTrialCount * kTrialCount; i++)
+ sum += pow(10.0, i & 255);
+ EXPECT_GT(sum, 0.0);
+}
+
+TEST_F(Platform, Whitespace_strlen) {
+ for (int i = 0; i < kTrialCount; i++) {
+ size_t l = strlen(whitespace_);
+ EXPECT_GT(l, whitespace_length_);
+ }
+}
+
+TEST_F(Platform, Whitespace_strspn) {
+ for (int i = 0; i < kTrialCount; i++) {
+ size_t l = strspn(whitespace_, " \n\r\t");
+ EXPECT_EQ(whitespace_length_, l);
+ }
+}
+
+TEST_F(Platform, fread) {
+ for (int i = 0; i < kTrialCount; i++) {
+ FILE *fp = fopen(filename_, "rb");
+ ASSERT_EQ(length_, fread(temp_, 1, length_, fp));
+ EXPECT_EQ(checkSum_, CheckSum());
+ fclose(fp);
+ }
+}
+
+#ifdef _MSC_VER
+TEST_F(Platform, read) {
+ for (int i = 0; i < kTrialCount; i++) {
+ int fd = _open(filename_, _O_BINARY | _O_RDONLY);
+ ASSERT_NE(-1, fd);
+ ASSERT_EQ(length_, _read(fd, temp_, length_));
+ EXPECT_EQ(checkSum_, CheckSum());
+ _close(fd);
+ }
+}
+#else
+TEST_F(Platform, read) {
+ for (int i = 0; i < kTrialCount; i++) {
+ int fd = open(filename_, O_RDONLY);
+ ASSERT_NE(-1, fd);
+ ASSERT_EQ(length_, read(fd, temp_, length_));
+ EXPECT_EQ(checkSum_, CheckSum());
+ close(fd);
+ }
+}
+#endif
+
+#ifdef _WIN32
+TEST_F(Platform, MapViewOfFile) {
+ for (int i = 0; i < kTrialCount; i++) {
+ HANDLE file = CreateFile(filename_, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ ASSERT_NE(INVALID_HANDLE_VALUE, file);
+ HANDLE mapObject = CreateFileMapping(file, NULL, PAGE_READONLY, 0, length_, NULL);
+ ASSERT_NE(INVALID_HANDLE_VALUE, mapObject);
+ void *p = MapViewOfFile(mapObject, FILE_MAP_READ, 0, 0, length_);
+ ASSERT_TRUE(p != NULL);
+ EXPECT_EQ(checkSum_, CheckSum());
+ ASSERT_TRUE(UnmapViewOfFile(p) == TRUE);
+ ASSERT_TRUE(CloseHandle(mapObject) == TRUE);
+ ASSERT_TRUE(CloseHandle(file) == TRUE);
+ }
+}
+#endif
+
+#ifdef _POSIX_MAPPED_FILES
+TEST_F(Platform, mmap) {
+ for (int i = 0; i < kTrialCount; i++) {
+ int fd = open(filename_, O_RDONLY);
+ ASSERT_NE(-1, fd);
+ void *p = mmap(NULL, length_, PROT_READ, MAP_PRIVATE, fd, 0);
+ ASSERT_TRUE(p != NULL);
+ EXPECT_EQ(checkSum_, CheckSum());
+ munmap(p, length_);
+ close(fd);
+ }
+}
+#endif
+
+#endif // TEST_PLATFORM
diff --git a/src/rapidjson/test/perftest/rapidjsontest.cpp b/src/rapidjson/test/perftest/rapidjsontest.cpp
new file mode 100644
index 000000000..675db3182
--- /dev/null
+++ b/src/rapidjson/test/perftest/rapidjsontest.cpp
@@ -0,0 +1,441 @@
+// 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. All rights reserved.
+//
+// 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 "perftest.h"
+
+#if TEST_RAPIDJSON
+
+#include "rapidjson/rapidjson.h"
+#include "rapidjson/document.h"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/memorystream.h"
+
+#ifdef RAPIDJSON_SSE2
+#define SIMD_SUFFIX(name) name##_SSE2
+#elif defined(RAPIDJSON_SSE42)
+#define SIMD_SUFFIX(name) name##_SSE42
+#else
+#define SIMD_SUFFIX(name) name
+#endif
+
+using namespace rapidjson;
+
+class RapidJson : public PerfTest {
+public:
+ RapidJson() : temp_(), doc_() {}
+
+ virtual void SetUp() {
+ PerfTest::SetUp();
+
+ // temp buffer for insitu parsing.
+ temp_ = (char *)malloc(length_ + 1);
+
+ // Parse as a document
+ EXPECT_FALSE(doc_.Parse(json_).HasParseError());
+
+ for (size_t i = 0; i < 7; i++)
+ EXPECT_FALSE(typesDoc_[i].Parse(types_[i]).HasParseError());
+ }
+
+ virtual void TearDown() {
+ PerfTest::TearDown();
+ free(temp_);
+ }
+
+private:
+ RapidJson(const RapidJson&);
+ RapidJson& operator=(const RapidJson&);
+
+protected:
+ char *temp_;
+ Document doc_;
+ Document typesDoc_[7];
+};
+
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ memcpy(temp_, json_, length_ + 1);
+ InsituStringStream s(temp_);
+ BaseReaderHandler<> h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag>(s, h));
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler_ValidateEncoding)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ memcpy(temp_, json_, length_ + 1);
+ InsituStringStream s(temp_);
+ BaseReaderHandler<> h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag>(s, h));
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ StringStream s(json_);
+ BaseReaderHandler<> h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse(s, h));
+ }
+}
+
+#define TEST_TYPED(index, Name)\
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_##Name)) {\
+ for (size_t i = 0; i < kTrialCount * 10; i++) {\
+ StringStream s(types_[index]);\
+ BaseReaderHandler<> h;\
+ Reader reader;\
+ EXPECT_TRUE(reader.Parse(s, h));\
+ }\
+}\
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler_##Name)) {\
+ for (size_t i = 0; i < kTrialCount * 10; i++) {\
+ memcpy(temp_, types_[index], typesLength_[index] + 1);\
+ InsituStringStream s(temp_);\
+ BaseReaderHandler<> h;\
+ Reader reader;\
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag>(s, h));\
+ }\
+}
+
+TEST_TYPED(0, Booleans)
+TEST_TYPED(1, Floats)
+TEST_TYPED(2, Guids)
+TEST_TYPED(3, Integers)
+TEST_TYPED(4, Mixed)
+TEST_TYPED(5, Nulls)
+TEST_TYPED(6, Paragraphs)
+
+#undef TEST_TYPED
+
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_FullPrecision)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ StringStream s(json_);
+ BaseReaderHandler<> h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseFullPrecisionFlag>(s, h));
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterative_DummyHandler)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ StringStream s(json_);
+ BaseReaderHandler<> h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseIterativeFlag>(s, h));
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativeInsitu_DummyHandler)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ memcpy(temp_, json_, length_ + 1);
+ InsituStringStream s(temp_);
+ BaseReaderHandler<> h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseIterativeFlag|kParseInsituFlag>(s, h));
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_ValidateEncoding)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ StringStream s(json_);
+ BaseReaderHandler<> h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseValidateEncodingFlag>(s, h));
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseInsitu_MemoryPoolAllocator)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ memcpy(temp_, json_, length_ + 1);
+ Document doc;
+ doc.ParseInsitu(temp_);
+ ASSERT_TRUE(doc.IsObject());
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseIterativeInsitu_MemoryPoolAllocator)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ memcpy(temp_, json_, length_ + 1);
+ Document doc;
+ doc.ParseInsitu<kParseIterativeFlag>(temp_);
+ ASSERT_TRUE(doc.IsObject());
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(DocumentParse_MemoryPoolAllocator)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ Document doc;
+ doc.Parse(json_);
+ ASSERT_TRUE(doc.IsObject());
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseLength_MemoryPoolAllocator)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ Document doc;
+ doc.Parse(json_, length_);
+ ASSERT_TRUE(doc.IsObject());
+ }
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseStdString_MemoryPoolAllocator)) {
+ const std::string s(json_, length_);
+ for (size_t i = 0; i < kTrialCount; i++) {
+ Document doc;
+ doc.Parse(s);
+ ASSERT_TRUE(doc.IsObject());
+ }
+}
+#endif
+
+TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseIterative_MemoryPoolAllocator)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ Document doc;
+ doc.Parse<kParseIterativeFlag>(json_);
+ ASSERT_TRUE(doc.IsObject());
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(DocumentParse_CrtAllocator)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ memcpy(temp_, json_, length_ + 1);
+ GenericDocument<UTF8<>, CrtAllocator> doc;
+ doc.Parse(temp_);
+ ASSERT_TRUE(doc.IsObject());
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseEncodedInputStream_MemoryStream)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ MemoryStream ms(json_, length_);
+ EncodedInputStream<UTF8<>, MemoryStream> is(ms);
+ Document doc;
+ doc.ParseStream<0, UTF8<> >(is);
+ ASSERT_TRUE(doc.IsObject());
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseAutoUTFInputStream_MemoryStream)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ MemoryStream ms(json_, length_);
+ AutoUTFInputStream<unsigned, MemoryStream> is(ms);
+ Document doc;
+ doc.ParseStream<0, AutoUTF<unsigned> >(is);
+ ASSERT_TRUE(doc.IsObject());
+ }
+}
+
+template<typename T>
+size_t Traverse(const T& value) {
+ size_t count = 1;
+ switch(value.GetType()) {
+ case kObjectType:
+ for (typename T::ConstMemberIterator itr = value.MemberBegin(); itr != value.MemberEnd(); ++itr) {
+ count++; // name
+ count += Traverse(itr->value);
+ }
+ break;
+
+ case kArrayType:
+ for (typename T::ConstValueIterator itr = value.Begin(); itr != value.End(); ++itr)
+ count += Traverse(*itr);
+ break;
+
+ default:
+ // Do nothing.
+ break;
+ }
+ return count;
+}
+
+TEST_F(RapidJson, DocumentTraverse) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ size_t count = Traverse(doc_);
+ EXPECT_EQ(4339u, count);
+ //if (i == 0)
+ // std::cout << count << std::endl;
+ }
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+struct ValueCounter : public BaseReaderHandler<> {
+ ValueCounter() : count_(1) {} // root
+
+ bool EndObject(SizeType memberCount) { count_ += memberCount * 2; return true; }
+ bool EndArray(SizeType elementCount) { count_ += elementCount; return true; }
+
+ SizeType count_;
+};
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+TEST_F(RapidJson, DocumentAccept) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ ValueCounter counter;
+ doc_.Accept(counter);
+ EXPECT_EQ(4339u, counter.count_);
+ }
+}
+
+struct NullStream {
+ typedef char Ch;
+
+ NullStream() /*: length_(0)*/ {}
+ void Put(Ch) { /*++length_;*/ }
+ void Flush() {}
+ //size_t length_;
+};
+
+TEST_F(RapidJson, Writer_NullStream) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ NullStream s;
+ Writer<NullStream> writer(s);
+ doc_.Accept(writer);
+ //if (i == 0)
+ // std::cout << s.length_ << std::endl;
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(Writer_StringBuffer)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ StringBuffer s(0, 1024 * 1024);
+ Writer<StringBuffer> writer(s);
+ doc_.Accept(writer);
+ const char* str = s.GetString();
+ (void)str;
+ //if (i == 0)
+ // std::cout << strlen(str) << std::endl;
+ }
+}
+
+#define TEST_TYPED(index, Name)\
+TEST_F(RapidJson, SIMD_SUFFIX(Writer_StringBuffer_##Name)) {\
+ for (size_t i = 0; i < kTrialCount * 10; i++) {\
+ StringBuffer s(0, 1024 * 1024);\
+ Writer<StringBuffer> writer(s);\
+ typesDoc_[index].Accept(writer);\
+ const char* str = s.GetString();\
+ (void)str;\
+ }\
+}
+
+TEST_TYPED(0, Booleans)
+TEST_TYPED(1, Floats)
+TEST_TYPED(2, Guids)
+TEST_TYPED(3, Integers)
+TEST_TYPED(4, Mixed)
+TEST_TYPED(5, Nulls)
+TEST_TYPED(6, Paragraphs)
+
+#undef TEST_TYPED
+
+TEST_F(RapidJson, SIMD_SUFFIX(PrettyWriter_StringBuffer)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ StringBuffer s(0, 2048 * 1024);
+ PrettyWriter<StringBuffer> writer(s);
+ writer.SetIndent(' ', 1);
+ doc_.Accept(writer);
+ const char* str = s.GetString();
+ (void)str;
+ //if (i == 0)
+ // std::cout << strlen(str) << std::endl;
+ }
+}
+
+TEST_F(RapidJson, internal_Pow10) {
+ double sum = 0;
+ for (size_t i = 0; i < kTrialCount * kTrialCount; i++)
+ sum += internal::Pow10(int(i & 255));
+ EXPECT_GT(sum, 0.0);
+}
+
+TEST_F(RapidJson, SkipWhitespace_Basic) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ rapidjson::StringStream s(whitespace_);
+ while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t')
+ s.Take();
+ ASSERT_EQ('[', s.Peek());
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(SkipWhitespace)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ rapidjson::StringStream s(whitespace_);
+ rapidjson::SkipWhitespace(s);
+ ASSERT_EQ('[', s.Peek());
+ }
+}
+
+TEST_F(RapidJson, SkipWhitespace_strspn) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ const char* s = whitespace_ + std::strspn(whitespace_, " \t\r\n");
+ ASSERT_EQ('[', *s);
+ }
+}
+
+TEST_F(RapidJson, UTF8_Validate) {
+ NullStream os;
+
+ for (size_t i = 0; i < kTrialCount; i++) {
+ StringStream is(json_);
+ bool result = true;
+ while (is.Peek() != '\0')
+ result &= UTF8<>::Validate(is, os);
+ EXPECT_TRUE(result);
+ }
+}
+
+TEST_F(RapidJson, FileReadStream) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ FILE *fp = fopen(filename_, "rb");
+ char buffer[65536];
+ FileReadStream s(fp, buffer, sizeof(buffer));
+ while (s.Take() != '\0')
+ ;
+ fclose(fp);
+ }
+}
+
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_FileReadStream)) {
+ for (size_t i = 0; i < kTrialCount; i++) {
+ FILE *fp = fopen(filename_, "rb");
+ char buffer[65536];
+ FileReadStream s(fp, buffer, sizeof(buffer));
+ BaseReaderHandler<> h;
+ Reader reader;
+ reader.Parse(s, h);
+ fclose(fp);
+ }
+}
+
+TEST_F(RapidJson, StringBuffer) {
+ StringBuffer sb;
+ for (int i = 0; i < 32 * 1024 * 1024; i++)
+ sb.Put(i & 0x7f);
+}
+
+#endif // TEST_RAPIDJSON
diff --git a/src/rapidjson/test/perftest/schematest.cpp b/src/rapidjson/test/perftest/schematest.cpp
new file mode 100644
index 000000000..468f5fe6f
--- /dev/null
+++ b/src/rapidjson/test/perftest/schematest.cpp
@@ -0,0 +1,216 @@
+#include "perftest.h"
+
+#if TEST_RAPIDJSON
+
+#include "rapidjson/schema.h"
+#include <ctime>
+#include <string>
+#include <vector>
+
+#define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0])
+
+using namespace rapidjson;
+
+template <typename Allocator>
+static char* ReadFile(const char* filename, Allocator& allocator) {
+ const char *paths[] = {
+ "",
+ "bin/",
+ "../bin/",
+ "../../bin/",
+ "../../../bin/"
+ };
+ char buffer[1024];
+ FILE *fp = 0;
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s%s", paths[i], filename);
+ fp = fopen(buffer, "rb");
+ if (fp)
+ break;
+ }
+
+ if (!fp)
+ return 0;
+
+ fseek(fp, 0, SEEK_END);
+ size_t length = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ char* json = reinterpret_cast<char*>(allocator.Malloc(length + 1));
+ size_t readLength = fread(json, 1, length, fp);
+ json[readLength] = '\0';
+ fclose(fp);
+ return json;
+}
+
+class Schema : public PerfTest {
+public:
+ Schema() {}
+
+ virtual void SetUp() {
+ PerfTest::SetUp();
+
+ const char* filenames[] = {
+ "additionalItems.json",
+ "additionalProperties.json",
+ "allOf.json",
+ "anyOf.json",
+ "default.json",
+ "definitions.json",
+ "dependencies.json",
+ "enum.json",
+ "items.json",
+ "maximum.json",
+ "maxItems.json",
+ "maxLength.json",
+ "maxProperties.json",
+ "minimum.json",
+ "minItems.json",
+ "minLength.json",
+ "minProperties.json",
+ "multipleOf.json",
+ "not.json",
+ "oneOf.json",
+ "pattern.json",
+ "patternProperties.json",
+ "properties.json",
+ "ref.json",
+ "refRemote.json",
+ "required.json",
+ "type.json",
+ "uniqueItems.json"
+ };
+
+ char jsonBuffer[65536];
+ MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer));
+
+ for (size_t i = 0; i < ARRAY_SIZE(filenames); i++) {
+ char filename[FILENAME_MAX];
+ sprintf(filename, "jsonschema/tests/draft4/%s", filenames[i]);
+ char* json = ReadFile(filename, jsonAllocator);
+ if (!json) {
+ printf("json test suite file %s not found", filename);
+ return;
+ }
+
+ Document d;
+ d.Parse(json);
+ if (d.HasParseError()) {
+ printf("json test suite file %s has parse error", filename);
+ return;
+ }
+
+ for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) {
+ std::string schemaDescription = (*schemaItr)["description"].GetString();
+ if (IsExcludeTestSuite(schemaDescription))
+ continue;
+
+ TestSuite* ts = new TestSuite;
+ ts->schema = new SchemaDocument((*schemaItr)["schema"]);
+
+ const Value& tests = (*schemaItr)["tests"];
+ for (Value::ConstValueIterator testItr = tests.Begin(); testItr != tests.End(); ++testItr) {
+ if (IsExcludeTest(schemaDescription + ", " + (*testItr)["description"].GetString()))
+ continue;
+
+ Document* d2 = new Document;
+ d2->CopyFrom((*testItr)["data"], d2->GetAllocator());
+ ts->tests.push_back(d2);
+ }
+ testSuites.push_back(ts);
+ }
+ }
+ }
+
+ virtual void TearDown() {
+ PerfTest::TearDown();
+ for (TestSuiteList::const_iterator itr = testSuites.begin(); itr != testSuites.end(); ++itr)
+ delete *itr;
+ testSuites.clear();
+ }
+
+private:
+ // Using the same exclusion in https://github.com/json-schema/JSON-Schema-Test-Suite
+ static bool IsExcludeTestSuite(const std::string& description) {
+ const char* excludeTestSuites[] = {
+ //lost failing these tests
+ "remote ref",
+ "remote ref, containing refs itself",
+ "fragment within remote ref",
+ "ref within remote ref",
+ "change resolution scope",
+ // these below were added to get jsck in the benchmarks)
+ "uniqueItems validation",
+ "valid definition",
+ "invalid definition"
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(excludeTestSuites); i++)
+ if (excludeTestSuites[i] == description)
+ return true;
+ return false;
+ }
+
+ // Using the same exclusion in https://github.com/json-schema/JSON-Schema-Test-Suite
+ static bool IsExcludeTest(const std::string& description) {
+ const char* excludeTests[] = {
+ //lots of validators fail these
+ "invalid definition, invalid definition schema",
+ "maxLength validation, two supplementary Unicode code points is long enough",
+ "minLength validation, one supplementary Unicode code point is not long enough",
+ //this is to get tv4 in the benchmarks
+ "heterogeneous enum validation, something else is invalid"
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(excludeTests); i++)
+ if (excludeTests[i] == description)
+ return true;
+ return false;
+ }
+
+ Schema(const Schema&);
+ Schema& operator=(const Schema&);
+
+protected:
+ typedef std::vector<Document*> DocumentList;
+
+ struct TestSuite {
+ TestSuite() : schema() {}
+ ~TestSuite() {
+ delete schema;
+ for (DocumentList::iterator itr = tests.begin(); itr != tests.end(); ++itr)
+ delete *itr;
+ }
+ SchemaDocument* schema;
+ DocumentList tests;
+ };
+
+ typedef std::vector<TestSuite* > TestSuiteList;
+ TestSuiteList testSuites;
+};
+
+TEST_F(Schema, TestSuite) {
+ char validatorBuffer[65536];
+ MemoryPoolAllocator<> validatorAllocator(validatorBuffer, sizeof(validatorBuffer));
+
+ const int trialCount = 100000;
+ int testCount = 0;
+ clock_t start = clock();
+ for (int i = 0; i < trialCount; i++) {
+ for (TestSuiteList::const_iterator itr = testSuites.begin(); itr != testSuites.end(); ++itr) {
+ const TestSuite& ts = **itr;
+ GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > validator(*ts.schema, &validatorAllocator);
+ for (DocumentList::const_iterator testItr = ts.tests.begin(); testItr != ts.tests.end(); ++testItr) {
+ validator.Reset();
+ (*testItr)->Accept(validator);
+ testCount++;
+ }
+ validatorAllocator.Clear();
+ }
+ }
+ clock_t end = clock();
+ double duration = double(end - start) / CLOCKS_PER_SEC;
+ printf("%d trials in %f s -> %f trials per sec\n", trialCount, duration, trialCount / duration);
+ printf("%d tests per trial\n", testCount / trialCount);
+}
+
+#endif
diff --git a/src/rapidjson/test/unittest/CMakeLists.txt b/src/rapidjson/test/unittest/CMakeLists.txt
new file mode 100644
index 000000000..b3204d6c8
--- /dev/null
+++ b/src/rapidjson/test/unittest/CMakeLists.txt
@@ -0,0 +1,92 @@
+include(CheckCXXCompilerFlag)
+
+set(UNITTEST_SOURCES
+ allocatorstest.cpp
+ bigintegertest.cpp
+ documenttest.cpp
+ dtoatest.cpp
+ encodedstreamtest.cpp
+ encodingstest.cpp
+ fwdtest.cpp
+ filestreamtest.cpp
+ itoatest.cpp
+ istreamwrappertest.cpp
+ jsoncheckertest.cpp
+ namespacetest.cpp
+ pointertest.cpp
+ prettywritertest.cpp
+ ostreamwrappertest.cpp
+ readertest.cpp
+ regextest.cpp
+ schematest.cpp
+ simdtest.cpp
+ strfunctest.cpp
+ stringbuffertest.cpp
+ strtodtest.cpp
+ unittest.cpp
+ valuetest.cpp
+ writertest.cpp)
+
+find_program(CCACHE_FOUND ccache)
+if(CCACHE_FOUND)
+ set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
+ set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
+ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -fcolor-diagnostics")
+ endif()
+endif(CCACHE_FOUND)
+
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal")
+elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal -Wimplicit-fallthrough -Weverything")
+ # If the user is running a newer version of Clang that includes the
+ # -Wdouble-promotion, we will ignore that warning.
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.7)
+ CHECK_CXX_COMPILER_FLAG("-Wno-double-promotion" HAS_NO_DOUBLE_PROMOTION)
+ if (HAS_NO_DOUBLE_PROMOTION)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-double-promotion")
+ endif()
+ endif()
+elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+ # Force to always compile with /W4
+ if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
+ string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
+ endif()
+
+ # Force to always compile with /WX
+ if(CMAKE_CXX_FLAGS MATCHES "/WX-")
+ string(REGEX REPLACE "/WX-" "/WX" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX")
+ endif()
+endif()
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRAPIDJSON_HAS_STDSTRING=1")
+
+add_library(namespacetest STATIC namespacetest.cpp)
+
+add_executable(unittest ${UNITTEST_SOURCES})
+target_link_libraries(unittest ${TEST_LIBRARIES} namespacetest)
+
+add_dependencies(tests unittest)
+
+add_test(NAME unittest
+ COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
+
+if(NOT MSVC)
+ # Not running SIMD.* unit test cases for Valgrind
+ add_test(NAME valgrind_unittest
+ COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest --gtest_filter=-SIMD.*
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
+
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ add_test(NAME symbol_check
+ COMMAND sh -c "objdump -t -C libnamespacetest.a | grep rapidjson ; test $? -ne 0"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
+
+endif(NOT MSVC)
diff --git a/src/rapidjson/test/unittest/allocatorstest.cpp b/src/rapidjson/test/unittest/allocatorstest.cpp
new file mode 100644
index 000000000..a5958de19
--- /dev/null
+++ b/src/rapidjson/test/unittest/allocatorstest.cpp
@@ -0,0 +1,102 @@
+// 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. All rights reserved.
+//
+// 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/allocators.h"
+
+using namespace rapidjson;
+
+template <typename Allocator>
+void TestAllocator(Allocator& a) {
+ EXPECT_TRUE(a.Malloc(0) == 0);
+
+ uint8_t* p = static_cast<uint8_t*>(a.Malloc(100));
+ EXPECT_TRUE(p != 0);
+ for (size_t i = 0; i < 100; i++)
+ p[i] = static_cast<uint8_t>(i);
+
+ // Expand
+ uint8_t* q = static_cast<uint8_t*>(a.Realloc(p, 100, 200));
+ EXPECT_TRUE(q != 0);
+ for (size_t i = 0; i < 100; i++)
+ EXPECT_EQ(i, q[i]);
+ for (size_t i = 100; i < 200; i++)
+ q[i] = static_cast<uint8_t>(i);
+
+ // Shrink
+ uint8_t *r = static_cast<uint8_t*>(a.Realloc(q, 200, 150));
+ EXPECT_TRUE(r != 0);
+ for (size_t i = 0; i < 150; i++)
+ EXPECT_EQ(i, r[i]);
+
+ Allocator::Free(r);
+
+ // Realloc to zero size
+ EXPECT_TRUE(a.Realloc(a.Malloc(1), 1, 0) == 0);
+}
+
+TEST(Allocator, CrtAllocator) {
+ CrtAllocator a;
+ TestAllocator(a);
+}
+
+TEST(Allocator, MemoryPoolAllocator) {
+ MemoryPoolAllocator<> a;
+ TestAllocator(a);
+
+ for (size_t i = 1; i < 1000; i++) {
+ EXPECT_TRUE(a.Malloc(i) != 0);
+ EXPECT_LE(a.Size(), a.Capacity());
+ }
+}
+
+TEST(Allocator, Alignment) {
+#if RAPIDJSON_64BIT == 1
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000000), RAPIDJSON_ALIGN(0));
+ for (uint64_t i = 1; i < 8; i++) {
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000008), RAPIDJSON_ALIGN(i));
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000010), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0x00000000, 0x00000008) + i));
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000001, 0x00000000), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0x00000000, 0xFFFFFFF8) + i));
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF8), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0) + i));
+ }
+#else
+ EXPECT_EQ(0u, RAPIDJSON_ALIGN(0u));
+ for (uint32_t i = 1; i < 4; i++) {
+ EXPECT_EQ(4u, RAPIDJSON_ALIGN(i));
+ EXPECT_EQ(8u, RAPIDJSON_ALIGN(4u + i));
+ EXPECT_EQ(0xFFFFFFF8u, RAPIDJSON_ALIGN(0xFFFFFFF4u + i));
+ EXPECT_EQ(0xFFFFFFFCu, RAPIDJSON_ALIGN(0xFFFFFFF8u + i));
+ }
+#endif
+}
+
+TEST(Allocator, Issue399) {
+ MemoryPoolAllocator<> a;
+ void* p = a.Malloc(100);
+ void* q = a.Realloc(p, 100, 200);
+ EXPECT_EQ(p, q);
+
+ // exhuasive testing
+ for (size_t j = 1; j < 32; j++) {
+ a.Clear();
+ a.Malloc(j); // some unaligned size
+ p = a.Malloc(1);
+ for (size_t i = 1; i < 1024; i++) {
+ q = a.Realloc(p, i, i + 1);
+ EXPECT_EQ(p, q);
+ p = q;
+ }
+ }
+}
diff --git a/src/rapidjson/test/unittest/bigintegertest.cpp b/src/rapidjson/test/unittest/bigintegertest.cpp
new file mode 100644
index 000000000..a68e14446
--- /dev/null
+++ b/src/rapidjson/test/unittest/bigintegertest.cpp
@@ -0,0 +1,133 @@
+// 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. All rights reserved.
+//
+// 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/internal/biginteger.h"
+
+using namespace rapidjson::internal;
+
+#define BIGINTEGER_LITERAL(s) BigInteger(s, sizeof(s) - 1)
+
+static const BigInteger kZero(0);
+static const BigInteger kOne(1);
+static const BigInteger kUint64Max = BIGINTEGER_LITERAL("18446744073709551615");
+static const BigInteger kTwo64 = BIGINTEGER_LITERAL("18446744073709551616");
+
+TEST(BigInteger, Constructor) {
+ EXPECT_TRUE(kZero.IsZero());
+ EXPECT_TRUE(kZero == kZero);
+ EXPECT_TRUE(kZero == BIGINTEGER_LITERAL("0"));
+ EXPECT_TRUE(kZero == BIGINTEGER_LITERAL("00"));
+
+ const BigInteger a(123);
+ EXPECT_TRUE(a == a);
+ EXPECT_TRUE(a == BIGINTEGER_LITERAL("123"));
+ EXPECT_TRUE(a == BIGINTEGER_LITERAL("0123"));
+
+ EXPECT_EQ(2u, kTwo64.GetCount());
+ EXPECT_EQ(0u, kTwo64.GetDigit(0));
+ EXPECT_EQ(1u, kTwo64.GetDigit(1));
+}
+
+TEST(BigInteger, AddUint64) {
+ BigInteger a = kZero;
+ a += 0u;
+ EXPECT_TRUE(kZero == a);
+
+ a += 1u;
+ EXPECT_TRUE(kOne == a);
+
+ a += 1u;
+ EXPECT_TRUE(BigInteger(2) == a);
+
+ EXPECT_TRUE(BigInteger(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)) == kUint64Max);
+ BigInteger b = kUint64Max;
+ b += 1u;
+ EXPECT_TRUE(kTwo64 == b);
+ b += RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("36893488147419103231") == b);
+}
+
+TEST(BigInteger, MultiplyUint64) {
+ BigInteger a = kZero;
+ a *= static_cast <uint64_t>(0);
+ EXPECT_TRUE(kZero == a);
+ a *= static_cast <uint64_t>(123);
+ EXPECT_TRUE(kZero == a);
+
+ BigInteger b = kOne;
+ b *= static_cast<uint64_t>(1);
+ EXPECT_TRUE(kOne == b);
+ b *= static_cast<uint64_t>(0);
+ EXPECT_TRUE(kZero == b);
+
+ BigInteger c(123);
+ c *= static_cast<uint64_t>(456u);
+ EXPECT_TRUE(BigInteger(123u * 456u) == c);
+ c *= RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("1034640981606221330982120") == c);
+ c *= RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("19085757395861596536664473018420572782123800") == c);
+}
+
+TEST(BigInteger, MultiplyUint32) {
+ BigInteger a = kZero;
+ a *= static_cast <uint32_t>(0);
+ EXPECT_TRUE(kZero == a);
+ a *= static_cast <uint32_t>(123);
+ EXPECT_TRUE(kZero == a);
+
+ BigInteger b = kOne;
+ b *= static_cast<uint32_t>(1);
+ EXPECT_TRUE(kOne == b);
+ b *= static_cast<uint32_t>(0);
+ EXPECT_TRUE(kZero == b);
+
+ BigInteger c(123);
+ c *= static_cast<uint32_t>(456u);
+ EXPECT_TRUE(BigInteger(123u * 456u) == c);
+ c *= 0xFFFFFFFFu;
+ EXPECT_TRUE(BIGINTEGER_LITERAL("240896125641960") == c);
+ c *= 0xFFFFFFFFu;
+ EXPECT_TRUE(BIGINTEGER_LITERAL("1034640981124429079698200") == c);
+}
+
+TEST(BigInteger, LeftShift) {
+ BigInteger a = kZero;
+ a <<= 1;
+ EXPECT_TRUE(kZero == a);
+ a <<= 64;
+ EXPECT_TRUE(kZero == a);
+
+ a = BigInteger(123);
+ a <<= 0;
+ EXPECT_TRUE(BigInteger(123) == a);
+ a <<= 1;
+ EXPECT_TRUE(BigInteger(246) == a);
+ a <<= 64;
+ EXPECT_TRUE(BIGINTEGER_LITERAL("4537899042132549697536") == a);
+ a <<= 99;
+ EXPECT_TRUE(BIGINTEGER_LITERAL("2876235222267216943024851750785644982682875244576768") == a);
+}
+
+TEST(BigInteger, Compare) {
+ EXPECT_EQ(0, kZero.Compare(kZero));
+ EXPECT_EQ(1, kOne.Compare(kZero));
+ EXPECT_EQ(-1, kZero.Compare(kOne));
+ EXPECT_EQ(0, kUint64Max.Compare(kUint64Max));
+ EXPECT_EQ(0, kTwo64.Compare(kTwo64));
+ EXPECT_EQ(-1, kUint64Max.Compare(kTwo64));
+ EXPECT_EQ(1, kTwo64.Compare(kUint64Max));
+}
diff --git a/src/rapidjson/test/unittest/documenttest.cpp b/src/rapidjson/test/unittest/documenttest.cpp
new file mode 100644
index 000000000..ecd4b79bc
--- /dev/null
+++ b/src/rapidjson/test/unittest/documenttest.cpp
@@ -0,0 +1,652 @@
+// 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. All rights reserved.
+//
+// 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/document.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/stringbuffer.h"
+#include <sstream>
+#include <algorithm>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+RAPIDJSON_DIAG_OFF(missing-variable-declarations)
+#endif
+
+using namespace rapidjson;
+
+template <typename DocumentType>
+void ParseCheck(DocumentType& doc) {
+ typedef typename DocumentType::ValueType ValueType;
+
+ EXPECT_FALSE(doc.HasParseError());
+ if (doc.HasParseError())
+ printf("Error: %d at %zu\n", static_cast<int>(doc.GetParseError()), doc.GetErrorOffset());
+ EXPECT_TRUE(static_cast<ParseResult>(doc));
+
+ EXPECT_TRUE(doc.IsObject());
+
+ EXPECT_TRUE(doc.HasMember("hello"));
+ const ValueType& hello = doc["hello"];
+ EXPECT_TRUE(hello.IsString());
+ EXPECT_STREQ("world", hello.GetString());
+
+ EXPECT_TRUE(doc.HasMember("t"));
+ const ValueType& t = doc["t"];
+ EXPECT_TRUE(t.IsTrue());
+
+ EXPECT_TRUE(doc.HasMember("f"));
+ const ValueType& f = doc["f"];
+ EXPECT_TRUE(f.IsFalse());
+
+ EXPECT_TRUE(doc.HasMember("n"));
+ const ValueType& n = doc["n"];
+ EXPECT_TRUE(n.IsNull());
+
+ EXPECT_TRUE(doc.HasMember("i"));
+ const ValueType& i = doc["i"];
+ EXPECT_TRUE(i.IsNumber());
+ EXPECT_EQ(123, i.GetInt());
+
+ EXPECT_TRUE(doc.HasMember("pi"));
+ const ValueType& pi = doc["pi"];
+ EXPECT_TRUE(pi.IsNumber());
+ EXPECT_DOUBLE_EQ(3.1416, pi.GetDouble());
+
+ EXPECT_TRUE(doc.HasMember("a"));
+ const ValueType& a = doc["a"];
+ EXPECT_TRUE(a.IsArray());
+ EXPECT_EQ(4u, a.Size());
+ for (SizeType j = 0; j < 4; j++)
+ EXPECT_EQ(j + 1, a[j].GetUint());
+}
+
+template <typename Allocator, typename StackAllocator>
+void ParseTest() {
+ typedef GenericDocument<UTF8<>, Allocator, StackAllocator> DocumentType;
+ DocumentType doc;
+
+ const char* json = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
+
+ doc.Parse(json);
+ ParseCheck(doc);
+
+ doc.SetNull();
+ StringStream s(json);
+ doc.template ParseStream<0>(s);
+ ParseCheck(doc);
+
+ doc.SetNull();
+ char *buffer = strdup(json);
+ doc.ParseInsitu(buffer);
+ ParseCheck(doc);
+ free(buffer);
+
+ // Parse(const Ch*, size_t)
+ size_t length = strlen(json);
+ buffer = reinterpret_cast<char*>(malloc(length * 2));
+ memcpy(buffer, json, length);
+ memset(buffer + length, 'X', length);
+#if RAPIDJSON_HAS_STDSTRING
+ std::string s2(buffer, length); // backup buffer
+#endif
+ doc.SetNull();
+ doc.Parse(buffer, length);
+ free(buffer);
+ ParseCheck(doc);
+
+#if RAPIDJSON_HAS_STDSTRING
+ // Parse(std::string)
+ doc.SetNull();
+ doc.Parse(s2);
+ ParseCheck(doc);
+#endif
+}
+
+TEST(Document, Parse) {
+ ParseTest<MemoryPoolAllocator<>, CrtAllocator>();
+ ParseTest<MemoryPoolAllocator<>, MemoryPoolAllocator<> >();
+ ParseTest<CrtAllocator, MemoryPoolAllocator<> >();
+ ParseTest<CrtAllocator, CrtAllocator>();
+}
+
+TEST(Document, UnchangedOnParseError) {
+ Document doc;
+ doc.SetArray().PushBack(0, doc.GetAllocator());
+
+ ParseResult err = doc.Parse("{]");
+ EXPECT_TRUE(doc.HasParseError());
+ EXPECT_EQ(err.Code(), doc.GetParseError());
+ EXPECT_EQ(err.Offset(), doc.GetErrorOffset());
+ EXPECT_TRUE(doc.IsArray());
+ EXPECT_EQ(doc.Size(), 1u);
+
+ err = doc.Parse("{}");
+ EXPECT_FALSE(doc.HasParseError());
+ EXPECT_FALSE(err.IsError());
+ EXPECT_EQ(err.Code(), doc.GetParseError());
+ EXPECT_EQ(err.Offset(), doc.GetErrorOffset());
+ EXPECT_TRUE(doc.IsObject());
+ EXPECT_EQ(doc.MemberCount(), 0u);
+}
+
+static FILE* OpenEncodedFile(const char* filename) {
+ const char *paths[] = {
+ "encodings",
+ "bin/encodings",
+ "../bin/encodings",
+ "../../bin/encodings",
+ "../../../bin/encodings"
+ };
+ char buffer[1024];
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s/%s", paths[i], filename);
+ FILE *fp = fopen(buffer, "rb");
+ if (fp)
+ return fp;
+ }
+ return 0;
+}
+
+TEST(Document, Parse_Encoding) {
+ const char* json = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
+
+ typedef GenericDocument<UTF16<> > DocumentType;
+ DocumentType doc;
+
+ // Parse<unsigned, SourceEncoding>(const SourceEncoding::Ch*)
+ // doc.Parse<kParseDefaultFlags, UTF8<> >(json);
+ // EXPECT_FALSE(doc.HasParseError());
+ // EXPECT_EQ(0, StrCmp(doc[L"hello"].GetString(), L"world"));
+
+ // Parse<unsigned, SourceEncoding>(const SourceEncoding::Ch*, size_t)
+ size_t length = strlen(json);
+ char* buffer = reinterpret_cast<char*>(malloc(length * 2));
+ memcpy(buffer, json, length);
+ memset(buffer + length, 'X', length);
+#if RAPIDJSON_HAS_STDSTRING
+ std::string s2(buffer, length); // backup buffer
+#endif
+ doc.SetNull();
+ doc.Parse<kParseDefaultFlags, UTF8<> >(buffer, length);
+ free(buffer);
+ EXPECT_FALSE(doc.HasParseError());
+ if (doc.HasParseError())
+ printf("Error: %d at %zu\n", static_cast<int>(doc.GetParseError()), doc.GetErrorOffset());
+ EXPECT_EQ(0, StrCmp(doc[L"hello"].GetString(), L"world"));
+
+#if RAPIDJSON_HAS_STDSTRING
+ // Parse<unsigned, SourceEncoding>(std::string)
+ doc.SetNull();
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+ doc.Parse<kParseDefaultFlags, UTF8<> >(s2.c_str()); // VS2010 or below cannot handle templated function overloading. Use const char* instead.
+#else
+ doc.Parse<kParseDefaultFlags, UTF8<> >(s2);
+#endif
+ EXPECT_FALSE(doc.HasParseError());
+ EXPECT_EQ(0, StrCmp(doc[L"hello"].GetString(), L"world"));
+#endif
+}
+
+TEST(Document, ParseStream_EncodedInputStream) {
+ // UTF8 -> UTF16
+ FILE* fp = OpenEncodedFile("utf8.json");
+ char buffer[256];
+ FileReadStream bis(fp, buffer, sizeof(buffer));
+ EncodedInputStream<UTF8<>, FileReadStream> eis(bis);
+
+ GenericDocument<UTF16<> > d;
+ d.ParseStream<0, UTF8<> >(eis);
+ EXPECT_FALSE(d.HasParseError());
+
+ fclose(fp);
+
+ wchar_t expected[] = L"I can eat glass and it doesn't hurt me.";
+ GenericValue<UTF16<> >& v = d[L"en"];
+ EXPECT_TRUE(v.IsString());
+ EXPECT_EQ(sizeof(expected) / sizeof(wchar_t) - 1, v.GetStringLength());
+ EXPECT_EQ(0, StrCmp(expected, v.GetString()));
+
+ // UTF16 -> UTF8 in memory
+ StringBuffer bos;
+ typedef EncodedOutputStream<UTF8<>, StringBuffer> OutputStream;
+ OutputStream eos(bos, false); // Not writing BOM
+ {
+ Writer<OutputStream, UTF16<>, UTF8<> > writer(eos);
+ d.Accept(writer);
+ }
+
+ // Condense the original file and compare.
+ fp = OpenEncodedFile("utf8.json");
+ FileReadStream is(fp, buffer, sizeof(buffer));
+ Reader reader;
+ StringBuffer bos2;
+ Writer<StringBuffer> writer2(bos2);
+ reader.Parse(is, writer2);
+ fclose(fp);
+
+ EXPECT_EQ(bos.GetSize(), bos2.GetSize());
+ EXPECT_EQ(0, memcmp(bos.GetString(), bos2.GetString(), bos2.GetSize()));
+}
+
+TEST(Document, ParseStream_AutoUTFInputStream) {
+ // Any -> UTF8
+ FILE* fp = OpenEncodedFile("utf32be.json");
+ char buffer[256];
+ FileReadStream bis(fp, buffer, sizeof(buffer));
+ AutoUTFInputStream<unsigned, FileReadStream> eis(bis);
+
+ Document d;
+ d.ParseStream<0, AutoUTF<unsigned> >(eis);
+ EXPECT_FALSE(d.HasParseError());
+
+ fclose(fp);
+
+ char expected[] = "I can eat glass and it doesn't hurt me.";
+ Value& v = d["en"];
+ EXPECT_TRUE(v.IsString());
+ EXPECT_EQ(sizeof(expected) - 1, v.GetStringLength());
+ EXPECT_EQ(0, StrCmp(expected, v.GetString()));
+
+ // UTF8 -> UTF8 in memory
+ StringBuffer bos;
+ Writer<StringBuffer> writer(bos);
+ d.Accept(writer);
+
+ // Condense the original file and compare.
+ fp = OpenEncodedFile("utf8.json");
+ FileReadStream is(fp, buffer, sizeof(buffer));
+ Reader reader;
+ StringBuffer bos2;
+ Writer<StringBuffer> writer2(bos2);
+ reader.Parse(is, writer2);
+ fclose(fp);
+
+ EXPECT_EQ(bos.GetSize(), bos2.GetSize());
+ EXPECT_EQ(0, memcmp(bos.GetString(), bos2.GetString(), bos2.GetSize()));
+}
+
+TEST(Document, Swap) {
+ Document d1;
+ Document::AllocatorType& a = d1.GetAllocator();
+
+ d1.SetArray().PushBack(1, a).PushBack(2, a);
+
+ Value o;
+ o.SetObject().AddMember("a", 1, a);
+
+ // Swap between Document and Value
+ // d1.Swap(o); // doesn't compile
+ o.Swap(d1);
+ EXPECT_TRUE(d1.IsObject());
+ EXPECT_TRUE(o.IsArray());
+
+ // Swap between Document and Document
+ Document d2;
+ d2.SetArray().PushBack(3, a);
+ d1.Swap(d2);
+ EXPECT_TRUE(d1.IsArray());
+ EXPECT_TRUE(d2.IsObject());
+ EXPECT_EQ(&d2.GetAllocator(), &a);
+
+ // reset value
+ Value().Swap(d1);
+ EXPECT_TRUE(d1.IsNull());
+
+ // reset document, including allocator
+ Document().Swap(d2);
+ EXPECT_TRUE(d2.IsNull());
+ EXPECT_NE(&d2.GetAllocator(), &a);
+
+ // testing std::swap compatibility
+ d1.SetBool(true);
+ using std::swap;
+ swap(d1, d2);
+ EXPECT_TRUE(d1.IsNull());
+ EXPECT_TRUE(d2.IsTrue());
+
+ swap(o, d2);
+ EXPECT_TRUE(o.IsTrue());
+ EXPECT_TRUE(d2.IsArray());
+}
+
+
+// This should be slow due to assignment in inner-loop.
+struct OutputStringStream : public std::ostringstream {
+ typedef char Ch;
+
+ virtual ~OutputStringStream();
+
+ void Put(char c) {
+ put(c);
+ }
+ void Flush() {}
+};
+
+OutputStringStream::~OutputStringStream() {}
+
+TEST(Document, AcceptWriter) {
+ Document doc;
+ doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
+
+ OutputStringStream os;
+ Writer<OutputStringStream> writer(os);
+ doc.Accept(writer);
+
+ EXPECT_EQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,4]}", os.str());
+}
+
+TEST(Document, UserBuffer) {
+ typedef GenericDocument<UTF8<>, MemoryPoolAllocator<>, MemoryPoolAllocator<> > DocumentType;
+ char valueBuffer[4096];
+ char parseBuffer[1024];
+ MemoryPoolAllocator<> valueAllocator(valueBuffer, sizeof(valueBuffer));
+ MemoryPoolAllocator<> parseAllocator(parseBuffer, sizeof(parseBuffer));
+ DocumentType doc(&valueAllocator, sizeof(parseBuffer) / 2, &parseAllocator);
+ doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
+ EXPECT_FALSE(doc.HasParseError());
+ EXPECT_LE(valueAllocator.Size(), sizeof(valueBuffer));
+ EXPECT_LE(parseAllocator.Size(), sizeof(parseBuffer));
+
+ // Cover MemoryPoolAllocator::Capacity()
+ EXPECT_LE(valueAllocator.Size(), valueAllocator.Capacity());
+ EXPECT_LE(parseAllocator.Size(), parseAllocator.Capacity());
+}
+
+// Issue 226: Value of string type should not point to NULL
+TEST(Document, AssertAcceptInvalidNameType) {
+ Document doc;
+ doc.SetObject();
+ doc.AddMember("a", 0, doc.GetAllocator());
+ doc.FindMember("a")->name.SetNull(); // Change name to non-string type.
+
+ OutputStringStream os;
+ Writer<OutputStringStream> writer(os);
+ ASSERT_THROW(doc.Accept(writer), AssertException);
+}
+
+// Issue 44: SetStringRaw doesn't work with wchar_t
+TEST(Document, UTF16_Document) {
+ GenericDocument< UTF16<> > json;
+ json.Parse<kParseValidateEncodingFlag>(L"[{\"created_at\":\"Wed Oct 30 17:13:20 +0000 2012\"}]");
+
+ ASSERT_TRUE(json.IsArray());
+ GenericValue< UTF16<> >& v = json[0];
+ ASSERT_TRUE(v.IsObject());
+
+ GenericValue< UTF16<> >& s = v[L"created_at"];
+ ASSERT_TRUE(s.IsString());
+
+ EXPECT_EQ(0, memcmp(L"Wed Oct 30 17:13:20 +0000 2012", s.GetString(), (s.GetStringLength() + 1) * sizeof(wchar_t)));
+}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#if 0 // Many old compiler does not support these. Turn it off temporaily.
+
+#include <type_traits>
+
+TEST(Document, Traits) {
+ static_assert(std::is_constructible<Document>::value, "");
+ static_assert(std::is_default_constructible<Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_constructible<Document>::value, "");
+#endif
+ static_assert(std::is_move_constructible<Document>::value, "");
+
+ static_assert(!std::is_nothrow_constructible<Document>::value, "");
+ static_assert(!std::is_nothrow_default_constructible<Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_nothrow_copy_constructible<Document>::value, "");
+ static_assert(std::is_nothrow_move_constructible<Document>::value, "");
+#endif
+
+ static_assert(std::is_assignable<Document,Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_assignable<Document>::value, "");
+#endif
+ static_assert(std::is_move_assignable<Document>::value, "");
+
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_assignable<Document, Document>::value, "");
+#endif
+ static_assert(!std::is_nothrow_copy_assignable<Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_move_assignable<Document>::value, "");
+#endif
+
+ static_assert( std::is_destructible<Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_destructible<Document>::value, "");
+#endif
+}
+
+#endif
+
+template <typename Allocator>
+struct DocumentMove: public ::testing::Test {
+};
+
+typedef ::testing::Types< CrtAllocator, MemoryPoolAllocator<> > MoveAllocatorTypes;
+TYPED_TEST_CASE(DocumentMove, MoveAllocatorTypes);
+
+TYPED_TEST(DocumentMove, MoveConstructor) {
+ typedef TypeParam Allocator;
+ typedef GenericDocument<UTF8<>, Allocator> D;
+ Allocator allocator;
+
+ D a(&allocator);
+ a.Parse("[\"one\", \"two\", \"three\"]");
+ EXPECT_FALSE(a.HasParseError());
+ EXPECT_TRUE(a.IsArray());
+ EXPECT_EQ(3u, a.Size());
+ EXPECT_EQ(&a.GetAllocator(), &allocator);
+
+ // Document b(a); // does not compile (!is_copy_constructible)
+ D b(std::move(a));
+ EXPECT_TRUE(a.IsNull());
+ EXPECT_TRUE(b.IsArray());
+ EXPECT_EQ(3u, b.Size());
+ EXPECT_THROW(a.GetAllocator(), AssertException);
+ EXPECT_EQ(&b.GetAllocator(), &allocator);
+
+ b.Parse("{\"Foo\": \"Bar\", \"Baz\": 42}");
+ EXPECT_FALSE(b.HasParseError());
+ EXPECT_TRUE(b.IsObject());
+ EXPECT_EQ(2u, b.MemberCount());
+
+ // Document c = a; // does not compile (!is_copy_constructible)
+ D c = std::move(b);
+ EXPECT_TRUE(b.IsNull());
+ EXPECT_TRUE(c.IsObject());
+ EXPECT_EQ(2u, c.MemberCount());
+ EXPECT_THROW(b.GetAllocator(), AssertException);
+ EXPECT_EQ(&c.GetAllocator(), &allocator);
+}
+
+TYPED_TEST(DocumentMove, MoveConstructorParseError) {
+ typedef TypeParam Allocator;
+ typedef GenericDocument<UTF8<>, Allocator> D;
+
+ ParseResult noError;
+ D a;
+ a.Parse("{ 4 = 4]");
+ ParseResult error(a.GetParseError(), a.GetErrorOffset());
+ EXPECT_TRUE(a.HasParseError());
+ EXPECT_NE(error.Code(), noError.Code());
+ EXPECT_NE(error.Offset(), noError.Offset());
+
+ D b(std::move(a));
+ EXPECT_FALSE(a.HasParseError());
+ EXPECT_TRUE(b.HasParseError());
+ EXPECT_EQ(a.GetParseError(), noError.Code());
+ EXPECT_EQ(b.GetParseError(), error.Code());
+ EXPECT_EQ(a.GetErrorOffset(), noError.Offset());
+ EXPECT_EQ(b.GetErrorOffset(), error.Offset());
+
+ D c(std::move(b));
+ EXPECT_FALSE(b.HasParseError());
+ EXPECT_TRUE(c.HasParseError());
+ EXPECT_EQ(b.GetParseError(), noError.Code());
+ EXPECT_EQ(c.GetParseError(), error.Code());
+ EXPECT_EQ(b.GetErrorOffset(), noError.Offset());
+ EXPECT_EQ(c.GetErrorOffset(), error.Offset());
+}
+
+// This test does not properly use parsing, just for testing.
+// It must call ClearStack() explicitly to prevent memory leak.
+// But here we cannot as ClearStack() is private.
+#if 0
+TYPED_TEST(DocumentMove, MoveConstructorStack) {
+ typedef TypeParam Allocator;
+ typedef UTF8<> Encoding;
+ typedef GenericDocument<Encoding, Allocator> Document;
+
+ Document a;
+ size_t defaultCapacity = a.GetStackCapacity();
+
+ // Trick Document into getting GetStackCapacity() to return non-zero
+ typedef GenericReader<Encoding, Encoding, Allocator> Reader;
+ Reader reader(&a.GetAllocator());
+ GenericStringStream<Encoding> is("[\"one\", \"two\", \"three\"]");
+ reader.template Parse<kParseDefaultFlags>(is, a);
+ size_t capacity = a.GetStackCapacity();
+ EXPECT_GT(capacity, 0u);
+
+ Document b(std::move(a));
+ EXPECT_EQ(a.GetStackCapacity(), defaultCapacity);
+ EXPECT_EQ(b.GetStackCapacity(), capacity);
+
+ Document c = std::move(b);
+ EXPECT_EQ(b.GetStackCapacity(), defaultCapacity);
+ EXPECT_EQ(c.GetStackCapacity(), capacity);
+}
+#endif
+
+TYPED_TEST(DocumentMove, MoveAssignment) {
+ typedef TypeParam Allocator;
+ typedef GenericDocument<UTF8<>, Allocator> D;
+ Allocator allocator;
+
+ D a(&allocator);
+ a.Parse("[\"one\", \"two\", \"three\"]");
+ EXPECT_FALSE(a.HasParseError());
+ EXPECT_TRUE(a.IsArray());
+ EXPECT_EQ(3u, a.Size());
+ EXPECT_EQ(&a.GetAllocator(), &allocator);
+
+ // Document b; b = a; // does not compile (!is_copy_assignable)
+ D b;
+ b = std::move(a);
+ EXPECT_TRUE(a.IsNull());
+ EXPECT_TRUE(b.IsArray());
+ EXPECT_EQ(3u, b.Size());
+ EXPECT_THROW(a.GetAllocator(), AssertException);
+ EXPECT_EQ(&b.GetAllocator(), &allocator);
+
+ b.Parse("{\"Foo\": \"Bar\", \"Baz\": 42}");
+ EXPECT_FALSE(b.HasParseError());
+ EXPECT_TRUE(b.IsObject());
+ EXPECT_EQ(2u, b.MemberCount());
+
+ // Document c; c = a; // does not compile (see static_assert)
+ D c;
+ c = std::move(b);
+ EXPECT_TRUE(b.IsNull());
+ EXPECT_TRUE(c.IsObject());
+ EXPECT_EQ(2u, c.MemberCount());
+ EXPECT_THROW(b.GetAllocator(), AssertException);
+ EXPECT_EQ(&c.GetAllocator(), &allocator);
+}
+
+TYPED_TEST(DocumentMove, MoveAssignmentParseError) {
+ typedef TypeParam Allocator;
+ typedef GenericDocument<UTF8<>, Allocator> D;
+
+ ParseResult noError;
+ D a;
+ a.Parse("{ 4 = 4]");
+ ParseResult error(a.GetParseError(), a.GetErrorOffset());
+ EXPECT_TRUE(a.HasParseError());
+ EXPECT_NE(error.Code(), noError.Code());
+ EXPECT_NE(error.Offset(), noError.Offset());
+
+ D b;
+ b = std::move(a);
+ EXPECT_FALSE(a.HasParseError());
+ EXPECT_TRUE(b.HasParseError());
+ EXPECT_EQ(a.GetParseError(), noError.Code());
+ EXPECT_EQ(b.GetParseError(), error.Code());
+ EXPECT_EQ(a.GetErrorOffset(), noError.Offset());
+ EXPECT_EQ(b.GetErrorOffset(), error.Offset());
+
+ D c;
+ c = std::move(b);
+ EXPECT_FALSE(b.HasParseError());
+ EXPECT_TRUE(c.HasParseError());
+ EXPECT_EQ(b.GetParseError(), noError.Code());
+ EXPECT_EQ(c.GetParseError(), error.Code());
+ EXPECT_EQ(b.GetErrorOffset(), noError.Offset());
+ EXPECT_EQ(c.GetErrorOffset(), error.Offset());
+}
+
+// This test does not properly use parsing, just for testing.
+// It must call ClearStack() explicitly to prevent memory leak.
+// But here we cannot as ClearStack() is private.
+#if 0
+TYPED_TEST(DocumentMove, MoveAssignmentStack) {
+ typedef TypeParam Allocator;
+ typedef UTF8<> Encoding;
+ typedef GenericDocument<Encoding, Allocator> D;
+
+ D a;
+ size_t defaultCapacity = a.GetStackCapacity();
+
+ // Trick Document into getting GetStackCapacity() to return non-zero
+ typedef GenericReader<Encoding, Encoding, Allocator> Reader;
+ Reader reader(&a.GetAllocator());
+ GenericStringStream<Encoding> is("[\"one\", \"two\", \"three\"]");
+ reader.template Parse<kParseDefaultFlags>(is, a);
+ size_t capacity = a.GetStackCapacity();
+ EXPECT_GT(capacity, 0u);
+
+ D b;
+ b = std::move(a);
+ EXPECT_EQ(a.GetStackCapacity(), defaultCapacity);
+ EXPECT_EQ(b.GetStackCapacity(), capacity);
+
+ D c;
+ c = std::move(b);
+ EXPECT_EQ(b.GetStackCapacity(), defaultCapacity);
+ EXPECT_EQ(c.GetStackCapacity(), capacity);
+}
+#endif
+
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+// Issue 22: Memory corruption via operator=
+// Fixed by making unimplemented assignment operator private.
+//TEST(Document, Assignment) {
+// Document d1;
+// Document d2;
+// d1 = d2;
+//}
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/dtoatest.cpp b/src/rapidjson/test/unittest/dtoatest.cpp
new file mode 100644
index 000000000..afd76eb09
--- /dev/null
+++ b/src/rapidjson/test/unittest/dtoatest.cpp
@@ -0,0 +1,98 @@
+// 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. All rights reserved.
+//
+// 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/internal/dtoa.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(type-limits)
+#endif
+
+using namespace rapidjson::internal;
+
+TEST(dtoa, normal) {
+ char buffer[30];
+
+#define TEST_DTOA(d, a)\
+ *dtoa(d, buffer) = '\0';\
+ EXPECT_STREQ(a, buffer)
+
+ TEST_DTOA(0.0, "0.0");
+ TEST_DTOA(-0.0, "-0.0");
+ TEST_DTOA(1.0, "1.0");
+ TEST_DTOA(-1.0, "-1.0");
+ TEST_DTOA(1.2345, "1.2345");
+ TEST_DTOA(1.2345678, "1.2345678");
+ TEST_DTOA(0.123456789012, "0.123456789012");
+ TEST_DTOA(1234567.8, "1234567.8");
+ TEST_DTOA(-79.39773355813419, "-79.39773355813419");
+ TEST_DTOA(0.000001, "0.000001");
+ TEST_DTOA(0.0000001, "1e-7");
+ TEST_DTOA(1e30, "1e30");
+ TEST_DTOA(1.234567890123456e30, "1.234567890123456e30");
+ TEST_DTOA(5e-324, "5e-324"); // Min subnormal positive double
+ TEST_DTOA(2.225073858507201e-308, "2.225073858507201e-308"); // Max subnormal positive double
+ TEST_DTOA(2.2250738585072014e-308, "2.2250738585072014e-308"); // Min normal positive double
+ TEST_DTOA(1.7976931348623157e308, "1.7976931348623157e308"); // Max double
+
+#undef TEST_DTOA
+}
+
+TEST(dtoa, maxDecimalPlaces) {
+ char buffer[30];
+
+#define TEST_DTOA(m, d, a)\
+ *dtoa(d, buffer, m) = '\0';\
+ EXPECT_STREQ(a, buffer)
+
+ TEST_DTOA(3, 0.0, "0.0");
+ TEST_DTOA(1, 0.0, "0.0");
+ TEST_DTOA(3, -0.0, "-0.0");
+ TEST_DTOA(3, 1.0, "1.0");
+ TEST_DTOA(3, -1.0, "-1.0");
+ TEST_DTOA(3, 1.2345, "1.234");
+ TEST_DTOA(2, 1.2345, "1.23");
+ TEST_DTOA(1, 1.2345, "1.2");
+ TEST_DTOA(3, 1.2345678, "1.234");
+ TEST_DTOA(3, 1.0001, "1.0");
+ TEST_DTOA(2, 1.0001, "1.0");
+ TEST_DTOA(1, 1.0001, "1.0");
+ TEST_DTOA(3, 0.123456789012, "0.123");
+ TEST_DTOA(2, 0.123456789012, "0.12");
+ TEST_DTOA(1, 0.123456789012, "0.1");
+ TEST_DTOA(4, 0.0001, "0.0001");
+ TEST_DTOA(3, 0.0001, "0.0");
+ TEST_DTOA(2, 0.0001, "0.0");
+ TEST_DTOA(1, 0.0001, "0.0");
+ TEST_DTOA(3, 1234567.8, "1234567.8");
+ TEST_DTOA(3, 1e30, "1e30");
+ TEST_DTOA(3, 5e-324, "0.0"); // Min subnormal positive double
+ TEST_DTOA(3, 2.225073858507201e-308, "0.0"); // Max subnormal positive double
+ TEST_DTOA(3, 2.2250738585072014e-308, "0.0"); // Min normal positive double
+ TEST_DTOA(3, 1.7976931348623157e308, "1.7976931348623157e308"); // Max double
+ TEST_DTOA(5, -0.14000000000000001, "-0.14");
+ TEST_DTOA(4, -0.14000000000000001, "-0.14");
+ TEST_DTOA(3, -0.14000000000000001, "-0.14");
+ TEST_DTOA(3, -0.10000000000000001, "-0.1");
+ TEST_DTOA(2, -0.10000000000000001, "-0.1");
+ TEST_DTOA(1, -0.10000000000000001, "-0.1");
+
+#undef TEST_DTOA
+}
+
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/encodedstreamtest.cpp b/src/rapidjson/test/unittest/encodedstreamtest.cpp
new file mode 100644
index 000000000..bc234d3ba
--- /dev/null
+++ b/src/rapidjson/test/unittest/encodedstreamtest.cpp
@@ -0,0 +1,313 @@
+// 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. All rights reserved.
+//
+// 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/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/memorystream.h"
+#include "rapidjson/memorybuffer.h"
+
+using namespace rapidjson;
+
+class EncodedStreamTest : public ::testing::Test {
+public:
+ EncodedStreamTest() : json_(), length_() {}
+ virtual ~EncodedStreamTest();
+
+ virtual void SetUp() {
+ json_ = ReadFile("utf8.json", true, &length_);
+ }
+
+ virtual void TearDown() {
+ free(json_);
+ json_ = 0;
+ }
+
+private:
+ EncodedStreamTest(const EncodedStreamTest&);
+ EncodedStreamTest& operator=(const EncodedStreamTest&);
+
+protected:
+ static FILE* Open(const char* filename) {
+ const char *paths[] = {
+ "encodings",
+ "bin/encodings",
+ "../bin/encodings",
+ "../../bin/encodings",
+ "../../../bin/encodings"
+ };
+ char buffer[1024];
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s/%s", paths[i], filename);
+ FILE *fp = fopen(buffer, "rb");
+ if (fp)
+ return fp;
+ }
+ return 0;
+ }
+
+ static char *ReadFile(const char* filename, bool appendPath, size_t* outLength) {
+ FILE *fp = appendPath ? Open(filename) : fopen(filename, "rb");
+
+ if (!fp) {
+ *outLength = 0;
+ return 0;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ *outLength = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ char* buffer = static_cast<char*>(malloc(*outLength + 1));
+ size_t readLength = fread(buffer, 1, *outLength, fp);
+ buffer[readLength] = '\0';
+ fclose(fp);
+ return buffer;
+ }
+
+ template <typename FileEncoding, typename MemoryEncoding>
+ void TestEncodedInputStream(const char* filename) {
+ // Test FileReadStream
+ {
+ char buffer[16];
+ FILE *fp = Open(filename);
+ ASSERT_TRUE(fp != 0);
+ FileReadStream fs(fp, buffer, sizeof(buffer));
+ EncodedInputStream<FileEncoding, FileReadStream> eis(fs);
+ StringStream s(json_);
+
+ while (eis.Peek() != '\0') {
+ unsigned expected, actual;
+ EXPECT_TRUE(UTF8<>::Decode(s, &expected));
+ EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+ EXPECT_EQ('\0', s.Peek());
+ fclose(fp);
+ }
+
+ // Test MemoryStream
+ {
+ size_t size;
+ char* data = ReadFile(filename, true, &size);
+ MemoryStream ms(data, size);
+ EncodedInputStream<FileEncoding, MemoryStream> eis(ms);
+ StringStream s(json_);
+
+ while (eis.Peek() != '\0') {
+ unsigned expected, actual;
+ EXPECT_TRUE(UTF8<>::Decode(s, &expected));
+ EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+ EXPECT_EQ('\0', s.Peek());
+ free(data);
+ EXPECT_EQ(size, eis.Tell());
+ }
+ }
+
+ void TestAutoUTFInputStream(const char *filename, bool expectHasBOM) {
+ // Test FileReadStream
+ {
+ char buffer[16];
+ FILE *fp = Open(filename);
+ ASSERT_TRUE(fp != 0);
+ FileReadStream fs(fp, buffer, sizeof(buffer));
+ AutoUTFInputStream<unsigned, FileReadStream> eis(fs);
+ EXPECT_EQ(expectHasBOM, eis.HasBOM());
+ StringStream s(json_);
+ while (eis.Peek() != '\0') {
+ unsigned expected, actual;
+ EXPECT_TRUE(UTF8<>::Decode(s, &expected));
+ EXPECT_TRUE(AutoUTF<unsigned>::Decode(eis, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+ EXPECT_EQ('\0', s.Peek());
+ fclose(fp);
+ }
+
+ // Test MemoryStream
+ {
+ size_t size;
+ char* data = ReadFile(filename, true, &size);
+ MemoryStream ms(data, size);
+ AutoUTFInputStream<unsigned, MemoryStream> eis(ms);
+ EXPECT_EQ(expectHasBOM, eis.HasBOM());
+ StringStream s(json_);
+
+ while (eis.Peek() != '\0') {
+ unsigned expected, actual;
+ EXPECT_TRUE(UTF8<>::Decode(s, &expected));
+ EXPECT_TRUE(AutoUTF<unsigned>::Decode(eis, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+ EXPECT_EQ('\0', s.Peek());
+ free(data);
+ EXPECT_EQ(size, eis.Tell());
+ }
+ }
+
+ template <typename FileEncoding, typename MemoryEncoding>
+ void TestEncodedOutputStream(const char* expectedFilename, bool putBOM) {
+ // Test FileWriteStream
+ {
+ char filename[L_tmpnam];
+ FILE* fp = TempFile(filename);
+ char buffer[16];
+ FileWriteStream os(fp, buffer, sizeof(buffer));
+ EncodedOutputStream<FileEncoding, FileWriteStream> eos(os, putBOM);
+ StringStream s(json_);
+ while (s.Peek() != '\0') {
+ bool success = Transcoder<UTF8<>, MemoryEncoding>::Transcode(s, eos);
+ EXPECT_TRUE(success);
+ }
+ eos.Flush();
+ fclose(fp);
+ EXPECT_TRUE(CompareFile(filename, expectedFilename));
+ remove(filename);
+ }
+
+ // Test MemoryBuffer
+ {
+ MemoryBuffer mb;
+ EncodedOutputStream<FileEncoding, MemoryBuffer> eos(mb, putBOM);
+ StringStream s(json_);
+ while (s.Peek() != '\0') {
+ bool success = Transcoder<UTF8<>, MemoryEncoding>::Transcode(s, eos);
+ EXPECT_TRUE(success);
+ }
+ eos.Flush();
+ EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename));
+ }
+ }
+
+ void TestAutoUTFOutputStream(UTFType type, bool putBOM, const char *expectedFilename) {
+ // Test FileWriteStream
+ {
+ char filename[L_tmpnam];
+ FILE* fp = TempFile(filename);
+
+ char buffer[16];
+ FileWriteStream os(fp, buffer, sizeof(buffer));
+ AutoUTFOutputStream<unsigned, FileWriteStream> eos(os, type, putBOM);
+ StringStream s(json_);
+ while (s.Peek() != '\0') {
+ bool success = Transcoder<UTF8<>, AutoUTF<unsigned> >::Transcode(s, eos);
+ EXPECT_TRUE(success);
+ }
+ eos.Flush();
+ fclose(fp);
+ EXPECT_TRUE(CompareFile(filename, expectedFilename));
+ remove(filename);
+ }
+
+ // Test MemoryBuffer
+ {
+ MemoryBuffer mb;
+ AutoUTFOutputStream<unsigned, MemoryBuffer> eos(mb, type, putBOM);
+ StringStream s(json_);
+ while (s.Peek() != '\0') {
+ bool success = Transcoder<UTF8<>, AutoUTF<unsigned> >::Transcode(s, eos);
+ EXPECT_TRUE(success);
+ }
+ eos.Flush();
+ EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename));
+ }
+ }
+
+ bool CompareFile(const char* filename, const char* expectedFilename) {
+ size_t actualLength, expectedLength;
+ char* actualBuffer = ReadFile(filename, false, &actualLength);
+ char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength);
+ bool ret = (expectedLength == actualLength) && memcmp(expectedBuffer, actualBuffer, actualLength) == 0;
+ free(actualBuffer);
+ free(expectedBuffer);
+ return ret;
+ }
+
+ bool CompareBufferFile(const char* actualBuffer, size_t actualLength, const char* expectedFilename) {
+ size_t expectedLength;
+ char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength);
+ bool ret = (expectedLength == actualLength) && memcmp(expectedBuffer, actualBuffer, actualLength) == 0;
+ free(expectedBuffer);
+ return ret;
+ }
+
+ char *json_;
+ size_t length_;
+};
+
+EncodedStreamTest::~EncodedStreamTest() {}
+
+TEST_F(EncodedStreamTest, EncodedInputStream) {
+ TestEncodedInputStream<UTF8<>, UTF8<> >("utf8.json");
+ TestEncodedInputStream<UTF8<>, UTF8<> >("utf8bom.json");
+ TestEncodedInputStream<UTF16LE<>, UTF16<> >("utf16le.json");
+ TestEncodedInputStream<UTF16LE<>, UTF16<> >("utf16lebom.json");
+ TestEncodedInputStream<UTF16BE<>, UTF16<> >("utf16be.json");
+ TestEncodedInputStream<UTF16BE<>, UTF16<> >("utf16bebom.json");
+ TestEncodedInputStream<UTF32LE<>, UTF32<> >("utf32le.json");
+ TestEncodedInputStream<UTF32LE<>, UTF32<> >("utf32lebom.json");
+ TestEncodedInputStream<UTF32BE<>, UTF32<> >("utf32be.json");
+ TestEncodedInputStream<UTF32BE<>, UTF32<> >("utf32bebom.json");
+}
+
+TEST_F(EncodedStreamTest, AutoUTFInputStream) {
+ TestAutoUTFInputStream("utf8.json", false);
+ TestAutoUTFInputStream("utf8bom.json", true);
+ TestAutoUTFInputStream("utf16le.json", false);
+ TestAutoUTFInputStream("utf16lebom.json",true);
+ TestAutoUTFInputStream("utf16be.json", false);
+ TestAutoUTFInputStream("utf16bebom.json",true);
+ TestAutoUTFInputStream("utf32le.json", false);
+ TestAutoUTFInputStream("utf32lebom.json",true);
+ TestAutoUTFInputStream("utf32be.json", false);
+ TestAutoUTFInputStream("utf32bebom.json", true);
+
+ {
+ // Auto detection fail, use user defined UTF type
+ const char json[] = "{ }";
+ MemoryStream ms(json, sizeof(json));
+ AutoUTFInputStream<unsigned, MemoryStream> eis(ms, kUTF8);
+ EXPECT_FALSE(eis.HasBOM());
+ EXPECT_EQ(kUTF8, eis.GetType());
+ }
+}
+
+TEST_F(EncodedStreamTest, EncodedOutputStream) {
+ TestEncodedOutputStream<UTF8<>, UTF8<> >("utf8.json", false);
+ TestEncodedOutputStream<UTF8<>, UTF8<> >("utf8bom.json", true);
+ TestEncodedOutputStream<UTF16LE<>, UTF16<> >("utf16le.json", false);
+ TestEncodedOutputStream<UTF16LE<>, UTF16<> >("utf16lebom.json",true);
+ TestEncodedOutputStream<UTF16BE<>, UTF16<> >("utf16be.json", false);
+ TestEncodedOutputStream<UTF16BE<>, UTF16<> >("utf16bebom.json",true);
+ TestEncodedOutputStream<UTF32LE<>, UTF32<> >("utf32le.json", false);
+ TestEncodedOutputStream<UTF32LE<>, UTF32<> >("utf32lebom.json",true);
+ TestEncodedOutputStream<UTF32BE<>, UTF32<> >("utf32be.json", false);
+ TestEncodedOutputStream<UTF32BE<>, UTF32<> >("utf32bebom.json",true);
+}
+
+TEST_F(EncodedStreamTest, AutoUTFOutputStream) {
+ TestAutoUTFOutputStream(kUTF8, false, "utf8.json");
+ TestAutoUTFOutputStream(kUTF8, true, "utf8bom.json");
+ TestAutoUTFOutputStream(kUTF16LE, false, "utf16le.json");
+ TestAutoUTFOutputStream(kUTF16LE, true, "utf16lebom.json");
+ TestAutoUTFOutputStream(kUTF16BE, false, "utf16be.json");
+ TestAutoUTFOutputStream(kUTF16BE, true, "utf16bebom.json");
+ TestAutoUTFOutputStream(kUTF32LE, false, "utf32le.json");
+ TestAutoUTFOutputStream(kUTF32LE, true, "utf32lebom.json");
+ TestAutoUTFOutputStream(kUTF32BE, false, "utf32be.json");
+ TestAutoUTFOutputStream(kUTF32BE, true, "utf32bebom.json");
+}
diff --git a/src/rapidjson/test/unittest/encodingstest.cpp b/src/rapidjson/test/unittest/encodingstest.cpp
new file mode 100644
index 000000000..67b0391ed
--- /dev/null
+++ b/src/rapidjson/test/unittest/encodingstest.cpp
@@ -0,0 +1,451 @@
+// 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. All rights reserved.
+//
+// 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/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/stringbuffer.h"
+
+using namespace rapidjson;
+
+// Verification of encoders/decoders with Hoehrmann's UTF8 decoder
+
+// http://www.unicode.org/Public/UNIDATA/Blocks.txt
+static const unsigned kCodepointRanges[] = {
+ 0x0000, 0x007F, // Basic Latin
+ 0x0080, 0x00FF, // Latin-1 Supplement
+ 0x0100, 0x017F, // Latin Extended-A
+ 0x0180, 0x024F, // Latin Extended-B
+ 0x0250, 0x02AF, // IPA Extensions
+ 0x02B0, 0x02FF, // Spacing Modifier Letters
+ 0x0300, 0x036F, // Combining Diacritical Marks
+ 0x0370, 0x03FF, // Greek and Coptic
+ 0x0400, 0x04FF, // Cyrillic
+ 0x0500, 0x052F, // Cyrillic Supplement
+ 0x0530, 0x058F, // Armenian
+ 0x0590, 0x05FF, // Hebrew
+ 0x0600, 0x06FF, // Arabic
+ 0x0700, 0x074F, // Syriac
+ 0x0750, 0x077F, // Arabic Supplement
+ 0x0780, 0x07BF, // Thaana
+ 0x07C0, 0x07FF, // NKo
+ 0x0800, 0x083F, // Samaritan
+ 0x0840, 0x085F, // Mandaic
+ 0x0900, 0x097F, // Devanagari
+ 0x0980, 0x09FF, // Bengali
+ 0x0A00, 0x0A7F, // Gurmukhi
+ 0x0A80, 0x0AFF, // Gujarati
+ 0x0B00, 0x0B7F, // Oriya
+ 0x0B80, 0x0BFF, // Tamil
+ 0x0C00, 0x0C7F, // Telugu
+ 0x0C80, 0x0CFF, // Kannada
+ 0x0D00, 0x0D7F, // Malayalam
+ 0x0D80, 0x0DFF, // Sinhala
+ 0x0E00, 0x0E7F, // Thai
+ 0x0E80, 0x0EFF, // Lao
+ 0x0F00, 0x0FFF, // Tibetan
+ 0x1000, 0x109F, // Myanmar
+ 0x10A0, 0x10FF, // Georgian
+ 0x1100, 0x11FF, // Hangul Jamo
+ 0x1200, 0x137F, // Ethiopic
+ 0x1380, 0x139F, // Ethiopic Supplement
+ 0x13A0, 0x13FF, // Cherokee
+ 0x1400, 0x167F, // Unified Canadian Aboriginal Syllabics
+ 0x1680, 0x169F, // Ogham
+ 0x16A0, 0x16FF, // Runic
+ 0x1700, 0x171F, // Tagalog
+ 0x1720, 0x173F, // Hanunoo
+ 0x1740, 0x175F, // Buhid
+ 0x1760, 0x177F, // Tagbanwa
+ 0x1780, 0x17FF, // Khmer
+ 0x1800, 0x18AF, // Mongolian
+ 0x18B0, 0x18FF, // Unified Canadian Aboriginal Syllabics Extended
+ 0x1900, 0x194F, // Limbu
+ 0x1950, 0x197F, // Tai Le
+ 0x1980, 0x19DF, // New Tai Lue
+ 0x19E0, 0x19FF, // Khmer Symbols
+ 0x1A00, 0x1A1F, // Buginese
+ 0x1A20, 0x1AAF, // Tai Tham
+ 0x1B00, 0x1B7F, // Balinese
+ 0x1B80, 0x1BBF, // Sundanese
+ 0x1BC0, 0x1BFF, // Batak
+ 0x1C00, 0x1C4F, // Lepcha
+ 0x1C50, 0x1C7F, // Ol Chiki
+ 0x1CD0, 0x1CFF, // Vedic Extensions
+ 0x1D00, 0x1D7F, // Phonetic Extensions
+ 0x1D80, 0x1DBF, // Phonetic Extensions Supplement
+ 0x1DC0, 0x1DFF, // Combining Diacritical Marks Supplement
+ 0x1E00, 0x1EFF, // Latin Extended Additional
+ 0x1F00, 0x1FFF, // Greek Extended
+ 0x2000, 0x206F, // General Punctuation
+ 0x2070, 0x209F, // Superscripts and Subscripts
+ 0x20A0, 0x20CF, // Currency Symbols
+ 0x20D0, 0x20FF, // Combining Diacritical Marks for Symbols
+ 0x2100, 0x214F, // Letterlike Symbols
+ 0x2150, 0x218F, // Number Forms
+ 0x2190, 0x21FF, // Arrows
+ 0x2200, 0x22FF, // Mathematical Operators
+ 0x2300, 0x23FF, // Miscellaneous Technical
+ 0x2400, 0x243F, // Control Pictures
+ 0x2440, 0x245F, // Optical Character Recognition
+ 0x2460, 0x24FF, // Enclosed Alphanumerics
+ 0x2500, 0x257F, // Box Drawing
+ 0x2580, 0x259F, // Block Elements
+ 0x25A0, 0x25FF, // Geometric Shapes
+ 0x2600, 0x26FF, // Miscellaneous Symbols
+ 0x2700, 0x27BF, // Dingbats
+ 0x27C0, 0x27EF, // Miscellaneous Mathematical Symbols-A
+ 0x27F0, 0x27FF, // Supplemental Arrows-A
+ 0x2800, 0x28FF, // Braille Patterns
+ 0x2900, 0x297F, // Supplemental Arrows-B
+ 0x2980, 0x29FF, // Miscellaneous Mathematical Symbols-B
+ 0x2A00, 0x2AFF, // Supplemental Mathematical Operators
+ 0x2B00, 0x2BFF, // Miscellaneous Symbols and Arrows
+ 0x2C00, 0x2C5F, // Glagolitic
+ 0x2C60, 0x2C7F, // Latin Extended-C
+ 0x2C80, 0x2CFF, // Coptic
+ 0x2D00, 0x2D2F, // Georgian Supplement
+ 0x2D30, 0x2D7F, // Tifinagh
+ 0x2D80, 0x2DDF, // Ethiopic Extended
+ 0x2DE0, 0x2DFF, // Cyrillic Extended-A
+ 0x2E00, 0x2E7F, // Supplemental Punctuation
+ 0x2E80, 0x2EFF, // CJK Radicals Supplement
+ 0x2F00, 0x2FDF, // Kangxi Radicals
+ 0x2FF0, 0x2FFF, // Ideographic Description Characters
+ 0x3000, 0x303F, // CJK Symbols and Punctuation
+ 0x3040, 0x309F, // Hiragana
+ 0x30A0, 0x30FF, // Katakana
+ 0x3100, 0x312F, // Bopomofo
+ 0x3130, 0x318F, // Hangul Compatibility Jamo
+ 0x3190, 0x319F, // Kanbun
+ 0x31A0, 0x31BF, // Bopomofo Extended
+ 0x31C0, 0x31EF, // CJK Strokes
+ 0x31F0, 0x31FF, // Katakana Phonetic Extensions
+ 0x3200, 0x32FF, // Enclosed CJK Letters and Months
+ 0x3300, 0x33FF, // CJK Compatibility
+ 0x3400, 0x4DBF, // CJK Unified Ideographs Extension A
+ 0x4DC0, 0x4DFF, // Yijing Hexagram Symbols
+ 0x4E00, 0x9FFF, // CJK Unified Ideographs
+ 0xA000, 0xA48F, // Yi Syllables
+ 0xA490, 0xA4CF, // Yi Radicals
+ 0xA4D0, 0xA4FF, // Lisu
+ 0xA500, 0xA63F, // Vai
+ 0xA640, 0xA69F, // Cyrillic Extended-B
+ 0xA6A0, 0xA6FF, // Bamum
+ 0xA700, 0xA71F, // Modifier Tone Letters
+ 0xA720, 0xA7FF, // Latin Extended-D
+ 0xA800, 0xA82F, // Syloti Nagri
+ 0xA830, 0xA83F, // Common Indic Number Forms
+ 0xA840, 0xA87F, // Phags-pa
+ 0xA880, 0xA8DF, // Saurashtra
+ 0xA8E0, 0xA8FF, // Devanagari Extended
+ 0xA900, 0xA92F, // Kayah Li
+ 0xA930, 0xA95F, // Rejang
+ 0xA960, 0xA97F, // Hangul Jamo Extended-A
+ 0xA980, 0xA9DF, // Javanese
+ 0xAA00, 0xAA5F, // Cham
+ 0xAA60, 0xAA7F, // Myanmar Extended-A
+ 0xAA80, 0xAADF, // Tai Viet
+ 0xAB00, 0xAB2F, // Ethiopic Extended-A
+ 0xABC0, 0xABFF, // Meetei Mayek
+ 0xAC00, 0xD7AF, // Hangul Syllables
+ 0xD7B0, 0xD7FF, // Hangul Jamo Extended-B
+ //0xD800, 0xDB7F, // High Surrogates
+ //0xDB80, 0xDBFF, // High Private Use Surrogates
+ //0xDC00, 0xDFFF, // Low Surrogates
+ 0xE000, 0xF8FF, // Private Use Area
+ 0xF900, 0xFAFF, // CJK Compatibility Ideographs
+ 0xFB00, 0xFB4F, // Alphabetic Presentation Forms
+ 0xFB50, 0xFDFF, // Arabic Presentation Forms-A
+ 0xFE00, 0xFE0F, // Variation Selectors
+ 0xFE10, 0xFE1F, // Vertical Forms
+ 0xFE20, 0xFE2F, // Combining Half Marks
+ 0xFE30, 0xFE4F, // CJK Compatibility Forms
+ 0xFE50, 0xFE6F, // Small Form Variants
+ 0xFE70, 0xFEFF, // Arabic Presentation Forms-B
+ 0xFF00, 0xFFEF, // Halfwidth and Fullwidth Forms
+ 0xFFF0, 0xFFFF, // Specials
+ 0x10000, 0x1007F, // Linear B Syllabary
+ 0x10080, 0x100FF, // Linear B Ideograms
+ 0x10100, 0x1013F, // Aegean Numbers
+ 0x10140, 0x1018F, // Ancient Greek Numbers
+ 0x10190, 0x101CF, // Ancient Symbols
+ 0x101D0, 0x101FF, // Phaistos Disc
+ 0x10280, 0x1029F, // Lycian
+ 0x102A0, 0x102DF, // Carian
+ 0x10300, 0x1032F, // Old Italic
+ 0x10330, 0x1034F, // Gothic
+ 0x10380, 0x1039F, // Ugaritic
+ 0x103A0, 0x103DF, // Old Persian
+ 0x10400, 0x1044F, // Deseret
+ 0x10450, 0x1047F, // Shavian
+ 0x10480, 0x104AF, // Osmanya
+ 0x10800, 0x1083F, // Cypriot Syllabary
+ 0x10840, 0x1085F, // Imperial Aramaic
+ 0x10900, 0x1091F, // Phoenician
+ 0x10920, 0x1093F, // Lydian
+ 0x10A00, 0x10A5F, // Kharoshthi
+ 0x10A60, 0x10A7F, // Old South Arabian
+ 0x10B00, 0x10B3F, // Avestan
+ 0x10B40, 0x10B5F, // Inscriptional Parthian
+ 0x10B60, 0x10B7F, // Inscriptional Pahlavi
+ 0x10C00, 0x10C4F, // Old Turkic
+ 0x10E60, 0x10E7F, // Rumi Numeral Symbols
+ 0x11000, 0x1107F, // Brahmi
+ 0x11080, 0x110CF, // Kaithi
+ 0x12000, 0x123FF, // Cuneiform
+ 0x12400, 0x1247F, // Cuneiform Numbers and Punctuation
+ 0x13000, 0x1342F, // Egyptian Hieroglyphs
+ 0x16800, 0x16A3F, // Bamum Supplement
+ 0x1B000, 0x1B0FF, // Kana Supplement
+ 0x1D000, 0x1D0FF, // Byzantine Musical Symbols
+ 0x1D100, 0x1D1FF, // Musical Symbols
+ 0x1D200, 0x1D24F, // Ancient Greek Musical Notation
+ 0x1D300, 0x1D35F, // Tai Xuan Jing Symbols
+ 0x1D360, 0x1D37F, // Counting Rod Numerals
+ 0x1D400, 0x1D7FF, // Mathematical Alphanumeric Symbols
+ 0x1F000, 0x1F02F, // Mahjong Tiles
+ 0x1F030, 0x1F09F, // Domino Tiles
+ 0x1F0A0, 0x1F0FF, // Playing Cards
+ 0x1F100, 0x1F1FF, // Enclosed Alphanumeric Supplement
+ 0x1F200, 0x1F2FF, // Enclosed Ideographic Supplement
+ 0x1F300, 0x1F5FF, // Miscellaneous Symbols And Pictographs
+ 0x1F600, 0x1F64F, // Emoticons
+ 0x1F680, 0x1F6FF, // Transport And Map Symbols
+ 0x1F700, 0x1F77F, // Alchemical Symbols
+ 0x20000, 0x2A6DF, // CJK Unified Ideographs Extension B
+ 0x2A700, 0x2B73F, // CJK Unified Ideographs Extension C
+ 0x2B740, 0x2B81F, // CJK Unified Ideographs Extension D
+ 0x2F800, 0x2FA1F, // CJK Compatibility Ideographs Supplement
+ 0xE0000, 0xE007F, // Tags
+ 0xE0100, 0xE01EF, // Variation Selectors Supplement
+ 0xF0000, 0xFFFFF, // Supplementary Private Use Area-A
+ 0x100000, 0x10FFFF, // Supplementary Private Use Area-B
+ 0xFFFFFFFF
+};
+
+// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+
+#define UTF8_ACCEPT 0u
+
+static const unsigned char utf8d[] = {
+ // The first part of the table maps bytes to character classes that
+ // to reduce the size of the transition table and create bitmasks.
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+ // The second part is a transition table that maps a combination
+ // of a state of the automaton and a character class to a state.
+ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+ 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+ 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+ 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+ 12,36,12,12,12,12,12,12,12,12,12,12,
+};
+
+static unsigned inline decode(unsigned* state, unsigned* codep, unsigned byte) {
+ unsigned type = utf8d[byte];
+
+ *codep = (*state != UTF8_ACCEPT) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xff >> type) & (byte);
+
+ *state = utf8d[256 + *state + type];
+ return *state;
+}
+
+//static bool IsUTF8(unsigned char* s) {
+// unsigned codepoint, state = 0;
+//
+// while (*s)
+// decode(&state, &codepoint, *s++);
+//
+// return state == UTF8_ACCEPT;
+//}
+
+TEST(EncodingsTest, UTF8) {
+ StringBuffer os, os2;
+ for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) {
+ for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) {
+ os.Clear();
+ UTF8<>::Encode(os, codepoint);
+ const char* encodedStr = os.GetString();
+
+ // Decode with Hoehrmann
+ {
+ unsigned decodedCodepoint = 0;
+ unsigned state = 0;
+
+ unsigned decodedCount = 0;
+ for (const char* s = encodedStr; *s; ++s)
+ if (!decode(&state, &decodedCodepoint, static_cast<unsigned char>(*s))) {
+ EXPECT_EQ(codepoint, decodedCodepoint);
+ decodedCount++;
+ }
+
+ if (*encodedStr) { // This decoder cannot handle U+0000
+ EXPECT_EQ(1u, decodedCount); // Should only contain one code point
+ }
+
+ EXPECT_EQ(UTF8_ACCEPT, state);
+ if (UTF8_ACCEPT != state)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Decode
+ {
+ StringStream is(encodedStr);
+ unsigned decodedCodepoint;
+ bool result = UTF8<>::Decode(is, &decodedCodepoint);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(codepoint, decodedCodepoint);
+ if (!result || codepoint != decodedCodepoint)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Validate
+ {
+ StringStream is(encodedStr);
+ os2.Clear();
+ bool result = UTF8<>::Validate(is, os2);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
+ }
+ }
+ }
+}
+
+TEST(EncodingsTest, UTF16) {
+ GenericStringBuffer<UTF16<> > os, os2;
+ GenericStringBuffer<UTF8<> > utf8os;
+ for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) {
+ for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) {
+ os.Clear();
+ UTF16<>::Encode(os, codepoint);
+ const UTF16<>::Ch* encodedStr = os.GetString();
+
+ // Encode with Hoehrmann's code
+ if (codepoint != 0) // cannot handle U+0000
+ {
+ // encode with UTF8<> first
+ utf8os.Clear();
+ UTF8<>::Encode(utf8os, codepoint);
+
+ // transcode from UTF8 to UTF16 with Hoehrmann's code
+ unsigned decodedCodepoint = 0;
+ unsigned state = 0;
+ UTF16<>::Ch buffer[3], *p = &buffer[0];
+ for (const char* s = utf8os.GetString(); *s; ++s) {
+ if (!decode(&state, &decodedCodepoint, static_cast<unsigned char>(*s)))
+ break;
+ }
+
+ if (codepoint <= 0xFFFF)
+ *p++ = static_cast<UTF16<>::Ch>(decodedCodepoint);
+ else {
+ // Encode code points above U+FFFF as surrogate pair.
+ *p++ = static_cast<UTF16<>::Ch>(0xD7C0 + (decodedCodepoint >> 10));
+ *p++ = static_cast<UTF16<>::Ch>(0xDC00 + (decodedCodepoint & 0x3FF));
+ }
+ *p++ = '\0';
+
+ EXPECT_EQ(0, StrCmp(buffer, encodedStr));
+ }
+
+ // Decode
+ {
+ GenericStringStream<UTF16<> > is(encodedStr);
+ unsigned decodedCodepoint;
+ bool result = UTF16<>::Decode(is, &decodedCodepoint);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(codepoint, decodedCodepoint);
+ if (!result || codepoint != decodedCodepoint)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Validate
+ {
+ GenericStringStream<UTF16<> > is(encodedStr);
+ os2.Clear();
+ bool result = UTF16<>::Validate(is, os2);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
+ }
+ }
+ }
+}
+
+TEST(EncodingsTest, UTF32) {
+ GenericStringBuffer<UTF32<> > os, os2;
+ for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) {
+ for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) {
+ os.Clear();
+ UTF32<>::Encode(os, codepoint);
+ const UTF32<>::Ch* encodedStr = os.GetString();
+
+ // Decode
+ {
+ GenericStringStream<UTF32<> > is(encodedStr);
+ unsigned decodedCodepoint;
+ bool result = UTF32<>::Decode(is, &decodedCodepoint);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(codepoint, decodedCodepoint);
+ if (!result || codepoint != decodedCodepoint)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Validate
+ {
+ GenericStringStream<UTF32<> > is(encodedStr);
+ os2.Clear();
+ bool result = UTF32<>::Validate(is, os2);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
+ }
+ }
+ }
+}
+
+TEST(EncodingsTest, ASCII) {
+ StringBuffer os, os2;
+ for (unsigned codepoint = 0; codepoint < 128; codepoint++) {
+ os.Clear();
+ ASCII<>::Encode(os, codepoint);
+ const ASCII<>::Ch* encodedStr = os.GetString();
+ {
+ StringStream is(encodedStr);
+ unsigned decodedCodepoint;
+ bool result = ASCII<>::Decode(is, &decodedCodepoint);
+ if (!result || codepoint != decodedCodepoint)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Validate
+ {
+ StringStream is(encodedStr);
+ os2.Clear();
+ bool result = ASCII<>::Validate(is, os2);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
+ }
+ }
+}
diff --git a/src/rapidjson/test/unittest/filestreamtest.cpp b/src/rapidjson/test/unittest/filestreamtest.cpp
new file mode 100644
index 000000000..a38133fa7
--- /dev/null
+++ b/src/rapidjson/test/unittest/filestreamtest.cpp
@@ -0,0 +1,112 @@
+// 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. All rights reserved.
+//
+// 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/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/encodedstream.h"
+
+using namespace rapidjson;
+
+class FileStreamTest : public ::testing::Test {
+public:
+ FileStreamTest() : filename_(), json_(), length_() {}
+ virtual ~FileStreamTest();
+
+ virtual void SetUp() {
+ const char *paths[] = {
+ "data/sample.json",
+ "bin/data/sample.json",
+ "../bin/data/sample.json",
+ "../../bin/data/sample.json",
+ "../../../bin/data/sample.json"
+ };
+ FILE* fp = 0;
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ fp = fopen(paths[i], "rb");
+ if (fp) {
+ filename_ = paths[i];
+ break;
+ }
+ }
+ ASSERT_TRUE(fp != 0);
+
+ fseek(fp, 0, SEEK_END);
+ length_ = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ json_ = static_cast<char*>(malloc(length_ + 1));
+ size_t readLength = fread(json_, 1, length_, fp);
+ json_[readLength] = '\0';
+ fclose(fp);
+ }
+
+ virtual void TearDown() {
+ free(json_);
+ json_ = 0;
+ }
+
+private:
+ FileStreamTest(const FileStreamTest&);
+ FileStreamTest& operator=(const FileStreamTest&);
+
+protected:
+ const char* filename_;
+ char *json_;
+ size_t length_;
+};
+
+FileStreamTest::~FileStreamTest() {}
+
+TEST_F(FileStreamTest, FileReadStream) {
+ FILE *fp = fopen(filename_, "rb");
+ ASSERT_TRUE(fp != 0);
+ char buffer[65536];
+ FileReadStream s(fp, buffer, sizeof(buffer));
+
+ for (size_t i = 0; i < length_; i++) {
+ EXPECT_EQ(json_[i], s.Peek());
+ EXPECT_EQ(json_[i], s.Peek()); // 2nd time should be the same
+ EXPECT_EQ(json_[i], s.Take());
+ }
+
+ EXPECT_EQ(length_, s.Tell());
+ EXPECT_EQ('\0', s.Peek());
+
+ fclose(fp);
+}
+
+TEST_F(FileStreamTest, FileWriteStream) {
+ char filename[L_tmpnam];
+ FILE* fp = TempFile(filename);
+
+ char buffer[65536];
+ FileWriteStream os(fp, buffer, sizeof(buffer));
+ for (size_t i = 0; i < length_; i++)
+ os.Put(json_[i]);
+ os.Flush();
+ fclose(fp);
+
+ // Read it back to verify
+ fp = fopen(filename, "rb");
+ FileReadStream is(fp, buffer, sizeof(buffer));
+
+ for (size_t i = 0; i < length_; i++)
+ EXPECT_EQ(json_[i], is.Take());
+
+ EXPECT_EQ(length_, is.Tell());
+ fclose(fp);
+
+ //std::cout << filename << std::endl;
+ remove(filename);
+}
diff --git a/src/rapidjson/test/unittest/fwdtest.cpp b/src/rapidjson/test/unittest/fwdtest.cpp
new file mode 100644
index 000000000..4f3268461
--- /dev/null
+++ b/src/rapidjson/test/unittest/fwdtest.cpp
@@ -0,0 +1,227 @@
+// 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. All rights reserved.
+//
+// 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"
+
+// Using forward declared types here.
+
+#include "rapidjson/fwd.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+using namespace rapidjson;
+
+struct Foo {
+ Foo();
+ ~Foo();
+
+ // encodings.h
+ UTF8<char>* utf8;
+ UTF16<wchar_t>* utf16;
+ UTF16BE<wchar_t>* utf16be;
+ UTF16LE<wchar_t>* utf16le;
+ UTF32<unsigned>* utf32;
+ UTF32BE<unsigned>* utf32be;
+ UTF32LE<unsigned>* utf32le;
+ ASCII<char>* ascii;
+ AutoUTF<unsigned>* autoutf;
+ Transcoder<UTF8<char>, UTF8<char> >* transcoder;
+
+ // allocators.h
+ CrtAllocator* crtallocator;
+ MemoryPoolAllocator<CrtAllocator>* memorypoolallocator;
+
+ // stream.h
+ StringStream* stringstream;
+ InsituStringStream* insitustringstream;
+
+ // stringbuffer.h
+ StringBuffer* stringbuffer;
+
+ // // filereadstream.h
+ // FileReadStream* filereadstream;
+
+ // // filewritestream.h
+ // FileWriteStream* filewritestream;
+
+ // memorybuffer.h
+ MemoryBuffer* memorybuffer;
+
+ // memorystream.h
+ MemoryStream* memorystream;
+
+ // reader.h
+ BaseReaderHandler<UTF8<char>, void>* basereaderhandler;
+ Reader* reader;
+
+ // writer.h
+ Writer<StringBuffer, UTF8<char>, UTF8<char>, CrtAllocator, 0>* writer;
+
+ // prettywriter.h
+ PrettyWriter<StringBuffer, UTF8<char>, UTF8<char>, CrtAllocator, 0>* prettywriter;
+
+ // document.h
+ Value* value;
+ Document* document;
+
+ // pointer.h
+ Pointer* pointer;
+
+ // schema.h
+ SchemaDocument* schemadocument;
+ SchemaValidator* schemavalidator;
+
+ // char buffer[16];
+};
+
+// Using type definitions here.
+
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/memorybuffer.h"
+#include "rapidjson/memorystream.h"
+#include "rapidjson/document.h" // -> reader.h
+#include "rapidjson/writer.h"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/schema.h" // -> pointer.h
+
+Foo::Foo() :
+ // encodings.h
+ utf8(RAPIDJSON_NEW(UTF8<>)),
+ utf16(RAPIDJSON_NEW(UTF16<>)),
+ utf16be(RAPIDJSON_NEW(UTF16BE<>)),
+ utf16le(RAPIDJSON_NEW(UTF16LE<>)),
+ utf32(RAPIDJSON_NEW(UTF32<>)),
+ utf32be(RAPIDJSON_NEW(UTF32BE<>)),
+ utf32le(RAPIDJSON_NEW(UTF32LE<>)),
+ ascii(RAPIDJSON_NEW(ASCII<>)),
+ autoutf(RAPIDJSON_NEW(AutoUTF<unsigned>)),
+ transcoder(RAPIDJSON_NEW((Transcoder<UTF8<>, UTF8<> >))),
+
+ // allocators.h
+ crtallocator(RAPIDJSON_NEW(CrtAllocator)),
+ memorypoolallocator(RAPIDJSON_NEW(MemoryPoolAllocator<>)),
+
+ // stream.h
+ stringstream(RAPIDJSON_NEW(StringStream(0))),
+ insitustringstream(RAPIDJSON_NEW(InsituStringStream(0))),
+
+ // stringbuffer.h
+ stringbuffer(RAPIDJSON_NEW(StringBuffer)),
+
+ // // filereadstream.h
+ // filereadstream(RAPIDJSON_NEW(FileReadStream(stdout, buffer, sizeof(buffer)))),
+
+ // // filewritestream.h
+ // filewritestream(RAPIDJSON_NEW(FileWriteStream(stdout, buffer, sizeof(buffer)))),
+
+ // memorybuffer.h
+ memorybuffer(RAPIDJSON_NEW(MemoryBuffer)),
+
+ // memorystream.h
+ memorystream(RAPIDJSON_NEW(MemoryStream(0, 0))),
+
+ // reader.h
+ basereaderhandler(RAPIDJSON_NEW((BaseReaderHandler<UTF8<>, void>))),
+ reader(RAPIDJSON_NEW(Reader)),
+
+ // writer.h
+ writer(RAPIDJSON_NEW((Writer<StringBuffer>))),
+
+ // prettywriter.h
+ prettywriter(RAPIDJSON_NEW((PrettyWriter<StringBuffer>))),
+
+ // document.h
+ value(RAPIDJSON_NEW(Value)),
+ document(RAPIDJSON_NEW(Document)),
+
+ // pointer.h
+ pointer(RAPIDJSON_NEW(Pointer)),
+
+ // schema.h
+ schemadocument(RAPIDJSON_NEW(SchemaDocument(*document))),
+ schemavalidator(RAPIDJSON_NEW(SchemaValidator(*schemadocument)))
+{
+
+}
+
+Foo::~Foo() {
+ // encodings.h
+ RAPIDJSON_DELETE(utf8);
+ RAPIDJSON_DELETE(utf16);
+ RAPIDJSON_DELETE(utf16be);
+ RAPIDJSON_DELETE(utf16le);
+ RAPIDJSON_DELETE(utf32);
+ RAPIDJSON_DELETE(utf32be);
+ RAPIDJSON_DELETE(utf32le);
+ RAPIDJSON_DELETE(ascii);
+ RAPIDJSON_DELETE(autoutf);
+ RAPIDJSON_DELETE(transcoder);
+
+ // allocators.h
+ RAPIDJSON_DELETE(crtallocator);
+ RAPIDJSON_DELETE(memorypoolallocator);
+
+ // stream.h
+ RAPIDJSON_DELETE(stringstream);
+ RAPIDJSON_DELETE(insitustringstream);
+
+ // stringbuffer.h
+ RAPIDJSON_DELETE(stringbuffer);
+
+ // // filereadstream.h
+ // RAPIDJSON_DELETE(filereadstream);
+
+ // // filewritestream.h
+ // RAPIDJSON_DELETE(filewritestream);
+
+ // memorybuffer.h
+ RAPIDJSON_DELETE(memorybuffer);
+
+ // memorystream.h
+ RAPIDJSON_DELETE(memorystream);
+
+ // reader.h
+ RAPIDJSON_DELETE(basereaderhandler);
+ RAPIDJSON_DELETE(reader);
+
+ // writer.h
+ RAPIDJSON_DELETE(writer);
+
+ // prettywriter.h
+ RAPIDJSON_DELETE(prettywriter);
+
+ // document.h
+ RAPIDJSON_DELETE(value);
+ RAPIDJSON_DELETE(document);
+
+ // pointer.h
+ RAPIDJSON_DELETE(pointer);
+
+ // schema.h
+ RAPIDJSON_DELETE(schemadocument);
+ RAPIDJSON_DELETE(schemavalidator);
+}
+
+TEST(Fwd, Fwd) {
+ Foo f;
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/istreamwrappertest.cpp b/src/rapidjson/test/unittest/istreamwrappertest.cpp
new file mode 100644
index 000000000..9d6fbcff0
--- /dev/null
+++ b/src/rapidjson/test/unittest/istreamwrappertest.cpp
@@ -0,0 +1,181 @@
+// 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. All rights reserved.
+//
+// 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/istreamwrapper.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/document.h"
+#include <sstream>
+#include <fstream>
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
+#endif
+
+using namespace rapidjson;
+using namespace std;
+
+template <typename StringStreamType>
+static void TestStringStream() {
+ typedef typename StringStreamType::char_type Ch;
+
+ {
+ StringStreamType iss;
+ BasicIStreamWrapper<StringStreamType> is(iss);
+ EXPECT_EQ(0, is.Tell());
+ if (sizeof(Ch) == 1) {
+ EXPECT_EQ(0, is.Peek4());
+ EXPECT_EQ(0, is.Tell());
+ }
+ EXPECT_EQ(0, is.Peek());
+ EXPECT_EQ(0, is.Take());
+ EXPECT_EQ(0, is.Tell());
+ }
+
+ {
+ Ch s[] = { 'A', 'B', 'C', '\0' };
+ StringStreamType iss(s);
+ BasicIStreamWrapper<StringStreamType> is(iss);
+ EXPECT_EQ(0, is.Tell());
+ if (sizeof(Ch) == 1) {
+ EXPECT_EQ(0, is.Peek4()); // less than 4 bytes
+ }
+ for (int i = 0; i < 3; i++) {
+ EXPECT_EQ(static_cast<size_t>(i), is.Tell());
+ EXPECT_EQ('A' + i, is.Peek());
+ EXPECT_EQ('A' + i, is.Peek());
+ EXPECT_EQ('A' + i, is.Take());
+ }
+ EXPECT_EQ(3, is.Tell());
+ EXPECT_EQ(0, is.Peek());
+ EXPECT_EQ(0, is.Take());
+ }
+
+ {
+ Ch s[] = { 'A', 'B', 'C', 'D', 'E', '\0' };
+ StringStreamType iss(s);
+ BasicIStreamWrapper<StringStreamType> is(iss);
+ if (sizeof(Ch) == 1) {
+ const Ch* c = is.Peek4();
+ for (int i = 0; i < 4; i++)
+ EXPECT_EQ('A' + i, c[i]);
+ EXPECT_EQ(0, is.Tell());
+ }
+ for (int i = 0; i < 5; i++) {
+ EXPECT_EQ(static_cast<size_t>(i), is.Tell());
+ EXPECT_EQ('A' + i, is.Peek());
+ EXPECT_EQ('A' + i, is.Peek());
+ EXPECT_EQ('A' + i, is.Take());
+ }
+ EXPECT_EQ(5, is.Tell());
+ EXPECT_EQ(0, is.Peek());
+ EXPECT_EQ(0, is.Take());
+ }
+}
+
+TEST(IStreamWrapper, istringstream) {
+ TestStringStream<istringstream>();
+}
+
+TEST(IStreamWrapper, stringstream) {
+ TestStringStream<stringstream>();
+}
+
+TEST(IStreamWrapper, wistringstream) {
+ TestStringStream<wistringstream>();
+}
+
+TEST(IStreamWrapper, wstringstream) {
+ TestStringStream<wstringstream>();
+}
+
+template <typename FileStreamType>
+static bool Open(FileStreamType& fs, const char* filename) {
+ const char *paths[] = {
+ "encodings",
+ "bin/encodings",
+ "../bin/encodings",
+ "../../bin/encodings",
+ "../../../bin/encodings"
+ };
+ char buffer[1024];
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s/%s", paths[i], filename);
+ fs.open(buffer, ios_base::in | ios_base::binary);
+ if (fs.is_open())
+ return true;
+ }
+ return false;
+}
+
+TEST(IStreamWrapper, ifstream) {
+ ifstream ifs;
+ ASSERT_TRUE(Open(ifs, "utf8bom.json"));
+ IStreamWrapper isw(ifs);
+ EncodedInputStream<UTF8<>, IStreamWrapper> eis(isw);
+ Document d;
+ EXPECT_TRUE(!d.ParseStream(eis).HasParseError());
+ EXPECT_TRUE(d.IsObject());
+ EXPECT_EQ(5, d.MemberCount());
+}
+
+TEST(IStreamWrapper, fstream) {
+ fstream fs;
+ ASSERT_TRUE(Open(fs, "utf8bom.json"));
+ IStreamWrapper isw(fs);
+ EncodedInputStream<UTF8<>, IStreamWrapper> eis(isw);
+ Document d;
+ EXPECT_TRUE(!d.ParseStream(eis).HasParseError());
+ EXPECT_TRUE(d.IsObject());
+ EXPECT_EQ(5, d.MemberCount());
+}
+
+// wifstream/wfstream only works on C++11 with codecvt_utf16
+// But many C++11 library still not have it.
+#if 0
+#include <codecvt>
+
+TEST(IStreamWrapper, wifstream) {
+ wifstream ifs;
+ ASSERT_TRUE(Open(ifs, "utf16bebom.json"));
+ ifs.imbue(std::locale(ifs.getloc(),
+ new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
+ WIStreamWrapper isw(ifs);
+ GenericDocument<UTF16<> > d;
+ d.ParseStream<kParseDefaultFlags, UTF16<>, WIStreamWrapper>(isw);
+ EXPECT_TRUE(!d.HasParseError());
+ EXPECT_TRUE(d.IsObject());
+ EXPECT_EQ(5, d.MemberCount());
+}
+
+TEST(IStreamWrapper, wfstream) {
+ wfstream fs;
+ ASSERT_TRUE(Open(fs, "utf16bebom.json"));
+ fs.imbue(std::locale(fs.getloc(),
+ new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
+ WIStreamWrapper isw(fs);
+ GenericDocument<UTF16<> > d;
+ d.ParseStream<kParseDefaultFlags, UTF16<>, WIStreamWrapper>(isw);
+ EXPECT_TRUE(!d.HasParseError());
+ EXPECT_TRUE(d.IsObject());
+ EXPECT_EQ(5, d.MemberCount());
+}
+
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/itoatest.cpp b/src/rapidjson/test/unittest/itoatest.cpp
new file mode 100644
index 000000000..b752a6a26
--- /dev/null
+++ b/src/rapidjson/test/unittest/itoatest.cpp
@@ -0,0 +1,160 @@
+// 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. All rights reserved.
+//
+// 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/internal/itoa.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(type-limits)
+#endif
+
+using namespace rapidjson::internal;
+
+template <typename T>
+struct Traits {
+};
+
+template <>
+struct Traits<uint32_t> {
+ enum { kBufferSize = 11 };
+ enum { kMaxDigit = 10 };
+ static uint32_t Negate(uint32_t x) { return x; }
+};
+
+template <>
+struct Traits<int32_t> {
+ enum { kBufferSize = 12 };
+ enum { kMaxDigit = 10 };
+ static int32_t Negate(int32_t x) { return -x; }
+};
+
+template <>
+struct Traits<uint64_t> {
+ enum { kBufferSize = 21 };
+ enum { kMaxDigit = 20 };
+ static uint64_t Negate(uint64_t x) { return x; }
+};
+
+template <>
+struct Traits<int64_t> {
+ enum { kBufferSize = 22 };
+ enum { kMaxDigit = 20 };
+ static int64_t Negate(int64_t x) { return -x; }
+};
+
+template <typename T>
+static void VerifyValue(T value, void(*f)(T, char*), char* (*g)(T, char*)) {
+ char buffer1[Traits<T>::kBufferSize];
+ char buffer2[Traits<T>::kBufferSize];
+
+ f(value, buffer1);
+ *g(value, buffer2) = '\0';
+
+
+ EXPECT_STREQ(buffer1, buffer2);
+}
+
+template <typename T>
+static void Verify(void(*f)(T, char*), char* (*g)(T, char*)) {
+ // Boundary cases
+ VerifyValue<T>(0, f, g);
+ VerifyValue<T>(std::numeric_limits<T>::min(), f, g);
+ VerifyValue<T>(std::numeric_limits<T>::max(), f, g);
+
+ // 2^n - 1, 2^n, 10^n - 1, 10^n until overflow
+ for (uint32_t power = 2; power <= 10; power += 8) {
+ T i = 1, last;
+ do {
+ VerifyValue<T>(i - 1, f, g);
+ VerifyValue<T>(i, f, g);
+ if (std::numeric_limits<T>::min() < 0) {
+ VerifyValue<T>(Traits<T>::Negate(i), f, g);
+ VerifyValue<T>(Traits<T>::Negate(i + 1), f, g);
+ }
+ last = i;
+ if (i > static_cast<T>(std::numeric_limits<T>::max() / static_cast<T>(power)))
+ break;
+ i *= power;
+ } while (last < i);
+ }
+}
+
+static void u32toa_naive(uint32_t value, char* buffer) {
+ char temp[10];
+ char *p = temp;
+ do {
+ *p++ = static_cast<char>(char(value % 10) + '0');
+ value /= 10;
+ } while (value > 0);
+
+ do {
+ *buffer++ = *--p;
+ } while (p != temp);
+
+ *buffer = '\0';
+}
+
+static void i32toa_naive(int32_t value, char* buffer) {
+ uint32_t u = static_cast<uint32_t>(value);
+ if (value < 0) {
+ *buffer++ = '-';
+ u = ~u + 1;
+ }
+ u32toa_naive(u, buffer);
+}
+
+static void u64toa_naive(uint64_t value, char* buffer) {
+ char temp[20];
+ char *p = temp;
+ do {
+ *p++ = static_cast<char>(char(value % 10) + '0');
+ value /= 10;
+ } while (value > 0);
+
+ do {
+ *buffer++ = *--p;
+ } while (p != temp);
+
+ *buffer = '\0';
+}
+
+static void i64toa_naive(int64_t value, char* buffer) {
+ uint64_t u = static_cast<uint64_t>(value);
+ if (value < 0) {
+ *buffer++ = '-';
+ u = ~u + 1;
+ }
+ u64toa_naive(u, buffer);
+}
+
+TEST(itoa, u32toa) {
+ Verify(u32toa_naive, u32toa);
+}
+
+TEST(itoa, i32toa) {
+ Verify(i32toa_naive, i32toa);
+}
+
+TEST(itoa, u64toa) {
+ Verify(u64toa_naive, u64toa);
+}
+
+TEST(itoa, i64toa) {
+ Verify(i64toa_naive, i64toa);
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/jsoncheckertest.cpp b/src/rapidjson/test/unittest/jsoncheckertest.cpp
new file mode 100644
index 000000000..bea788d26
--- /dev/null
+++ b/src/rapidjson/test/unittest/jsoncheckertest.cpp
@@ -0,0 +1,99 @@
+// 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. All rights reserved.
+//
+// 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/document.h"
+
+using namespace rapidjson;
+
+static char* ReadFile(const char* filename, size_t& length) {
+ const char *paths[] = {
+ "jsonchecker",
+ "bin/jsonchecker",
+ "../bin/jsonchecker",
+ "../../bin/jsonchecker",
+ "../../../bin/jsonchecker"
+ };
+ char buffer[1024];
+ FILE *fp = 0;
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s/%s", paths[i], filename);
+ fp = fopen(buffer, "rb");
+ if (fp)
+ break;
+ }
+
+ if (!fp)
+ return 0;
+
+ fseek(fp, 0, SEEK_END);
+ length = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ char* json = static_cast<char*>(malloc(length + 1));
+ size_t readLength = fread(json, 1, length, fp);
+ json[readLength] = '\0';
+ fclose(fp);
+ return json;
+}
+
+TEST(JsonChecker, Reader) {
+ char filename[256];
+
+ // jsonchecker/failXX.json
+ for (int i = 1; i <= 33; i++) {
+ if (i == 1) // fail1.json is valid in rapidjson, which has no limitation on type of root element (RFC 7159).
+ continue;
+ if (i == 18) // fail18.json is valid in rapidjson, which has no limitation on depth of nesting.
+ continue;
+
+ sprintf(filename, "fail%d.json", i);
+ size_t length;
+ char* json = ReadFile(filename, length);
+ if (!json) {
+ printf("jsonchecker file %s not found", filename);
+ ADD_FAILURE();
+ continue;
+ }
+
+ GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
+ document.Parse(json);
+ EXPECT_TRUE(document.HasParseError());
+
+ document.Parse<kParseIterativeFlag>(json);
+ EXPECT_TRUE(document.HasParseError());
+
+ free(json);
+ }
+
+ // passX.json
+ for (int i = 1; i <= 3; i++) {
+ sprintf(filename, "pass%d.json", i);
+ size_t length;
+ char* json = ReadFile(filename, length);
+ if (!json) {
+ printf("jsonchecker file %s not found", filename);
+ continue;
+ }
+
+ GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
+ document.Parse(json);
+ EXPECT_FALSE(document.HasParseError());
+
+ document.Parse<kParseIterativeFlag>(json);
+ EXPECT_FALSE(document.HasParseError());
+
+ free(json);
+ }
+}
diff --git a/src/rapidjson/test/unittest/namespacetest.cpp b/src/rapidjson/test/unittest/namespacetest.cpp
new file mode 100644
index 000000000..1814724ae
--- /dev/null
+++ b/src/rapidjson/test/unittest/namespacetest.cpp
@@ -0,0 +1,70 @@
+// 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. All rights reserved.
+//
+// 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"
+
+// test another instantiation of RapidJSON in a different namespace
+
+#define RAPIDJSON_NAMESPACE my::rapid::json
+#define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapid { namespace json {
+#define RAPIDJSON_NAMESPACE_END } } }
+
+// include lots of RapidJSON files
+
+#include "rapidjson/document.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/stringbuffer.h"
+
+static const char json[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,4]}";
+
+TEST(NamespaceTest,Using) {
+ using namespace RAPIDJSON_NAMESPACE;
+ typedef GenericDocument<UTF8<>, CrtAllocator> DocumentType;
+ DocumentType doc;
+
+ doc.Parse(json);
+ EXPECT_TRUE(!doc.HasParseError());
+}
+
+TEST(NamespaceTest,Direct) {
+ typedef RAPIDJSON_NAMESPACE::Document Document;
+ typedef RAPIDJSON_NAMESPACE::Reader Reader;
+ typedef RAPIDJSON_NAMESPACE::StringStream StringStream;
+ typedef RAPIDJSON_NAMESPACE::StringBuffer StringBuffer;
+ typedef RAPIDJSON_NAMESPACE::Writer<StringBuffer> WriterType;
+
+ StringStream s(json);
+ StringBuffer buffer;
+ WriterType writer(buffer);
+ buffer.ShrinkToFit();
+ Reader reader;
+ reader.Parse(s, writer);
+
+ EXPECT_STREQ(json, buffer.GetString());
+ EXPECT_EQ(sizeof(json)-1, buffer.GetSize());
+ EXPECT_TRUE(writer.IsComplete());
+
+ Document doc;
+ doc.Parse(buffer.GetString());
+ EXPECT_TRUE(!doc.HasParseError());
+
+ buffer.Clear();
+ writer.Reset(buffer);
+ doc.Accept(writer);
+ EXPECT_STREQ(json, buffer.GetString());
+ EXPECT_TRUE(writer.IsComplete());
+}
diff --git a/src/rapidjson/test/unittest/ostreamwrappertest.cpp b/src/rapidjson/test/unittest/ostreamwrappertest.cpp
new file mode 100644
index 000000000..b1d1cd827
--- /dev/null
+++ b/src/rapidjson/test/unittest/ostreamwrappertest.cpp
@@ -0,0 +1,91 @@
+// 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. All rights reserved.
+//
+// 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/ostreamwrapper.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/document.h"
+#include <sstream>
+#include <fstream>
+
+using namespace rapidjson;
+using namespace std;
+
+template <typename StringStreamType>
+static void TestStringStream() {
+ typedef typename StringStreamType::char_type Ch;
+
+ Ch s[] = { 'A', 'B', 'C', '\0' };
+ StringStreamType oss(s);
+ BasicOStreamWrapper<StringStreamType> os(oss);
+ for (size_t i = 0; i < 3; i++)
+ os.Put(s[i]);
+ os.Flush();
+ for (size_t i = 0; i < 3; i++)
+ EXPECT_EQ(s[i], oss.str()[i]);
+}
+
+TEST(OStreamWrapper, ostringstream) {
+ TestStringStream<ostringstream>();
+}
+
+TEST(OStreamWrapper, stringstream) {
+ TestStringStream<stringstream>();
+}
+
+TEST(OStreamWrapper, wostringstream) {
+ TestStringStream<wostringstream>();
+}
+
+TEST(OStreamWrapper, wstringstream) {
+ TestStringStream<wstringstream>();
+}
+
+TEST(OStreamWrapper, cout) {
+ OStreamWrapper os(cout);
+ const char* s = "Hello World!\n";
+ while (*s)
+ os.Put(*s++);
+ os.Flush();
+}
+
+template <typename FileStreamType>
+static void TestFileStream() {
+ char filename[L_tmpnam];
+ FILE* fp = TempFile(filename);
+ fclose(fp);
+
+ const char* s = "Hello World!\n";
+ {
+ ofstream ofs(filename, ios::out | ios::binary);
+ BasicOStreamWrapper<ofstream> osw(ofs);
+ for (const char* p = s; *p; p++)
+ osw.Put(*p);
+ osw.Flush();
+ }
+
+ fp = fopen(filename, "r");
+ for (const char* p = s; *p; p++)
+ EXPECT_EQ(*p, static_cast<char>(fgetc(fp)));
+ fclose(fp);
+}
+
+TEST(OStreamWrapper, ofstream) {
+ TestFileStream<ofstream>();
+}
+
+TEST(OStreamWrapper, fstream) {
+ TestFileStream<fstream>();
+}
diff --git a/src/rapidjson/test/unittest/pointertest.cpp b/src/rapidjson/test/unittest/pointertest.cpp
new file mode 100644
index 000000000..dbddbedee
--- /dev/null
+++ b/src/rapidjson/test/unittest/pointertest.cpp
@@ -0,0 +1,1524 @@
+// 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. All rights reserved.
+//
+// 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/pointer.h"
+#include "rapidjson/stringbuffer.h"
+#include <sstream>
+
+using namespace rapidjson;
+
+static const char kJson[] = "{\n"
+" \"foo\":[\"bar\", \"baz\"],\n"
+" \"\" : 0,\n"
+" \"a/b\" : 1,\n"
+" \"c%d\" : 2,\n"
+" \"e^f\" : 3,\n"
+" \"g|h\" : 4,\n"
+" \"i\\\\j\" : 5,\n"
+" \"k\\\"l\" : 6,\n"
+" \" \" : 7,\n"
+" \"m~n\" : 8\n"
+"}";
+
+TEST(Pointer, DefaultConstructor) {
+ Pointer p;
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(0u, p.GetTokenCount());
+}
+
+TEST(Pointer, Parse) {
+ {
+ Pointer p("");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(0u, p.GetTokenCount());
+ }
+
+ {
+ Pointer p("/");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ {
+ Pointer p("/foo");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ #if RAPIDJSON_HAS_STDSTRING
+ {
+ Pointer p(std::string("/foo"));
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+ #endif
+
+ {
+ Pointer p("/foo/0");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("0", p.GetTokens()[1].name);
+ EXPECT_EQ(0u, p.GetTokens()[1].index);
+ }
+
+ {
+ // Unescape ~1
+ Pointer p("/a~1b");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("a/b", p.GetTokens()[0].name);
+ }
+
+ {
+ // Unescape ~0
+ Pointer p("/m~0n");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("m~n", p.GetTokens()[0].name);
+ }
+
+ {
+ // empty name
+ Pointer p("/");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ }
+
+ {
+ // empty and non-empty name
+ Pointer p("//a");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("a", p.GetTokens()[1].name);
+ }
+
+ {
+ // Null characters
+ Pointer p("/\0\0", 3);
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(2u, p.GetTokens()[0].length);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[0]);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[1]);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[2]);
+ }
+
+ {
+ // Valid index
+ Pointer p("/123");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("123", p.GetTokens()[0].name);
+ EXPECT_EQ(123u, p.GetTokens()[0].index);
+ }
+
+ {
+ // Invalid index (with leading zero)
+ Pointer p("/01");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("01", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ if (sizeof(SizeType) == 4) {
+ // Invalid index (overflow)
+ Pointer p("/4294967296");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("4294967296", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ {
+ // kPointerParseErrorTokenMustBeginWithSolidus
+ Pointer p(" ");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorTokenMustBeginWithSolidus, p.GetParseErrorCode());
+ EXPECT_EQ(0u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidEscape
+ Pointer p("/~");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidEscape
+ Pointer p("/~2");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+}
+
+TEST(Pointer, Parse_URIFragment) {
+ {
+ Pointer p("#");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(0u, p.GetTokenCount());
+ }
+
+ {
+ Pointer p("#/foo");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ }
+
+ {
+ Pointer p("#/foo/0");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("0", p.GetTokens()[1].name);
+ EXPECT_EQ(0u, p.GetTokens()[1].index);
+ }
+
+ {
+ // Unescape ~1
+ Pointer p("#/a~1b");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("a/b", p.GetTokens()[0].name);
+ }
+
+ {
+ // Unescape ~0
+ Pointer p("#/m~0n");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("m~n", p.GetTokens()[0].name);
+ }
+
+ {
+ // empty name
+ Pointer p("#/");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ }
+
+ {
+ // empty and non-empty name
+ Pointer p("#//a");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("a", p.GetTokens()[1].name);
+ }
+
+ {
+ // Null characters
+ Pointer p("#/%00%00");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(2u, p.GetTokens()[0].length);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[0]);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[1]);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[2]);
+ }
+
+ {
+ // Percentage Escapes
+ EXPECT_STREQ("c%d", Pointer("#/c%25d").GetTokens()[0].name);
+ EXPECT_STREQ("e^f", Pointer("#/e%5Ef").GetTokens()[0].name);
+ EXPECT_STREQ("g|h", Pointer("#/g%7Ch").GetTokens()[0].name);
+ EXPECT_STREQ("i\\j", Pointer("#/i%5Cj").GetTokens()[0].name);
+ EXPECT_STREQ("k\"l", Pointer("#/k%22l").GetTokens()[0].name);
+ EXPECT_STREQ(" ", Pointer("#/%20").GetTokens()[0].name);
+ }
+
+ {
+ // Valid index
+ Pointer p("#/123");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("123", p.GetTokens()[0].name);
+ EXPECT_EQ(123u, p.GetTokens()[0].index);
+ }
+
+ {
+ // Invalid index (with leading zero)
+ Pointer p("#/01");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("01", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ if (sizeof(SizeType) == 4) {
+ // Invalid index (overflow)
+ Pointer p("#/4294967296");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("4294967296", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ {
+ // Decode UTF-8 perecent encoding to UTF-8
+ Pointer p("#/%C2%A2");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("\xC2\xA2", p.GetTokens()[0].name);
+ }
+
+ {
+ // Decode UTF-8 perecent encoding to UTF-16
+ GenericPointer<GenericValue<UTF16<> > > p(L"#/%C2%A2");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(static_cast<UTF16<>::Ch>(0x00A2), p.GetTokens()[0].name[0]);
+ EXPECT_EQ(1u, p.GetTokens()[0].length);
+ }
+
+ {
+ // Decode UTF-8 perecent encoding to UTF-16
+ GenericPointer<GenericValue<UTF16<> > > p(L"#/%E2%82%AC");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(static_cast<UTF16<>::Ch>(0x20AC), p.GetTokens()[0].name[0]);
+ EXPECT_EQ(1u, p.GetTokens()[0].length);
+ }
+
+ {
+ // kPointerParseErrorTokenMustBeginWithSolidus
+ Pointer p("# ");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorTokenMustBeginWithSolidus, p.GetParseErrorCode());
+ EXPECT_EQ(1u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidEscape
+ Pointer p("#/~");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
+ EXPECT_EQ(3u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidEscape
+ Pointer p("#/~2");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
+ EXPECT_EQ(3u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidPercentEncoding
+ Pointer p("#/%");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidPercentEncoding (invalid hex)
+ Pointer p("#/%g0");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidPercentEncoding (invalid hex)
+ Pointer p("#/%0g");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidPercentEncoding (incomplete UTF-8 sequence)
+ Pointer p("#/%C2");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorCharacterMustPercentEncode
+ Pointer p("#/ ");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorCharacterMustPercentEncode, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorCharacterMustPercentEncode
+ Pointer p("#/\n");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorCharacterMustPercentEncode, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+}
+
+TEST(Pointer, Stringify) {
+ // Test by roundtrip
+ const char* sources[] = {
+ "",
+ "/foo",
+ "/foo/0",
+ "/",
+ "/a~1b",
+ "/c%d",
+ "/e^f",
+ "/g|h",
+ "/i\\j",
+ "/k\"l",
+ "/ ",
+ "/m~0n",
+ "/\xC2\xA2",
+ "/\xE2\x82\xAC",
+ "/\xF0\x9D\x84\x9E"
+ };
+
+ for (size_t i = 0; i < sizeof(sources) / sizeof(sources[0]); i++) {
+ Pointer p(sources[i]);
+ StringBuffer s;
+ EXPECT_TRUE(p.Stringify(s));
+ EXPECT_STREQ(sources[i], s.GetString());
+
+ // Stringify to URI fragment
+ StringBuffer s2;
+ EXPECT_TRUE(p.StringifyUriFragment(s2));
+ Pointer p2(s2.GetString(), s2.GetSize());
+ EXPECT_TRUE(p2.IsValid());
+ EXPECT_TRUE(p == p2);
+ }
+
+ {
+ // Strigify to URI fragment with an invalid UTF-8 sequence
+ Pointer p("/\xC2");
+ StringBuffer s;
+ EXPECT_FALSE(p.StringifyUriFragment(s));
+ }
+}
+
+// Construct a Pointer with static tokens, no dynamic allocation involved.
+#define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex }
+#define INDEX(i) { #i, sizeof(#i) - 1, i }
+
+static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(0) }; // equivalent to "/foo/0"
+
+#undef NAME
+#undef INDEX
+
+TEST(Pointer, ConstructorWithToken) {
+ Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("0", p.GetTokens()[1].name);
+ EXPECT_EQ(0u, p.GetTokens()[1].index);
+}
+
+TEST(Pointer, CopyConstructor) {
+ {
+ Pointer p("/foo/0");
+ Pointer q(p);
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+ }
+
+ // Static tokens
+ {
+ Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
+ Pointer q(p);
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+ }
+}
+
+TEST(Pointer, Assignment) {
+ {
+ Pointer p("/foo/0");
+ Pointer q;
+ q = p;
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+ q = q;
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+ }
+
+ // Static tokens
+ {
+ Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
+ Pointer q;
+ q = p;
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+ }
+}
+
+TEST(Pointer, Append) {
+ {
+ Pointer p;
+ Pointer q = p.Append("foo");
+ EXPECT_TRUE(Pointer("/foo") == q);
+ q = q.Append(1234);
+ EXPECT_TRUE(Pointer("/foo/1234") == q);
+ q = q.Append("");
+ EXPECT_TRUE(Pointer("/foo/1234/") == q);
+ }
+
+ {
+ Pointer p;
+ Pointer q = p.Append(Value("foo").Move());
+ EXPECT_TRUE(Pointer("/foo") == q);
+ q = q.Append(Value(1234).Move());
+ EXPECT_TRUE(Pointer("/foo/1234") == q);
+ q = q.Append(Value(kStringType).Move());
+ EXPECT_TRUE(Pointer("/foo/1234/") == q);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ {
+ Pointer p;
+ Pointer q = p.Append(std::string("foo"));
+ EXPECT_TRUE(Pointer("/foo") == q);
+ }
+#endif
+}
+
+TEST(Pointer, Equality) {
+ EXPECT_TRUE(Pointer("/foo/0") == Pointer("/foo/0"));
+ EXPECT_FALSE(Pointer("/foo/0") == Pointer("/foo/1"));
+ EXPECT_FALSE(Pointer("/foo/0") == Pointer("/foo/0/1"));
+ EXPECT_FALSE(Pointer("/foo/0") == Pointer("a"));
+ EXPECT_FALSE(Pointer("a") == Pointer("a")); // Invalid always not equal
+}
+
+TEST(Pointer, Inequality) {
+ EXPECT_FALSE(Pointer("/foo/0") != Pointer("/foo/0"));
+ EXPECT_TRUE(Pointer("/foo/0") != Pointer("/foo/1"));
+ EXPECT_TRUE(Pointer("/foo/0") != Pointer("/foo/0/1"));
+ EXPECT_TRUE(Pointer("/foo/0") != Pointer("a"));
+ EXPECT_TRUE(Pointer("a") != Pointer("a")); // Invalid always not equal
+}
+
+TEST(Pointer, Create) {
+ Document d;
+ {
+ Value* v = &Pointer("").Create(d, d.GetAllocator());
+ EXPECT_EQ(&d, v);
+ }
+ {
+ Value* v = &Pointer("/foo").Create(d, d.GetAllocator());
+ EXPECT_EQ(&d["foo"], v);
+ }
+ {
+ Value* v = &Pointer("/foo/0").Create(d, d.GetAllocator());
+ EXPECT_EQ(&d["foo"][0], v);
+ }
+ {
+ Value* v = &Pointer("/foo/-").Create(d, d.GetAllocator());
+ EXPECT_EQ(&d["foo"][1], v);
+ }
+
+ {
+ Value* v = &Pointer("/foo/-/-").Create(d, d.GetAllocator());
+ // "foo/-" is a newly created null value x.
+ // "foo/-/-" finds that x is not an array, it converts x to empty object
+ // and treats - as "-" member name
+ EXPECT_EQ(&d["foo"][2]["-"], v);
+ }
+
+ {
+ // Document with no allocator
+ Value* v = &Pointer("/foo/-").Create(d);
+ EXPECT_EQ(&d["foo"][3], v);
+ }
+
+ {
+ // Value (not document) must give allocator
+ Value* v = &Pointer("/-").Create(d["foo"], d.GetAllocator());
+ EXPECT_EQ(&d["foo"][4], v);
+ }
+}
+
+TEST(Pointer, Get) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_EQ(&d, Pointer("").Get(d));
+ EXPECT_EQ(&d["foo"], Pointer("/foo").Get(d));
+ EXPECT_EQ(&d["foo"][0], Pointer("/foo/0").Get(d));
+ EXPECT_EQ(&d[""], Pointer("/").Get(d));
+ EXPECT_EQ(&d["a/b"], Pointer("/a~1b").Get(d));
+ EXPECT_EQ(&d["c%d"], Pointer("/c%d").Get(d));
+ EXPECT_EQ(&d["e^f"], Pointer("/e^f").Get(d));
+ EXPECT_EQ(&d["g|h"], Pointer("/g|h").Get(d));
+ EXPECT_EQ(&d["i\\j"], Pointer("/i\\j").Get(d));
+ EXPECT_EQ(&d["k\"l"], Pointer("/k\"l").Get(d));
+ EXPECT_EQ(&d[" "], Pointer("/ ").Get(d));
+ EXPECT_EQ(&d["m~n"], Pointer("/m~0n").Get(d));
+ EXPECT_TRUE(Pointer("/abc").Get(d) == 0);
+ size_t unresolvedTokenIndex;
+ EXPECT_TRUE(Pointer("/foo/2").Get(d, &unresolvedTokenIndex) == 0); // Out of boundary
+ EXPECT_EQ(1, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/a").Get(d, &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a"
+ EXPECT_EQ(1, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/0/0").Get(d, &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/0/a").Get(d, &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2, unresolvedTokenIndex);
+}
+
+TEST(Pointer, GetWithDefault) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ Document::AllocatorType& a = d.GetAllocator();
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == Pointer("/foo/0").GetWithDefault(d, v, a));
+ EXPECT_TRUE(Value("baz") == Pointer("/foo/1").GetWithDefault(d, v, a));
+ EXPECT_TRUE(Value("qux") == Pointer("/foo/2").GetWithDefault(d, v, a));
+ EXPECT_TRUE(Value("last") == Pointer("/foo/-").GetWithDefault(d, Value("last").Move(), a));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(Pointer("/foo/null").GetWithDefault(d, Value().Move(), a).IsNull());
+ EXPECT_TRUE(Pointer("/foo/null").GetWithDefault(d, "x", a).IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, Pointer("/foo/int").GetWithDefault(d, -1, a).GetInt());
+ EXPECT_EQ(-1, Pointer("/foo/int").GetWithDefault(d, -2, a).GetInt());
+ EXPECT_EQ(0x87654321, Pointer("/foo/uint").GetWithDefault(d, 0x87654321, a).GetUint());
+ EXPECT_EQ(0x87654321, Pointer("/foo/uint").GetWithDefault(d, 0x12345678, a).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, Pointer("/foo/int64").GetWithDefault(d, i64, a).GetInt64());
+ EXPECT_EQ(i64, Pointer("/foo/int64").GetWithDefault(d, i64 + 1, a).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, Pointer("/foo/uint64").GetWithDefault(d, u64, a).GetUint64());
+ EXPECT_EQ(u64, Pointer("/foo/uint64").GetWithDefault(d, u64 - 1, a).GetUint64());
+
+ EXPECT_TRUE(Pointer("/foo/true").GetWithDefault(d, true, a).IsTrue());
+ EXPECT_TRUE(Pointer("/foo/true").GetWithDefault(d, false, a).IsTrue());
+
+ EXPECT_TRUE(Pointer("/foo/false").GetWithDefault(d, false, a).IsFalse());
+ EXPECT_TRUE(Pointer("/foo/false").GetWithDefault(d, true, a).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", Pointer("/foo/hello").GetWithDefault(d, "Hello", a).GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", Pointer("/foo/world").GetWithDefault(d, buffer, a).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", Pointer("/foo/C++").GetWithDefault(d, std::string("C++"), a).GetString());
+#endif
+}
+
+TEST(Pointer, GetWithDefault_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == Pointer("/foo/0").GetWithDefault(d, v));
+ EXPECT_TRUE(Value("baz") == Pointer("/foo/1").GetWithDefault(d, v));
+ EXPECT_TRUE(Value("qux") == Pointer("/foo/2").GetWithDefault(d, v));
+ EXPECT_TRUE(Value("last") == Pointer("/foo/-").GetWithDefault(d, Value("last").Move()));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(Pointer("/foo/null").GetWithDefault(d, Value().Move()).IsNull());
+ EXPECT_TRUE(Pointer("/foo/null").GetWithDefault(d, "x").IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, Pointer("/foo/int").GetWithDefault(d, -1).GetInt());
+ EXPECT_EQ(-1, Pointer("/foo/int").GetWithDefault(d, -2).GetInt());
+ EXPECT_EQ(0x87654321, Pointer("/foo/uint").GetWithDefault(d, 0x87654321).GetUint());
+ EXPECT_EQ(0x87654321, Pointer("/foo/uint").GetWithDefault(d, 0x12345678).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, Pointer("/foo/int64").GetWithDefault(d, i64).GetInt64());
+ EXPECT_EQ(i64, Pointer("/foo/int64").GetWithDefault(d, i64 + 1).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, Pointer("/foo/uint64").GetWithDefault(d, u64).GetUint64());
+ EXPECT_EQ(u64, Pointer("/foo/uint64").GetWithDefault(d, u64 - 1).GetUint64());
+
+ EXPECT_TRUE(Pointer("/foo/true").GetWithDefault(d, true).IsTrue());
+ EXPECT_TRUE(Pointer("/foo/true").GetWithDefault(d, false).IsTrue());
+
+ EXPECT_TRUE(Pointer("/foo/false").GetWithDefault(d, false).IsFalse());
+ EXPECT_TRUE(Pointer("/foo/false").GetWithDefault(d, true).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", Pointer("/foo/hello").GetWithDefault(d, "Hello").GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", Pointer("/foo/world").GetWithDefault(d, buffer).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", Pointer("/foo/C++").GetWithDefault(d, std::string("C++")).GetString());
+#endif
+}
+
+TEST(Pointer, Set) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+
+ // Value version
+ Pointer("/foo/0").Set(d, Value(123).Move(), a);
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ Pointer("/foo/-").Set(d, Value(456).Move(), a);
+ EXPECT_EQ(456, d["foo"][2].GetInt());
+
+ Pointer("/foo/null").Set(d, Value().Move(), a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], a);
+ Pointer("/clone").Set(d, foo, a);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ Pointer("/foo/int").Set(d, -1, a);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ Pointer("/foo/uint").Set(d, 0x87654321, a);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ Pointer("/foo/int64").Set(d, i64, a);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ Pointer("/foo/uint64").Set(d, u64, a);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ Pointer("/foo/true").Set(d, true, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ Pointer("/foo/false").Set(d, false, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ Pointer("/foo/hello").Set(d, "Hello", a);
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ Pointer("/foo/world").Set(d, buffer, a);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ Pointer("/foo/c++").Set(d, std::string("C++"), a);
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, Set_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ Pointer("/foo/0").Set(d, Value(123).Move());
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ Pointer("/foo/-").Set(d, Value(456).Move());
+ EXPECT_EQ(456, d["foo"][2].GetInt());
+
+ Pointer("/foo/null").Set(d, Value().Move());
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ Pointer("/clone").Set(d, foo);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ Pointer("/foo/int").Set(d, -1);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ Pointer("/foo/uint").Set(d, 0x87654321);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ Pointer("/foo/int64").Set(d, i64);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ Pointer("/foo/uint64").Set(d, u64);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ Pointer("/foo/true").Set(d, true);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ Pointer("/foo/false").Set(d, false);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ Pointer("/foo/hello").Set(d, "Hello");
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ Pointer("/foo/world").Set(d, buffer);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ Pointer("/foo/c++").Set(d, std::string("C++"));
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, Swap) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+ Pointer("/foo/0").Swap(d, *Pointer("/foo/1").Get(d), a);
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_STREQ("bar", d["foo"][1].GetString());
+}
+
+TEST(Pointer, Swap_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+ Pointer("/foo/0").Swap(d, *Pointer("/foo/1").Get(d));
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_STREQ("bar", d["foo"][1].GetString());
+}
+
+TEST(Pointer, Erase) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_FALSE(Pointer("").Erase(d));
+ EXPECT_FALSE(Pointer("/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/nonexist/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/nonexist/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/0/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/0/nonexist/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/2/nonexist").Erase(d));
+ EXPECT_TRUE(Pointer("/foo/0").Erase(d));
+ EXPECT_EQ(1u, d["foo"].Size());
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_TRUE(Pointer("/foo/0").Erase(d));
+ EXPECT_TRUE(d["foo"].Empty());
+ EXPECT_TRUE(Pointer("/foo").Erase(d));
+ EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
+
+ Pointer("/a/0/b/0").Create(d);
+
+ EXPECT_TRUE(Pointer("/a/0/b/0").Get(d) != 0);
+ EXPECT_TRUE(Pointer("/a/0/b/0").Erase(d));
+ EXPECT_TRUE(Pointer("/a/0/b/0").Get(d) == 0);
+
+ EXPECT_TRUE(Pointer("/a/0/b").Get(d) != 0);
+ EXPECT_TRUE(Pointer("/a/0/b").Erase(d));
+ EXPECT_TRUE(Pointer("/a/0/b").Get(d) == 0);
+
+ EXPECT_TRUE(Pointer("/a/0").Get(d) != 0);
+ EXPECT_TRUE(Pointer("/a/0").Erase(d));
+ EXPECT_TRUE(Pointer("/a/0").Get(d) == 0);
+
+ EXPECT_TRUE(Pointer("/a").Get(d) != 0);
+ EXPECT_TRUE(Pointer("/a").Erase(d));
+ EXPECT_TRUE(Pointer("/a").Get(d) == 0);
+}
+
+TEST(Pointer, CreateValueByPointer) {
+ Document d;
+ Document::AllocatorType& a = d.GetAllocator();
+
+ {
+ Value& v = CreateValueByPointer(d, Pointer("/foo/0"), a);
+ EXPECT_EQ(&d["foo"][0], &v);
+ }
+ {
+ Value& v = CreateValueByPointer(d, "/foo/1", a);
+ EXPECT_EQ(&d["foo"][1], &v);
+ }
+}
+
+TEST(Pointer, CreateValueByPointer_NoAllocator) {
+ Document d;
+
+ {
+ Value& v = CreateValueByPointer(d, Pointer("/foo/0"));
+ EXPECT_EQ(&d["foo"][0], &v);
+ }
+ {
+ Value& v = CreateValueByPointer(d, "/foo/1");
+ EXPECT_EQ(&d["foo"][1], &v);
+ }
+}
+
+TEST(Pointer, GetValueByPointer) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_EQ(&d["foo"][0], GetValueByPointer(d, Pointer("/foo/0")));
+ EXPECT_EQ(&d["foo"][0], GetValueByPointer(d, "/foo/0"));
+
+ size_t unresolvedTokenIndex;
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/2", &unresolvedTokenIndex) == 0); // Out of boundary
+ EXPECT_EQ(1, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/a", &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a"
+ EXPECT_EQ(1, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/0/0", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/0/a", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2, unresolvedTokenIndex);
+
+ // const version
+ const Value& v = d;
+ EXPECT_EQ(&d["foo"][0], GetValueByPointer(v, Pointer("/foo/0")));
+ EXPECT_EQ(&d["foo"][0], GetValueByPointer(v, "/foo/0"));
+
+ EXPECT_TRUE(GetValueByPointer(v, "/foo/2", &unresolvedTokenIndex) == 0); // Out of boundary
+ EXPECT_EQ(1, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(v, "/foo/a", &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a"
+ EXPECT_EQ(1, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(v, "/foo/0/0", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(v, "/foo/0/a", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2, unresolvedTokenIndex);
+
+}
+
+TEST(Pointer, GetValueByPointerWithDefault_Pointer) {
+ Document d;
+ d.Parse(kJson);
+
+ Document::AllocatorType& a = d.GetAllocator();
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, Pointer("/foo/0"), v, a));
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, Pointer("/foo/0"), v, a));
+ EXPECT_TRUE(Value("baz") == GetValueByPointerWithDefault(d, Pointer("/foo/1"), v, a));
+ EXPECT_TRUE(Value("qux") == GetValueByPointerWithDefault(d, Pointer("/foo/2"), v, a));
+ EXPECT_TRUE(Value("last") == GetValueByPointerWithDefault(d, Pointer("/foo/-"), Value("last").Move(), a));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/null"), Value().Move(), a).IsNull());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/null"), "x", a).IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, Pointer("/foo/int"), -1, a).GetInt());
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, Pointer("/foo/int"), -2, a).GetInt());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, Pointer("/foo/uint"), 0x87654321, a).GetUint());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, Pointer("/foo/uint"), 0x12345678, a).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, Pointer("/foo/int64"), i64, a).GetInt64());
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, Pointer("/foo/int64"), i64 + 1, a).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, Pointer("/foo/uint64"), u64, a).GetUint64());
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, Pointer("/foo/uint64"), u64 - 1, a).GetUint64());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/true"), true, a).IsTrue());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/true"), false, a).IsTrue());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/false"), false, a).IsFalse());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/false"), true, a).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", GetValueByPointerWithDefault(d, Pointer("/foo/hello"), "Hello", a).GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", GetValueByPointerWithDefault(d, Pointer("/foo/world"), buffer, a).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, Pointer("/foo/world"))->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", GetValueByPointerWithDefault(d, Pointer("/foo/C++"), std::string("C++"), a).GetString());
+#endif
+}
+
+TEST(Pointer, GetValueByPointerWithDefault_String) {
+ Document d;
+ d.Parse(kJson);
+
+ Document::AllocatorType& a = d.GetAllocator();
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, "/foo/0", v, a));
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, "/foo/0", v, a));
+ EXPECT_TRUE(Value("baz") == GetValueByPointerWithDefault(d, "/foo/1", v, a));
+ EXPECT_TRUE(Value("qux") == GetValueByPointerWithDefault(d, "/foo/2", v, a));
+ EXPECT_TRUE(Value("last") == GetValueByPointerWithDefault(d, "/foo/-", Value("last").Move(), a));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/null", Value().Move(), a).IsNull());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/null", "x", a).IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, "/foo/int", -1, a).GetInt());
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, "/foo/int", -2, a).GetInt());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, "/foo/uint", 0x87654321, a).GetUint());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, "/foo/uint", 0x12345678, a).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, "/foo/int64", i64, a).GetInt64());
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, "/foo/int64", i64 + 1, a).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, "/foo/uint64", u64, a).GetUint64());
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, "/foo/uint64", u64 - 1, a).GetUint64());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/true", true, a).IsTrue());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/true", false, a).IsTrue());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/false", false, a).IsFalse());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/false", true, a).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", GetValueByPointerWithDefault(d, "/foo/hello", "Hello", a).GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", GetValueByPointerWithDefault(d, "/foo/world", buffer, a).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", GetValueByPointerWithDefault(d, "/foo/C++", std::string("C++"), a).GetString());
+#endif
+}
+
+TEST(Pointer, GetValueByPointerWithDefault_Pointer_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, Pointer("/foo/0"), v));
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, Pointer("/foo/0"), v));
+ EXPECT_TRUE(Value("baz") == GetValueByPointerWithDefault(d, Pointer("/foo/1"), v));
+ EXPECT_TRUE(Value("qux") == GetValueByPointerWithDefault(d, Pointer("/foo/2"), v));
+ EXPECT_TRUE(Value("last") == GetValueByPointerWithDefault(d, Pointer("/foo/-"), Value("last").Move()));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/null"), Value().Move()).IsNull());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/null"), "x").IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, Pointer("/foo/int"), -1).GetInt());
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, Pointer("/foo/int"), -2).GetInt());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, Pointer("/foo/uint"), 0x87654321).GetUint());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, Pointer("/foo/uint"), 0x12345678).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, Pointer("/foo/int64"), i64).GetInt64());
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, Pointer("/foo/int64"), i64 + 1).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, Pointer("/foo/uint64"), u64).GetUint64());
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, Pointer("/foo/uint64"), u64 - 1).GetUint64());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/true"), true).IsTrue());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/true"), false).IsTrue());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/false"), false).IsFalse());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/false"), true).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", GetValueByPointerWithDefault(d, Pointer("/foo/hello"), "Hello").GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", GetValueByPointerWithDefault(d, Pointer("/foo/world"), buffer).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, Pointer("/foo/world"))->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", GetValueByPointerWithDefault(d, Pointer("/foo/C++"), std::string("C++")).GetString());
+#endif
+}
+
+TEST(Pointer, GetValueByPointerWithDefault_String_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, "/foo/0", v));
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, "/foo/0", v));
+ EXPECT_TRUE(Value("baz") == GetValueByPointerWithDefault(d, "/foo/1", v));
+ EXPECT_TRUE(Value("qux") == GetValueByPointerWithDefault(d, "/foo/2", v));
+ EXPECT_TRUE(Value("last") == GetValueByPointerWithDefault(d, "/foo/-", Value("last").Move()));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/null", Value().Move()).IsNull());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/null", "x").IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, "/foo/int", -1).GetInt());
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, "/foo/int", -2).GetInt());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, "/foo/uint", 0x87654321).GetUint());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, "/foo/uint", 0x12345678).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, "/foo/int64", i64).GetInt64());
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, "/foo/int64", i64 + 1).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, "/foo/uint64", u64).GetUint64());
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, "/foo/uint64", u64 - 1).GetUint64());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/true", true).IsTrue());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/true", false).IsTrue());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/false", false).IsFalse());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/false", true).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", GetValueByPointerWithDefault(d, "/foo/hello", "Hello").GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", GetValueByPointerWithDefault(d, "/foo/world", buffer).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", GetValueByPointerWithDefault(d, Pointer("/foo/C++"), std::string("C++")).GetString());
+#endif
+}
+
+TEST(Pointer, SetValueByPointer_Pointer) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+
+ // Value version
+ SetValueByPointer(d, Pointer("/foo/0"), Value(123).Move(), a);
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ SetValueByPointer(d, Pointer("/foo/null"), Value().Move(), a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ SetValueByPointer(d, Pointer("/clone"), foo, a);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ SetValueByPointer(d, Pointer("/foo/int"), -1, a);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ SetValueByPointer(d, Pointer("/foo/uint"), 0x87654321, a);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ SetValueByPointer(d, Pointer("/foo/int64"), i64, a);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ SetValueByPointer(d, Pointer("/foo/uint64"), u64, a);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ SetValueByPointer(d, Pointer("/foo/true"), true, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ SetValueByPointer(d, Pointer("/foo/false"), false, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ SetValueByPointer(d, Pointer("/foo/hello"), "Hello", a);
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ SetValueByPointer(d, Pointer("/foo/world"), buffer, a);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ SetValueByPointer(d, Pointer("/foo/c++"), std::string("C++"), a);
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, SetValueByPointer_String) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+
+ // Value version
+ SetValueByPointer(d, "/foo/0", Value(123).Move(), a);
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ SetValueByPointer(d, "/foo/null", Value().Move(), a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ SetValueByPointer(d, "/clone", foo, a);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ SetValueByPointer(d, "/foo/int", -1, a);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ SetValueByPointer(d, "/foo/uint", 0x87654321, a);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ SetValueByPointer(d, "/foo/int64", i64, a);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ SetValueByPointer(d, "/foo/uint64", u64, a);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ SetValueByPointer(d, "/foo/true", true, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ SetValueByPointer(d, "/foo/false", false, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ SetValueByPointer(d, "/foo/hello", "Hello", a);
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ SetValueByPointer(d, "/foo/world", buffer, a);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ SetValueByPointer(d, "/foo/c++", std::string("C++"), a);
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, SetValueByPointer_Pointer_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ SetValueByPointer(d, Pointer("/foo/0"), Value(123).Move());
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ SetValueByPointer(d, Pointer("/foo/null"), Value().Move());
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ SetValueByPointer(d, Pointer("/clone"), foo);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ SetValueByPointer(d, Pointer("/foo/int"), -1);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ SetValueByPointer(d, Pointer("/foo/uint"), 0x87654321);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ SetValueByPointer(d, Pointer("/foo/int64"), i64);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ SetValueByPointer(d, Pointer("/foo/uint64"), u64);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ SetValueByPointer(d, Pointer("/foo/true"), true);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ SetValueByPointer(d, Pointer("/foo/false"), false);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ SetValueByPointer(d, Pointer("/foo/hello"), "Hello");
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ SetValueByPointer(d, Pointer("/foo/world"), buffer);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ SetValueByPointer(d, Pointer("/foo/c++"), std::string("C++"));
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, SetValueByPointer_String_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ SetValueByPointer(d, "/foo/0", Value(123).Move());
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ SetValueByPointer(d, "/foo/null", Value().Move());
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ SetValueByPointer(d, "/clone", foo);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ SetValueByPointer(d, "/foo/int", -1);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ SetValueByPointer(d, "/foo/uint", 0x87654321);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ SetValueByPointer(d, "/foo/int64", i64);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ SetValueByPointer(d, "/foo/uint64", u64);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ SetValueByPointer(d, "/foo/true", true);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ SetValueByPointer(d, "/foo/false", false);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ SetValueByPointer(d, "/foo/hello", "Hello");
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ SetValueByPointer(d, "/foo/world", buffer);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ SetValueByPointer(d, "/foo/c++", std::string("C++"));
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, SwapValueByPointer) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+ SwapValueByPointer(d, Pointer("/foo/0"), *GetValueByPointer(d, "/foo/1"), a);
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_STREQ("bar", d["foo"][1].GetString());
+
+ SwapValueByPointer(d, "/foo/0", *GetValueByPointer(d, "/foo/1"), a);
+ EXPECT_STREQ("bar", d["foo"][0].GetString());
+ EXPECT_STREQ("baz", d["foo"][1].GetString());
+}
+
+TEST(Pointer, SwapValueByPointer_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+ SwapValueByPointer(d, Pointer("/foo/0"), *GetValueByPointer(d, "/foo/1"));
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_STREQ("bar", d["foo"][1].GetString());
+
+ SwapValueByPointer(d, "/foo/0", *GetValueByPointer(d, "/foo/1"));
+ EXPECT_STREQ("bar", d["foo"][0].GetString());
+ EXPECT_STREQ("baz", d["foo"][1].GetString());
+}
+
+TEST(Pointer, EraseValueByPointer_Pointer) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_FALSE(EraseValueByPointer(d, Pointer("")));
+ EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
+ EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo/0")));
+ EXPECT_EQ(1u, d["foo"].Size());
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo/0")));
+ EXPECT_TRUE(d["foo"].Empty());
+ EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo")));
+ EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
+}
+
+TEST(Pointer, EraseValueByPointer_String) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_FALSE(EraseValueByPointer(d, ""));
+ EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
+ EXPECT_TRUE(EraseValueByPointer(d, "/foo/0"));
+ EXPECT_EQ(1u, d["foo"].Size());
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_TRUE(EraseValueByPointer(d, "/foo/0"));
+ EXPECT_TRUE(d["foo"].Empty());
+ EXPECT_TRUE(EraseValueByPointer(d, "/foo"));
+ EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
+}
+
+TEST(Pointer, Ambiguity) {
+ {
+ Document d;
+ d.Parse("{\"0\" : [123]}");
+ EXPECT_EQ(123, Pointer("/0/0").Get(d)->GetInt());
+ Pointer("/0/a").Set(d, 456); // Change array [123] to object {456}
+ EXPECT_EQ(456, Pointer("/0/a").Get(d)->GetInt());
+ }
+
+ {
+ Document d;
+ EXPECT_FALSE(d.Parse("[{\"0\": 123}]").HasParseError());
+ EXPECT_EQ(123, Pointer("/0/0").Get(d)->GetInt());
+ Pointer("/0/1").Set(d, 456); // 1 is treated as "1" to index object
+ EXPECT_EQ(123, Pointer("/0/0").Get(d)->GetInt());
+ EXPECT_EQ(456, Pointer("/0/1").Get(d)->GetInt());
+ }
+}
+
+// https://github.com/miloyip/rapidjson/issues/483
+namespace myjson {
+
+class MyAllocator
+{
+public:
+ static const bool kNeedFree = true;
+ void * Malloc(size_t _size) { return malloc(_size); }
+ void * Realloc(void *_org_p, size_t _org_size, size_t _new_size) { (void)_org_size; return realloc(_org_p, _new_size); }
+ static void Free(void *_p) { return free(_p); }
+};
+
+typedef rapidjson::GenericDocument<
+ rapidjson::UTF8<>,
+ rapidjson::MemoryPoolAllocator< MyAllocator >,
+ MyAllocator
+ > Document;
+
+typedef rapidjson::GenericPointer<
+ ::myjson::Document::ValueType,
+ MyAllocator
+ > Pointer;
+
+typedef ::myjson::Document::ValueType Value;
+
+}
+
+TEST(Pointer, Issue483) {
+ std::string mystr, path;
+ myjson::Document document;
+ myjson::Value value(rapidjson::kStringType);
+ value.SetString(mystr.c_str(), static_cast<SizeType>(mystr.length()), document.GetAllocator());
+ myjson::Pointer(path.c_str()).Set(document, value, document.GetAllocator());
+}
diff --git a/src/rapidjson/test/unittest/prettywritertest.cpp b/src/rapidjson/test/unittest/prettywritertest.cpp
new file mode 100644
index 000000000..a372f7986
--- /dev/null
+++ b/src/rapidjson/test/unittest/prettywritertest.cpp
@@ -0,0 +1,203 @@
+// 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. All rights reserved.
+//
+// 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/prettywriter.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/filewritestream.h"
+
+using namespace rapidjson;
+
+static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,-1],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}";
+static const char kPrettyJson[] =
+"{\n"
+" \"hello\": \"world\",\n"
+" \"t\": true,\n"
+" \"f\": false,\n"
+" \"n\": null,\n"
+" \"i\": 123,\n"
+" \"pi\": 3.1416,\n"
+" \"a\": [\n"
+" 1,\n"
+" 2,\n"
+" 3,\n"
+" -1\n"
+" ],\n"
+" \"u64\": 1234567890123456789,\n"
+" \"i64\": -1234567890123456789\n"
+"}";
+
+static const char kPrettyJson_FormatOptions_SLA[] =
+"{\n"
+" \"hello\": \"world\",\n"
+" \"t\": true,\n"
+" \"f\": false,\n"
+" \"n\": null,\n"
+" \"i\": 123,\n"
+" \"pi\": 3.1416,\n"
+" \"a\": [1, 2, 3, -1],\n"
+" \"u64\": 1234567890123456789,\n"
+" \"i64\": -1234567890123456789\n"
+"}";
+
+TEST(PrettyWriter, Basic) {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ Reader reader;
+ StringStream s(kJson);
+ reader.Parse(s, writer);
+ EXPECT_STREQ(kPrettyJson, buffer.GetString());
+}
+
+TEST(PrettyWriter, FormatOptions) {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.SetFormatOptions(kFormatSingleLineArray);
+ Reader reader;
+ StringStream s(kJson);
+ reader.Parse(s, writer);
+ EXPECT_STREQ(kPrettyJson_FormatOptions_SLA, buffer.GetString());
+}
+
+TEST(PrettyWriter, SetIndent) {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.SetIndent('\t', 1);
+ Reader reader;
+ StringStream s(kJson);
+ reader.Parse(s, writer);
+ EXPECT_STREQ(
+ "{\n"
+ "\t\"hello\": \"world\",\n"
+ "\t\"t\": true,\n"
+ "\t\"f\": false,\n"
+ "\t\"n\": null,\n"
+ "\t\"i\": 123,\n"
+ "\t\"pi\": 3.1416,\n"
+ "\t\"a\": [\n"
+ "\t\t1,\n"
+ "\t\t2,\n"
+ "\t\t3,\n"
+ "\t\t-1\n"
+ "\t],\n"
+ "\t\"u64\": 1234567890123456789,\n"
+ "\t\"i64\": -1234567890123456789\n"
+ "}",
+ buffer.GetString());
+}
+
+TEST(PrettyWriter, String) {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ EXPECT_TRUE(writer.StartArray());
+ EXPECT_TRUE(writer.String("Hello\n"));
+ EXPECT_TRUE(writer.EndArray());
+ EXPECT_STREQ("[\n \"Hello\\n\"\n]", buffer.GetString());
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+TEST(PrettyWriter, String_STDSTRING) {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ EXPECT_TRUE(writer.StartArray());
+ EXPECT_TRUE(writer.String(std::string("Hello\n")));
+ EXPECT_TRUE(writer.EndArray());
+ EXPECT_STREQ("[\n \"Hello\\n\"\n]", buffer.GetString());
+}
+#endif
+
+#include <sstream>
+
+class OStreamWrapper {
+public:
+ typedef char Ch;
+
+ OStreamWrapper(std::ostream& os) : os_(os) {}
+
+ Ch Peek() const { assert(false); return '\0'; }
+ Ch Take() { assert(false); return '\0'; }
+ size_t Tell() const { return 0; }
+
+ Ch* PutBegin() { assert(false); return 0; }
+ void Put(Ch c) { os_.put(c); }
+ void Flush() { os_.flush(); }
+ size_t PutEnd(Ch*) { assert(false); return 0; }
+
+private:
+ OStreamWrapper(const OStreamWrapper&);
+ OStreamWrapper& operator=(const OStreamWrapper&);
+
+ std::ostream& os_;
+};
+
+// For covering PutN() generic version
+TEST(PrettyWriter, OStreamWrapper) {
+ StringStream s(kJson);
+
+ std::stringstream ss;
+ OStreamWrapper os(ss);
+
+ PrettyWriter<OStreamWrapper> writer(os);
+
+ Reader reader;
+ reader.Parse(s, writer);
+
+ std::string actual = ss.str();
+ EXPECT_STREQ(kPrettyJson, actual.c_str());
+}
+
+// For covering FileWriteStream::PutN()
+TEST(PrettyWriter, FileWriteStream) {
+ char filename[L_tmpnam];
+ FILE* fp = TempFile(filename);
+ char buffer[16];
+ FileWriteStream os(fp, buffer, sizeof(buffer));
+ PrettyWriter<FileWriteStream> writer(os);
+ Reader reader;
+ StringStream s(kJson);
+ reader.Parse(s, writer);
+ fclose(fp);
+
+ fp = fopen(filename, "rb");
+ fseek(fp, 0, SEEK_END);
+ size_t size = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ char* json = static_cast<char*>(malloc(size + 1));
+ size_t readLength = fread(json, 1, size, fp);
+ json[readLength] = '\0';
+ fclose(fp);
+ remove(filename);
+ EXPECT_STREQ(kPrettyJson, json);
+ free(json);
+}
+
+TEST(PrettyWriter, RawValue) {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.Key("a");
+ writer.Int(1);
+ writer.Key("raw");
+ const char json[] = "[\"Hello\\nWorld\", 123.456]";
+ writer.RawValue(json, strlen(json), kArrayType);
+ writer.EndObject();
+ EXPECT_TRUE(writer.IsComplete());
+ EXPECT_STREQ(
+ "{\n"
+ " \"a\": 1,\n"
+ " \"raw\": [\"Hello\\nWorld\", 123.456]\n" // no indentation within raw value
+ "}",
+ buffer.GetString());
+}
diff --git a/src/rapidjson/test/unittest/readertest.cpp b/src/rapidjson/test/unittest/readertest.cpp
new file mode 100644
index 000000000..64a1f9c3c
--- /dev/null
+++ b/src/rapidjson/test/unittest/readertest.cpp
@@ -0,0 +1,1844 @@
+// 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. All rights reserved.
+//
+// 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, "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/miloyip/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, "0.017976931348623157e+310", 1.7976931348623157e+308); // Max double in another form
+
+ // Since
+ // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... �� 10^-324
+ // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... �� 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... �� 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;
+ }
+ }
+#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);
+}
+
+TEST(Reader, ParseNumber_Error) {
+#define TEST_NUMBER_ERROR(errorCode, str, errorOffset, streamPos) \
+ { \
+ char buffer[1001]; \
+ sprintf(buffer, "%s", str); \
+ 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());\
+ }
+
+ // 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, 0, 309);
+ }
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0, 5);
+
+ // Miss fraction part in number.
+ TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.", 2, 2);
+ TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a", 2, 2);
+
+ // Miss exponent in number.
+ TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e", 2, 2);
+ TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_", 2, 2);
+
+#undef TEST_NUMBER_ERROR
+}
+
+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\"]", 2, 3);
+
+ // Incorrect hex digit after \\u escape in string.
+ TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]", 2, 7);
+
+ // Quotation in \\u escape in string (Issue #288)
+ TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uaaa\"]", 2, 7);
+ TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFFF\"]", 2, 13);
+
+ // The surrogate pair in string is invalid.
+ TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]", 2, 8);
+ TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFFFF\"]", 2, 14);
+
+ // Missing a closing quotation mark in string.
+ TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]", 7, 7);
+
+ // 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);
+ int 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, 2, 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'));
+
+#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) \
+ { \
+ int 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", 2);
+ TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}", 2);
+ TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]", 3);
+
+ // Array cannot have a trailing comma (without kParseTrailingCommasFlag);
+ // a value must follow a comma
+ TEST_ARRAY_ERROR(kParseErrorValueInvalid, "[1,]", 3);
+
+#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) \
+ { \
+ int 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, "", 0);
+ TEST_ERROR(kParseErrorDocumentEmpty, " ", 1);
+ TEST_ERROR(kParseErrorDocumentEmpty, " \n", 2);
+
+ // The document root must not follow by other values.
+ TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0", 3);
+ TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0", 3);
+ TEST_ERROR(kParseErrorDocumentRootNotSingular, "null []", 5);
+ TEST_ERROR(kParseErrorDocumentRootNotSingular, "0 {}", 2);
+}
+
+TEST(Reader, ParseValue_Error) {
+ // Invalid value.
+ TEST_ERROR(kParseErrorValueInvalid, "nulL", 3);
+ TEST_ERROR(kParseErrorValueInvalid, "truE", 3);
+ TEST_ERROR(kParseErrorValueInvalid, "falsE", 4);
+ TEST_ERROR(kParseErrorValueInvalid, "a]", 0);
+ TEST_ERROR(kParseErrorValueInvalid, ".1", 0);
+}
+
+TEST(Reader, ParseObject_Error) {
+ // Missing a name for object member.
+ TEST_ERROR(kParseErrorObjectMissName, "{1}", 1);
+ TEST_ERROR(kParseErrorObjectMissName, "{:1}", 1);
+ TEST_ERROR(kParseErrorObjectMissName, "{null:1}", 1);
+ TEST_ERROR(kParseErrorObjectMissName, "{true:1}", 1);
+ TEST_ERROR(kParseErrorObjectMissName, "{false:1}", 1);
+ TEST_ERROR(kParseErrorObjectMissName, "{1:1}", 1);
+ TEST_ERROR(kParseErrorObjectMissName, "{[]:1}", 1);
+ TEST_ERROR(kParseErrorObjectMissName, "{{}:1}", 1);
+ TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}", 1);
+
+ // Missing a colon after a name of object member.
+ TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}", 5);
+ TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}", 4);
+
+ // Must be a comma or '}' after an object member
+ TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]", 6);
+
+ // Object cannot have a trailing comma (without kParseTrailingCommasFlag);
+ // an object member name must follow a comma
+ TEST_ERROR(kParseErrorObjectMissName, "{\"a\":1,}", 7);
+
+ // 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_;
+};
+
+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)\
+{\
+ int 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 int LOG_NULL = -1;
+ const static int LOG_BOOL = -2;
+ const static int LOG_INT = -3;
+ const static int LOG_UINT = -4;
+ const static int LOG_INT64 = -5;
+ const static int LOG_UINT64 = -6;
+ const static int LOG_DOUBLE = -7;
+ const static int LOG_STRING = -8;
+ const static int LOG_STARTOBJECT = -9;
+ const static int LOG_KEY = -10;
+ const static int LOG_ENDOBJECT = -11;
+ const static int LOG_STARTARRAY = -12;
+ const static int LOG_ENDARRAY = -13;
+
+ const static size_t LogCapacity = 256;
+ int Logs[LogCapacity];
+ 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);
+ Logs[LogCount++] = LOG_ENDOBJECT;
+ Logs[LogCount++] = static_cast<int>(c);
+ return true;
+ }
+
+ bool StartArray() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTARRAY; return true; }
+
+ bool EndArray(SizeType c) {
+ RAPIDJSON_ASSERT(LogCount < LogCapacity);
+ Logs[LogCount++] = LOG_ENDARRAY;
+ Logs[LogCount++] = static_cast<int>(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());
+
+ int 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());
+
+ int 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 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));
+ }
+}
+
+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) \
+ { \
+ int 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, "nan", 1);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-Infinty", 6);
+
+#undef TEST_NAN_INF_ERROR
+#undef TEST_NAN_INF
+}
+
+RAPIDJSON_DIAG_POP
diff --git a/src/rapidjson/test/unittest/regextest.cpp b/src/rapidjson/test/unittest/regextest.cpp
new file mode 100644
index 000000000..4fb5b222e
--- /dev/null
+++ b/src/rapidjson/test/unittest/regextest.cpp
@@ -0,0 +1,592 @@
+// 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. All rights reserved.
+//
+// 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/internal/regex.h"
+
+using namespace rapidjson::internal;
+
+TEST(Regex, Single) {
+ Regex re("a");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("b"));
+}
+
+TEST(Regex, Concatenation) {
+ Regex re("abc");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abc"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("a"));
+ EXPECT_FALSE(re.Match("b"));
+ EXPECT_FALSE(re.Match("ab"));
+ EXPECT_FALSE(re.Match("abcd"));
+}
+
+TEST(Regex, Alternation1) {
+ Regex re("abab|abbb");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abab"));
+ EXPECT_TRUE(re.Match("abbb"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("ab"));
+ EXPECT_FALSE(re.Match("ababa"));
+ EXPECT_FALSE(re.Match("abb"));
+ EXPECT_FALSE(re.Match("abbbb"));
+}
+
+TEST(Regex, Alternation2) {
+ Regex re("a|b|c");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("b"));
+ EXPECT_TRUE(re.Match("c"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("aa"));
+ EXPECT_FALSE(re.Match("ab"));
+}
+
+TEST(Regex, Parenthesis1) {
+ Regex re("(ab)c");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abc"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("a"));
+ EXPECT_FALSE(re.Match("b"));
+ EXPECT_FALSE(re.Match("ab"));
+ EXPECT_FALSE(re.Match("abcd"));
+}
+
+TEST(Regex, Parenthesis2) {
+ Regex re("a(bc)");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abc"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("a"));
+ EXPECT_FALSE(re.Match("b"));
+ EXPECT_FALSE(re.Match("ab"));
+ EXPECT_FALSE(re.Match("abcd"));
+}
+
+TEST(Regex, Parenthesis3) {
+ Regex re("(a|b)(c|d)");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("ac"));
+ EXPECT_TRUE(re.Match("ad"));
+ EXPECT_TRUE(re.Match("bc"));
+ EXPECT_TRUE(re.Match("bd"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("ab"));
+ EXPECT_FALSE(re.Match("cd"));
+}
+
+TEST(Regex, ZeroOrOne1) {
+ Regex re("a?");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match(""));
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_FALSE(re.Match("aa"));
+}
+
+TEST(Regex, ZeroOrOne2) {
+ Regex re("a?b");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("b"));
+ EXPECT_TRUE(re.Match("ab"));
+ EXPECT_FALSE(re.Match("a"));
+ EXPECT_FALSE(re.Match("aa"));
+ EXPECT_FALSE(re.Match("bb"));
+ EXPECT_FALSE(re.Match("ba"));
+}
+
+TEST(Regex, ZeroOrOne3) {
+ Regex re("ab?");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("ab"));
+ EXPECT_FALSE(re.Match("b"));
+ EXPECT_FALSE(re.Match("aa"));
+ EXPECT_FALSE(re.Match("bb"));
+ EXPECT_FALSE(re.Match("ba"));
+}
+
+TEST(Regex, ZeroOrOne4) {
+ Regex re("a?b?");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match(""));
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("b"));
+ EXPECT_TRUE(re.Match("ab"));
+ EXPECT_FALSE(re.Match("aa"));
+ EXPECT_FALSE(re.Match("bb"));
+ EXPECT_FALSE(re.Match("ba"));
+ EXPECT_FALSE(re.Match("abc"));
+}
+
+TEST(Regex, ZeroOrOne5) {
+ Regex re("a(ab)?b");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("ab"));
+ EXPECT_TRUE(re.Match("aabb"));
+ EXPECT_FALSE(re.Match("aab"));
+ EXPECT_FALSE(re.Match("abb"));
+}
+
+TEST(Regex, ZeroOrMore1) {
+ Regex re("a*");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match(""));
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("aa"));
+ EXPECT_FALSE(re.Match("b"));
+ EXPECT_FALSE(re.Match("ab"));
+}
+
+TEST(Regex, ZeroOrMore2) {
+ Regex re("a*b");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("b"));
+ EXPECT_TRUE(re.Match("ab"));
+ EXPECT_TRUE(re.Match("aab"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("bb"));
+}
+
+TEST(Regex, ZeroOrMore3) {
+ Regex re("a*b*");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match(""));
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("aa"));
+ EXPECT_TRUE(re.Match("b"));
+ EXPECT_TRUE(re.Match("bb"));
+ EXPECT_TRUE(re.Match("ab"));
+ EXPECT_TRUE(re.Match("aabb"));
+ EXPECT_FALSE(re.Match("ba"));
+}
+
+TEST(Regex, ZeroOrMore4) {
+ Regex re("a(ab)*b");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("ab"));
+ EXPECT_TRUE(re.Match("aabb"));
+ EXPECT_TRUE(re.Match("aababb"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("aa"));
+}
+
+TEST(Regex, OneOrMore1) {
+ Regex re("a+");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("aa"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("b"));
+ EXPECT_FALSE(re.Match("ab"));
+}
+
+TEST(Regex, OneOrMore2) {
+ Regex re("a+b");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("ab"));
+ EXPECT_TRUE(re.Match("aab"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("b"));
+}
+
+TEST(Regex, OneOrMore3) {
+ Regex re("a+b+");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("ab"));
+ EXPECT_TRUE(re.Match("aab"));
+ EXPECT_TRUE(re.Match("abb"));
+ EXPECT_TRUE(re.Match("aabb"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("b"));
+ EXPECT_FALSE(re.Match("ba"));
+}
+
+TEST(Regex, OneOrMore4) {
+ Regex re("a(ab)+b");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("aabb"));
+ EXPECT_TRUE(re.Match("aababb"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("ab"));
+}
+
+TEST(Regex, QuantifierExact1) {
+ Regex re("ab{3}c");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abbbc"));
+ EXPECT_FALSE(re.Match("ac"));
+ EXPECT_FALSE(re.Match("abc"));
+ EXPECT_FALSE(re.Match("abbc"));
+ EXPECT_FALSE(re.Match("abbbbc"));
+}
+
+TEST(Regex, QuantifierExact2) {
+ Regex re("a(bc){3}d");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abcbcbcd"));
+ EXPECT_FALSE(re.Match("ad"));
+ EXPECT_FALSE(re.Match("abcd"));
+ EXPECT_FALSE(re.Match("abcbcd"));
+ EXPECT_FALSE(re.Match("abcbcbcbcd"));
+}
+
+TEST(Regex, QuantifierExact3) {
+ Regex re("a(b|c){3}d");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abbbd"));
+ EXPECT_TRUE(re.Match("acccd"));
+ EXPECT_TRUE(re.Match("abcbd"));
+ EXPECT_FALSE(re.Match("ad"));
+ EXPECT_FALSE(re.Match("abbd"));
+ EXPECT_FALSE(re.Match("accccd"));
+ EXPECT_FALSE(re.Match("abbbbd"));
+}
+
+TEST(Regex, QuantifierMin1) {
+ Regex re("ab{3,}c");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abbbc"));
+ EXPECT_TRUE(re.Match("abbbbc"));
+ EXPECT_TRUE(re.Match("abbbbbc"));
+ EXPECT_FALSE(re.Match("ac"));
+ EXPECT_FALSE(re.Match("abc"));
+ EXPECT_FALSE(re.Match("abbc"));
+}
+
+TEST(Regex, QuantifierMin2) {
+ Regex re("a(bc){3,}d");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abcbcbcd"));
+ EXPECT_TRUE(re.Match("abcbcbcbcd"));
+ EXPECT_FALSE(re.Match("ad"));
+ EXPECT_FALSE(re.Match("abcd"));
+ EXPECT_FALSE(re.Match("abcbcd"));
+}
+
+TEST(Regex, QuantifierMin3) {
+ Regex re("a(b|c){3,}d");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abbbd"));
+ EXPECT_TRUE(re.Match("acccd"));
+ EXPECT_TRUE(re.Match("abcbd"));
+ EXPECT_TRUE(re.Match("accccd"));
+ EXPECT_TRUE(re.Match("abbbbd"));
+ EXPECT_FALSE(re.Match("ad"));
+ EXPECT_FALSE(re.Match("abbd"));
+}
+
+TEST(Regex, QuantifierMinMax1) {
+ Regex re("ab{3,5}c");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abbbc"));
+ EXPECT_TRUE(re.Match("abbbbc"));
+ EXPECT_TRUE(re.Match("abbbbbc"));
+ EXPECT_FALSE(re.Match("ac"));
+ EXPECT_FALSE(re.Match("abc"));
+ EXPECT_FALSE(re.Match("abbc"));
+ EXPECT_FALSE(re.Match("abbbbbbc"));
+}
+
+TEST(Regex, QuantifierMinMax2) {
+ Regex re("a(bc){3,5}d");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abcbcbcd"));
+ EXPECT_TRUE(re.Match("abcbcbcbcd"));
+ EXPECT_TRUE(re.Match("abcbcbcbcbcd"));
+ EXPECT_FALSE(re.Match("ad"));
+ EXPECT_FALSE(re.Match("abcd"));
+ EXPECT_FALSE(re.Match("abcbcd"));
+ EXPECT_FALSE(re.Match("abcbcbcbcbcbcd"));
+}
+
+TEST(Regex, QuantifierMinMax3) {
+ Regex re("a(b|c){3,5}d");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("abbbd"));
+ EXPECT_TRUE(re.Match("acccd"));
+ EXPECT_TRUE(re.Match("abcbd"));
+ EXPECT_TRUE(re.Match("accccd"));
+ EXPECT_TRUE(re.Match("abbbbd"));
+ EXPECT_TRUE(re.Match("acccccd"));
+ EXPECT_TRUE(re.Match("abbbbbd"));
+ EXPECT_FALSE(re.Match("ad"));
+ EXPECT_FALSE(re.Match("abbd"));
+ EXPECT_FALSE(re.Match("accccccd"));
+ EXPECT_FALSE(re.Match("abbbbbbd"));
+}
+
+// Issue538
+TEST(Regex, QuantifierMinMax4) {
+ Regex re("a(b|c){0,3}d");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("ad"));
+ EXPECT_TRUE(re.Match("abd"));
+ EXPECT_TRUE(re.Match("acd"));
+ EXPECT_TRUE(re.Match("abbd"));
+ EXPECT_TRUE(re.Match("accd"));
+ EXPECT_TRUE(re.Match("abcd"));
+ EXPECT_TRUE(re.Match("abbbd"));
+ EXPECT_TRUE(re.Match("acccd"));
+ EXPECT_FALSE(re.Match("abbbbd"));
+ EXPECT_FALSE(re.Match("add"));
+ EXPECT_FALSE(re.Match("accccd"));
+ EXPECT_FALSE(re.Match("abcbcd"));
+}
+
+// Issue538
+TEST(Regex, QuantifierMinMax5) {
+ Regex re("a(b|c){0,}d");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("ad"));
+ EXPECT_TRUE(re.Match("abd"));
+ EXPECT_TRUE(re.Match("acd"));
+ EXPECT_TRUE(re.Match("abbd"));
+ EXPECT_TRUE(re.Match("accd"));
+ EXPECT_TRUE(re.Match("abcd"));
+ EXPECT_TRUE(re.Match("abbbd"));
+ EXPECT_TRUE(re.Match("acccd"));
+ EXPECT_TRUE(re.Match("abbbbd"));
+ EXPECT_TRUE(re.Match("accccd"));
+ EXPECT_TRUE(re.Match("abcbcd"));
+ EXPECT_FALSE(re.Match("add"));
+ EXPECT_FALSE(re.Match("aad"));
+}
+
+#define EURO "\xE2\x82\xAC" // "\xE2\x82\xAC" is UTF-8 sequence of Euro sign U+20AC
+
+TEST(Regex, Unicode) {
+ Regex re("a" EURO "+b");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a" EURO "b"));
+ EXPECT_TRUE(re.Match("a" EURO EURO "b"));
+ EXPECT_FALSE(re.Match("a?b"));
+ EXPECT_FALSE(re.Match("a" EURO "\xAC" "b")); // unaware of UTF-8 will match
+}
+
+TEST(Regex, AnyCharacter) {
+ Regex re(".");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("b"));
+ EXPECT_TRUE(re.Match(EURO));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("aa"));
+}
+
+TEST(Regex, CharacterRange1) {
+ Regex re("[abc]");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("b"));
+ EXPECT_TRUE(re.Match("c"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("`"));
+ EXPECT_FALSE(re.Match("d"));
+ EXPECT_FALSE(re.Match("aa"));
+}
+
+TEST(Regex, CharacterRange2) {
+ Regex re("[^abc]");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("`"));
+ EXPECT_TRUE(re.Match("d"));
+ EXPECT_FALSE(re.Match("a"));
+ EXPECT_FALSE(re.Match("b"));
+ EXPECT_FALSE(re.Match("c"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("aa"));
+}
+
+TEST(Regex, CharacterRange3) {
+ Regex re("[a-c]");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("b"));
+ EXPECT_TRUE(re.Match("c"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("`"));
+ EXPECT_FALSE(re.Match("d"));
+ EXPECT_FALSE(re.Match("aa"));
+}
+
+TEST(Regex, CharacterRange4) {
+ Regex re("[^a-c]");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("`"));
+ EXPECT_TRUE(re.Match("d"));
+ EXPECT_FALSE(re.Match("a"));
+ EXPECT_FALSE(re.Match("b"));
+ EXPECT_FALSE(re.Match("c"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("aa"));
+}
+
+TEST(Regex, CharacterRange5) {
+ Regex re("[-]");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("-"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("a"));
+}
+
+TEST(Regex, CharacterRange6) {
+ Regex re("[a-]");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("-"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("`"));
+ EXPECT_FALSE(re.Match("b"));
+}
+
+TEST(Regex, CharacterRange7) {
+ Regex re("[-a]");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("a"));
+ EXPECT_TRUE(re.Match("-"));
+ EXPECT_FALSE(re.Match(""));
+ EXPECT_FALSE(re.Match("`"));
+ EXPECT_FALSE(re.Match("b"));
+}
+
+TEST(Regex, CharacterRange8) {
+ Regex re("[a-zA-Z0-9]*");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("Milo"));
+ EXPECT_TRUE(re.Match("MT19937"));
+ EXPECT_TRUE(re.Match("43"));
+ EXPECT_FALSE(re.Match("a_b"));
+ EXPECT_FALSE(re.Match("!"));
+}
+
+TEST(Regex, Search) {
+ Regex re("abc");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Search("abc"));
+ EXPECT_TRUE(re.Search("_abc"));
+ EXPECT_TRUE(re.Search("abc_"));
+ EXPECT_TRUE(re.Search("_abc_"));
+ EXPECT_TRUE(re.Search("__abc__"));
+ EXPECT_TRUE(re.Search("abcabc"));
+ EXPECT_FALSE(re.Search("a"));
+ EXPECT_FALSE(re.Search("ab"));
+ EXPECT_FALSE(re.Search("bc"));
+ EXPECT_FALSE(re.Search("cba"));
+}
+
+TEST(Regex, Search_BeginAnchor) {
+ Regex re("^abc");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Search("abc"));
+ EXPECT_TRUE(re.Search("abc_"));
+ EXPECT_TRUE(re.Search("abcabc"));
+ EXPECT_FALSE(re.Search("_abc"));
+ EXPECT_FALSE(re.Search("_abc_"));
+ EXPECT_FALSE(re.Search("a"));
+ EXPECT_FALSE(re.Search("ab"));
+ EXPECT_FALSE(re.Search("bc"));
+ EXPECT_FALSE(re.Search("cba"));
+}
+
+TEST(Regex, Search_EndAnchor) {
+ Regex re("abc$");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Search("abc"));
+ EXPECT_TRUE(re.Search("_abc"));
+ EXPECT_TRUE(re.Search("abcabc"));
+ EXPECT_FALSE(re.Search("abc_"));
+ EXPECT_FALSE(re.Search("_abc_"));
+ EXPECT_FALSE(re.Search("a"));
+ EXPECT_FALSE(re.Search("ab"));
+ EXPECT_FALSE(re.Search("bc"));
+ EXPECT_FALSE(re.Search("cba"));
+}
+
+TEST(Regex, Search_BothAnchor) {
+ Regex re("^abc$");
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Search("abc"));
+ EXPECT_FALSE(re.Search(""));
+ EXPECT_FALSE(re.Search("a"));
+ EXPECT_FALSE(re.Search("b"));
+ EXPECT_FALSE(re.Search("ab"));
+ EXPECT_FALSE(re.Search("abcd"));
+}
+
+TEST(Regex, Escape) {
+ const char* s = "\\^\\$\\|\\(\\)\\?\\*\\+\\.\\[\\]\\{\\}\\\\\\f\\n\\r\\t\\v[\\b][\\[][\\]]";
+ Regex re(s);
+ ASSERT_TRUE(re.IsValid());
+ EXPECT_TRUE(re.Match("^$|()?*+.[]{}\\\x0C\n\r\t\x0B\b[]"));
+ EXPECT_FALSE(re.Match(s)); // Not escaping
+}
+
+TEST(Regex, Invalid) {
+#define TEST_INVALID(s) \
+ {\
+ Regex re(s);\
+ EXPECT_FALSE(re.IsValid());\
+ }
+
+ TEST_INVALID("");
+ TEST_INVALID("a|");
+ TEST_INVALID("()");
+ TEST_INVALID(")");
+ TEST_INVALID("(a))");
+ TEST_INVALID("(a|)");
+ TEST_INVALID("(a||b)");
+ TEST_INVALID("(|b)");
+ TEST_INVALID("?");
+ TEST_INVALID("*");
+ TEST_INVALID("+");
+ TEST_INVALID("{");
+ TEST_INVALID("{}");
+ TEST_INVALID("a{a}");
+ TEST_INVALID("a{0}");
+ TEST_INVALID("a{-1}");
+ TEST_INVALID("a{}");
+ // TEST_INVALID("a{0,}"); // Support now
+ TEST_INVALID("a{,0}");
+ TEST_INVALID("a{1,0}");
+ TEST_INVALID("a{-1,0}");
+ TEST_INVALID("a{-1,1}");
+ TEST_INVALID("a{4294967296}"); // overflow of unsigned
+ TEST_INVALID("a{1a}");
+ TEST_INVALID("[");
+ TEST_INVALID("[]");
+ TEST_INVALID("[^]");
+ TEST_INVALID("[\\a]");
+ TEST_INVALID("\\a");
+
+#undef TEST_INVALID
+}
+
+TEST(Regex, Issue538) {
+ Regex re("^[0-9]+(\\\\.[0-9]+){0,2}");
+ EXPECT_TRUE(re.IsValid());
+}
+
+TEST(Regex, Issue583) {
+ Regex re("[0-9]{99999}");
+ ASSERT_TRUE(re.IsValid());
+}
+
+#undef EURO
diff --git a/src/rapidjson/test/unittest/schematest.cpp b/src/rapidjson/test/unittest/schematest.cpp
new file mode 100644
index 000000000..d75b1e593
--- /dev/null
+++ b/src/rapidjson/test/unittest/schematest.cpp
@@ -0,0 +1,1313 @@
+// 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. All rights reserved.
+//
+// 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/schema.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/writer.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(variadic-macros)
+#endif
+
+using namespace rapidjson;
+
+#define TEST_HASHER(json1, json2, expected) \
+{\
+ Document d1, d2;\
+ d1.Parse(json1);\
+ ASSERT_FALSE(d1.HasParseError());\
+ d2.Parse(json2);\
+ ASSERT_FALSE(d2.HasParseError());\
+ internal::Hasher<Value, CrtAllocator> h1, h2;\
+ d1.Accept(h1);\
+ d2.Accept(h2);\
+ ASSERT_TRUE(h1.IsValid());\
+ ASSERT_TRUE(h2.IsValid());\
+ /*printf("%s: 0x%016llx\n%s: 0x%016llx\n\n", json1, h1.GetHashCode(), json2, h2.GetHashCode());*/\
+ EXPECT_TRUE(expected == (h1.GetHashCode() == h2.GetHashCode()));\
+}
+
+TEST(SchemaValidator, Hasher) {
+ TEST_HASHER("null", "null", true);
+
+ TEST_HASHER("true", "true", true);
+ TEST_HASHER("false", "false", true);
+ TEST_HASHER("true", "false", false);
+ TEST_HASHER("false", "true", false);
+ TEST_HASHER("true", "null", false);
+ TEST_HASHER("false", "null", false);
+
+ TEST_HASHER("1", "1", true);
+ TEST_HASHER("2147483648", "2147483648", true); // 2^31 can only be fit in unsigned
+ TEST_HASHER("-2147483649", "-2147483649", true); // -2^31 - 1 can only be fit in int64_t
+ TEST_HASHER("2147483648", "2147483648", true); // 2^31 can only be fit in unsigned
+ TEST_HASHER("4294967296", "4294967296", true); // 2^32 can only be fit in int64_t
+ TEST_HASHER("9223372036854775808", "9223372036854775808", true); // 2^63 can only be fit in uint64_t
+ TEST_HASHER("1.5", "1.5", true);
+ TEST_HASHER("1", "1.0", true);
+ TEST_HASHER("1", "-1", false);
+ TEST_HASHER("0.0", "-0.0", false);
+ TEST_HASHER("1", "true", false);
+ TEST_HASHER("0", "false", false);
+ TEST_HASHER("0", "null", false);
+
+ TEST_HASHER("\"\"", "\"\"", true);
+ TEST_HASHER("\"\"", "\"\\u0000\"", false);
+ TEST_HASHER("\"Hello\"", "\"Hello\"", true);
+ TEST_HASHER("\"Hello\"", "\"World\"", false);
+ TEST_HASHER("\"Hello\"", "null", false);
+ TEST_HASHER("\"Hello\\u0000\"", "\"Hello\"", false);
+ TEST_HASHER("\"\"", "null", false);
+ TEST_HASHER("\"\"", "true", false);
+ TEST_HASHER("\"\"", "false", false);
+
+ TEST_HASHER("[]", "[ ]", true);
+ TEST_HASHER("[1, true, false]", "[1, true, false]", true);
+ TEST_HASHER("[1, true, false]", "[1, true]", false);
+ TEST_HASHER("[1, 2]", "[2, 1]", false);
+ TEST_HASHER("[[1], 2]", "[[1, 2]]", false);
+ TEST_HASHER("[1, 2]", "[1, [2]]", false);
+ TEST_HASHER("[]", "null", false);
+ TEST_HASHER("[]", "true", false);
+ TEST_HASHER("[]", "false", false);
+ TEST_HASHER("[]", "0", false);
+ TEST_HASHER("[]", "0.0", false);
+ TEST_HASHER("[]", "\"\"", false);
+
+ TEST_HASHER("{}", "{ }", true);
+ TEST_HASHER("{\"a\":1}", "{\"a\":1}", true);
+ TEST_HASHER("{\"a\":1}", "{\"b\":1}", false);
+ TEST_HASHER("{\"a\":1}", "{\"a\":2}", false);
+ TEST_HASHER("{\"a\":1, \"b\":2}", "{\"b\":2, \"a\":1}", true); // Member order insensitive
+ TEST_HASHER("{}", "null", false);
+ TEST_HASHER("{}", "false", false);
+ TEST_HASHER("{}", "true", false);
+ TEST_HASHER("{}", "0", false);
+ TEST_HASHER("{}", "0.0", false);
+ TEST_HASHER("{}", "\"\"", false);
+}
+
+// Test cases following http://spacetelescope.github.io/understanding-json-schema
+
+#define VALIDATE(schema, json, expected) \
+{\
+ SchemaValidator validator(schema);\
+ Document d;\
+ /*printf("\n%s\n", json);*/\
+ d.Parse(json);\
+ EXPECT_FALSE(d.HasParseError());\
+ EXPECT_TRUE(expected == d.Accept(validator));\
+ EXPECT_TRUE(expected == validator.IsValid());\
+ if ((expected) && !validator.IsValid()) {\
+ StringBuffer sb;\
+ validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);\
+ printf("Invalid schema: %s\n", sb.GetString());\
+ printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());\
+ sb.Clear();\
+ validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);\
+ printf("Invalid document: %s\n", sb.GetString());\
+ }\
+}
+
+#define INVALIDATE(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer) \
+{\
+ SchemaValidator validator(schema);\
+ Document d;\
+ /*printf("\n%s\n", json);*/\
+ d.Parse(json);\
+ EXPECT_FALSE(d.HasParseError());\
+ EXPECT_FALSE(d.Accept(validator));\
+ EXPECT_FALSE(validator.IsValid());\
+ if (validator.GetInvalidSchemaPointer() != Pointer(invalidSchemaPointer)) {\
+ StringBuffer sb;\
+ validator.GetInvalidSchemaPointer().Stringify(sb);\
+ printf("GetInvalidSchemaPointer() Expected: %s Actual: %s\n", invalidSchemaPointer, sb.GetString());\
+ ADD_FAILURE();\
+ }\
+ ASSERT_TRUE(validator.GetInvalidSchemaKeyword() != 0);\
+ if (strcmp(validator.GetInvalidSchemaKeyword(), invalidSchemaKeyword) != 0) {\
+ printf("GetInvalidSchemaKeyword() Expected: %s Actual %s\n", invalidSchemaKeyword, validator.GetInvalidSchemaKeyword());\
+ ADD_FAILURE();\
+ }\
+ if (validator.GetInvalidDocumentPointer() != Pointer(invalidDocumentPointer)) {\
+ StringBuffer sb;\
+ validator.GetInvalidDocumentPointer().Stringify(sb);\
+ printf("GetInvalidDocumentPointer() Expected: %s Actual: %s\n", invalidDocumentPointer, sb.GetString());\
+ ADD_FAILURE();\
+ }\
+}
+
+TEST(SchemaValidator, Typeless) {
+ Document sd;
+ sd.Parse("{}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "\"I'm a string\"", true);
+ VALIDATE(s, "{ \"an\": [ \"arbitrarily\", \"nested\" ], \"data\": \"structure\" }", true);
+}
+
+TEST(SchemaValidator, MultiType) {
+ Document sd;
+ sd.Parse("{ \"type\": [\"number\", \"string\"] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "\"Life, the universe, and everything\"", true);
+ INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", "");
+}
+
+TEST(SchemaValidator, Enum_Typed) {
+ Document sd;
+ sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"red\"", true);
+ INVALIDATE(s, "\"blue\"", "", "enum", "");
+}
+
+TEST(SchemaValidator, Enum_Typless) {
+ Document sd;
+ sd.Parse("{ \"enum\": [\"red\", \"amber\", \"green\", null, 42] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"red\"", true);
+ VALIDATE(s, "null", true);
+ VALIDATE(s, "42", true);
+ INVALIDATE(s, "0", "", "enum", "");
+}
+
+TEST(SchemaValidator, Enum_InvalidType) {
+ Document sd;
+ sd.Parse("{ \"type\": \"string\", \"enum\": [\"red\", \"amber\", \"green\", null] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"red\"", true);
+ INVALIDATE(s, "null", "", "type", "");
+}
+
+TEST(SchemaValidator, AllOf) {
+ {
+ Document sd;
+ sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"string\", \"maxLength\": 5 }]}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"ok\"", true);
+ INVALIDATE(s, "\"too long\"", "", "allOf", "");
+ }
+ {
+ Document sd;
+ sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"number\" } ] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"No way\"", false);
+ INVALIDATE(s, "-1", "", "allOf", "");
+ }
+}
+
+TEST(SchemaValidator, AnyOf) {
+ Document sd;
+ sd.Parse("{\"anyOf\": [{ \"type\": \"string\" }, { \"type\": \"number\" } ] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"Yes\"", true);
+ VALIDATE(s, "42", true);
+ INVALIDATE(s, "{ \"Not a\": \"string or number\" }", "", "anyOf", "");
+}
+
+TEST(SchemaValidator, OneOf) {
+ Document sd;
+ sd.Parse("{\"oneOf\": [{ \"type\": \"number\", \"multipleOf\": 5 }, { \"type\": \"number\", \"multipleOf\": 3 } ] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "9", true);
+ INVALIDATE(s, "2", "", "oneOf", "");
+ INVALIDATE(s, "15", "", "oneOf", "");
+}
+
+TEST(SchemaValidator, Not) {
+ Document sd;
+ sd.Parse("{\"not\":{ \"type\": \"string\"}}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "{ \"key\": \"value\" }", true);
+ INVALIDATE(s, "\"I am a string\"", "", "not", "");
+}
+
+TEST(SchemaValidator, Ref) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"$schema\": \"http://json-schema.org/draft-04/schema#\","
+ ""
+ " \"definitions\": {"
+ " \"address\": {"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"street_address\": { \"type\": \"string\" },"
+ " \"city\": { \"type\": \"string\" },"
+ " \"state\": { \"type\": \"string\" }"
+ " },"
+ " \"required\": [\"street_address\", \"city\", \"state\"]"
+ " }"
+ " },"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"billing_address\": { \"$ref\": \"#/definitions/address\" },"
+ " \"shipping_address\": { \"$ref\": \"#/definitions/address\" }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"}, \"billing_address\": {\"street_address\": \"1st Street SE\", \"city\": \"Washington\", \"state\": \"DC\"} }", true);
+}
+
+TEST(SchemaValidator, Ref_AllOf) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"$schema\": \"http://json-schema.org/draft-04/schema#\","
+ ""
+ " \"definitions\": {"
+ " \"address\": {"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"street_address\": { \"type\": \"string\" },"
+ " \"city\": { \"type\": \"string\" },"
+ " \"state\": { \"type\": \"string\" }"
+ " },"
+ " \"required\": [\"street_address\", \"city\", \"state\"]"
+ " }"
+ " },"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"billing_address\": { \"$ref\": \"#/definitions/address\" },"
+ " \"shipping_address\": {"
+ " \"allOf\": ["
+ " { \"$ref\": \"#/definitions/address\" },"
+ " { \"properties\":"
+ " { \"type\": { \"enum\": [ \"residential\", \"business\" ] } },"
+ " \"required\": [\"type\"]"
+ " }"
+ " ]"
+ " }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "allOf", "/shipping_address");
+ VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true);
+}
+
+TEST(SchemaValidator, String) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"I'm a string\"", true);
+ INVALIDATE(s, "42", "", "type", "");
+ INVALIDATE(s, "2147483648", "", "type", ""); // 2^31 can only be fit in unsigned
+ INVALIDATE(s, "-2147483649", "", "type", ""); // -2^31 - 1 can only be fit in int64_t
+ INVALIDATE(s, "4294967296", "", "type", ""); // 2^32 can only be fit in int64_t
+ INVALIDATE(s, "3.1415926", "", "type", "");
+}
+
+TEST(SchemaValidator, String_LengthRange) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "\"A\"", "", "minLength", "");
+ VALIDATE(s, "\"AB\"", true);
+ VALIDATE(s, "\"ABC\"", true);
+ INVALIDATE(s, "\"ABCD\"", "", "maxLength", "");
+}
+
+#if RAPIDJSON_SCHEMA_HAS_REGEX
+TEST(SchemaValidator, String_Pattern) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"pattern\":\"^(\\\\([0-9]{3}\\\\))?[0-9]{3}-[0-9]{4}$\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"555-1212\"", true);
+ VALIDATE(s, "\"(888)555-1212\"", true);
+ INVALIDATE(s, "\"(888)555-1212 ext. 532\"", "", "pattern", "");
+ INVALIDATE(s, "\"(800)FLOWERS\"", "", "pattern", "");
+}
+
+TEST(SchemaValidator, String_Pattern_Invalid) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"pattern\":\"a{0}\"}"); // TODO: report regex is invalid somehow
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"\"", true);
+ VALIDATE(s, "\"a\"", true);
+ VALIDATE(s, "\"aa\"", true);
+}
+#endif
+
+TEST(SchemaValidator, Integer) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "-1", true);
+ VALIDATE(s, "2147483648", true); // 2^31 can only be fit in unsigned
+ VALIDATE(s, "-2147483649", true); // -2^31 - 1 can only be fit in int64_t
+ VALIDATE(s, "2147483648", true); // 2^31 can only be fit in unsigned
+ VALIDATE(s, "4294967296", true); // 2^32 can only be fit in int64_t
+ INVALIDATE(s, "3.1415926", "", "type", "");
+ INVALIDATE(s, "\"42\"", "", "type", "");
+}
+
+TEST(SchemaValidator, Integer_Range) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-1", "", "minimum", "");
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "99", true);
+ INVALIDATE(s, "100", "", "maximum", "");
+ INVALIDATE(s, "101", "", "maximum", "");
+}
+
+TEST(SchemaValidator, Integer_Range64Boundary) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775807,\"maximum\":9223372036854775806}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
+ VALIDATE(s, "-9223372036854775807", true);
+ VALIDATE(s, "-2147483648", true); // int min
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "2147483647", true); // int max
+ VALIDATE(s, "2147483648", true); // unsigned first
+ VALIDATE(s, "4294967295", true); // unsigned max
+ VALIDATE(s, "9223372036854775806", true);
+ INVALIDATE(s, "9223372036854775807", "", "maximum", "");
+ INVALIDATE(s, "18446744073709551615", "", "maximum", ""); // uint64_t max
+}
+
+TEST(SchemaValidator, Integer_RangeU64Boundary) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"minimum\":9223372036854775808,\"maximum\":18446744073709551614}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
+ INVALIDATE(s, "9223372036854775807", "", "minimum", "");
+ INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min
+ INVALIDATE(s, "0", "", "minimum", "");
+ INVALIDATE(s, "2147483647", "", "minimum", ""); // int max
+ INVALIDATE(s, "2147483648", "", "minimum", ""); // unsigned first
+ INVALIDATE(s, "4294967295", "", "minimum", ""); // unsigned max
+ VALIDATE(s, "9223372036854775808", true);
+ VALIDATE(s, "18446744073709551614", true);
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "");
+}
+
+TEST(SchemaValidator, Integer_Range64BoundaryExclusive) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775808,\"maximum\":18446744073709551615,\"exclusiveMinimum\":true,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
+ VALIDATE(s, "-9223372036854775807", true);
+ VALIDATE(s, "18446744073709551614", true);
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "");
+}
+
+TEST(SchemaValidator, Integer_MultipleOf) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"multipleOf\":10}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "-10", true);
+ VALIDATE(s, "20", true);
+ INVALIDATE(s, "23", "", "multipleOf", "");
+ INVALIDATE(s, "-23", "", "multipleOf", "");
+}
+
+TEST(SchemaValidator, Integer_MultipleOf64Boundary) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"multipleOf\":18446744073709551615}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "18446744073709551615", true);
+ INVALIDATE(s, "18446744073709551614", "", "multipleOf", "");
+}
+
+TEST(SchemaValidator, Number_Range) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-1", "", "minimum", "");
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "0.1", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "99", true);
+ VALIDATE(s, "99.9", true);
+ INVALIDATE(s, "100", "", "maximum", "");
+ INVALIDATE(s, "100.0", "", "maximum", "");
+ INVALIDATE(s, "101.5", "", "maximum", "");
+}
+
+TEST(SchemaValidator, Number_RangeInt) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"minimum\":-100,\"maximum\":-1,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-101", "", "minimum", "");
+ INVALIDATE(s, "-100.1", "", "minimum", "");
+ VALIDATE(s, "-100", true);
+ VALIDATE(s, "-2", true);
+ INVALIDATE(s, "-1", "", "maximum", "");
+ INVALIDATE(s, "-0.9", "", "maximum", "");
+ INVALIDATE(s, "0", "", "maximum", "");
+ INVALIDATE(s, "2147483647", "", "maximum", ""); // int max
+ INVALIDATE(s, "2147483648", "", "maximum", ""); // unsigned first
+ INVALIDATE(s, "4294967295", "", "maximum", ""); // unsigned max
+ INVALIDATE(s, "9223372036854775808", "", "maximum", "");
+ INVALIDATE(s, "18446744073709551614", "", "maximum", "");
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "");
+}
+
+TEST(SchemaValidator, Number_RangeDouble) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"minimum\":0.1,\"maximum\":100.1,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
+ INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min
+ INVALIDATE(s, "-1", "", "minimum", "");
+ VALIDATE(s, "0.1", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "99", true);
+ VALIDATE(s, "100", true);
+ INVALIDATE(s, "101", "", "maximum", "");
+ INVALIDATE(s, "101.5", "", "maximum", "");
+ INVALIDATE(s, "18446744073709551614", "", "maximum", "");
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "");
+ INVALIDATE(s, "2147483647", "", "maximum", ""); // int max
+ INVALIDATE(s, "2147483648", "", "maximum", ""); // unsigned first
+ INVALIDATE(s, "4294967295", "", "maximum", ""); // unsigned max
+ INVALIDATE(s, "9223372036854775808", "", "maximum", "");
+ INVALIDATE(s, "18446744073709551614", "", "maximum", "");
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "");
+}
+
+TEST(SchemaValidator, Number_RangeDoubleU64Boundary) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"minimum\":9223372036854775808.0,\"maximum\":18446744073709550000.0}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
+ INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min
+ INVALIDATE(s, "0", "", "minimum", "");
+ INVALIDATE(s, "2147483647", "", "minimum", ""); // int max
+ INVALIDATE(s, "2147483648", "", "minimum", ""); // unsigned first
+ INVALIDATE(s, "4294967295", "", "minimum", ""); // unsigned max
+ VALIDATE(s, "9223372036854775808", true);
+ VALIDATE(s, "18446744073709540000", true);
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "");
+}
+
+TEST(SchemaValidator, Number_MultipleOf) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"multipleOf\":10.0}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "-10", true);
+ VALIDATE(s, "20", true);
+ INVALIDATE(s, "23", "", "multipleOf", "");
+ INVALIDATE(s, "-2147483648", "", "multipleOf", ""); // int min
+ VALIDATE(s, "-2147483640", true);
+ INVALIDATE(s, "2147483647", "", "multipleOf", ""); // int max
+ INVALIDATE(s, "2147483648", "", "multipleOf", ""); // unsigned first
+ VALIDATE(s, "2147483650", true);
+ INVALIDATE(s, "4294967295", "", "multipleOf", ""); // unsigned max
+ VALIDATE(s, "4294967300", true);
+}
+
+TEST(SchemaValidator, Number_MultipleOfOne) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"multipleOf\":1}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "42.0", true);
+ INVALIDATE(s, "3.1415926", "", "multipleOf", "");
+}
+
+TEST(SchemaValidator, Object) {
+ Document sd;
+ sd.Parse("{\"type\":\"object\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{\"key\":\"value\",\"another_key\":\"another_value\"}", true);
+ VALIDATE(s, "{\"Sun\":1.9891e30,\"Jupiter\":1.8986e27,\"Saturn\":5.6846e26,\"Neptune\":10.243e25,\"Uranus\":8.6810e25,\"Earth\":5.9736e24,\"Venus\":4.8685e24,\"Mars\":6.4185e23,\"Mercury\":3.3022e23,\"Moon\":7.349e22,\"Pluto\":1.25e22}", true);
+ INVALIDATE(s, "[\"An\", \"array\", \"not\", \"an\", \"object\"]", "", "type", "");
+ INVALIDATE(s, "\"Not an object\"", "", "type", "");
+}
+
+TEST(SchemaValidator, Object_Properties) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"number\": { \"type\": \"number\" },"
+ " \"street_name\" : { \"type\": \"string\" },"
+ " \"street_type\" : { \"type\": \"string\", \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"] }"
+ " }"
+ "}");
+
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true);
+ INVALIDATE(s, "{ \"number\": \"1600\", \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", "/properties/number", "type", "/number");
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\" }", true);
+ VALIDATE(s, "{}", true);
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true);
+}
+
+TEST(SchemaValidator, Object_AdditionalPropertiesBoolean) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"number\": { \"type\": \"number\" },"
+ " \"street_name\" : { \"type\": \"string\" },"
+ " \"street_type\" : { \"type\": \"string\","
+ " \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"]"
+ " }"
+ " },"
+ " \"additionalProperties\": false"
+ "}");
+
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true);
+ INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", "", "additionalProperties", "/direction");
+}
+
+TEST(SchemaValidator, Object_AdditionalPropertiesObject) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"number\": { \"type\": \"number\" },"
+ " \"street_name\" : { \"type\": \"string\" },"
+ " \"street_type\" : { \"type\": \"string\","
+ " \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"]"
+ " }"
+ " },"
+ " \"additionalProperties\": { \"type\": \"string\" }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true);
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true);
+ INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"office_number\": 201 }", "/additionalProperties", "type", "/office_number");
+}
+
+TEST(SchemaValidator, Object_Required) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"name\": { \"type\": \"string\" },"
+ " \"email\" : { \"type\": \"string\" },"
+ " \"address\" : { \"type\": \"string\" },"
+ " \"telephone\" : { \"type\": \"string\" }"
+ " },"
+ " \"required\":[\"name\", \"email\"]"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\" }", true);
+ VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\", \"authorship\" : \"in question\"}", true);
+ INVALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", "", "required", "");
+}
+
+
+TEST(SchemaValidator, Object_PropertiesRange) {
+ Document sd;
+ sd.Parse("{\"type\":\"object\", \"minProperties\":2, \"maxProperties\":3}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "{}", "", "minProperties", "");
+ INVALIDATE(s, "{\"a\":0}", "", "minProperties", "");
+ VALIDATE(s, "{\"a\":0,\"b\":1}", true);
+ VALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2}", true);
+ INVALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2,\"d\":3}", "", "maxProperties", "");
+}
+
+TEST(SchemaValidator, Object_PropertyDependencies) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"name\": { \"type\": \"string\" },"
+ " \"credit_card\": { \"type\": \"number\" },"
+ " \"billing_address\": { \"type\": \"string\" }"
+ " },"
+ " \"required\": [\"name\"],"
+ " \"dependencies\": {"
+ " \"credit_card\": [\"billing_address\"]"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555, \"billing_address\": \"555 Debtor's Lane\" }", true);
+ INVALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555 }", "", "dependencies", "");
+ VALIDATE(s, "{ \"name\": \"John Doe\"}", true);
+ VALIDATE(s, "{ \"name\": \"John Doe\", \"billing_address\": \"555 Debtor's Lane\" }", true);
+}
+
+TEST(SchemaValidator, Object_SchemaDependencies) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"name\": { \"type\": \"string\" },"
+ " \"credit_card\" : { \"type\": \"number\" }"
+ " },"
+ " \"required\" : [\"name\"],"
+ " \"dependencies\" : {"
+ " \"credit_card\": {"
+ " \"properties\": {"
+ " \"billing_address\": { \"type\": \"string\" }"
+ " },"
+ " \"required\" : [\"billing_address\"]"
+ " }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555,\"billing_address\" : \"555 Debtor's Lane\"}", true);
+ INVALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555 }", "", "dependencies", "");
+ VALIDATE(s, "{\"name\": \"John Doe\", \"billing_address\" : \"555 Debtor's Lane\"}", true);
+}
+
+#if RAPIDJSON_SCHEMA_HAS_REGEX
+TEST(SchemaValidator, Object_PatternProperties) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"patternProperties\": {"
+ " \"^S_\": { \"type\": \"string\" },"
+ " \"^I_\": { \"type\": \"integer\" }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"S_25\": \"This is a string\" }", true);
+ VALIDATE(s, "{ \"I_0\": 42 }", true);
+ INVALIDATE(s, "{ \"S_0\": 42 }", "", "patternProperties", "/S_0");
+ INVALIDATE(s, "{ \"I_42\": \"This is a string\" }", "", "patternProperties", "/I_42");
+ VALIDATE(s, "{ \"keyword\": \"value\" }", true);
+}
+
+TEST(SchemaValidator, Object_PatternProperties_AdditionalProperties) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"builtin\": { \"type\": \"number\" }"
+ " },"
+ " \"patternProperties\": {"
+ " \"^S_\": { \"type\": \"string\" },"
+ " \"^I_\": { \"type\": \"integer\" }"
+ " },"
+ " \"additionalProperties\": { \"type\": \"string\" }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"builtin\": 42 }", true);
+ VALIDATE(s, "{ \"keyword\": \"value\" }", true);
+ INVALIDATE(s, "{ \"keyword\": 42 }", "/additionalProperties", "type", "/keyword");
+}
+#endif
+
+TEST(SchemaValidator, Array) {
+ Document sd;
+ sd.Parse("{\"type\":\"array\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1, 2, 3, 4, 5]", true);
+ VALIDATE(s, "[3, \"different\", { \"types\" : \"of values\" }]", true);
+ INVALIDATE(s, "{\"Not\": \"an array\"}", "", "type", "");
+}
+
+TEST(SchemaValidator, Array_ItemsList) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"array\","
+ " \"items\" : {"
+ " \"type\": \"number\""
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1, 2, 3, 4, 5]", true);
+ INVALIDATE(s, "[1, 2, \"3\", 4, 5]", "/items", "type", "/2");
+ VALIDATE(s, "[]", true);
+}
+
+TEST(SchemaValidator, Array_ItemsTuple) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"array\","
+ " \"items\": ["
+ " {"
+ " \"type\": \"number\""
+ " },"
+ " {"
+ " \"type\": \"string\""
+ " },"
+ " {"
+ " \"type\": \"string\","
+ " \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"]"
+ " },"
+ " {"
+ " \"type\": \"string\","
+ " \"enum\": [\"NW\", \"NE\", \"SW\", \"SE\"]"
+ " }"
+ " ]"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true);
+ INVALIDATE(s, "[24, \"Sussex\", \"Drive\"]", "/items/2", "enum", "/2");
+ INVALIDATE(s, "[\"Palais de l'Elysee\"]", "/items/0", "type", "/0");
+ VALIDATE(s, "[10, \"Downing\", \"Street\"]", true);
+ VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", true);
+}
+
+TEST(SchemaValidator, Array_AdditionalItmes) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"array\","
+ " \"items\": ["
+ " {"
+ " \"type\": \"number\""
+ " },"
+ " {"
+ " \"type\": \"string\""
+ " },"
+ " {"
+ " \"type\": \"string\","
+ " \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"]"
+ " },"
+ " {"
+ " \"type\": \"string\","
+ " \"enum\": [\"NW\", \"NE\", \"SW\", \"SE\"]"
+ " }"
+ " ],"
+ " \"additionalItems\": false"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true);
+ VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\"]", true);
+ INVALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", "", "items", "/4");
+}
+
+TEST(SchemaValidator, Array_ItemsRange) {
+ Document sd;
+ sd.Parse("{\"type\": \"array\",\"minItems\": 2,\"maxItems\" : 3}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "[]", "", "minItems", "");
+ INVALIDATE(s, "[1]", "", "minItems", "");
+ VALIDATE(s, "[1, 2]", true);
+ VALIDATE(s, "[1, 2, 3]", true);
+ INVALIDATE(s, "[1, 2, 3, 4]", "", "maxItems", "");
+}
+
+TEST(SchemaValidator, Array_UniqueItems) {
+ Document sd;
+ sd.Parse("{\"type\": \"array\", \"uniqueItems\": true}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1, 2, 3, 4, 5]", true);
+ INVALIDATE(s, "[1, 2, 3, 3, 4]", "", "uniqueItems", "/3");
+ VALIDATE(s, "[]", true);
+}
+
+TEST(SchemaValidator, Boolean) {
+ Document sd;
+ sd.Parse("{\"type\":\"boolean\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "true", true);
+ VALIDATE(s, "false", true);
+ INVALIDATE(s, "\"true\"", "", "type", "");
+ INVALIDATE(s, "0", "", "type", "");
+}
+
+TEST(SchemaValidator, Null) {
+ Document sd;
+ sd.Parse("{\"type\":\"null\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "null", true);
+ INVALIDATE(s, "false", "", "type", "");
+ INVALIDATE(s, "0", "", "type", "");
+ INVALIDATE(s, "\"\"", "", "type", "");
+}
+
+// Additional tests
+
+TEST(SchemaValidator, ObjectInArray) {
+ Document sd;
+ sd.Parse("{\"type\":\"array\", \"items\": { \"type\":\"string\" }}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[\"a\"]", true);
+ INVALIDATE(s, "[1]", "/items", "type", "/0");
+ INVALIDATE(s, "[{}]", "/items", "type", "/0");
+}
+
+TEST(SchemaValidator, MultiTypeInObject) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\":\"object\","
+ " \"properties\": {"
+ " \"tel\" : {"
+ " \"type\":[\"integer\", \"string\"]"
+ " }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"tel\": 999 }", true);
+ VALIDATE(s, "{ \"tel\": \"123-456\" }", true);
+ INVALIDATE(s, "{ \"tel\": true }", "/properties/tel", "type", "/tel");
+}
+
+TEST(SchemaValidator, MultiTypeWithObject) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": [\"object\",\"string\"],"
+ " \"properties\": {"
+ " \"tel\" : {"
+ " \"type\": \"integer\""
+ " }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"Hello\"", true);
+ VALIDATE(s, "{ \"tel\": 999 }", true);
+ INVALIDATE(s, "{ \"tel\": \"fail\" }", "/properties/tel", "type", "/tel");
+}
+
+TEST(SchemaValidator, AllOf_Nested) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"allOf\": ["
+ " { \"type\": \"string\", \"minLength\": 2 },"
+ " { \"type\": \"string\", \"maxLength\": 5 },"
+ " { \"allOf\": [ { \"enum\" : [\"ok\", \"okay\", \"OK\", \"o\"] }, { \"enum\" : [\"ok\", \"OK\", \"o\"]} ] }"
+ " ]"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"ok\"", true);
+ VALIDATE(s, "\"OK\"", true);
+ INVALIDATE(s, "\"okay\"", "", "allOf", "");
+ INVALIDATE(s, "\"o\"", "", "allOf", "");
+ INVALIDATE(s, "\"n\"", "", "allOf", "");
+ INVALIDATE(s, "\"too long\"", "", "allOf", "");
+ INVALIDATE(s, "123", "", "allOf", "");
+}
+
+TEST(SchemaValidator, EscapedPointer) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"~/\": { \"type\": \"number\" }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+ INVALIDATE(s, "{\"~/\":true}", "/properties/~0~1", "type", "/~0~1");
+}
+
+template <typename Allocator>
+static char* ReadFile(const char* filename, Allocator& allocator) {
+ const char *paths[] = {
+ "",
+ "bin/",
+ "../bin/",
+ "../../bin/",
+ "../../../bin/"
+ };
+ char buffer[1024];
+ FILE *fp = 0;
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s%s", paths[i], filename);
+ fp = fopen(buffer, "rb");
+ if (fp)
+ break;
+ }
+
+ if (!fp)
+ return 0;
+
+ fseek(fp, 0, SEEK_END);
+ size_t length = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ char* json = reinterpret_cast<char*>(allocator.Malloc(length + 1));
+ size_t readLength = fread(json, 1, length, fp);
+ json[readLength] = '\0';
+ fclose(fp);
+ return json;
+}
+
+TEST(SchemaValidator, ValidateMetaSchema) {
+ CrtAllocator allocator;
+ char* json = ReadFile("draft-04/schema", allocator);
+ Document d;
+ d.Parse(json);
+ ASSERT_FALSE(d.HasParseError());
+ SchemaDocument sd(d);
+ SchemaValidator validator(sd);
+ if (!d.Accept(validator)) {
+ StringBuffer sb;
+ validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
+ printf("Invalid schema: %s\n", sb.GetString());
+ printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());
+ sb.Clear();
+ validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
+ printf("Invalid document: %s\n", sb.GetString());
+ ADD_FAILURE();
+ }
+ CrtAllocator::Free(json);
+}
+
+TEST(SchemaValidator, ValidateMetaSchema_UTF16) {
+ typedef GenericDocument<UTF16<> > D;
+ typedef GenericSchemaDocument<D::ValueType> SD;
+ typedef GenericSchemaValidator<SD> SV;
+
+ CrtAllocator allocator;
+ char* json = ReadFile("draft-04/schema", allocator);
+
+ D d;
+ StringStream ss(json);
+ d.ParseStream<0, UTF8<> >(ss);
+ ASSERT_FALSE(d.HasParseError());
+ SD sd(d);
+ SV validator(sd);
+ if (!d.Accept(validator)) {
+ GenericStringBuffer<UTF16<> > sb;
+ validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
+ wprintf(L"Invalid schema: %ls\n", sb.GetString());
+ wprintf(L"Invalid keyword: %ls\n", validator.GetInvalidSchemaKeyword());
+ sb.Clear();
+ validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
+ wprintf(L"Invalid document: %ls\n", sb.GetString());
+ ADD_FAILURE();
+ }
+ CrtAllocator::Free(json);
+}
+
+template <typename SchemaDocumentType = SchemaDocument>
+class RemoteSchemaDocumentProvider : public IGenericRemoteSchemaDocumentProvider<SchemaDocumentType> {
+public:
+ RemoteSchemaDocumentProvider() :
+ documentAllocator_(documentBuffer_, sizeof(documentBuffer_)),
+ schemaAllocator_(schemaBuffer_, sizeof(schemaBuffer_))
+ {
+ const char* filenames[kCount] = {
+ "jsonschema/remotes/integer.json",
+ "jsonschema/remotes/subSchemas.json",
+ "jsonschema/remotes/folder/folderInteger.json",
+ "draft-04/schema"
+ };
+
+ for (size_t i = 0; i < kCount; i++) {
+ sd_[i] = 0;
+
+ char jsonBuffer[8192];
+ MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer));
+ char* json = ReadFile(filenames[i], jsonAllocator);
+ if (!json) {
+ printf("json remote file %s not found", filenames[i]);
+ ADD_FAILURE();
+ }
+ else {
+ char stackBuffer[4096];
+ MemoryPoolAllocator<> stackAllocator(stackBuffer, sizeof(stackBuffer));
+ DocumentType d(&documentAllocator_, 1024, &stackAllocator);
+ d.Parse(json);
+ sd_[i] = new SchemaDocumentType(d, 0, &schemaAllocator_);
+ MemoryPoolAllocator<>::Free(json);
+ }
+ };
+ }
+
+ ~RemoteSchemaDocumentProvider() {
+ for (size_t i = 0; i < kCount; i++)
+ delete sd_[i];
+ }
+
+ virtual const SchemaDocumentType* GetRemoteDocument(const char* uri, SizeType length) {
+ const char* uris[kCount] = {
+ "http://localhost:1234/integer.json",
+ "http://localhost:1234/subSchemas.json",
+ "http://localhost:1234/folder/folderInteger.json",
+ "http://json-schema.org/draft-04/schema"
+ };
+
+ for (size_t i = 0; i < kCount; i++)
+ if (strncmp(uri, uris[i], length) == 0)
+ return sd_[i];
+ return 0;
+ }
+
+private:
+ typedef GenericDocument<typename SchemaDocumentType::EncodingType, MemoryPoolAllocator<>, MemoryPoolAllocator<> > DocumentType;
+
+ RemoteSchemaDocumentProvider(const RemoteSchemaDocumentProvider&);
+ RemoteSchemaDocumentProvider& operator=(const RemoteSchemaDocumentProvider&);
+
+ static const size_t kCount = 4;
+ SchemaDocumentType* sd_[kCount];
+ typename DocumentType::AllocatorType documentAllocator_;
+ typename SchemaDocumentType::AllocatorType schemaAllocator_;
+ char documentBuffer_[16384];
+ char schemaBuffer_[128 * 1024];
+};
+
+TEST(SchemaValidator, TestSuite) {
+ const char* filenames[] = {
+ "additionalItems.json",
+ "additionalProperties.json",
+ "allOf.json",
+ "anyOf.json",
+ "default.json",
+ "definitions.json",
+ "dependencies.json",
+ "enum.json",
+ "items.json",
+ "maximum.json",
+ "maxItems.json",
+ "maxLength.json",
+ "maxProperties.json",
+ "minimum.json",
+ "minItems.json",
+ "minLength.json",
+ "minProperties.json",
+ "multipleOf.json",
+ "not.json",
+ "oneOf.json",
+ "pattern.json",
+ "patternProperties.json",
+ "properties.json",
+ "ref.json",
+ "refRemote.json",
+ "required.json",
+ "type.json",
+ "uniqueItems.json"
+ };
+
+ const char* onlyRunDescription = 0;
+ //const char* onlyRunDescription = "a string is a string";
+
+ unsigned testCount = 0;
+ unsigned passCount = 0;
+
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+
+ char jsonBuffer[65536];
+ char documentBuffer[65536];
+ char documentStackBuffer[65536];
+ char schemaBuffer[65536];
+ char validatorBuffer[65536];
+ MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer));
+ MemoryPoolAllocator<> documentAllocator(documentBuffer, sizeof(documentBuffer));
+ MemoryPoolAllocator<> documentStackAllocator(documentStackBuffer, sizeof(documentStackBuffer));
+ MemoryPoolAllocator<> schemaAllocator(schemaBuffer, sizeof(schemaBuffer));
+ MemoryPoolAllocator<> validatorAllocator(validatorBuffer, sizeof(validatorBuffer));
+
+ for (size_t i = 0; i < sizeof(filenames) / sizeof(filenames[0]); i++) {
+ char filename[FILENAME_MAX];
+ sprintf(filename, "jsonschema/tests/draft4/%s", filenames[i]);
+ char* json = ReadFile(filename, jsonAllocator);
+ if (!json) {
+ printf("json test suite file %s not found", filename);
+ ADD_FAILURE();
+ }
+ else {
+ GenericDocument<UTF8<>, MemoryPoolAllocator<>, MemoryPoolAllocator<> > d(&documentAllocator, 1024, &documentStackAllocator);
+ d.Parse(json);
+ if (d.HasParseError()) {
+ printf("json test suite file %s has parse error", filename);
+ ADD_FAILURE();
+ }
+ else {
+ for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) {
+ {
+ SchemaDocumentType schema((*schemaItr)["schema"], &provider, &schemaAllocator);
+ GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > validator(schema, &validatorAllocator);
+ const char* description1 = (*schemaItr)["description"].GetString();
+ const Value& tests = (*schemaItr)["tests"];
+ for (Value::ConstValueIterator testItr = tests.Begin(); testItr != tests.End(); ++testItr) {
+ const char* description2 = (*testItr)["description"].GetString();
+ if (!onlyRunDescription || strcmp(description2, onlyRunDescription) == 0) {
+ const Value& data = (*testItr)["data"];
+ bool expected = (*testItr)["valid"].GetBool();
+ testCount++;
+ validator.Reset();
+ bool actual = data.Accept(validator);
+ if (expected != actual)
+ printf("Fail: %30s \"%s\" \"%s\"\n", filename, description1, description2);
+ else
+ passCount++;
+ }
+ }
+ //printf("%zu %zu %zu\n", documentAllocator.Size(), schemaAllocator.Size(), validatorAllocator.Size());
+ }
+ schemaAllocator.Clear();
+ validatorAllocator.Clear();
+ }
+ }
+ }
+ documentAllocator.Clear();
+ MemoryPoolAllocator<>::Free(json);
+ jsonAllocator.Clear();
+ }
+ printf("%d / %d passed (%2d%%)\n", passCount, testCount, passCount * 100 / testCount);
+ // if (passCount != testCount)
+ // ADD_FAILURE();
+}
+
+TEST(SchemaValidatingReader, Simple) {
+ Document sd;
+ sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }");
+ SchemaDocument s(sd);
+
+ Document d;
+ StringStream ss("\"red\"");
+ SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s);
+ d.Populate(reader);
+ EXPECT_TRUE(reader.GetParseResult());
+ EXPECT_TRUE(reader.IsValid());
+ EXPECT_TRUE(d.IsString());
+ EXPECT_STREQ("red", d.GetString());
+}
+
+TEST(SchemaValidatingReader, Invalid) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
+ SchemaDocument s(sd);
+
+ Document d;
+ StringStream ss("\"ABCD\"");
+ SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s);
+ d.Populate(reader);
+ EXPECT_FALSE(reader.GetParseResult());
+ EXPECT_FALSE(reader.IsValid());
+ EXPECT_EQ(kParseErrorTermination, reader.GetParseResult().Code());
+ EXPECT_STREQ("maxLength", reader.GetInvalidSchemaKeyword());
+ EXPECT_TRUE(reader.GetInvalidSchemaPointer() == SchemaDocument::PointerType(""));
+ EXPECT_TRUE(reader.GetInvalidDocumentPointer() == SchemaDocument::PointerType(""));
+ EXPECT_TRUE(d.IsNull());
+}
+
+TEST(SchemaValidatingWriter, Simple) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
+ SchemaDocument s(sd);
+
+ Document d;
+ StringBuffer sb;
+ Writer<StringBuffer> writer(sb);
+ GenericSchemaValidator<SchemaDocument, Writer<StringBuffer> > validator(s, writer);
+
+ d.Parse("\"red\"");
+ EXPECT_TRUE(d.Accept(validator));
+ EXPECT_TRUE(validator.IsValid());
+ EXPECT_STREQ("\"red\"", sb.GetString());
+
+ sb.Clear();
+ validator.Reset();
+ d.Parse("\"ABCD\"");
+ EXPECT_FALSE(d.Accept(validator));
+ EXPECT_FALSE(validator.IsValid());
+ EXPECT_TRUE(validator.GetInvalidSchemaPointer() == SchemaDocument::PointerType(""));
+ EXPECT_TRUE(validator.GetInvalidDocumentPointer() == SchemaDocument::PointerType(""));
+}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+static SchemaDocument ReturnSchemaDocument() {
+ Document sd;
+ sd.Parse("{ \"type\": [\"number\", \"string\"] }");
+ SchemaDocument s(sd);
+ return s;
+}
+
+TEST(Schema, Issue552) {
+ SchemaDocument s = ReturnSchemaDocument();
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "\"Life, the universe, and everything\"", true);
+ INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", "");
+}
+
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+TEST(SchemaValidator, Issue608) {
+ Document sd;
+ sd.Parse("{\"required\": [\"a\", \"b\"] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{\"a\" : null, \"b\": null}", true);
+ INVALIDATE(s, "{\"a\" : null, \"a\" : null}", "", "required", "");
+}
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/simdtest.cpp b/src/rapidjson/test/unittest/simdtest.cpp
new file mode 100644
index 000000000..b01b559f4
--- /dev/null
+++ b/src/rapidjson/test/unittest/simdtest.cpp
@@ -0,0 +1,215 @@
+// 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. All rights reserved.
+//
+// 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.
+
+// Since Travis CI installs old Valgrind 3.7.0, which fails with some SSE4.2
+// The unit tests prefix with SIMD should be skipped by Valgrind test
+
+// __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler.
+// We use -march=native with gmake to enable -msse2 and -msse4.2, if supported.
+#if defined(__SSE4_2__)
+# define RAPIDJSON_SSE42
+#elif defined(__SSE2__)
+# define RAPIDJSON_SSE2
+#endif
+
+#define RAPIDJSON_NAMESPACE rapidjson_simd
+
+#include "unittest.h"
+
+#include "rapidjson/reader.h"
+#include "rapidjson/writer.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+using namespace rapidjson_simd;
+
+#ifdef RAPIDJSON_SSE2
+#define SIMD_SUFFIX(name) name##_SSE2
+#elif defined(RAPIDJSON_SSE42)
+#define SIMD_SUFFIX(name) name##_SSE42
+#else
+#define SIMD_SUFFIX(name) name
+#endif
+
+template <typename StreamType>
+void TestSkipWhitespace() {
+ for (size_t step = 1; step < 32; step++) {
+ char buffer[1025];
+ for (size_t i = 0; i < 1024; i++)
+ buffer[i] = " \t\r\n"[i % 4];
+ for (size_t i = 0; i < 1024; i += step)
+ buffer[i] = 'X';
+ buffer[1024] = '\0';
+
+ StreamType s(buffer);
+ size_t i = 0;
+ for (;;) {
+ SkipWhitespace(s);
+ if (s.Peek() == '\0')
+ break;
+ EXPECT_EQ(i, s.Tell());
+ EXPECT_EQ('X', s.Take());
+ i += step;
+ }
+ }
+}
+
+TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) {
+ TestSkipWhitespace<StringStream>();
+ TestSkipWhitespace<InsituStringStream>();
+}
+
+TEST(SIMD, SIMD_SUFFIX(SkipWhitespace_EncodedMemoryStream)) {
+ for (size_t step = 1; step < 32; step++) {
+ char buffer[1024];
+ for (size_t i = 0; i < 1024; i++)
+ buffer[i] = " \t\r\n"[i % 4];
+ for (size_t i = 0; i < 1024; i += step)
+ buffer[i] = 'X';
+
+ MemoryStream ms(buffer, 1024);
+ EncodedInputStream<UTF8<>, MemoryStream> s(ms);
+ size_t i = 0;
+ for (;;) {
+ SkipWhitespace(s);
+ if (s.Peek() == '\0')
+ break;
+ //EXPECT_EQ(i, s.Tell());
+ EXPECT_EQ('X', s.Take());
+ i += step;
+ }
+ }
+}
+
+struct ScanCopyUnescapedStringHandler : BaseReaderHandler<UTF8<>, ScanCopyUnescapedStringHandler> {
+ bool String(const char* str, size_t length, bool) {
+ memcpy(buffer, str, length + 1);
+ return true;
+ }
+ char buffer[1024 + 5 + 32];
+};
+
+template <unsigned parseFlags, typename StreamType>
+void TestScanCopyUnescapedString() {
+ char buffer[1024 + 5 + 32];
+ char backup[1024 + 5 + 32];
+
+ // Test "ABCDABCD...\\"
+ for (size_t offset = 0; offset < 32; offset++) {
+ for (size_t step = 0; step < 1024; step++) {
+ char* json = buffer + offset;
+ char *p = json;
+ *p++ = '\"';
+ for (size_t i = 0; i < step; i++)
+ *p++ = "ABCD"[i % 4];
+ *p++ = '\\';
+ *p++ = '\\';
+ *p++ = '\"';
+ *p++ = '\0';
+ strcpy(backup, json); // insitu parsing will overwrite buffer, so need to backup first
+
+ StreamType s(json);
+ Reader reader;
+ ScanCopyUnescapedStringHandler h;
+ reader.Parse<parseFlags>(s, h);
+ EXPECT_TRUE(memcmp(h.buffer, backup + 1, step) == 0);
+ EXPECT_EQ('\\', h.buffer[step]); // escaped
+ EXPECT_EQ('\0', h.buffer[step + 1]);
+ }
+ }
+
+ // Test "\\ABCDABCD..."
+ for (size_t offset = 0; offset < 32; offset++) {
+ for (size_t step = 0; step < 1024; step++) {
+ char* json = buffer + offset;
+ char *p = json;
+ *p++ = '\"';
+ *p++ = '\\';
+ *p++ = '\\';
+ for (size_t i = 0; i < step; i++)
+ *p++ = "ABCD"[i % 4];
+ *p++ = '\"';
+ *p++ = '\0';
+ strcpy(backup, json); // insitu parsing will overwrite buffer, so need to backup first
+
+ StreamType s(json);
+ Reader reader;
+ ScanCopyUnescapedStringHandler h;
+ reader.Parse<parseFlags>(s, h);
+ EXPECT_TRUE(memcmp(h.buffer + 1, backup + 3, step) == 0);
+ EXPECT_EQ('\\', h.buffer[0]); // escaped
+ EXPECT_EQ('\0', h.buffer[step + 1]);
+ }
+ }
+}
+
+TEST(SIMD, SIMD_SUFFIX(ScanCopyUnescapedString)) {
+ TestScanCopyUnescapedString<kParseDefaultFlags, StringStream>();
+ TestScanCopyUnescapedString<kParseInsituFlag, InsituStringStream>();
+}
+
+TEST(SIMD, SIMD_SUFFIX(ScanWriteUnescapedString)) {
+ char buffer[2048 + 1 + 32];
+ for (size_t offset = 0; offset < 32; offset++) {
+ for (size_t step = 0; step < 1024; step++) {
+ char* s = buffer + offset;
+ char* p = s;
+ for (size_t i = 0; i < step; i++)
+ *p++ = "ABCD"[i % 4];
+ char escape = "\0\n\\\""[step % 4];
+ *p++ = escape;
+ for (size_t i = 0; i < step; i++)
+ *p++ = "ABCD"[i % 4];
+
+ StringBuffer sb;
+ Writer<StringBuffer> writer(sb);
+ writer.String(s, SizeType(step * 2 + 1));
+ const char* q = sb.GetString();
+ EXPECT_EQ('\"', *q++);
+ for (size_t i = 0; i < step; i++)
+ EXPECT_EQ("ABCD"[i % 4], *q++);
+ if (escape == '\0') {
+ EXPECT_EQ('\\', *q++);
+ EXPECT_EQ('u', *q++);
+ EXPECT_EQ('0', *q++);
+ EXPECT_EQ('0', *q++);
+ EXPECT_EQ('0', *q++);
+ EXPECT_EQ('0', *q++);
+ }
+ else if (escape == '\n') {
+ EXPECT_EQ('\\', *q++);
+ EXPECT_EQ('n', *q++);
+ }
+ else if (escape == '\\') {
+ EXPECT_EQ('\\', *q++);
+ EXPECT_EQ('\\', *q++);
+ }
+ else if (escape == '\"') {
+ EXPECT_EQ('\\', *q++);
+ EXPECT_EQ('\"', *q++);
+ }
+ for (size_t i = 0; i < step; i++)
+ EXPECT_EQ("ABCD"[i % 4], *q++);
+ EXPECT_EQ('\"', *q++);
+ EXPECT_EQ('\0', *q++);
+ }
+ }
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/strfunctest.cpp b/src/rapidjson/test/unittest/strfunctest.cpp
new file mode 100644
index 000000000..cc1bb22f0
--- /dev/null
+++ b/src/rapidjson/test/unittest/strfunctest.cpp
@@ -0,0 +1,30 @@
+// 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. All rights reserved.
+//
+// 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/internal/strfunc.h"
+
+using namespace rapidjson;
+using namespace rapidjson::internal;
+
+TEST(StrFunc, CountStringCodePoint) {
+ SizeType count;
+ EXPECT_TRUE(CountStringCodePoint<UTF8<> >("", 0, &count));
+ EXPECT_EQ(0u, count);
+ EXPECT_TRUE(CountStringCodePoint<UTF8<> >("Hello", 5, &count));
+ EXPECT_EQ(5u, count);
+ EXPECT_TRUE(CountStringCodePoint<UTF8<> >("\xC2\xA2\xE2\x82\xAC\xF0\x9D\x84\x9E", 9, &count)); // cents euro G-clef
+ EXPECT_EQ(3u, count);
+ EXPECT_FALSE(CountStringCodePoint<UTF8<> >("\xC2\xA2\xE2\x82\xAC\xF0\x9D\x84\x9E\x80", 10, &count));
+}
diff --git a/src/rapidjson/test/unittest/stringbuffertest.cpp b/src/rapidjson/test/unittest/stringbuffertest.cpp
new file mode 100644
index 000000000..ded513cdd
--- /dev/null
+++ b/src/rapidjson/test/unittest/stringbuffertest.cpp
@@ -0,0 +1,170 @@
+// 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. All rights reserved.
+//
+// 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/stringbuffer.h"
+#include "rapidjson/writer.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+using namespace rapidjson;
+
+TEST(StringBuffer, InitialSize) {
+ StringBuffer buffer;
+ EXPECT_EQ(0u, buffer.GetSize());
+ EXPECT_STREQ("", buffer.GetString());
+}
+
+TEST(StringBuffer, Put) {
+ StringBuffer buffer;
+ buffer.Put('A');
+
+ EXPECT_EQ(1u, buffer.GetSize());
+ EXPECT_STREQ("A", buffer.GetString());
+}
+
+TEST(StringBuffer, PutN_Issue672) {
+ GenericStringBuffer<UTF8<>, MemoryPoolAllocator<> > buffer;
+ EXPECT_EQ(0, buffer.GetSize());
+ rapidjson::PutN(buffer, ' ', 1);
+ EXPECT_EQ(1, buffer.GetSize());
+}
+
+TEST(StringBuffer, Clear) {
+ StringBuffer buffer;
+ buffer.Put('A');
+ buffer.Put('B');
+ buffer.Put('C');
+ buffer.Clear();
+
+ EXPECT_EQ(0u, buffer.GetSize());
+ EXPECT_STREQ("", buffer.GetString());
+}
+
+TEST(StringBuffer, Push) {
+ StringBuffer buffer;
+ buffer.Push(5);
+
+ EXPECT_EQ(5u, buffer.GetSize());
+
+ // Causes sudden expansion to make the stack's capacity equal to size
+ buffer.Push(65536u);
+ EXPECT_EQ(5u + 65536u, buffer.GetSize());
+}
+
+TEST(StringBuffer, Pop) {
+ StringBuffer buffer;
+ buffer.Put('A');
+ buffer.Put('B');
+ buffer.Put('C');
+ buffer.Put('D');
+ buffer.Put('E');
+ buffer.Pop(3);
+
+ EXPECT_EQ(2u, buffer.GetSize());
+ EXPECT_STREQ("AB", buffer.GetString());
+}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#if 0 // Many old compiler does not support these. Turn it off temporaily.
+
+#include <type_traits>
+
+TEST(StringBuffer, Traits) {
+ static_assert( std::is_constructible<StringBuffer>::value, "");
+ static_assert( std::is_default_constructible<StringBuffer>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_constructible<StringBuffer>::value, "");
+#endif
+ static_assert( std::is_move_constructible<StringBuffer>::value, "");
+
+ static_assert(!std::is_nothrow_constructible<StringBuffer>::value, "");
+ static_assert(!std::is_nothrow_default_constructible<StringBuffer>::value, "");
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1800
+ static_assert(!std::is_nothrow_copy_constructible<StringBuffer>::value, "");
+ static_assert(!std::is_nothrow_move_constructible<StringBuffer>::value, "");
+#endif
+
+ static_assert( std::is_assignable<StringBuffer,StringBuffer>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_assignable<StringBuffer>::value, "");
+#endif
+ static_assert( std::is_move_assignable<StringBuffer>::value, "");
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1800
+ static_assert(!std::is_nothrow_assignable<StringBuffer, StringBuffer>::value, "");
+#endif
+
+ static_assert(!std::is_nothrow_copy_assignable<StringBuffer>::value, "");
+ static_assert(!std::is_nothrow_move_assignable<StringBuffer>::value, "");
+
+ static_assert( std::is_destructible<StringBuffer>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_destructible<StringBuffer>::value, "");
+#endif
+}
+
+#endif
+
+TEST(StringBuffer, MoveConstructor) {
+ StringBuffer x;
+ x.Put('A');
+ x.Put('B');
+ x.Put('C');
+ x.Put('D');
+
+ EXPECT_EQ(4u, x.GetSize());
+ EXPECT_STREQ("ABCD", x.GetString());
+
+ // StringBuffer y(x); // does not compile (!is_copy_constructible)
+ StringBuffer y(std::move(x));
+ EXPECT_EQ(0u, x.GetSize());
+ EXPECT_EQ(4u, y.GetSize());
+ EXPECT_STREQ("ABCD", y.GetString());
+
+ // StringBuffer z = y; // does not compile (!is_copy_assignable)
+ StringBuffer z = std::move(y);
+ EXPECT_EQ(0u, y.GetSize());
+ EXPECT_EQ(4u, z.GetSize());
+ EXPECT_STREQ("ABCD", z.GetString());
+}
+
+TEST(StringBuffer, MoveAssignment) {
+ StringBuffer x;
+ x.Put('A');
+ x.Put('B');
+ x.Put('C');
+ x.Put('D');
+
+ EXPECT_EQ(4u, x.GetSize());
+ EXPECT_STREQ("ABCD", x.GetString());
+
+ StringBuffer y;
+ // y = x; // does not compile (!is_copy_assignable)
+ y = std::move(x);
+ EXPECT_EQ(0u, x.GetSize());
+ EXPECT_EQ(4u, y.GetSize());
+ EXPECT_STREQ("ABCD", y.GetString());
+}
+
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/strtodtest.cpp b/src/rapidjson/test/unittest/strtodtest.cpp
new file mode 100644
index 000000000..cde836c7e
--- /dev/null
+++ b/src/rapidjson/test/unittest/strtodtest.cpp
@@ -0,0 +1,132 @@
+// 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. All rights reserved.
+//
+// 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/internal/strtod.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(unreachable-code)
+#endif
+
+#define BIGINTEGER_LITERAL(s) BigInteger(s, sizeof(s) - 1)
+
+using namespace rapidjson::internal;
+
+TEST(Strtod, CheckApproximationCase) {
+ static const int kSignificandSize = 52;
+ static const int kExponentBias = 0x3FF;
+ static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
+ static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
+ static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
+
+ // http://www.exploringbinary.com/using-integers-to-check-a-floating-point-approximation/
+ // Let b = 0x1.465a72e467d88p-149
+ // = 5741268244528520 x 2^-201
+ union {
+ double d;
+ uint64_t u;
+ }u;
+ u.u = 0x465a72e467d88 | ((static_cast<uint64_t>(-149 + kExponentBias)) << kSignificandSize);
+ const double b = u.d;
+ const uint64_t bInt = (u.u & kSignificandMask) | kHiddenBit;
+ const int bExp = static_cast<int>(((u.u & kExponentMask) >> kSignificandSize) - kExponentBias - kSignificandSize);
+ EXPECT_DOUBLE_EQ(1.7864e-45, b);
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x001465a7, 0x2e467d88), bInt);
+ EXPECT_EQ(-201, bExp);
+
+ // Let d = 17864 x 10-49
+ const char dInt[] = "17864";
+ const int dExp = -49;
+
+ // Let h = 2^(bExp-1)
+ const int hExp = bExp - 1;
+ EXPECT_EQ(-202, hExp);
+
+ int dS_Exp2 = 0;
+ int dS_Exp5 = 0;
+ int bS_Exp2 = 0;
+ int bS_Exp5 = 0;
+ int hS_Exp2 = 0;
+ int hS_Exp5 = 0;
+
+ // Adjust for decimal exponent
+ if (dExp >= 0) {
+ dS_Exp2 += dExp;
+ dS_Exp5 += dExp;
+ }
+ else {
+ bS_Exp2 -= dExp;
+ bS_Exp5 -= dExp;
+ hS_Exp2 -= dExp;
+ hS_Exp5 -= dExp;
+ }
+
+ // Adjust for binary exponent
+ if (bExp >= 0)
+ bS_Exp2 += bExp;
+ else {
+ dS_Exp2 -= bExp;
+ hS_Exp2 -= bExp;
+ }
+
+ // Adjust for half ulp exponent
+ if (hExp >= 0)
+ hS_Exp2 += hExp;
+ else {
+ dS_Exp2 -= hExp;
+ bS_Exp2 -= hExp;
+ }
+
+ // Remove common power of two factor from all three scaled values
+ int common_Exp2 = std::min(dS_Exp2, std::min(bS_Exp2, hS_Exp2));
+ dS_Exp2 -= common_Exp2;
+ bS_Exp2 -= common_Exp2;
+ hS_Exp2 -= common_Exp2;
+
+ EXPECT_EQ(153, dS_Exp2);
+ EXPECT_EQ(0, dS_Exp5);
+ EXPECT_EQ(1, bS_Exp2);
+ EXPECT_EQ(49, bS_Exp5);
+ EXPECT_EQ(0, hS_Exp2);
+ EXPECT_EQ(49, hS_Exp5);
+
+ BigInteger dS = BIGINTEGER_LITERAL(dInt);
+ dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<size_t>(dS_Exp2);
+
+ BigInteger bS(bInt);
+ bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<size_t>(bS_Exp2);
+
+ BigInteger hS(1);
+ hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<size_t>(hS_Exp2);
+
+ EXPECT_TRUE(BIGINTEGER_LITERAL("203970822259994138521801764465966248930731085529088") == dS);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("203970822259994122305215569213032722473144531250000") == bS);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("17763568394002504646778106689453125") == hS);
+
+ EXPECT_EQ(1, dS.Compare(bS));
+
+ BigInteger delta(0);
+ EXPECT_FALSE(dS.Difference(bS, &delta));
+ EXPECT_TRUE(BIGINTEGER_LITERAL("16216586195252933526457586554279088") == delta);
+ EXPECT_TRUE(bS.Difference(dS, &delta));
+ EXPECT_TRUE(BIGINTEGER_LITERAL("16216586195252933526457586554279088") == delta);
+
+ EXPECT_EQ(-1, delta.Compare(hS));
+}
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/unittest.cpp b/src/rapidjson/test/unittest/unittest.cpp
new file mode 100644
index 000000000..b754563ea
--- /dev/null
+++ b/src/rapidjson/test/unittest/unittest.cpp
@@ -0,0 +1,51 @@
+// 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. All rights reserved.
+//
+// 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/rapidjson.h"
+
+#ifdef __clang__
+#pragma GCC diagnostic push
+#if __has_warning("-Wdeprecated")
+#pragma GCC diagnostic ignored "-Wdeprecated"
+#endif
+#endif
+
+AssertException::~AssertException() throw() {}
+
+#ifdef __clang__
+#pragma GCC diagnostic pop
+#endif
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ std::cout << "RapidJSON v" << RAPIDJSON_VERSION_STRING << std::endl;
+
+#ifdef _MSC_VER
+ _CrtMemState memoryState = { 0 };
+ (void)memoryState;
+ _CrtMemCheckpoint(&memoryState);
+ //_CrtSetBreakAlloc(X);
+ //void *testWhetherMemoryLeakDetectionWorks = malloc(1);
+#endif
+
+ int ret = RUN_ALL_TESTS();
+
+#ifdef _MSC_VER
+ // Current gtest constantly leak 2 blocks at exit
+ _CrtMemDumpAllObjectsSince(&memoryState);
+#endif
+ return ret;
+}
diff --git a/src/rapidjson/test/unittest/unittest.h b/src/rapidjson/test/unittest/unittest.h
new file mode 100644
index 000000000..e125bf88d
--- /dev/null
+++ b/src/rapidjson/test/unittest/unittest.h
@@ -0,0 +1,135 @@
+// 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. All rights reserved.
+//
+// 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.
+
+#ifndef UNITTEST_H_
+#define UNITTEST_H_
+
+// gtest indirectly included inttypes.h, without __STDC_CONSTANT_MACROS.
+#ifndef __STDC_CONSTANT_MACROS
+#ifdef __clang__
+#pragma GCC diagnostic push
+#if __has_warning("-Wreserved-id-macro")
+#pragma GCC diagnostic ignored "-Wreserved-id-macro"
+#endif
+#endif
+
+# define __STDC_CONSTANT_MACROS 1 // required by C++ standard
+
+#ifdef __clang__
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+#ifdef _MSC_VER
+#define _CRTDBG_MAP_ALLOC
+#include <crtdbg.h>
+#pragma warning(disable : 4996) // 'function': was declared deprecated
+#endif
+
+#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+#if defined(__clang__) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#pragma GCC diagnostic push
+#endif
+#pragma GCC diagnostic ignored "-Weffc++"
+#endif
+
+#include "gtest/gtest.h"
+#include <stdexcept>
+
+#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#pragma GCC diagnostic pop
+#endif
+
+#ifdef __clang__
+// All TEST() macro generated this warning, disable globally
+#pragma GCC diagnostic ignored "-Wglobal-constructors"
+#endif
+
+template <typename Ch>
+inline unsigned StrLen(const Ch* s) {
+ const Ch* p = s;
+ while (*p) p++;
+ return unsigned(p - s);
+}
+
+template<typename Ch>
+inline int StrCmp(const Ch* s1, const Ch* s2) {
+ while(*s1 && (*s1 == *s2)) { s1++; s2++; }
+ return static_cast<unsigned>(*s1) < static_cast<unsigned>(*s2) ? -1 : static_cast<unsigned>(*s1) > static_cast<unsigned>(*s2);
+}
+
+template <typename Ch>
+inline Ch* StrDup(const Ch* str) {
+ size_t bufferSize = sizeof(Ch) * (StrLen(str) + 1);
+ Ch* buffer = static_cast<Ch*>(malloc(bufferSize));
+ memcpy(buffer, str, bufferSize);
+ return buffer;
+}
+
+inline FILE* TempFile(char *filename) {
+#ifdef _MSC_VER
+ filename = tmpnam(filename);
+
+ // For Visual Studio, tmpnam() adds a backslash in front. Remove it.
+ if (filename[0] == '\\')
+ for (int i = 0; filename[i] != '\0'; i++)
+ filename[i] = filename[i + 1];
+
+ return fopen(filename, "wb");
+#else
+ strcpy(filename, "/tmp/fileXXXXXX");
+ int fd = mkstemp(filename);
+ return fdopen(fd, "w");
+#endif
+}
+
+// Use exception for catching assert
+#ifdef _MSC_VER
+#pragma warning(disable : 4127)
+#endif
+
+#ifdef __clang__
+#pragma GCC diagnostic push
+#if __has_warning("-Wdeprecated")
+#pragma GCC diagnostic ignored "-Wdeprecated"
+#endif
+#endif
+
+class AssertException : public std::logic_error {
+public:
+ AssertException(const char* w) : std::logic_error(w) {}
+ AssertException(const AssertException& rhs) : std::logic_error(rhs) {}
+ virtual ~AssertException() throw();
+};
+
+#ifdef __clang__
+#pragma GCC diagnostic pop
+#endif
+
+#define RAPIDJSON_ASSERT(x) if (!(x)) throw AssertException(RAPIDJSON_STRINGIFY(x))
+
+class Random {
+public:
+ Random(unsigned seed = 0) : mSeed(seed) {}
+
+ unsigned operator()() {
+ mSeed = 214013 * mSeed + 2531011;
+ return mSeed;
+ }
+
+private:
+ unsigned mSeed;
+};
+
+#endif // UNITTEST_H_
diff --git a/src/rapidjson/test/unittest/valuetest.cpp b/src/rapidjson/test/unittest/valuetest.cpp
new file mode 100644
index 000000000..fefc001d4
--- /dev/null
+++ b/src/rapidjson/test/unittest/valuetest.cpp
@@ -0,0 +1,1792 @@
+// 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. All rights reserved.
+//
+// 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/document.h"
+#include <algorithm>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+using namespace rapidjson;
+
+TEST(Value, Size) {
+ if (sizeof(SizeType) == 4) {
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION
+ EXPECT_EQ(16, sizeof(Value));
+#elif RAPIDJSON_64BIT
+ EXPECT_EQ(24, sizeof(Value));
+#else
+ EXPECT_EQ(16, sizeof(Value));
+#endif
+ }
+}
+
+TEST(Value, DefaultConstructor) {
+ Value x;
+ EXPECT_EQ(kNullType, x.GetType());
+ EXPECT_TRUE(x.IsNull());
+
+ //std::cout << "sizeof(Value): " << sizeof(x) << std::endl;
+}
+
+// Should not pass compilation
+//TEST(Value, copy_constructor) {
+// Value x(1234);
+// Value y = x;
+//}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#if 0 // Many old compiler does not support these. Turn it off temporaily.
+
+#include <type_traits>
+
+TEST(Value, Traits) {
+ typedef GenericValue<UTF8<>, CrtAllocator> Value;
+ static_assert(std::is_constructible<Value>::value, "");
+ static_assert(std::is_default_constructible<Value>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_constructible<Value>::value, "");
+#endif
+ static_assert(std::is_move_constructible<Value>::value, "");
+
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_constructible<Value>::value, "");
+ static_assert(std::is_nothrow_default_constructible<Value>::value, "");
+ static_assert(!std::is_nothrow_copy_constructible<Value>::value, "");
+ static_assert(std::is_nothrow_move_constructible<Value>::value, "");
+#endif
+
+ static_assert(std::is_assignable<Value,Value>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_assignable<Value>::value, "");
+#endif
+ static_assert(std::is_move_assignable<Value>::value, "");
+
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_assignable<Value, Value>::value, "");
+#endif
+ static_assert(!std::is_nothrow_copy_assignable<Value>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_move_assignable<Value>::value, "");
+#endif
+
+ static_assert(std::is_destructible<Value>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_destructible<Value>::value, "");
+#endif
+}
+
+#endif
+
+TEST(Value, MoveConstructor) {
+ typedef GenericValue<UTF8<>, CrtAllocator> V;
+ V::AllocatorType allocator;
+
+ V x((V(kArrayType)));
+ x.Reserve(4u, allocator);
+ x.PushBack(1, allocator).PushBack(2, allocator).PushBack(3, allocator).PushBack(4, allocator);
+ EXPECT_TRUE(x.IsArray());
+ EXPECT_EQ(4u, x.Size());
+
+ // Value y(x); // does not compile (!is_copy_constructible)
+ V y(std::move(x));
+ EXPECT_TRUE(x.IsNull());
+ EXPECT_TRUE(y.IsArray());
+ EXPECT_EQ(4u, y.Size());
+
+ // Value z = y; // does not compile (!is_copy_assignable)
+ V z = std::move(y);
+ EXPECT_TRUE(y.IsNull());
+ EXPECT_TRUE(z.IsArray());
+ EXPECT_EQ(4u, z.Size());
+}
+
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+TEST(Value, AssignmentOperator) {
+ Value x(1234);
+ Value y;
+ y = x;
+ EXPECT_TRUE(x.IsNull()); // move semantic
+ EXPECT_EQ(1234, y.GetInt());
+
+ y = 5678;
+ EXPECT_TRUE(y.IsInt());
+ EXPECT_EQ(5678, y.GetInt());
+
+ x = "Hello";
+ EXPECT_TRUE(x.IsString());
+ EXPECT_STREQ(x.GetString(),"Hello");
+
+ y = StringRef(x.GetString(),x.GetStringLength());
+ EXPECT_TRUE(y.IsString());
+ EXPECT_EQ(y.GetString(),x.GetString());
+ EXPECT_EQ(y.GetStringLength(),x.GetStringLength());
+
+ static char mstr[] = "mutable";
+ // y = mstr; // should not compile
+ y = StringRef(mstr);
+ EXPECT_TRUE(y.IsString());
+ EXPECT_EQ(y.GetString(),mstr);
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ // C++11 move assignment
+ x = Value("World");
+ EXPECT_TRUE(x.IsString());
+ EXPECT_STREQ("World", x.GetString());
+
+ x = std::move(y);
+ EXPECT_TRUE(y.IsNull());
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), mstr);
+
+ y = std::move(Value().SetInt(1234));
+ EXPECT_TRUE(y.IsInt());
+ EXPECT_EQ(1234, y);
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+}
+
+template <typename A, typename B>
+void TestEqual(const A& a, const B& b) {
+ EXPECT_TRUE (a == b);
+ EXPECT_FALSE(a != b);
+ EXPECT_TRUE (b == a);
+ EXPECT_FALSE(b != a);
+}
+
+template <typename A, typename B>
+void TestUnequal(const A& a, const B& b) {
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE (a != b);
+ EXPECT_FALSE(b == a);
+ EXPECT_TRUE (b != a);
+}
+
+TEST(Value, EqualtoOperator) {
+ Value::AllocatorType allocator;
+ Value x(kObjectType);
+ x.AddMember("hello", "world", allocator)
+ .AddMember("t", Value(true).Move(), allocator)
+ .AddMember("f", Value(false).Move(), allocator)
+ .AddMember("n", Value(kNullType).Move(), allocator)
+ .AddMember("i", 123, allocator)
+ .AddMember("pi", 3.14, allocator)
+ .AddMember("a", Value(kArrayType).Move().PushBack(1, allocator).PushBack(2, allocator).PushBack(3, allocator), allocator);
+
+ // Test templated operator==() and operator!=()
+ TestEqual(x["hello"], "world");
+ const char* cc = "world";
+ TestEqual(x["hello"], cc);
+ char* c = strdup("world");
+ TestEqual(x["hello"], c);
+ free(c);
+
+ TestEqual(x["t"], true);
+ TestEqual(x["f"], false);
+ TestEqual(x["i"], 123);
+ TestEqual(x["pi"], 3.14);
+
+ // Test operator==() (including different allocators)
+ CrtAllocator crtAllocator;
+ GenericValue<UTF8<>, CrtAllocator> y;
+ GenericDocument<UTF8<>, CrtAllocator> z(&crtAllocator);
+ y.CopyFrom(x, crtAllocator);
+ z.CopyFrom(y, z.GetAllocator());
+ TestEqual(x, y);
+ TestEqual(y, z);
+ TestEqual(z, x);
+
+ // Swapping member order should be fine.
+ EXPECT_TRUE(y.RemoveMember("t"));
+ TestUnequal(x, y);
+ TestUnequal(z, y);
+ EXPECT_TRUE(z.RemoveMember("t"));
+ TestUnequal(x, z);
+ TestEqual(y, z);
+ y.AddMember("t", false, crtAllocator);
+ z.AddMember("t", false, z.GetAllocator());
+ TestUnequal(x, y);
+ TestUnequal(z, x);
+ y["t"] = true;
+ z["t"] = true;
+ TestEqual(x, y);
+ TestEqual(y, z);
+ TestEqual(z, x);
+
+ // Swapping element order is not OK
+ x["a"][0].Swap(x["a"][1]);
+ TestUnequal(x, y);
+ x["a"][0].Swap(x["a"][1]);
+ TestEqual(x, y);
+
+ // Array of different size
+ x["a"].PushBack(4, allocator);
+ TestUnequal(x, y);
+ x["a"].PopBack();
+ TestEqual(x, y);
+
+ // Issue #129: compare Uint64
+ x.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0));
+ y.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF));
+ TestUnequal(x, y);
+}
+
+template <typename Value>
+void TestCopyFrom() {
+ typename Value::AllocatorType a;
+ Value v1(1234);
+ Value v2(v1, a); // deep copy constructor
+ EXPECT_TRUE(v1.GetType() == v2.GetType());
+ EXPECT_EQ(v1.GetInt(), v2.GetInt());
+
+ v1.SetString("foo");
+ v2.CopyFrom(v1, a);
+ EXPECT_TRUE(v1.GetType() == v2.GetType());
+ EXPECT_STREQ(v1.GetString(), v2.GetString());
+ EXPECT_EQ(v1.GetString(), v2.GetString()); // string NOT copied
+
+ v1.SetString("bar", a); // copy string
+ v2.CopyFrom(v1, a);
+ EXPECT_TRUE(v1.GetType() == v2.GetType());
+ EXPECT_STREQ(v1.GetString(), v2.GetString());
+ EXPECT_NE(v1.GetString(), v2.GetString()); // string copied
+
+
+ v1.SetArray().PushBack(1234, a);
+ v2.CopyFrom(v1, a);
+ EXPECT_TRUE(v2.IsArray());
+ EXPECT_EQ(v1.Size(), v2.Size());
+
+ v1.PushBack(Value().SetString("foo", a), a); // push string copy
+ EXPECT_TRUE(v1.Size() != v2.Size());
+ v2.CopyFrom(v1, a);
+ EXPECT_TRUE(v1.Size() == v2.Size());
+ EXPECT_STREQ(v1[1].GetString(), v2[1].GetString());
+ EXPECT_NE(v1[1].GetString(), v2[1].GetString()); // string got copied
+}
+
+TEST(Value, CopyFrom) {
+ TestCopyFrom<Value>();
+ TestCopyFrom<GenericValue<UTF8<>, CrtAllocator> >();
+}
+
+TEST(Value, Swap) {
+ Value v1(1234);
+ Value v2(kObjectType);
+
+ EXPECT_EQ(&v1, &v1.Swap(v2));
+ EXPECT_TRUE(v1.IsObject());
+ EXPECT_TRUE(v2.IsInt());
+ EXPECT_EQ(1234, v2.GetInt());
+
+ // testing std::swap compatibility
+ using std::swap;
+ swap(v1, v2);
+ EXPECT_TRUE(v1.IsInt());
+ EXPECT_TRUE(v2.IsObject());
+}
+
+TEST(Value, Null) {
+ // Default constructor
+ Value x;
+ EXPECT_EQ(kNullType, x.GetType());
+ EXPECT_TRUE(x.IsNull());
+
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsNumber());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // Constructor with type
+ Value y(kNullType);
+ EXPECT_TRUE(y.IsNull());
+
+ // SetNull();
+ Value z(true);
+ z.SetNull();
+ EXPECT_TRUE(z.IsNull());
+}
+
+TEST(Value, True) {
+ // Constructor with bool
+ Value x(true);
+ EXPECT_EQ(kTrueType, x.GetType());
+ EXPECT_TRUE(x.GetBool());
+ EXPECT_TRUE(x.IsBool());
+ EXPECT_TRUE(x.IsTrue());
+
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsNumber());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // Constructor with type
+ Value y(kTrueType);
+ EXPECT_TRUE(y.IsTrue());
+
+ // SetBool()
+ Value z;
+ z.SetBool(true);
+ EXPECT_TRUE(z.IsTrue());
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<bool>());
+ EXPECT_TRUE(z.Get<bool>());
+ EXPECT_FALSE(z.Set<bool>(false).Get<bool>());
+ EXPECT_TRUE(z.Set(true).Get<bool>());
+}
+
+TEST(Value, False) {
+ // Constructor with bool
+ Value x(false);
+ EXPECT_EQ(kFalseType, x.GetType());
+ EXPECT_TRUE(x.IsBool());
+ EXPECT_TRUE(x.IsFalse());
+
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.GetBool());
+ //EXPECT_FALSE((bool)x);
+ EXPECT_FALSE(x.IsNumber());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // Constructor with type
+ Value y(kFalseType);
+ EXPECT_TRUE(y.IsFalse());
+
+ // SetBool()
+ Value z;
+ z.SetBool(false);
+ EXPECT_TRUE(z.IsFalse());
+}
+
+TEST(Value, Int) {
+ // Constructor with int
+ Value x(1234);
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_EQ(1234, x.GetInt());
+ EXPECT_EQ(1234u, x.GetUint());
+ EXPECT_EQ(1234, x.GetInt64());
+ EXPECT_EQ(1234u, x.GetUint64());
+ EXPECT_NEAR(1234.0, x.GetDouble(), 0.0);
+ //EXPECT_EQ(1234, (int)x);
+ //EXPECT_EQ(1234, (unsigned)x);
+ //EXPECT_EQ(1234, (int64_t)x);
+ //EXPECT_EQ(1234, (uint64_t)x);
+ //EXPECT_EQ(1234, (double)x);
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsInt());
+ EXPECT_TRUE(x.IsUint());
+ EXPECT_TRUE(x.IsInt64());
+ EXPECT_TRUE(x.IsUint64());
+
+ EXPECT_FALSE(x.IsDouble());
+ EXPECT_FALSE(x.IsFloat());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ Value nx(-1234);
+ EXPECT_EQ(-1234, nx.GetInt());
+ EXPECT_EQ(-1234, nx.GetInt64());
+ EXPECT_TRUE(nx.IsInt());
+ EXPECT_TRUE(nx.IsInt64());
+ EXPECT_FALSE(nx.IsUint());
+ EXPECT_FALSE(nx.IsUint64());
+
+ // Constructor with type
+ Value y(kNumberType);
+ EXPECT_TRUE(y.IsNumber());
+ EXPECT_TRUE(y.IsInt());
+ EXPECT_EQ(0, y.GetInt());
+
+ // SetInt()
+ Value z;
+ z.SetInt(1234);
+ EXPECT_EQ(1234, z.GetInt());
+
+ // operator=(int)
+ z = 5678;
+ EXPECT_EQ(5678, z.GetInt());
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<int>());
+ EXPECT_EQ(5678, z.Get<int>());
+ EXPECT_EQ(5679, z.Set(5679).Get<int>());
+ EXPECT_EQ(5680, z.Set<int>(5680).Get<int>());
+}
+
+TEST(Value, Uint) {
+ // Constructor with int
+ Value x(1234u);
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_EQ(1234, x.GetInt());
+ EXPECT_EQ(1234u, x.GetUint());
+ EXPECT_EQ(1234, x.GetInt64());
+ EXPECT_EQ(1234u, x.GetUint64());
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsInt());
+ EXPECT_TRUE(x.IsUint());
+ EXPECT_TRUE(x.IsInt64());
+ EXPECT_TRUE(x.IsUint64());
+ EXPECT_NEAR(1234.0, x.GetDouble(), 0.0); // Number can always be cast as double but !IsDouble().
+
+ EXPECT_FALSE(x.IsDouble());
+ EXPECT_FALSE(x.IsFloat());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // SetUint()
+ Value z;
+ z.SetUint(1234);
+ EXPECT_EQ(1234u, z.GetUint());
+
+ // operator=(unsigned)
+ z = 5678u;
+ EXPECT_EQ(5678u, z.GetUint());
+
+ z = 2147483648u; // 2^31, cannot cast as int
+ EXPECT_EQ(2147483648u, z.GetUint());
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_TRUE(z.IsInt64()); // Issue 41: Incorrect parsing of unsigned int number types
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<unsigned>());
+ EXPECT_EQ(2147483648u, z.Get<unsigned>());
+ EXPECT_EQ(2147483649u, z.Set(2147483649u).Get<unsigned>());
+ EXPECT_EQ(2147483650u, z.Set<unsigned>(2147483650u).Get<unsigned>());
+}
+
+TEST(Value, Int64) {
+ // Constructor with int
+ Value x(int64_t(1234));
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_EQ(1234, x.GetInt());
+ EXPECT_EQ(1234u, x.GetUint());
+ EXPECT_EQ(1234, x.GetInt64());
+ EXPECT_EQ(1234u, x.GetUint64());
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsInt());
+ EXPECT_TRUE(x.IsUint());
+ EXPECT_TRUE(x.IsInt64());
+ EXPECT_TRUE(x.IsUint64());
+
+ EXPECT_FALSE(x.IsDouble());
+ EXPECT_FALSE(x.IsFloat());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ Value nx(int64_t(-1234));
+ EXPECT_EQ(-1234, nx.GetInt());
+ EXPECT_EQ(-1234, nx.GetInt64());
+ EXPECT_TRUE(nx.IsInt());
+ EXPECT_TRUE(nx.IsInt64());
+ EXPECT_FALSE(nx.IsUint());
+ EXPECT_FALSE(nx.IsUint64());
+
+ // SetInt64()
+ Value z;
+ z.SetInt64(1234);
+ EXPECT_EQ(1234, z.GetInt64());
+
+ z.SetInt64(2147483648u); // 2^31, cannot cast as int
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_TRUE(z.IsUint());
+ EXPECT_NEAR(2147483648.0, z.GetDouble(), 0.0);
+
+ z.SetInt64(int64_t(4294967295u) + 1); // 2^32, cannot cast as uint
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_FALSE(z.IsUint());
+ EXPECT_NEAR(4294967296.0, z.GetDouble(), 0.0);
+
+ z.SetInt64(-int64_t(2147483648u) - 1); // -2^31-1, cannot cast as int
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_NEAR(-2147483649.0, z.GetDouble(), 0.0);
+
+ int64_t i = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 00000000));
+ z.SetInt64(i);
+ EXPECT_DOUBLE_EQ(-9223372036854775808.0, z.GetDouble());
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<int64_t>());
+ EXPECT_EQ(i, z.Get<int64_t>());
+#if 0 // signed integer underflow is undefined behaviour
+ EXPECT_EQ(i - 1, z.Set(i - 1).Get<int64_t>());
+ EXPECT_EQ(i - 2, z.Set<int64_t>(i - 2).Get<int64_t>());
+#endif
+}
+
+TEST(Value, Uint64) {
+ // Constructor with int
+ Value x(uint64_t(1234));
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_EQ(1234, x.GetInt());
+ EXPECT_EQ(1234u, x.GetUint());
+ EXPECT_EQ(1234, x.GetInt64());
+ EXPECT_EQ(1234u, x.GetUint64());
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsInt());
+ EXPECT_TRUE(x.IsUint());
+ EXPECT_TRUE(x.IsInt64());
+ EXPECT_TRUE(x.IsUint64());
+
+ EXPECT_FALSE(x.IsDouble());
+ EXPECT_FALSE(x.IsFloat());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // SetUint64()
+ Value z;
+ z.SetUint64(1234);
+ EXPECT_EQ(1234u, z.GetUint64());
+
+ z.SetUint64(uint64_t(2147483648u)); // 2^31, cannot cast as int
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_TRUE(z.IsUint());
+ EXPECT_TRUE(z.IsInt64());
+
+ z.SetUint64(uint64_t(4294967295u) + 1); // 2^32, cannot cast as uint
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_FALSE(z.IsUint());
+ EXPECT_TRUE(z.IsInt64());
+
+ uint64_t u = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
+ z.SetUint64(u); // 2^63 cannot cast as int64
+ EXPECT_FALSE(z.IsInt64());
+ EXPECT_EQ(u, z.GetUint64()); // Issue 48
+ EXPECT_DOUBLE_EQ(9223372036854775808.0, z.GetDouble());
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<uint64_t>());
+ EXPECT_EQ(u, z.Get<uint64_t>());
+ EXPECT_EQ(u + 1, z.Set(u + 1).Get<uint64_t>());
+ EXPECT_EQ(u + 2, z.Set<uint64_t>(u + 2).Get<uint64_t>());
+}
+
+TEST(Value, Double) {
+ // Constructor with double
+ Value x(12.34);
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_NEAR(12.34, x.GetDouble(), 0.0);
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsDouble());
+
+ EXPECT_FALSE(x.IsInt());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // SetDouble()
+ Value z;
+ z.SetDouble(12.34);
+ EXPECT_NEAR(12.34, z.GetDouble(), 0.0);
+
+ z = 56.78;
+ EXPECT_NEAR(56.78, z.GetDouble(), 0.0);
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<double>());
+ EXPECT_EQ(56.78, z.Get<double>());
+ EXPECT_EQ(57.78, z.Set(57.78).Get<double>());
+ EXPECT_EQ(58.78, z.Set<double>(58.78).Get<double>());
+}
+
+TEST(Value, Float) {
+ // Constructor with double
+ Value x(12.34f);
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_NEAR(12.34f, x.GetFloat(), 0.0);
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsDouble());
+ EXPECT_TRUE(x.IsFloat());
+
+ EXPECT_FALSE(x.IsInt());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // SetFloat()
+ Value z;
+ z.SetFloat(12.34f);
+ EXPECT_NEAR(12.34f, z.GetFloat(), 0.0f);
+
+ // Issue 573
+ z.SetInt(0);
+ EXPECT_EQ(0.0f, z.GetFloat());
+
+ z = 56.78f;
+ EXPECT_NEAR(56.78f, z.GetFloat(), 0.0f);
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<float>());
+ EXPECT_EQ(56.78f, z.Get<float>());
+ EXPECT_EQ(57.78f, z.Set(57.78f).Get<float>());
+ EXPECT_EQ(58.78f, z.Set<float>(58.78f).Get<float>());
+}
+
+TEST(Value, IsLosslessDouble) {
+ EXPECT_TRUE(Value(0.0).IsLosslessDouble());
+ EXPECT_TRUE(Value(12.34).IsLosslessDouble());
+ EXPECT_TRUE(Value(-123).IsLosslessDouble());
+ EXPECT_TRUE(Value(2147483648u).IsLosslessDouble());
+ EXPECT_TRUE(Value(-static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x40000000, 0x00000000))).IsLosslessDouble());
+#if !(defined(_MSC_VER) && _MSC_VER < 1800) // VC2010 has problem
+ EXPECT_TRUE(Value(RAPIDJSON_UINT64_C2(0xA0000000, 0x00000000)).IsLosslessDouble());
+#endif
+
+ EXPECT_FALSE(Value(static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF))).IsLosslessDouble()); // INT64_MAX
+ EXPECT_FALSE(Value(-static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF))).IsLosslessDouble()); // -INT64_MAX
+ EXPECT_TRUE(Value(-static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF)) - 1).IsLosslessDouble()); // INT64_MIN
+ EXPECT_FALSE(Value(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)).IsLosslessDouble()); // UINT64_MAX
+
+ EXPECT_TRUE(Value(3.4028234e38f).IsLosslessDouble()); // FLT_MAX
+ EXPECT_TRUE(Value(-3.4028234e38f).IsLosslessDouble()); // -FLT_MAX
+ EXPECT_TRUE(Value(1.17549435e-38f).IsLosslessDouble()); // FLT_MIN
+ EXPECT_TRUE(Value(-1.17549435e-38f).IsLosslessDouble()); // -FLT_MIN
+ EXPECT_TRUE(Value(1.7976931348623157e+308).IsLosslessDouble()); // DBL_MAX
+ EXPECT_TRUE(Value(-1.7976931348623157e+308).IsLosslessDouble()); // -DBL_MAX
+ EXPECT_TRUE(Value(2.2250738585072014e-308).IsLosslessDouble()); // DBL_MIN
+ EXPECT_TRUE(Value(-2.2250738585072014e-308).IsLosslessDouble()); // -DBL_MIN
+}
+
+TEST(Value, IsLosslessFloat) {
+ EXPECT_TRUE(Value(12.25).IsLosslessFloat());
+ EXPECT_TRUE(Value(-123).IsLosslessFloat());
+ EXPECT_TRUE(Value(2147483648u).IsLosslessFloat());
+ EXPECT_TRUE(Value(3.4028234e38f).IsLosslessFloat());
+ EXPECT_TRUE(Value(-3.4028234e38f).IsLosslessFloat());
+ EXPECT_FALSE(Value(3.4028235e38).IsLosslessFloat());
+ EXPECT_FALSE(Value(0.3).IsLosslessFloat());
+}
+
+TEST(Value, String) {
+ // Construction with const string
+ Value x("Hello", 5); // literal
+ EXPECT_EQ(kStringType, x.GetType());
+ EXPECT_TRUE(x.IsString());
+ EXPECT_STREQ("Hello", x.GetString());
+ EXPECT_EQ(5u, x.GetStringLength());
+
+ EXPECT_FALSE(x.IsNumber());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ static const char cstr[] = "World"; // const array
+ Value(cstr).Swap(x);
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), cstr);
+ EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1);
+
+ static char mstr[] = "Howdy"; // non-const array
+ // Value(mstr).Swap(x); // should not compile
+ Value(StringRef(mstr)).Swap(x);
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), mstr);
+ EXPECT_EQ(x.GetStringLength(), sizeof(mstr)-1);
+ strncpy(mstr,"Hello", sizeof(mstr));
+ EXPECT_STREQ(x.GetString(), "Hello");
+
+ const char* pstr = cstr;
+ //Value(pstr).Swap(x); // should not compile
+ Value(StringRef(pstr)).Swap(x);
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), cstr);
+ EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1);
+
+ char* mpstr = mstr;
+ Value(StringRef(mpstr,sizeof(mstr)-1)).Swap(x);
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), mstr);
+ EXPECT_EQ(x.GetStringLength(), 5u);
+ EXPECT_STREQ(x.GetString(), "Hello");
+
+ // Constructor with copy string
+ MemoryPoolAllocator<> allocator;
+ Value c(x.GetString(), x.GetStringLength(), allocator);
+ EXPECT_NE(x.GetString(), c.GetString());
+ EXPECT_EQ(x.GetStringLength(), c.GetStringLength());
+ EXPECT_STREQ(x.GetString(), c.GetString());
+ //x.SetString("World");
+ x.SetString("World", 5);
+ EXPECT_STREQ("Hello", c.GetString());
+ EXPECT_EQ(5u, c.GetStringLength());
+
+ // Constructor with type
+ Value y(kStringType);
+ EXPECT_TRUE(y.IsString());
+ EXPECT_STREQ("", y.GetString()); // Empty string should be "" instead of 0 (issue 226)
+ EXPECT_EQ(0u, y.GetStringLength());
+
+ // SetConsttring()
+ Value z;
+ z.SetString("Hello");
+ EXPECT_TRUE(x.IsString());
+ z.SetString("Hello", 5);
+ EXPECT_STREQ("Hello", z.GetString());
+ EXPECT_STREQ("Hello", z.GetString());
+ EXPECT_EQ(5u, z.GetStringLength());
+
+ z.SetString("Hello");
+ EXPECT_TRUE(z.IsString());
+ EXPECT_STREQ("Hello", z.GetString());
+
+ //z.SetString(mstr); // should not compile
+ //z.SetString(pstr); // should not compile
+ z.SetString(StringRef(mstr));
+ EXPECT_TRUE(z.IsString());
+ EXPECT_STREQ(z.GetString(), mstr);
+
+ z.SetString(cstr);
+ EXPECT_TRUE(z.IsString());
+ EXPECT_EQ(cstr, z.GetString());
+
+ z = cstr;
+ EXPECT_TRUE(z.IsString());
+ EXPECT_EQ(cstr, z.GetString());
+
+ // SetString()
+ char s[] = "World";
+ Value w;
+ w.SetString(s, static_cast<SizeType>(strlen(s)), allocator);
+ s[0] = '\0';
+ EXPECT_STREQ("World", w.GetString());
+ EXPECT_EQ(5u, w.GetStringLength());
+
+ // templated functions
+ EXPECT_TRUE(z.Is<const char*>());
+ EXPECT_STREQ(cstr, z.Get<const char*>());
+ EXPECT_STREQ("Apple", z.Set<const char*>("Apple").Get<const char*>());
+
+#if RAPIDJSON_HAS_STDSTRING
+ {
+ std::string str = "Hello World";
+ str[5] = '\0';
+ EXPECT_STREQ(str.data(),"Hello"); // embedded '\0'
+ EXPECT_EQ(str.size(), 11u);
+
+ // no copy
+ Value vs0(StringRef(str));
+ EXPECT_TRUE(vs0.IsString());
+ EXPECT_EQ(vs0.GetString(), str.data());
+ EXPECT_EQ(vs0.GetStringLength(), str.size());
+ TestEqual(vs0, str);
+
+ // do copy
+ Value vs1(str, allocator);
+ EXPECT_TRUE(vs1.IsString());
+ EXPECT_NE(vs1.GetString(), str.data());
+ EXPECT_NE(vs1.GetString(), str); // not equal due to embedded '\0'
+ EXPECT_EQ(vs1.GetStringLength(), str.size());
+ TestEqual(vs1, str);
+
+ // SetString
+ str = "World";
+ vs0.SetNull().SetString(str, allocator);
+ EXPECT_TRUE(vs0.IsString());
+ EXPECT_STREQ(vs0.GetString(), str.c_str());
+ EXPECT_EQ(vs0.GetStringLength(), str.size());
+ TestEqual(str, vs0);
+ TestUnequal(str, vs1);
+
+ // vs1 = str; // should not compile
+ vs1 = StringRef(str);
+ TestEqual(str, vs1);
+ TestEqual(vs0, vs1);
+
+ // Templated function.
+ EXPECT_TRUE(vs0.Is<std::string>());
+ EXPECT_EQ(str, vs0.Get<std::string>());
+ vs0.Set<std::string>(std::string("Apple"), allocator);
+ EXPECT_EQ(std::string("Apple"), vs0.Get<std::string>());
+ vs0.Set(std::string("Orange"), allocator);
+ EXPECT_EQ(std::string("Orange"), vs0.Get<std::string>());
+ }
+#endif // RAPIDJSON_HAS_STDSTRING
+}
+
+// Issue 226: Value of string type should not point to NULL
+TEST(Value, SetStringNullException) {
+ Value v;
+ EXPECT_THROW(v.SetString(0, 0), AssertException);
+}
+
+template <typename T, typename Allocator>
+static void TestArray(T& x, Allocator& allocator) {
+ const T& y = x;
+
+ // PushBack()
+ Value v;
+ x.PushBack(v, allocator);
+ v.SetBool(true);
+ x.PushBack(v, allocator);
+ v.SetBool(false);
+ x.PushBack(v, allocator);
+ v.SetInt(123);
+ x.PushBack(v, allocator);
+ //x.PushBack((const char*)"foo", allocator); // should not compile
+ x.PushBack("foo", allocator);
+
+ EXPECT_FALSE(x.Empty());
+ EXPECT_EQ(5u, x.Size());
+ EXPECT_FALSE(y.Empty());
+ EXPECT_EQ(5u, y.Size());
+ EXPECT_TRUE(x[SizeType(0)].IsNull());
+ EXPECT_TRUE(x[1].IsTrue());
+ EXPECT_TRUE(x[2].IsFalse());
+ EXPECT_TRUE(x[3].IsInt());
+ EXPECT_EQ(123, x[3].GetInt());
+ EXPECT_TRUE(y[SizeType(0)].IsNull());
+ EXPECT_TRUE(y[1].IsTrue());
+ EXPECT_TRUE(y[2].IsFalse());
+ EXPECT_TRUE(y[3].IsInt());
+ EXPECT_EQ(123, y[3].GetInt());
+ EXPECT_TRUE(y[4].IsString());
+ EXPECT_STREQ("foo", y[4].GetString());
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ // PushBack(GenericValue&&, Allocator&);
+ {
+ Value y2(kArrayType);
+ y2.PushBack(Value(true), allocator);
+ y2.PushBack(std::move(Value(kArrayType).PushBack(Value(1), allocator).PushBack("foo", allocator)), allocator);
+ EXPECT_EQ(2u, y2.Size());
+ EXPECT_TRUE(y2[0].IsTrue());
+ EXPECT_TRUE(y2[1].IsArray());
+ EXPECT_EQ(2u, y2[1].Size());
+ EXPECT_TRUE(y2[1][0].IsInt());
+ EXPECT_TRUE(y2[1][1].IsString());
+ }
+#endif
+
+ // iterator
+ typename T::ValueIterator itr = x.Begin();
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsNull());
+ ++itr;
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsTrue());
+ ++itr;
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsFalse());
+ ++itr;
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsInt());
+ EXPECT_EQ(123, itr->GetInt());
+ ++itr;
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsString());
+ EXPECT_STREQ("foo", itr->GetString());
+
+ // const iterator
+ typename T::ConstValueIterator citr = y.Begin();
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsNull());
+ ++citr;
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsTrue());
+ ++citr;
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsFalse());
+ ++citr;
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsInt());
+ EXPECT_EQ(123, citr->GetInt());
+ ++citr;
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsString());
+ EXPECT_STREQ("foo", citr->GetString());
+
+ // PopBack()
+ x.PopBack();
+ EXPECT_EQ(4u, x.Size());
+ EXPECT_TRUE(y[SizeType(0)].IsNull());
+ EXPECT_TRUE(y[1].IsTrue());
+ EXPECT_TRUE(y[2].IsFalse());
+ EXPECT_TRUE(y[3].IsInt());
+
+ // Clear()
+ x.Clear();
+ EXPECT_TRUE(x.Empty());
+ EXPECT_EQ(0u, x.Size());
+ EXPECT_TRUE(y.Empty());
+ EXPECT_EQ(0u, y.Size());
+
+ // Erase(ValueIterator)
+
+ // Use array of array to ensure removed elements' destructor is called.
+ // [[0],[1],[2],...]
+ for (int i = 0; i < 10; i++)
+ x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator);
+
+ // Erase the first
+ itr = x.Erase(x.Begin());
+ EXPECT_EQ(x.Begin(), itr);
+ EXPECT_EQ(9u, x.Size());
+ for (int i = 0; i < 9; i++)
+ EXPECT_EQ(i + 1, x[static_cast<SizeType>(i)][0].GetInt());
+
+ // Ease the last
+ itr = x.Erase(x.End() - 1);
+ EXPECT_EQ(x.End(), itr);
+ EXPECT_EQ(8u, x.Size());
+ for (int i = 0; i < 8; i++)
+ EXPECT_EQ(i + 1, x[static_cast<SizeType>(i)][0].GetInt());
+
+ // Erase the middle
+ itr = x.Erase(x.Begin() + 4);
+ EXPECT_EQ(x.Begin() + 4, itr);
+ EXPECT_EQ(7u, x.Size());
+ for (int i = 0; i < 4; i++)
+ EXPECT_EQ(i + 1, x[static_cast<SizeType>(i)][0].GetInt());
+ for (int i = 4; i < 7; i++)
+ EXPECT_EQ(i + 2, x[static_cast<SizeType>(i)][0].GetInt());
+
+ // Erase(ValueIterator, ValueIterator)
+ // Exhaustive test with all 0 <= first < n, first <= last <= n cases
+ const unsigned n = 10;
+ for (unsigned first = 0; first < n; first++) {
+ for (unsigned last = first; last <= n; last++) {
+ x.Clear();
+ for (unsigned i = 0; i < n; i++)
+ x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator);
+
+ itr = x.Erase(x.Begin() + first, x.Begin() + last);
+ if (last == n)
+ EXPECT_EQ(x.End(), itr);
+ else
+ EXPECT_EQ(x.Begin() + first, itr);
+
+ size_t removeCount = last - first;
+ EXPECT_EQ(n - removeCount, x.Size());
+ for (unsigned i = 0; i < first; i++)
+ EXPECT_EQ(i, x[i][0].GetUint());
+ for (unsigned i = first; i < n - removeCount; i++)
+ EXPECT_EQ(i + removeCount, x[static_cast<SizeType>(i)][0].GetUint());
+ }
+ }
+}
+
+TEST(Value, Array) {
+ Value x(kArrayType);
+ const Value& y = x;
+ Value::AllocatorType allocator;
+
+ EXPECT_EQ(kArrayType, x.GetType());
+ EXPECT_TRUE(x.IsArray());
+ EXPECT_TRUE(x.Empty());
+ EXPECT_EQ(0u, x.Size());
+ EXPECT_TRUE(y.IsArray());
+ EXPECT_TRUE(y.Empty());
+ EXPECT_EQ(0u, y.Size());
+
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+
+ TestArray(x, allocator);
+
+ // Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed.
+ // http://en.wikipedia.org/wiki/Erase-remove_idiom
+ x.Clear();
+ for (int i = 0; i < 10; i++)
+ if (i % 2 == 0)
+ x.PushBack(i, allocator);
+ else
+ x.PushBack(Value(kNullType).Move(), allocator);
+
+ const Value null(kNullType);
+ x.Erase(std::remove(x.Begin(), x.End(), null), x.End());
+ EXPECT_EQ(5u, x.Size());
+ for (int i = 0; i < 5; i++)
+ EXPECT_EQ(i * 2, x[static_cast<SizeType>(i)]);
+
+ // SetArray()
+ Value z;
+ z.SetArray();
+ EXPECT_TRUE(z.IsArray());
+ EXPECT_TRUE(z.Empty());
+}
+
+TEST(Value, ArrayHelper) {
+ Value::AllocatorType allocator;
+ {
+ Value x(kArrayType);
+ Value::Array a = x.GetArray();
+ TestArray(a, allocator);
+ }
+
+ {
+ Value x(kArrayType);
+ Value::Array a = x.GetArray();
+ a.PushBack(1, allocator);
+
+ Value::Array a2(a); // copy constructor
+ EXPECT_EQ(1, a2.Size());
+
+ Value::Array a3 = a;
+ EXPECT_EQ(1, a3.Size());
+
+ Value::ConstArray y = static_cast<const Value&>(x).GetArray();
+ (void)y;
+ // y.PushBack(1, allocator); // should not compile
+
+ // Templated functions
+ x.Clear();
+ EXPECT_TRUE(x.Is<Value::Array>());
+ EXPECT_TRUE(x.Is<Value::ConstArray>());
+ a.PushBack(1, allocator);
+ EXPECT_EQ(1, x.Get<Value::Array>()[0].GetInt());
+ EXPECT_EQ(1, x.Get<Value::ConstArray>()[0].GetInt());
+
+ Value x2;
+ x2.Set<Value::Array>(a);
+ EXPECT_TRUE(x.IsArray()); // IsArray() is invariant after moving.
+ EXPECT_EQ(1, x2.Get<Value::Array>()[0].GetInt());
+ }
+
+ {
+ Value y(kArrayType);
+ y.PushBack(123, allocator);
+
+ Value x(y.GetArray()); // Construct value form array.
+ EXPECT_TRUE(x.IsArray());
+ EXPECT_EQ(123, x[0].GetInt());
+ EXPECT_TRUE(y.IsArray()); // Invariant
+ EXPECT_TRUE(y.Empty());
+ }
+
+ {
+ Value x(kArrayType);
+ Value y(kArrayType);
+ y.PushBack(123, allocator);
+ x.PushBack(y.GetArray(), allocator); // Implicit constructor to convert Array to GenericValue
+
+ EXPECT_EQ(1, x.Size());
+ EXPECT_EQ(123, x[0][0].GetInt());
+ EXPECT_TRUE(y.IsArray());
+ EXPECT_TRUE(y.Empty());
+ }
+}
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+TEST(Value, ArrayHelperRangeFor) {
+ Value::AllocatorType allocator;
+ Value x(kArrayType);
+
+ for (int i = 0; i < 10; i++)
+ x.PushBack(i, allocator);
+
+ {
+ int i = 0;
+ for (auto& v : x.GetArray()) {
+ EXPECT_EQ(i, v.GetInt());
+ i++;
+ }
+ EXPECT_EQ(i, 10);
+ }
+ {
+ int i = 0;
+ for (const auto& v : const_cast<const Value&>(x).GetArray()) {
+ EXPECT_EQ(i, v.GetInt());
+ i++;
+ }
+ EXPECT_EQ(i, 10);
+ }
+
+ // Array a = x.GetArray();
+ // Array ca = const_cast<const Value&>(x).GetArray();
+}
+#endif
+
+template <typename T, typename Allocator>
+static void TestObject(T& x, Allocator& allocator) {
+ const T& y = x; // const version
+
+ // AddMember()
+ x.AddMember("A", "Apple", allocator);
+ EXPECT_FALSE(x.ObjectEmpty());
+ EXPECT_EQ(1u, x.MemberCount());
+
+ Value value("Banana", 6);
+ x.AddMember("B", "Banana", allocator);
+ EXPECT_EQ(2u, x.MemberCount());
+
+ // AddMember<T>(StringRefType, T, Allocator)
+ {
+ Value o(kObjectType);
+ o.AddMember("true", true, allocator);
+ o.AddMember("false", false, allocator);
+ o.AddMember("int", -1, allocator);
+ o.AddMember("uint", 1u, allocator);
+ o.AddMember("int64", int64_t(-4294967296), allocator);
+ o.AddMember("uint64", uint64_t(4294967296), allocator);
+ o.AddMember("double", 3.14, allocator);
+ o.AddMember("string", "Jelly", allocator);
+
+ EXPECT_TRUE(o["true"].GetBool());
+ EXPECT_FALSE(o["false"].GetBool());
+ EXPECT_EQ(-1, o["int"].GetInt());
+ EXPECT_EQ(1u, o["uint"].GetUint());
+ EXPECT_EQ(int64_t(-4294967296), o["int64"].GetInt64());
+ EXPECT_EQ(uint64_t(4294967296), o["uint64"].GetUint64());
+ EXPECT_STREQ("Jelly",o["string"].GetString());
+ EXPECT_EQ(8u, o.MemberCount());
+ }
+
+ // AddMember<T>(Value&, T, Allocator)
+ {
+ Value o(kObjectType);
+
+ Value n("s");
+ o.AddMember(n, "string", allocator);
+ EXPECT_EQ(1u, o.MemberCount());
+
+ Value count("#");
+ o.AddMember(count, o.MemberCount(), allocator);
+ EXPECT_EQ(2u, o.MemberCount());
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ {
+ // AddMember(StringRefType, const std::string&, Allocator)
+ Value o(kObjectType);
+ o.AddMember("b", std::string("Banana"), allocator);
+ EXPECT_STREQ("Banana", o["b"].GetString());
+
+ // RemoveMember(const std::string&)
+ o.RemoveMember(std::string("b"));
+ EXPECT_TRUE(o.ObjectEmpty());
+ }
+#endif
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ // AddMember(GenericValue&&, ...) variants
+ {
+ Value o(kObjectType);
+ o.AddMember(Value("true"), Value(true), allocator);
+ o.AddMember(Value("false"), Value(false).Move(), allocator); // value is lvalue ref
+ o.AddMember(Value("int").Move(), Value(-1), allocator); // name is lvalue ref
+ o.AddMember("uint", std::move(Value().SetUint(1u)), allocator); // name is literal, value is rvalue
+ EXPECT_TRUE(o["true"].GetBool());
+ EXPECT_FALSE(o["false"].GetBool());
+ EXPECT_EQ(-1, o["int"].GetInt());
+ EXPECT_EQ(1u, o["uint"].GetUint());
+ EXPECT_EQ(4u, o.MemberCount());
+ }
+#endif
+
+ // Tests a member with null character
+ Value name;
+ const Value C0D("C\0D", 3);
+ name.SetString(C0D.GetString(), 3);
+ value.SetString("CherryD", 7);
+ x.AddMember(name, value, allocator);
+
+ // HasMember()
+ EXPECT_TRUE(x.HasMember("A"));
+ EXPECT_TRUE(x.HasMember("B"));
+ EXPECT_TRUE(y.HasMember("A"));
+ EXPECT_TRUE(y.HasMember("B"));
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_TRUE(x.HasMember(std::string("A")));
+#endif
+
+ name.SetString("C\0D");
+ EXPECT_TRUE(x.HasMember(name));
+ EXPECT_TRUE(y.HasMember(name));
+
+ GenericValue<UTF8<>, CrtAllocator> othername("A");
+ EXPECT_TRUE(x.HasMember(othername));
+ EXPECT_TRUE(y.HasMember(othername));
+ othername.SetString("C\0D");
+ EXPECT_TRUE(x.HasMember(othername));
+ EXPECT_TRUE(y.HasMember(othername));
+
+ // operator[]
+ EXPECT_STREQ("Apple", x["A"].GetString());
+ EXPECT_STREQ("Banana", x["B"].GetString());
+ EXPECT_STREQ("CherryD", x[C0D].GetString());
+ EXPECT_STREQ("CherryD", x[othername].GetString());
+ EXPECT_THROW(x["nonexist"], AssertException);
+
+ // const operator[]
+ EXPECT_STREQ("Apple", y["A"].GetString());
+ EXPECT_STREQ("Banana", y["B"].GetString());
+ EXPECT_STREQ("CherryD", y[C0D].GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("Apple", x["A"].GetString());
+ EXPECT_STREQ("Apple", y[std::string("A")].GetString());
+#endif
+
+ // member iterator
+ Value::MemberIterator itr = x.MemberBegin();
+ EXPECT_TRUE(itr != x.MemberEnd());
+ EXPECT_STREQ("A", itr->name.GetString());
+ EXPECT_STREQ("Apple", itr->value.GetString());
+ ++itr;
+ EXPECT_TRUE(itr != x.MemberEnd());
+ EXPECT_STREQ("B", itr->name.GetString());
+ EXPECT_STREQ("Banana", itr->value.GetString());
+ ++itr;
+ EXPECT_TRUE(itr != x.MemberEnd());
+ EXPECT_TRUE(memcmp(itr->name.GetString(), "C\0D", 4) == 0);
+ EXPECT_STREQ("CherryD", itr->value.GetString());
+ ++itr;
+ EXPECT_FALSE(itr != x.MemberEnd());
+
+ // const member iterator
+ Value::ConstMemberIterator citr = y.MemberBegin();
+ EXPECT_TRUE(citr != y.MemberEnd());
+ EXPECT_STREQ("A", citr->name.GetString());
+ EXPECT_STREQ("Apple", citr->value.GetString());
+ ++citr;
+ EXPECT_TRUE(citr != y.MemberEnd());
+ EXPECT_STREQ("B", citr->name.GetString());
+ EXPECT_STREQ("Banana", citr->value.GetString());
+ ++citr;
+ EXPECT_TRUE(citr != y.MemberEnd());
+ EXPECT_TRUE(memcmp(citr->name.GetString(), "C\0D", 4) == 0);
+ EXPECT_STREQ("CherryD", citr->value.GetString());
+ ++citr;
+ EXPECT_FALSE(citr != y.MemberEnd());
+
+ // member iterator conversions/relations
+ itr = x.MemberBegin();
+ citr = x.MemberBegin(); // const conversion
+ TestEqual(itr, citr);
+ EXPECT_TRUE(itr < x.MemberEnd());
+ EXPECT_FALSE(itr > y.MemberEnd());
+ EXPECT_TRUE(citr < x.MemberEnd());
+ EXPECT_FALSE(citr > y.MemberEnd());
+ ++citr;
+ TestUnequal(itr, citr);
+ EXPECT_FALSE(itr < itr);
+ EXPECT_TRUE(itr < citr);
+ EXPECT_FALSE(itr > itr);
+ EXPECT_TRUE(citr > itr);
+ EXPECT_EQ(1, citr - x.MemberBegin());
+ EXPECT_EQ(0, itr - y.MemberBegin());
+ itr += citr - x.MemberBegin();
+ EXPECT_EQ(1, itr - y.MemberBegin());
+ TestEqual(citr, itr);
+ EXPECT_TRUE(itr <= citr);
+ EXPECT_TRUE(citr <= itr);
+ itr++;
+ EXPECT_TRUE(itr >= citr);
+ EXPECT_FALSE(citr >= itr);
+
+ // RemoveMember()
+ EXPECT_TRUE(x.RemoveMember("A"));
+ EXPECT_FALSE(x.HasMember("A"));
+
+ EXPECT_TRUE(x.RemoveMember("B"));
+ EXPECT_FALSE(x.HasMember("B"));
+
+ EXPECT_FALSE(x.RemoveMember("nonexist"));
+
+ EXPECT_TRUE(x.RemoveMember(othername));
+ EXPECT_FALSE(x.HasMember(name));
+
+ EXPECT_TRUE(x.MemberBegin() == x.MemberEnd());
+
+ // EraseMember(ConstMemberIterator)
+
+ // Use array members to ensure removed elements' destructor is called.
+ // { "a": [0], "b": [1],[2],...]
+ const char keys[][2] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" };
+ for (int i = 0; i < 10; i++)
+ x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator);
+
+ // MemberCount, iterator difference
+ EXPECT_EQ(x.MemberCount(), SizeType(x.MemberEnd() - x.MemberBegin()));
+
+ // Erase the first
+ itr = x.EraseMember(x.MemberBegin());
+ EXPECT_FALSE(x.HasMember(keys[0]));
+ EXPECT_EQ(x.MemberBegin(), itr);
+ EXPECT_EQ(9u, x.MemberCount());
+ for (; itr != x.MemberEnd(); ++itr) {
+ size_t i = static_cast<size_t>((itr - x.MemberBegin())) + 1;
+ EXPECT_STREQ(itr->name.GetString(), keys[i]);
+ EXPECT_EQ(i, itr->value[0].GetInt());
+ }
+
+ // Erase the last
+ itr = x.EraseMember(x.MemberEnd() - 1);
+ EXPECT_FALSE(x.HasMember(keys[9]));
+ EXPECT_EQ(x.MemberEnd(), itr);
+ EXPECT_EQ(8u, x.MemberCount());
+ for (; itr != x.MemberEnd(); ++itr) {
+ size_t i = static_cast<size_t>(itr - x.MemberBegin()) + 1;
+ EXPECT_STREQ(itr->name.GetString(), keys[i]);
+ EXPECT_EQ(i, itr->value[0].GetInt());
+ }
+
+ // Erase the middle
+ itr = x.EraseMember(x.MemberBegin() + 4);
+ EXPECT_FALSE(x.HasMember(keys[5]));
+ EXPECT_EQ(x.MemberBegin() + 4, itr);
+ EXPECT_EQ(7u, x.MemberCount());
+ for (; itr != x.MemberEnd(); ++itr) {
+ size_t i = static_cast<size_t>(itr - x.MemberBegin());
+ i += (i < 4) ? 1 : 2;
+ EXPECT_STREQ(itr->name.GetString(), keys[i]);
+ EXPECT_EQ(i, itr->value[0].GetInt());
+ }
+
+ // EraseMember(ConstMemberIterator, ConstMemberIterator)
+ // Exhaustive test with all 0 <= first < n, first <= last <= n cases
+ const unsigned n = 10;
+ for (unsigned first = 0; first < n; first++) {
+ for (unsigned last = first; last <= n; last++) {
+ x.RemoveAllMembers();
+ for (unsigned i = 0; i < n; i++)
+ x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator);
+
+ itr = x.EraseMember(x.MemberBegin() + static_cast<int>(first), x.MemberBegin() + static_cast<int>(last));
+ if (last == n)
+ EXPECT_EQ(x.MemberEnd(), itr);
+ else
+ EXPECT_EQ(x.MemberBegin() + static_cast<int>(first), itr);
+
+ size_t removeCount = last - first;
+ EXPECT_EQ(n - removeCount, x.MemberCount());
+ for (unsigned i = 0; i < first; i++)
+ EXPECT_EQ(i, x[keys[i]][0].GetUint());
+ for (unsigned i = first; i < n - removeCount; i++)
+ EXPECT_EQ(i + removeCount, x[keys[i+removeCount]][0].GetUint());
+ }
+ }
+
+ // RemoveAllMembers()
+ x.RemoveAllMembers();
+ EXPECT_TRUE(x.ObjectEmpty());
+ EXPECT_EQ(0u, x.MemberCount());
+}
+
+TEST(Value, Object) {
+ Value x(kObjectType);
+ const Value& y = x; // const version
+ Value::AllocatorType allocator;
+
+ EXPECT_EQ(kObjectType, x.GetType());
+ EXPECT_TRUE(x.IsObject());
+ EXPECT_TRUE(x.ObjectEmpty());
+ EXPECT_EQ(0u, x.MemberCount());
+ EXPECT_EQ(kObjectType, y.GetType());
+ EXPECT_TRUE(y.IsObject());
+ EXPECT_TRUE(y.ObjectEmpty());
+ EXPECT_EQ(0u, y.MemberCount());
+
+ TestObject(x, allocator);
+
+ // SetObject()
+ Value z;
+ z.SetObject();
+ EXPECT_TRUE(z.IsObject());
+}
+
+TEST(Value, ObjectHelper) {
+ Value::AllocatorType allocator;
+ {
+ Value x(kObjectType);
+ Value::Object o = x.GetObject();
+ TestObject(o, allocator);
+ }
+
+ {
+ Value x(kObjectType);
+ Value::Object o = x.GetObject();
+ o.AddMember("1", 1, allocator);
+
+ Value::Object o2(o); // copy constructor
+ EXPECT_EQ(1, o2.MemberCount());
+
+ Value::Object o3 = o;
+ EXPECT_EQ(1, o3.MemberCount());
+
+ Value::ConstObject y = static_cast<const Value&>(x).GetObject();
+ (void)y;
+ // y.AddMember("1", 1, allocator); // should not compile
+
+ // Templated functions
+ x.RemoveAllMembers();
+ EXPECT_TRUE(x.Is<Value::Object>());
+ EXPECT_TRUE(x.Is<Value::ConstObject>());
+ o.AddMember("1", 1, allocator);
+ EXPECT_EQ(1, x.Get<Value::Object>()["1"].GetInt());
+ EXPECT_EQ(1, x.Get<Value::ConstObject>()["1"].GetInt());
+
+ Value x2;
+ x2.Set<Value::Object>(o);
+ EXPECT_TRUE(x.IsObject()); // IsObject() is invariant after moving
+ EXPECT_EQ(1, x2.Get<Value::Object>()["1"].GetInt());
+ }
+
+ {
+ Value x(kObjectType);
+ x.AddMember("a", "apple", allocator);
+ Value y(x.GetObject());
+ EXPECT_STREQ("apple", y["a"].GetString());
+ EXPECT_TRUE(x.IsObject()); // Invariant
+ }
+
+ {
+ Value x(kObjectType);
+ x.AddMember("a", "apple", allocator);
+ Value y(kObjectType);
+ y.AddMember("fruits", x.GetObject(), allocator);
+ EXPECT_STREQ("apple", y["fruits"]["a"].GetString());
+ EXPECT_TRUE(x.IsObject()); // Invariant
+ }
+}
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+TEST(Value, ObjectHelperRangeFor) {
+ Value::AllocatorType allocator;
+ Value x(kObjectType);
+
+ for (int i = 0; i < 10; i++) {
+ char name[10];
+ Value n(name, static_cast<SizeType>(sprintf(name, "%d", i)), allocator);
+ x.AddMember(n, i, allocator);
+ }
+
+ {
+ int i = 0;
+ for (auto& m : x.GetObject()) {
+ char name[10];
+ sprintf(name, "%d", i);
+ EXPECT_STREQ(name, m.name.GetString());
+ EXPECT_EQ(i, m.value.GetInt());
+ i++;
+ }
+ EXPECT_EQ(i, 10);
+ }
+ {
+ int i = 0;
+ for (const auto& m : const_cast<const Value&>(x).GetObject()) {
+ char name[10];
+ sprintf(name, "%d", i);
+ EXPECT_STREQ(name, m.name.GetString());
+ EXPECT_EQ(i, m.value.GetInt());
+ i++;
+ }
+ EXPECT_EQ(i, 10);
+ }
+
+ // Object a = x.GetObject();
+ // Object ca = const_cast<const Value&>(x).GetObject();
+}
+#endif
+
+TEST(Value, EraseMember_String) {
+ Value::AllocatorType allocator;
+ Value x(kObjectType);
+ x.AddMember("A", "Apple", allocator);
+ x.AddMember("B", "Banana", allocator);
+
+ EXPECT_TRUE(x.EraseMember("B"));
+ EXPECT_FALSE(x.HasMember("B"));
+
+ EXPECT_FALSE(x.EraseMember("nonexist"));
+
+ GenericValue<UTF8<>, CrtAllocator> othername("A");
+ EXPECT_TRUE(x.EraseMember(othername));
+ EXPECT_FALSE(x.HasMember("A"));
+
+ EXPECT_TRUE(x.MemberBegin() == x.MemberEnd());
+}
+
+TEST(Value, BigNestedArray) {
+ MemoryPoolAllocator<> allocator;
+ Value x(kArrayType);
+ static const SizeType n = 200;
+
+ for (SizeType i = 0; i < n; i++) {
+ Value y(kArrayType);
+ for (SizeType j = 0; j < n; j++) {
+ Value number(static_cast<int>(i * n + j));
+ y.PushBack(number, allocator);
+ }
+ x.PushBack(y, allocator);
+ }
+
+ for (SizeType i = 0; i < n; i++)
+ for (SizeType j = 0; j < n; j++) {
+ EXPECT_TRUE(x[i][j].IsInt());
+ EXPECT_EQ(static_cast<int>(i * n + j), x[i][j].GetInt());
+ }
+}
+
+TEST(Value, BigNestedObject) {
+ MemoryPoolAllocator<> allocator;
+ Value x(kObjectType);
+ static const SizeType n = 200;
+
+ for (SizeType i = 0; i < n; i++) {
+ char name1[10];
+ sprintf(name1, "%d", i);
+
+ // Value name(name1); // should not compile
+ Value name(name1, static_cast<SizeType>(strlen(name1)), allocator);
+ Value object(kObjectType);
+
+ for (SizeType j = 0; j < n; j++) {
+ char name2[10];
+ sprintf(name2, "%d", j);
+
+ Value name3(name2, static_cast<SizeType>(strlen(name2)), allocator);
+ Value number(static_cast<int>(i * n + j));
+ object.AddMember(name3, number, allocator);
+ }
+
+ // x.AddMember(name1, object, allocator); // should not compile
+ x.AddMember(name, object, allocator);
+ }
+
+ for (SizeType i = 0; i < n; i++) {
+ char name1[10];
+ sprintf(name1, "%d", i);
+
+ for (SizeType j = 0; j < n; j++) {
+ char name2[10];
+ sprintf(name2, "%d", j);
+ x[name1];
+ EXPECT_EQ(static_cast<int>(i * n + j), x[name1][name2].GetInt());
+ }
+ }
+}
+
+// Issue 18: Error removing last element of object
+// http://code.google.com/p/rapidjson/issues/detail?id=18
+TEST(Value, RemoveLastElement) {
+ rapidjson::Document doc;
+ rapidjson::Document::AllocatorType& allocator = doc.GetAllocator();
+ rapidjson::Value objVal(rapidjson::kObjectType);
+ objVal.AddMember("var1", 123, allocator);
+ objVal.AddMember("var2", "444", allocator);
+ objVal.AddMember("var3", 555, allocator);
+ EXPECT_TRUE(objVal.HasMember("var3"));
+ objVal.RemoveMember("var3"); // Assertion here in r61
+ EXPECT_FALSE(objVal.HasMember("var3"));
+}
+
+// Issue 38: Segmentation fault with CrtAllocator
+TEST(Document, CrtAllocator) {
+ typedef GenericValue<UTF8<>, CrtAllocator> V;
+
+ V::AllocatorType allocator;
+ V o(kObjectType);
+ o.AddMember("x", 1, allocator); // Should not call destructor on uninitialized name/value of newly allocated members.
+
+ V a(kArrayType);
+ a.PushBack(1, allocator); // Should not call destructor on uninitialized Value of newly allocated elements.
+}
+
+static void TestShortStringOptimization(const char* str) {
+ const rapidjson::SizeType len = static_cast<rapidjson::SizeType>(strlen(str));
+
+ rapidjson::Document doc;
+ rapidjson::Value val;
+ val.SetString(str, len, doc.GetAllocator());
+
+ EXPECT_EQ(val.GetStringLength(), len);
+ EXPECT_STREQ(val.GetString(), str);
+}
+
+TEST(Value, AllocateShortString) {
+ TestShortStringOptimization(""); // edge case: empty string
+ TestShortStringOptimization("12345678"); // regular case for short strings: 8 chars
+ TestShortStringOptimization("12345678901"); // edge case: 11 chars in 32-bit mode (=> short string)
+ TestShortStringOptimization("123456789012"); // edge case: 12 chars in 32-bit mode (=> regular string)
+ TestShortStringOptimization("123456789012345"); // edge case: 15 chars in 64-bit mode (=> short string)
+ TestShortStringOptimization("1234567890123456"); // edge case: 16 chars in 64-bit mode (=> regular string)
+}
+
+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)\
+{\
+ Document d; \
+ EXPECT_FALSE(d.Parse(json).HasParseError()); \
+ Reader reader; \
+ TerminateHandler<e> h;\
+ EXPECT_FALSE(d.Accept(h));\
+}
+
+TEST(Value, AcceptTerminationByHandler) {
+ TEST_TERMINATION(0, "[null]");
+ TEST_TERMINATION(1, "[true]");
+ TEST_TERMINATION(1, "[false]");
+ TEST_TERMINATION(2, "[-1]");
+ TEST_TERMINATION(3, "[2147483648]");
+ TEST_TERMINATION(4, "[-1234567890123456789]");
+ TEST_TERMINATION(5, "[9223372036854775808]");
+ TEST_TERMINATION(6, "[0.5]");
+ // RawNumber() is never called
+ TEST_TERMINATION(8, "[\"a\"]");
+ TEST_TERMINATION(9, "[{}]");
+ TEST_TERMINATION(10, "[{\"a\":1}]");
+ TEST_TERMINATION(11, "[{}]");
+ TEST_TERMINATION(12, "{\"a\":[]}");
+ TEST_TERMINATION(13, "{\"a\":[]}");
+}
+
+struct ValueIntComparer {
+ bool operator()(const Value& lhs, const Value& rhs) const {
+ return lhs.GetInt() < rhs.GetInt();
+ }
+};
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+TEST(Value, Sorting) {
+ Value::AllocatorType allocator;
+ Value a(kArrayType);
+ a.PushBack(5, allocator);
+ a.PushBack(1, allocator);
+ a.PushBack(3, allocator);
+ std::sort(a.Begin(), a.End(), ValueIntComparer());
+ EXPECT_EQ(1, a[0].GetInt());
+ EXPECT_EQ(3, a[1].GetInt());
+ EXPECT_EQ(5, a[2].GetInt());
+}
+#endif
+
+// http://stackoverflow.com/questions/35222230/
+
+static void MergeDuplicateKey(Value& v, Value::AllocatorType& a) {
+ if (v.IsObject()) {
+ // Convert all key:value into key:[value]
+ for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
+ itr->value = Value(kArrayType).Move().PushBack(itr->value, a);
+
+ // Merge arrays if key is duplicated
+ for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd();) {
+ Value::MemberIterator itr2 = v.FindMember(itr->name);
+ if (itr != itr2) {
+ itr2->value.PushBack(itr->value[0], a);
+ itr = v.EraseMember(itr);
+ }
+ else
+ ++itr;
+ }
+
+ // Convert key:[values] back to key:value if there is only one value
+ for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) {
+ if (itr->value.Size() == 1)
+ itr->value = itr->value[0];
+ MergeDuplicateKey(itr->value, a); // Recursion on the value
+ }
+ }
+ else if (v.IsArray())
+ for (Value::ValueIterator itr = v.Begin(); itr != v.End(); ++itr)
+ MergeDuplicateKey(*itr, a);
+}
+
+TEST(Value, MergeDuplicateKey) {
+ Document d;
+ d.Parse(
+ "{"
+ " \"key1\": {"
+ " \"a\": \"asdf\","
+ " \"b\": \"foo\","
+ " \"b\": \"bar\","
+ " \"c\": \"fdas\""
+ " }"
+ "}");
+
+ Document d2;
+ d2.Parse(
+ "{"
+ " \"key1\": {"
+ " \"a\": \"asdf\","
+ " \"b\": ["
+ " \"foo\","
+ " \"bar\""
+ " ],"
+ " \"c\": \"fdas\""
+ " }"
+ "}");
+
+ EXPECT_NE(d2, d);
+ MergeDuplicateKey(d, d.GetAllocator());
+ EXPECT_EQ(d2, d);
+}
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/rapidjson/test/unittest/writertest.cpp b/src/rapidjson/test/unittest/writertest.cpp
new file mode 100644
index 000000000..29f762609
--- /dev/null
+++ b/src/rapidjson/test/unittest/writertest.cpp
@@ -0,0 +1,497 @@
+// 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. All rights reserved.
+//
+// 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/document.h"
+#include "rapidjson/reader.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/memorybuffer.h"
+
+using namespace rapidjson;
+
+TEST(Writer, Compact) {
+ StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ");
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ buffer.ShrinkToFit();
+ Reader reader;
+ reader.Parse<0>(s, writer);
+ EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", buffer.GetString());
+ EXPECT_EQ(77u, buffer.GetSize());
+ EXPECT_TRUE(writer.IsComplete());
+}
+
+// json -> parse -> writer -> json
+#define TEST_ROUNDTRIP(json) \
+ { \
+ StringStream s(json); \
+ StringBuffer buffer; \
+ Writer<StringBuffer> writer(buffer); \
+ Reader reader; \
+ reader.Parse<kParseFullPrecisionFlag>(s, writer); \
+ EXPECT_STREQ(json, buffer.GetString()); \
+ EXPECT_TRUE(writer.IsComplete()); \
+ }
+
+TEST(Writer, Root) {
+ TEST_ROUNDTRIP("null");
+ TEST_ROUNDTRIP("true");
+ TEST_ROUNDTRIP("false");
+ TEST_ROUNDTRIP("0");
+ TEST_ROUNDTRIP("\"foo\"");
+ TEST_ROUNDTRIP("[]");
+ TEST_ROUNDTRIP("{}");
+}
+
+TEST(Writer, Int) {
+ TEST_ROUNDTRIP("[-1]");
+ TEST_ROUNDTRIP("[-123]");
+ TEST_ROUNDTRIP("[-2147483648]");
+}
+
+TEST(Writer, UInt) {
+ TEST_ROUNDTRIP("[0]");
+ TEST_ROUNDTRIP("[1]");
+ TEST_ROUNDTRIP("[123]");
+ TEST_ROUNDTRIP("[2147483647]");
+ TEST_ROUNDTRIP("[4294967295]");
+}
+
+TEST(Writer, Int64) {
+ TEST_ROUNDTRIP("[-1234567890123456789]");
+ TEST_ROUNDTRIP("[-9223372036854775808]");
+}
+
+TEST(Writer, Uint64) {
+ TEST_ROUNDTRIP("[1234567890123456789]");
+ TEST_ROUNDTRIP("[9223372036854775807]");
+}
+
+TEST(Writer, String) {
+ TEST_ROUNDTRIP("[\"Hello\"]");
+ TEST_ROUNDTRIP("[\"Hello\\u0000World\"]");
+ TEST_ROUNDTRIP("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]");
+
+#if RAPIDJSON_HAS_STDSTRING
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.String(std::string("Hello\n"));
+ EXPECT_STREQ("\"Hello\\n\"", buffer.GetString());
+ }
+#endif
+}
+
+TEST(Writer, ScanWriteUnescapedString) {
+ const char json[] = "[\" \\\"0123456789ABCDEF\"]";
+ // ^ scanning stops here.
+ char buffer2[sizeof(json) + 32];
+
+ // Use different offset to test different alignments
+ for (int i = 0; i < 32; i++) {
+ char* p = buffer2 + i;
+ memcpy(p, json, sizeof(json));
+ TEST_ROUNDTRIP(p);
+ }
+}
+
+TEST(Writer, Double) {
+ TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]");
+ TEST_ROUNDTRIP("0.0");
+ TEST_ROUNDTRIP("-0.0"); // Issue #289
+ TEST_ROUNDTRIP("1e30");
+ TEST_ROUNDTRIP("1.0");
+ TEST_ROUNDTRIP("5e-324"); // Min subnormal positive double
+ TEST_ROUNDTRIP("2.225073858507201e-308"); // Max subnormal positive double
+ TEST_ROUNDTRIP("2.2250738585072014e-308"); // Min normal positive double
+ TEST_ROUNDTRIP("1.7976931348623157e308"); // Max double
+
+}
+
+// UTF8 -> TargetEncoding -> UTF8
+template <typename TargetEncoding>
+void TestTranscode(const char* json) {
+ StringStream s(json);
+ GenericStringBuffer<TargetEncoding> buffer;
+ Writer<GenericStringBuffer<TargetEncoding>, UTF8<>, TargetEncoding> writer(buffer);
+ Reader reader;
+ reader.Parse(s, writer);
+
+ StringBuffer buffer2;
+ Writer<StringBuffer> writer2(buffer2);
+ GenericReader<TargetEncoding, UTF8<> > reader2;
+ GenericStringStream<TargetEncoding> s2(buffer.GetString());
+ reader2.Parse(s2, writer2);
+
+ EXPECT_STREQ(json, buffer2.GetString());
+}
+
+TEST(Writer, Transcode) {
+ const char json[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"dollar\":\"\x24\",\"cents\":\"\xC2\xA2\",\"euro\":\"\xE2\x82\xAC\",\"gclef\":\"\xF0\x9D\x84\x9E\"}";
+
+ // UTF8 -> UTF16 -> UTF8
+ TestTranscode<UTF8<> >(json);
+
+ // UTF8 -> ASCII -> UTF8
+ TestTranscode<ASCII<> >(json);
+
+ // UTF8 -> UTF16 -> UTF8
+ TestTranscode<UTF16<> >(json);
+
+ // UTF8 -> UTF32 -> UTF8
+ TestTranscode<UTF32<> >(json);
+
+ // UTF8 -> AutoUTF -> UTF8
+ UTFType types[] = { kUTF8, kUTF16LE , kUTF16BE, kUTF32LE , kUTF32BE };
+ for (size_t i = 0; i < 5; i++) {
+ StringStream s(json);
+ MemoryBuffer buffer;
+ AutoUTFOutputStream<unsigned, MemoryBuffer> os(buffer, types[i], true);
+ Writer<AutoUTFOutputStream<unsigned, MemoryBuffer>, UTF8<>, AutoUTF<unsigned> > writer(os);
+ Reader reader;
+ reader.Parse(s, writer);
+
+ StringBuffer buffer2;
+ Writer<StringBuffer> writer2(buffer2);
+ GenericReader<AutoUTF<unsigned>, UTF8<> > reader2;
+ MemoryStream s2(buffer.GetBuffer(), buffer.GetSize());
+ AutoUTFInputStream<unsigned, MemoryStream> is(s2);
+ reader2.Parse(is, writer2);
+
+ EXPECT_STREQ(json, buffer2.GetString());
+ }
+
+}
+
+#include <sstream>
+
+class OStreamWrapper {
+public:
+ typedef char Ch;
+
+ OStreamWrapper(std::ostream& os) : os_(os) {}
+
+ Ch Peek() const { assert(false); return '\0'; }
+ Ch Take() { assert(false); return '\0'; }
+ size_t Tell() const { return 0; }
+
+ Ch* PutBegin() { assert(false); return 0; }
+ void Put(Ch c) { os_.put(c); }
+ void Flush() { os_.flush(); }
+ size_t PutEnd(Ch*) { assert(false); return 0; }
+
+private:
+ OStreamWrapper(const OStreamWrapper&);
+ OStreamWrapper& operator=(const OStreamWrapper&);
+
+ std::ostream& os_;
+};
+
+TEST(Writer, OStreamWrapper) {
+ StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3], \"u64\": 1234567890123456789, \"i64\":-1234567890123456789 } ");
+
+ std::stringstream ss;
+ OStreamWrapper os(ss);
+
+ Writer<OStreamWrapper> writer(os);
+
+ Reader reader;
+ reader.Parse<0>(s, writer);
+
+ std::string actual = ss.str();
+ EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}", actual.c_str());
+}
+
+TEST(Writer, AssertRootMayBeAnyValue) {
+#define T(x)\
+ {\
+ StringBuffer buffer;\
+ Writer<StringBuffer> writer(buffer);\
+ EXPECT_TRUE(x);\
+ }
+ T(writer.Bool(false));
+ T(writer.Bool(true));
+ T(writer.Null());
+ T(writer.Int(0));
+ T(writer.Uint(0));
+ T(writer.Int64(0));
+ T(writer.Uint64(0));
+ T(writer.Double(0));
+ T(writer.String("foo"));
+#undef T
+}
+
+TEST(Writer, AssertIncorrectObjectLevel) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.EndObject();
+ ASSERT_THROW(writer.EndObject(), AssertException);
+}
+
+TEST(Writer, AssertIncorrectArrayLevel) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartArray();
+ writer.EndArray();
+ ASSERT_THROW(writer.EndArray(), AssertException);
+}
+
+TEST(Writer, AssertIncorrectEndObject) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ ASSERT_THROW(writer.EndArray(), AssertException);
+}
+
+TEST(Writer, AssertIncorrectEndArray) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ ASSERT_THROW(writer.EndArray(), AssertException);
+}
+
+TEST(Writer, AssertObjectKeyNotString) {
+#define T(x)\
+ {\
+ StringBuffer buffer;\
+ Writer<StringBuffer> writer(buffer);\
+ writer.StartObject();\
+ ASSERT_THROW(x, AssertException); \
+ }
+ T(writer.Bool(false));
+ T(writer.Bool(true));
+ T(writer.Null());
+ T(writer.Int(0));
+ T(writer.Uint(0));
+ T(writer.Int64(0));
+ T(writer.Uint64(0));
+ T(writer.Double(0));
+ T(writer.StartObject());
+ T(writer.StartArray());
+#undef T
+}
+
+TEST(Writer, AssertMultipleRoot) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+
+ writer.StartObject();
+ writer.EndObject();
+ ASSERT_THROW(writer.StartObject(), AssertException);
+
+ writer.Reset(buffer);
+ writer.Null();
+ ASSERT_THROW(writer.Int(0), AssertException);
+
+ writer.Reset(buffer);
+ writer.String("foo");
+ ASSERT_THROW(writer.StartArray(), AssertException);
+
+ writer.Reset(buffer);
+ writer.StartArray();
+ writer.EndArray();
+ //ASSERT_THROW(writer.Double(3.14), AssertException);
+}
+
+TEST(Writer, RootObjectIsComplete) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.IsComplete());
+ writer.StartObject();
+ EXPECT_FALSE(writer.IsComplete());
+ writer.String("foo");
+ EXPECT_FALSE(writer.IsComplete());
+ writer.Int(1);
+ EXPECT_FALSE(writer.IsComplete());
+ writer.EndObject();
+ EXPECT_TRUE(writer.IsComplete());
+}
+
+TEST(Writer, RootArrayIsComplete) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.IsComplete());
+ writer.StartArray();
+ EXPECT_FALSE(writer.IsComplete());
+ writer.String("foo");
+ EXPECT_FALSE(writer.IsComplete());
+ writer.Int(1);
+ EXPECT_FALSE(writer.IsComplete());
+ writer.EndArray();
+ EXPECT_TRUE(writer.IsComplete());
+}
+
+TEST(Writer, RootValueIsComplete) {
+#define T(x)\
+ {\
+ StringBuffer buffer;\
+ Writer<StringBuffer> writer(buffer);\
+ EXPECT_FALSE(writer.IsComplete()); \
+ x; \
+ EXPECT_TRUE(writer.IsComplete()); \
+ }
+ T(writer.Null());
+ T(writer.Bool(true));
+ T(writer.Bool(false));
+ T(writer.Int(0));
+ T(writer.Uint(0));
+ T(writer.Int64(0));
+ T(writer.Uint64(0));
+ T(writer.Double(0));
+ T(writer.String(""));
+#undef T
+}
+
+TEST(Writer, InvalidEncoding) {
+ // Fail in decoding invalid UTF-8 sequence http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+ {
+ GenericStringBuffer<UTF16<> > buffer;
+ Writer<GenericStringBuffer<UTF16<> >, UTF8<>, UTF16<> > writer(buffer);
+ writer.StartArray();
+ EXPECT_FALSE(writer.String("\xfe"));
+ EXPECT_FALSE(writer.String("\xff"));
+ EXPECT_FALSE(writer.String("\xfe\xfe\xff\xff"));
+ writer.EndArray();
+ }
+
+ // Fail in encoding
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer, UTF32<> > writer(buffer);
+ static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF
+ EXPECT_FALSE(writer.String(s));
+ }
+
+ // Fail in unicode escaping in ASCII output
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer, UTF32<>, ASCII<> > writer(buffer);
+ static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF
+ EXPECT_FALSE(writer.String(s));
+ }
+}
+
+TEST(Writer, ValidateEncoding) {
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteValidateEncodingFlag> writer(buffer);
+ writer.StartArray();
+ EXPECT_TRUE(writer.String("\x24")); // Dollar sign U+0024
+ EXPECT_TRUE(writer.String("\xC2\xA2")); // Cents sign U+00A2
+ EXPECT_TRUE(writer.String("\xE2\x82\xAC")); // Euro sign U+20AC
+ EXPECT_TRUE(writer.String("\xF0\x9D\x84\x9E")); // G clef sign U+1D11E
+ writer.EndArray();
+ EXPECT_STREQ("[\"\x24\",\"\xC2\xA2\",\"\xE2\x82\xAC\",\"\xF0\x9D\x84\x9E\"]", buffer.GetString());
+ }
+
+ // Fail in decoding invalid UTF-8 sequence http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteValidateEncodingFlag> writer(buffer);
+ writer.StartArray();
+ EXPECT_FALSE(writer.String("\xfe"));
+ EXPECT_FALSE(writer.String("\xff"));
+ EXPECT_FALSE(writer.String("\xfe\xfe\xff\xff"));
+ writer.EndArray();
+ }
+}
+
+TEST(Writer, InvalidEventSequence) {
+ // {]
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ EXPECT_THROW(writer.EndArray(), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+
+ // [}
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartArray();
+ EXPECT_THROW(writer.EndObject(), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+
+ // { 1:
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ EXPECT_THROW(writer.Int(1), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+}
+
+TEST(Writer, NaN) {
+ double nan = std::numeric_limits<double>::quiet_NaN();
+
+ EXPECT_TRUE(internal::Double(nan).IsNan());
+ StringBuffer buffer;
+ {
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.Double(nan));
+ }
+ {
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
+ EXPECT_TRUE(writer.Double(nan));
+ EXPECT_STREQ("NaN", buffer.GetString());
+ }
+ GenericStringBuffer<UTF16<> > buffer2;
+ Writer<GenericStringBuffer<UTF16<> > > writer2(buffer2);
+ EXPECT_FALSE(writer2.Double(nan));
+}
+
+TEST(Writer, Inf) {
+ double inf = std::numeric_limits<double>::infinity();
+
+ EXPECT_TRUE(internal::Double(inf).IsInf());
+ StringBuffer buffer;
+ {
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.Double(inf));
+ }
+ {
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.Double(-inf));
+ }
+ {
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
+ EXPECT_TRUE(writer.Double(inf));
+ }
+ {
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
+ EXPECT_TRUE(writer.Double(-inf));
+ }
+ EXPECT_STREQ("Infinity-Infinity", buffer.GetString());
+}
+
+TEST(Writer, RawValue) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.Key("a");
+ writer.Int(1);
+ writer.Key("raw");
+ const char json[] = "[\"Hello\\nWorld\", 123.456]";
+ writer.RawValue(json, strlen(json), kArrayType);
+ writer.EndObject();
+ EXPECT_TRUE(writer.IsComplete());
+ EXPECT_STREQ("{\"a\":1,\"raw\":[\"Hello\\nWorld\", 123.456]}", buffer.GetString());
+}