Commit 6d0f0b2a authored by Milo Yip's avatar Milo Yip

Merge pull request #564 from corporateshark/stringnumbers

Implemented feature as in #560 (ints/doubles as strings)
parents 29c95808 b5966c32
...@@ -24,6 +24,7 @@ struct CapitalizeFilter { ...@@ -24,6 +24,7 @@ struct CapitalizeFilter {
bool Int64(int64_t i) { return out_.Int64(i); } bool Int64(int64_t i) { return out_.Int64(i); }
bool Uint64(uint64_t u) { return out_.Uint64(u); } bool Uint64(uint64_t u) { return out_.Uint64(u); }
bool Double(double d) { return out_.Double(d); } bool Double(double d) { return out_.Double(d); }
bool RawNumber(const char* str, SizeType length, bool copy) { return out_.RawNumber(str, length, copy); }
bool String(const char* str, SizeType length, bool) { bool String(const char* str, SizeType length, bool) {
buffer_.clear(); buffer_.clear();
for (SizeType i = 0; i < length; i++) for (SizeType i = 0; i < length; i++)
......
...@@ -57,6 +57,13 @@ public: ...@@ -57,6 +57,13 @@ public:
return WriteNumberElement(buffer, sprintf(buffer, "%.17g", d)); return WriteNumberElement(buffer, sprintf(buffer, "%.17g", d));
} }
bool RawNumber(const char* str, SizeType length, bool) {
return
WriteStartElement("number") &&
WriteEscapedText(str, length) &&
WriteEndElement("number");
}
bool String(const char* str, SizeType length, bool) { bool String(const char* str, SizeType length, bool) {
return return
WriteStartElement("string") && WriteStartElement("string") &&
......
...@@ -12,6 +12,10 @@ struct MyHandler { ...@@ -12,6 +12,10 @@ struct MyHandler {
bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; } bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; }
bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; } bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; }
bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; } bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; }
bool RawNumber(const char* str, SizeType length, bool copy) {
cout << "Number(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool String(const char* str, SizeType length, bool copy) { bool String(const char* str, SizeType length, bool copy) {
cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl; cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true; return true;
......
...@@ -2325,6 +2325,14 @@ public: ...@@ -2325,6 +2325,14 @@ public:
bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; } bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; } bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; }
bool RawNumber(const Ch* str, SizeType length, bool copy) {
if (copy)
new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
else
new (stack_.template Push<ValueType>()) ValueType(str, length);
return true;
}
bool String(const Ch* str, SizeType length, bool copy) { bool String(const Ch* str, SizeType length, bool copy) {
if (copy) if (copy)
new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator()); new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
......
...@@ -74,6 +74,12 @@ public: ...@@ -74,6 +74,12 @@ public:
bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
(void)copy;
PrettyPrefix(kNumberType);
return Base::WriteString(str, length);
}
bool String(const Ch* str, SizeType length, bool copy = false) { bool String(const Ch* str, SizeType length, bool copy = false) {
(void)copy; (void)copy;
PrettyPrefix(kStringType); PrettyPrefix(kStringType);
......
...@@ -148,6 +148,7 @@ enum ParseFlag { ...@@ -148,6 +148,7 @@ enum ParseFlag {
kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error.
kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower).
kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments.
kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings.
kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS
}; };
...@@ -169,6 +170,8 @@ concept Handler { ...@@ -169,6 +170,8 @@ concept Handler {
bool Int64(int64_t i); bool Int64(int64_t i);
bool Uint64(uint64_t i); bool Uint64(uint64_t i);
bool Double(double d); bool Double(double d);
/// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
bool RawNumber(const Ch* str, SizeType length, bool copy);
bool String(const Ch* str, SizeType length, bool copy); bool String(const Ch* str, SizeType length, bool copy);
bool StartObject(); bool StartObject();
bool Key(const Ch* str, SizeType length, bool copy); bool Key(const Ch* str, SizeType length, bool copy);
...@@ -199,6 +202,8 @@ struct BaseReaderHandler { ...@@ -199,6 +202,8 @@ struct BaseReaderHandler {
bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); } bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); }
bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); } bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); }
bool Double(double) { return static_cast<Override&>(*this).Default(); } bool Double(double) { return static_cast<Override&>(*this).Default(); }
/// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); } bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); }
bool StartObject() { return static_cast<Override&>(*this).Default(); } bool StartObject() { return static_cast<Override&>(*this).Default(); }
bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); } bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
...@@ -1053,6 +1058,8 @@ private: ...@@ -1053,6 +1058,8 @@ private:
RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); }
RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); }
RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); }
RAPIDJSON_FORCEINLINE void Push(char) {}
size_t Tell() { return is.Tell(); } size_t Tell() { return is.Tell(); }
size_t Length() { return 0; } size_t Length() { return 0; }
const char* Pop() { return 0; } const char* Pop() { return 0; }
...@@ -1075,6 +1082,10 @@ private: ...@@ -1075,6 +1082,10 @@ private:
return Base::is.Take(); return Base::is.Take();
} }
RAPIDJSON_FORCEINLINE void Push(char c) {
stackStream.Put(c);
}
size_t Length() { return stackStream.Length(); } size_t Length() { return stackStream.Length(); }
const char* Pop() { const char* Pop() {
...@@ -1089,7 +1100,11 @@ private: ...@@ -1089,7 +1100,11 @@ private:
template<unsigned parseFlags, typename InputStream, typename Handler> template<unsigned parseFlags, typename InputStream, typename Handler>
void ParseNumber(InputStream& is, Handler& handler) { void ParseNumber(InputStream& is, Handler& handler) {
internal::StreamLocalCopy<InputStream> copy(is); internal::StreamLocalCopy<InputStream> copy(is);
NumberStream<InputStream, (parseFlags & kParseFullPrecisionFlag) != 0> s(*this, copy.s); NumberStream<InputStream,
((parseFlags & kParseNumbersAsStringsFlag) != 0) ?
((parseFlags & kParseInsituFlag) == 0) :
((parseFlags & kParseFullPrecisionFlag) != 0)> s(*this, copy.s);
size_t startOffset = s.Tell(); size_t startOffset = s.Tell();
// Parse minus // Parse minus
...@@ -1176,6 +1191,9 @@ private: ...@@ -1176,6 +1191,9 @@ private:
int expFrac = 0; int expFrac = 0;
size_t decimalPosition; size_t decimalPosition;
if (Consume(s, '.')) { if (Consume(s, '.')) {
if (((parseFlags & kParseNumbersAsStringsFlag) != 0) && ((parseFlags & kParseInsituFlag) == 0)) {
s.Push('.');
}
decimalPosition = s.Length(); decimalPosition = s.Length();
if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9')))
...@@ -1223,6 +1241,10 @@ private: ...@@ -1223,6 +1241,10 @@ private:
// Parse exp = e [ minus / plus ] 1*DIGIT // Parse exp = e [ minus / plus ] 1*DIGIT
int exp = 0; int exp = 0;
if (Consume(s, 'e') || Consume(s, 'E')) { if (Consume(s, 'e') || Consume(s, 'E')) {
if ( ((parseFlags & kParseNumbersAsStringsFlag) != 0) && ((parseFlags & kParseInsituFlag) == 0) ) {
s.Push( 'e' );
}
if (!useDouble) { if (!useDouble) {
d = static_cast<double>(use64bit ? i64 : i); d = static_cast<double>(use64bit ? i64 : i);
useDouble = true; useDouble = true;
...@@ -1263,6 +1285,32 @@ private: ...@@ -1263,6 +1285,32 @@ private:
// Finish parsing, call event according to the type of number. // Finish parsing, call event according to the type of number.
bool cont = true; bool cont = true;
if (parseFlags & kParseNumbersAsStringsFlag) {
if (parseFlags & kParseInsituFlag) {
s.Pop(); // Pop stack no matter if it will be used or not.
typename InputStream::Ch* head = is.PutBegin();
const size_t length = s.Tell() - startOffset;
RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
// *(head + length) = '\0';
const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head);
cont = handler.RawNumber(str, SizeType(length), false);
}
else {
StackStream<typename TargetEncoding::Ch> stackStream(stack_);
SizeType numCharsToCopy = static_cast<SizeType>(s.Length());
while (numCharsToCopy--) {
Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, stackStream);
}
stackStream.Put('\0');
const typename TargetEncoding::Ch* str = stackStream.Pop();
const SizeType length = static_cast<SizeType>(stackStream.Length()) - 1;
cont = handler.RawNumber(str, SizeType(length), true);
}
}
else {
size_t length = s.Length(); size_t length = s.Length();
const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not.
...@@ -1289,6 +1337,7 @@ private: ...@@ -1289,6 +1337,7 @@ private:
cont = handler.Uint(i); cont = handler.Uint(i);
} }
} }
}
if (RAPIDJSON_UNLIKELY(!cont)) if (RAPIDJSON_UNLIKELY(!cont))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset);
} }
......
...@@ -183,6 +183,11 @@ public: ...@@ -183,6 +183,11 @@ public:
return WriteNumber(n); return WriteNumber(n);
} }
bool RawNumber(const Ch* str, SizeType len, bool) {
WriteBuffer(kNumberType, str, len * sizeof(Ch));
return true;
}
bool String(const Ch* str, SizeType len, bool) { bool String(const Ch* str, SizeType len, bool) {
WriteBuffer(kStringType, str, len * sizeof(Ch)); WriteBuffer(kStringType, str, len * sizeof(Ch));
return true; return true;
...@@ -1679,6 +1684,8 @@ RAPIDJSON_MULTILINEMACRO_END ...@@ -1679,6 +1684,8 @@ RAPIDJSON_MULTILINEMACRO_END
bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
bool RawNumber(const Ch* str, SizeType length, bool copy)
{ RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
bool String(const Ch* str, SizeType length, bool copy) bool String(const Ch* str, SizeType length, bool copy)
{ RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
......
...@@ -181,6 +181,12 @@ public: ...@@ -181,6 +181,12 @@ public:
*/ */
bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); }
bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
(void)copy;
Prefix(kNumberType);
return WriteString(str, length);
}
bool String(const Ch* str, SizeType length, bool copy = false) { bool String(const Ch* str, SizeType length, bool copy = false) {
(void)copy; (void)copy;
Prefix(kStringType); Prefix(kStringType);
......
...@@ -1170,6 +1170,8 @@ struct IterativeParsingReaderHandler { ...@@ -1170,6 +1170,8 @@ struct IterativeParsingReaderHandler {
bool Double(double) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_DOUBLE; return true; } bool Double(double) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_DOUBLE; return true; }
bool RawNumber(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STRING; return true; }
bool String(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STRING; return true; } bool String(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STRING; return true; }
bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTOBJECT; return true; } bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTOBJECT; return true; }
...@@ -1349,12 +1351,13 @@ struct TerminateHandler { ...@@ -1349,12 +1351,13 @@ struct TerminateHandler {
bool Int64(int64_t) { return e != 4; } bool Int64(int64_t) { return e != 4; }
bool Uint64(uint64_t) { return e != 5; } bool Uint64(uint64_t) { return e != 5; }
bool Double(double) { return e != 6; } bool Double(double) { return e != 6; }
bool String(const char*, SizeType, bool) { return e != 7; } bool RawNumber(const char*, SizeType, bool) { return e != 7; }
bool StartObject() { return e != 8; } bool String(const char*, SizeType, bool) { return e != 8; }
bool Key(const char*, SizeType, bool) { return e != 9; } bool StartObject() { return e != 9; }
bool EndObject(SizeType) { return e != 10; } bool Key(const char*, SizeType, bool) { return e != 10; }
bool StartArray() { return e != 11; } bool EndObject(SizeType) { return e != 11; }
bool EndArray(SizeType) { return e != 12; } bool StartArray() { return e != 12; }
bool EndArray(SizeType) { return e != 13; }
}; };
#define TEST_TERMINATION(e, json)\ #define TEST_TERMINATION(e, json)\
...@@ -1375,14 +1378,15 @@ TEST(Reader, ParseTerminationByHandler) { ...@@ -1375,14 +1378,15 @@ TEST(Reader, ParseTerminationByHandler) {
TEST_TERMINATION(4, "[-1234567890123456789"); TEST_TERMINATION(4, "[-1234567890123456789");
TEST_TERMINATION(5, "[1234567890123456789"); TEST_TERMINATION(5, "[1234567890123456789");
TEST_TERMINATION(6, "[0.5]"); TEST_TERMINATION(6, "[0.5]");
TEST_TERMINATION(7, "[\"a\""); // RawNumber() is never called
TEST_TERMINATION(8, "[{"); TEST_TERMINATION(8, "[\"a\"");
TEST_TERMINATION(9, "[{\"a\""); TEST_TERMINATION(9, "[{");
TEST_TERMINATION(10, "[{}"); TEST_TERMINATION(10, "[{\"a\"");
TEST_TERMINATION(10, "[{\"a\":1}"); // non-empty object TEST_TERMINATION(11, "[{}");
TEST_TERMINATION(11, "{\"a\":["); TEST_TERMINATION(11, "[{\"a\":1}"); // non-empty object
TEST_TERMINATION(12, "{\"a\":[]"); TEST_TERMINATION(12, "{\"a\":[");
TEST_TERMINATION(12, "{\"a\":[1]"); // non-empty array TEST_TERMINATION(13, "{\"a\":[]");
TEST_TERMINATION(13, "{\"a\":[1]"); // non-empty array
} }
TEST(Reader, ParseComments) { TEST(Reader, ParseComments) {
...@@ -1508,6 +1512,46 @@ TEST(Reader, UnrecognizedComment) { ...@@ -1508,6 +1512,46 @@ TEST(Reader, UnrecognizedComment) {
EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode()); EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode());
} }
struct NumbersAsStringsHandler {
bool Null() { return true; }
bool Bool(bool) { return true; }
bool Int(int) { return true; }
bool Uint(unsigned) { return true; }
bool Int64(int64_t) { return true; }
bool Uint64(uint64_t) { return true; }
bool Double(double) { return true; }
// 'str' is not null-terminated
bool RawNumber(const char* str, SizeType length, bool) {
EXPECT_TRUE(str != 0);
EXPECT_TRUE(strncmp(str, "3.1416", length) == 0);
return true;
}
bool String(const char*, SizeType, bool) { return true; }
bool StartObject() { return true; }
bool Key(const char*, SizeType, bool) { return true; }
bool EndObject(SizeType) { return true; }
bool StartArray() { return true; }
bool EndArray(SizeType) { return true; }
};
TEST(Reader, NumbersAsStrings) {
{
const char* json = "{ \"pi\": 3.1416 } ";
StringStream s(json);
NumbersAsStringsHandler h;
Reader reader;
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
}
{
char* json = StrDup("{ \"pi\": 3.1416 } ");
InsituStringStream s(json);
NumbersAsStringsHandler h;
Reader reader;
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, h));
free(json);
}
}
#ifdef __GNUC__ #ifdef __GNUC__
RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_POP
#endif #endif
......
...@@ -1644,12 +1644,13 @@ struct TerminateHandler { ...@@ -1644,12 +1644,13 @@ struct TerminateHandler {
bool Int64(int64_t) { return e != 4; } bool Int64(int64_t) { return e != 4; }
bool Uint64(uint64_t) { return e != 5; } bool Uint64(uint64_t) { return e != 5; }
bool Double(double) { return e != 6; } bool Double(double) { return e != 6; }
bool String(const char*, SizeType, bool) { return e != 7; } bool RawNumber(const char*, SizeType, bool) { return e != 7; }
bool StartObject() { return e != 8; } bool String(const char*, SizeType, bool) { return e != 8; }
bool Key(const char*, SizeType, bool) { return e != 9; } bool StartObject() { return e != 9; }
bool EndObject(SizeType) { return e != 10; } bool Key(const char*, SizeType, bool) { return e != 10; }
bool StartArray() { return e != 11; } bool EndObject(SizeType) { return e != 11; }
bool EndArray(SizeType) { return e != 12; } bool StartArray() { return e != 12; }
bool EndArray(SizeType) { return e != 13; }
}; };
#define TEST_TERMINATION(e, json)\ #define TEST_TERMINATION(e, json)\
...@@ -1670,12 +1671,13 @@ TEST(Value, AcceptTerminationByHandler) { ...@@ -1670,12 +1671,13 @@ TEST(Value, AcceptTerminationByHandler) {
TEST_TERMINATION(4, "[-1234567890123456789]"); TEST_TERMINATION(4, "[-1234567890123456789]");
TEST_TERMINATION(5, "[9223372036854775808]"); TEST_TERMINATION(5, "[9223372036854775808]");
TEST_TERMINATION(6, "[0.5]"); TEST_TERMINATION(6, "[0.5]");
TEST_TERMINATION(7, "[\"a\"]"); // RawNumber() is never called
TEST_TERMINATION(8, "[{}]"); TEST_TERMINATION(8, "[\"a\"]");
TEST_TERMINATION(9, "[{\"a\":1}]"); TEST_TERMINATION(9, "[{}]");
TEST_TERMINATION(10, "[{}]"); TEST_TERMINATION(10, "[{\"a\":1}]");
TEST_TERMINATION(11, "{\"a\":[]}"); TEST_TERMINATION(11, "[{}]");
TEST_TERMINATION(12, "{\"a\":[]}"); TEST_TERMINATION(12, "{\"a\":[]}");
TEST_TERMINATION(13, "{\"a\":[]}");
} }
struct ValueIntComparer { struct ValueIntComparer {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment