first commit

This commit is contained in:
sanya
2025-09-01 14:20:39 +00:00
committed by ExternPointer
commit 490fc11f6a
4328 changed files with 1796224 additions and 0 deletions

View File

@@ -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(${GTEST_INCLUDE_DIR})
set(TEST_LIBRARIES gtest gtest_main)
add_custom_target(tests ALL)
add_subdirectory(perftest)
add_subdirectory(unittest)
ENDIF(GTESTSRC_FOUND)

View File

@@ -0,0 +1,16 @@
set(PERFTEST_SOURCES
misctest.cpp
perftest.cpp
platformtest.cpp
rapidjsontest.cpp)
add_executable(perftest ${PERFTEST_SOURCES})
target_link_libraries(perftest ${TEST_LIBRARIES})
add_dependencies(tests perftest)
IF(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
add_test(NAME perftest
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/perftest
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
ENDIF()

View File

@@ -0,0 +1,980 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "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 (int 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 (int 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

View File

@@ -0,0 +1,30 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "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();
}

View File

@@ -0,0 +1,126 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#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
////////////////////////////////////////////////////////////////////////////////
// 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() {
FILE *fp = fopen(filename_ = "data/sample.json", "rb");
if (!fp)
fp = fopen(filename_ = "../../bin/data/sample.json", "rb");
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';
}
virtual void TearDown() {
free(json_);
free(whitespace_);
json_ = 0;
whitespace_ = 0;
}
private:
PerfTest(const PerfTest&);
PerfTest& operator=(const PerfTest&);
protected:
const char* filename_;
char *json_;
size_t length_;
char *whitespace_;
size_t whitespace_length_;
static const size_t kTrialCount = 1000;
};
#endif // __cplusplus
#endif // PERFTEST_H_

View File

@@ -0,0 +1,172 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "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

View File

@@ -0,0 +1,361 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "perftest.h"
#if TEST_RAPIDJSON
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/filestream.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_).IsNull());
}
virtual void TearDown() {
PerfTest::TearDown();
free(temp_);
}
private:
RapidJson(const RapidJson&);
RapidJson& operator=(const RapidJson&);
protected:
char *temp_;
Document doc_;
};
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));
}
}
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(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 {
NullStream() /*: length_(0)*/ {}
void Put(char) { /*++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, 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;
}
}
TEST_F(RapidJson, 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, SIMD_SUFFIX(Whitespace)) {
for (size_t i = 0; i < kTrialCount; i++) {
Document doc;
ASSERT_TRUE(doc.Parse(whitespace_).IsArray());
}
}
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);
}
}
// Deprecated.
//TEST_F(RapidJson, FileStream_Read) {
// for (size_t i = 0; i < kTrialCount; i++) {
// FILE *fp = fopen(filename_, "rb");
// FileStream s(fp);
// while (s.Take() != '\0')
// ;
// fclose(fp);
// }
//}
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);
}
}
#endif // TEST_RAPIDJSON

View File

@@ -0,0 +1,46 @@
set(UNITTEST_SOURCES
bigintegertest.cpp
documenttest.cpp
encodedstreamtest.cpp
encodingstest.cpp
filestreamtest.cpp
jsoncheckertest.cpp
namespacetest.cpp
readertest.cpp
stringbuffertest.cpp
strtodtest.cpp
unittest.cpp
valuetest.cpp
writertest.cpp)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Weffc++ -Wswitch-default")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Weffc++ -Wswitch-default")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_definitions(-D_CRT_SECURE_NO_WARNINGS=1)
endif()
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)
add_test(NAME valgrind_unittest
COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest
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)

View File

@@ -0,0 +1,139 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
#include "rapidjson/internal/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));
}

View File

@@ -0,0 +1,488 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/encodedstream.h"
#include "rapidjson/stringbuffer.h"
#include <sstream>
using namespace rapidjson;
template <typename Allocator, typename StackAllocator>
void ParseTest() {
typedef GenericDocument<UTF8<>, Allocator, StackAllocator> DocumentType;
typedef typename DocumentType::ValueType ValueType;
DocumentType doc;
doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
EXPECT_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_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 i = 0; i < 4; i++)
EXPECT_EQ(i + 1, a[i].GetUint());
}
TEST(Document, Parse) {
ParseTest<MemoryPoolAllocator<>, CrtAllocator>();
ParseTest<MemoryPoolAllocator<>, MemoryPoolAllocator<> >();
ParseTest<CrtAllocator, MemoryPoolAllocator<> >();
ParseTest<CrtAllocator, CrtAllocator>();
}
static FILE* OpenEncodedFile(const char* filename) {
char buffer[1024];
sprintf(buffer, "encodings/%s", filename);
FILE *fp = fopen(buffer, "rb");
if (!fp) {
sprintf(buffer, "../../bin/encodings/%s", filename);
fp = fopen(buffer, "rb");
}
return fp;
}
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.
FILE *fp = OpenEncodedFile("utf8.json");
FileReadStream is(fp, buffer, sizeof(buffer));
Reader reader;
StringBuffer bos2;
Writer<StringBuffer> writer(bos2);
reader.Parse(is, writer);
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.
FILE *fp = OpenEncodedFile("utf8.json");
FileReadStream is(fp, buffer, sizeof(buffer));
Reader reader;
StringBuffer bos2;
Writer<StringBuffer> writer(bos2);
reader.Parse(is, writer);
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);
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());
}
// This should be slow due to assignment in inner-loop.
struct OutputStringStream : public std::ostringstream {
typedef char Ch;
void Put(char c) {
put(c);
}
void Flush() {}
};
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());
}
// 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, wcscmp(L"Wed Oct 30 17:13:20 +0000 2012", s.GetString()));
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
#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
}
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> Document;
Allocator allocator;
Document 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)
Document b(std::move(a));
EXPECT_TRUE(a.IsNull());
EXPECT_TRUE(b.IsArray());
EXPECT_EQ(3u, b.Size());
EXPECT_EQ(&a.GetAllocator(), (void*)0);
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)
Document c = std::move(b);
EXPECT_TRUE(b.IsNull());
EXPECT_TRUE(c.IsObject());
EXPECT_EQ(2u, c.MemberCount());
EXPECT_EQ(&b.GetAllocator(), (void*)0);
EXPECT_EQ(&c.GetAllocator(), &allocator);
}
TYPED_TEST(DocumentMove, MoveConstructorParseError) {
typedef TypeParam Allocator;
typedef GenericDocument<UTF8<>, Allocator> Document;
ParseResult noError;
Document 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());
Document 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());
Document 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> Document;
Allocator allocator;
Document 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)
Document b;
b = std::move(a);
EXPECT_TRUE(a.IsNull());
EXPECT_TRUE(b.IsArray());
EXPECT_EQ(3u, b.Size());
EXPECT_EQ(&a.GetAllocator(), (void*)0);
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)
Document c;
c = std::move(b);
EXPECT_TRUE(b.IsNull());
EXPECT_TRUE(c.IsObject());
EXPECT_EQ(2u, c.MemberCount());
EXPECT_EQ(&b.GetAllocator(), (void*)0);
EXPECT_EQ(&c.GetAllocator(), &allocator);
}
TYPED_TEST(DocumentMove, MoveAssignmentParseError) {
typedef TypeParam Allocator;
typedef GenericDocument<UTF8<>, Allocator> Document;
ParseResult noError;
Document 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());
Document 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());
Document 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> 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;
b = std::move(a);
EXPECT_EQ(a.GetStackCapacity(), defaultCapacity);
EXPECT_EQ(b.GetStackCapacity(), capacity);
Document 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;
//}

View File

@@ -0,0 +1,296 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
#include "rapidjson/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 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) {
char buffer[1024];
sprintf(buffer, "encodings/%s", filename);
FILE *fp = fopen(buffer, "rb");
if (!fp) {
sprintf(buffer, "../../bin/encodings/%s", filename);
fp = fopen(buffer, "rb");
}
return fp;
}
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 = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET);
char* buffer = (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);
}
}
void TestAutoUTFInputStream(const char *filename) {
// Test FileReadStream
{
char buffer[16];
FILE *fp = Open(filename);
ASSERT_TRUE(fp != 0);
FileReadStream fs(fp, buffer, sizeof(buffer));
AutoUTFInputStream<unsigned, FileReadStream> eis(fs);
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);
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);
}
}
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_;
};
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");
TestAutoUTFInputStream("utf8bom.json");
TestAutoUTFInputStream("utf16le.json");
TestAutoUTFInputStream("utf16lebom.json");
TestAutoUTFInputStream("utf16be.json");
TestAutoUTFInputStream("utf16bebom.json");
TestAutoUTFInputStream("utf32le.json");
TestAutoUTFInputStream("utf32lebom.json");
TestAutoUTFInputStream("utf32be.json");
TestAutoUTFInputStream("utf32bebom.json");
}
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");
}

View File

@@ -0,0 +1,432 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
#include "rapidjson/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
#define UTF8_REJECT 12u
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, (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, (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()));
}
}
}
}

View File

@@ -0,0 +1,122 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
#include "rapidjson/filestream.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 void SetUp() {
FILE *fp = fopen(filename_ = "data/sample.json", "rb");
if (!fp)
fp = fopen(filename_ = "../../bin/data/sample.json", "rb");
ASSERT_TRUE(fp != 0);
fseek(fp, 0, SEEK_END);
length_ = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET);
json_ = (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_;
};
// Deprecated
//TEST_F(FileStreamTest, FileStream_Read) {
// FILE *fp = fopen(filename_, "rb");
// ASSERT_TRUE(fp != 0);
// FileStream s(fp);
//
// 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, 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);
}

View File

@@ -0,0 +1,100 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
#include "rapidjson/document.h"
using namespace rapidjson;
static char* ReadFile(const char* filename, size_t& length) {
FILE *fp = fopen(filename, "rb");
if (!fp)
fp = fopen(filename, "rb");
if (!fp)
return 0;
fseek(fp, 0, SEEK_END);
length = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET);
char* json = (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, "jsonchecker/fail%d.json", i);
size_t length;
char* json = ReadFile(filename, length);
if (!json) {
sprintf(filename, "../../bin/jsonchecker/fail%d.json", i);
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((const char*)json);
EXPECT_TRUE(document.HasParseError());
document.Parse<kParseIterativeFlag>((const char*)json);
EXPECT_TRUE(document.HasParseError());
free(json);
}
// passX.json
for (int i = 1; i <= 3; i++) {
sprintf(filename, "jsonchecker/pass%d.json", i);
size_t length;
char* json = ReadFile(filename, length);
if (!json) {
sprintf(filename, "../../bin/jsonchecker/pass%d.json", i);
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((const char*)json);
EXPECT_FALSE(document.HasParseError());
document.Parse<kParseIterativeFlag>((const char*)json);
EXPECT_FALSE(document.HasParseError());
free(json);
}
}

View File

@@ -0,0 +1,76 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
// 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());
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
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, 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());
}
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
#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
}
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

View File

@@ -0,0 +1,129 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
#include "rapidjson/internal/strtod.h"
#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 = ((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(dS_Exp5) <<= dS_Exp2;
BigInteger bS(bInt);
bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2;
BigInteger hS(1);
hS.MultiplyPow5(hS_Exp5) <<= 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));
}

View File

@@ -0,0 +1,40 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
#if _MSC_VER
_CrtMemState memoryState = { 0 };
_CrtMemCheckpoint(&memoryState);
//_CrtSetBreakAlloc(X);
//void *testWhetherMemoryLeakDetectionWorks = malloc(1);
#endif
int ret = RUN_ALL_TESTS();
#if _MSC_VER
// Current gtest constantly leak 2 blocks at exit
_CrtMemDumpAllObjectsSince(&memoryState);
#endif
return ret;
}

View File

@@ -0,0 +1,113 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef UNITTEST_H_
#define UNITTEST_H_
// gtest indirectly included inttypes.h, without __STDC_CONSTANT_MACROS.
#ifndef __STDC_CONSTANT_MACROS
# define __STDC_CONSTANT_MACROS 1 // required by C++ standard
#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
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 (unsigned)*s1 < (unsigned)*s2 ? -1 : (unsigned)*s1 > (unsigned)*s2;
}
template <typename Ch>
inline Ch* StrDup(const Ch* str) {
size_t bufferSize = sizeof(Ch) * (StrLen(str) + 1);
Ch* buffer = (Ch*)malloc(bufferSize);
memcpy(buffer, str, bufferSize);
return buffer;
}
inline FILE* TempFile(char *filename) {
#if _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
#if _MSC_VER
#pragma warning(disable : 4127)
#endif
class AssertException : public std::logic_error {
public:
AssertException(const char* w) : std::logic_error(w) {}
};
#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_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h"
#include "rapidjson/document.h"
#include "rapidjson/reader.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.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<0>(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\"]");
}
TEST(Writer, Double) {
TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]");
}
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
{
StringStream s(json);
StringBuffer buffer;
Writer<StringBuffer, UTF16<>, UTF8<> > writer(buffer);
GenericReader<UTF8<>, UTF16<> > reader;
reader.Parse(s, writer);
EXPECT_STREQ(json, buffer.GetString());
}
// UTF8 -> UTF8 -> ASCII -> UTF8 -> UTF8
{
StringStream s(json);
StringBuffer buffer;
Writer<StringBuffer, UTF8<>, ASCII<> > writer(buffer);
Reader reader;
reader.Parse(s, writer);
StringBuffer buffer2;
Writer<StringBuffer> writer2(buffer2);
GenericReader<ASCII<>, UTF8<> > reader2;
StringStream s2(buffer.GetString());
reader2.Parse(s2, 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] } ");
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]}", 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
}