Merge pull request #641 from m7thon/allow_nan_and_inf
Allow options for writing and parsing NaN/Infinity
This commit is contained in:
commit
e6895ec115
@ -118,6 +118,7 @@ Parse flags | Meaning
|
|||||||
`kParseCommentsFlag` | Allow one-line `// ...` and multi-line `/* ... */` comments (relaxed JSON syntax).
|
`kParseCommentsFlag` | Allow one-line `// ...` and multi-line `/* ... */` comments (relaxed JSON syntax).
|
||||||
`kParseNumbersAsStringsFlag` | Parse numerical type values as strings.
|
`kParseNumbersAsStringsFlag` | Parse numerical type values as strings.
|
||||||
`kParseTrailingCommasFlag` | Allow trailing commas at the end of objects and arrays (relaxed JSON syntax).
|
`kParseTrailingCommasFlag` | Allow trailing commas at the end of objects and arrays (relaxed JSON syntax).
|
||||||
|
`kParseNanAndInfFlag` | Allow parsing `NaN`, `Inf`, `Infinity`, `-Inf` and `-Infinity` as `double` values (relaxed JSON syntax).
|
||||||
|
|
||||||
By using a non-type template parameter, instead of a function parameter, C++ compiler can generate code which is optimized for specified combinations, improving speed, and reducing code size (if only using a single specialization). The downside is the flags needed to be determined in compile-time.
|
By using a non-type template parameter, instead of a function parameter, C++ compiler can generate code which is optimized for specified combinations, improving speed, and reducing code size (if only using a single specialization). The downside is the flags needed to be determined in compile-time.
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "internal/meta.h"
|
#include "internal/meta.h"
|
||||||
#include "internal/stack.h"
|
#include "internal/stack.h"
|
||||||
#include "internal/strtod.h"
|
#include "internal/strtod.h"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
|
#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
@ -150,6 +151,7 @@ enum ParseFlag {
|
|||||||
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.
|
kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings.
|
||||||
kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
|
kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
|
||||||
|
kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles.
|
||||||
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
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1137,6 +1139,8 @@ private:
|
|||||||
(parseFlags & kParseInsituFlag) == 0> s(*this, copy.s);
|
(parseFlags & kParseInsituFlag) == 0> s(*this, copy.s);
|
||||||
|
|
||||||
size_t startOffset = s.Tell();
|
size_t startOffset = s.Tell();
|
||||||
|
double d = 0.0;
|
||||||
|
bool useNanOrInf = false;
|
||||||
|
|
||||||
// Parse minus
|
// Parse minus
|
||||||
bool minus = Consume(s, '-');
|
bool minus = Consume(s, '-');
|
||||||
@ -1178,12 +1182,26 @@ private:
|
|||||||
significandDigit++;
|
significandDigit++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Parse NaN or Infinity here
|
||||||
|
else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) {
|
||||||
|
useNanOrInf = true;
|
||||||
|
if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) {
|
||||||
|
d = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
}
|
||||||
|
else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) {
|
||||||
|
d = (minus ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity());
|
||||||
|
if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n')
|
||||||
|
&& Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y'))))
|
||||||
|
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
|
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
|
||||||
|
|
||||||
// Parse 64bit int
|
// Parse 64bit int
|
||||||
bool useDouble = false;
|
bool useDouble = false;
|
||||||
double d = 0.0;
|
|
||||||
if (use64bit) {
|
if (use64bit) {
|
||||||
if (minus)
|
if (minus)
|
||||||
while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
|
while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
|
||||||
@ -1346,6 +1364,9 @@ private:
|
|||||||
|
|
||||||
cont = handler.Double(minus ? -d : d);
|
cont = handler.Double(minus ? -d : d);
|
||||||
}
|
}
|
||||||
|
else if (useNanOrInf) {
|
||||||
|
cont = handler.Double(d);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if (use64bit) {
|
if (use64bit) {
|
||||||
if (minus)
|
if (minus)
|
||||||
|
@ -62,6 +62,7 @@ RAPIDJSON_NAMESPACE_BEGIN
|
|||||||
enum WriteFlag {
|
enum WriteFlag {
|
||||||
kWriteNoFlags = 0, //!< No flags are set.
|
kWriteNoFlags = 0, //!< No flags are set.
|
||||||
kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
|
kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
|
||||||
|
kWriteNanAndInfFlag = 2, //!< Allow writing of Inf, -Inf and NaN.
|
||||||
kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
|
kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -319,9 +320,25 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WriteDouble(double d) {
|
bool WriteDouble(double d) {
|
||||||
if (internal::Double(d).IsNanOrInf())
|
if (internal::Double(d).IsNanOrInf()) {
|
||||||
return false;
|
if (!(writeFlags & kWriteNanAndInfFlag))
|
||||||
|
return false;
|
||||||
|
if (internal::Double(d).IsNan()) {
|
||||||
|
PutReserve(*os_, 3);
|
||||||
|
PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (internal::Double(d).Sign()) {
|
||||||
|
PutReserve(*os_, 9);
|
||||||
|
PutUnsafe(*os_, '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
PutReserve(*os_, 8);
|
||||||
|
PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
|
||||||
|
PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
char buffer[25];
|
char buffer[25];
|
||||||
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
||||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||||
@ -489,8 +506,25 @@ inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool Writer<StringBuffer>::WriteDouble(double d) {
|
inline bool Writer<StringBuffer>::WriteDouble(double d) {
|
||||||
if (internal::Double(d).IsNanOrInf())
|
if (internal::Double(d).IsNanOrInf()) {
|
||||||
return false;
|
// Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag).
|
||||||
|
if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
|
||||||
|
return false;
|
||||||
|
if (internal::Double(d).IsNan()) {
|
||||||
|
PutReserve(*os_, 3);
|
||||||
|
PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (internal::Double(d).Sign()) {
|
||||||
|
PutReserve(*os_, 9);
|
||||||
|
PutUnsafe(*os_, '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
PutReserve(*os_, 8);
|
||||||
|
PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
|
||||||
|
PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
char *buffer = os_->Push(25);
|
char *buffer = os_->Push(25);
|
||||||
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include "rapidjson/internal/itoa.h"
|
#include "rapidjson/internal/itoa.h"
|
||||||
#include "rapidjson/memorystream.h"
|
#include "rapidjson/memorystream.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
@ -1774,6 +1776,69 @@ TEST(Reader, TrailingCommaHandlerTerminationIterative) {
|
|||||||
TestTrailingCommaHandlerTermination<kParseIterativeFlag>();
|
TestTrailingCommaHandlerTermination<kParseIterativeFlag>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Reader, ParseNanAndInfinity) {
|
||||||
|
#define TEST_NAN_INF(str, x) \
|
||||||
|
{ \
|
||||||
|
{ \
|
||||||
|
StringStream s(str); \
|
||||||
|
ParseDoubleHandler h; \
|
||||||
|
Reader reader; \
|
||||||
|
ASSERT_EQ(kParseErrorNone, reader.Parse<kParseNanAndInfFlag>(s, h).Code()); \
|
||||||
|
EXPECT_EQ(1u, h.step_); \
|
||||||
|
internal::Double e(x), a(h.actual_); \
|
||||||
|
EXPECT_EQ(e.IsNan(), a.IsNan()); \
|
||||||
|
EXPECT_EQ(e.IsInf(), a.IsInf()); \
|
||||||
|
if (!e.IsNan()) \
|
||||||
|
EXPECT_EQ(e.Sign(), a.Sign()); \
|
||||||
|
} \
|
||||||
|
{ \
|
||||||
|
const char* json = "{ \"naninfdouble\": " str " } "; \
|
||||||
|
StringStream s(json); \
|
||||||
|
NumbersAsStringsHandler h(str); \
|
||||||
|
Reader reader; \
|
||||||
|
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag|kParseNanAndInfFlag>(s, h)); \
|
||||||
|
} \
|
||||||
|
{ \
|
||||||
|
char* json = StrDup("{ \"naninfdouble\": " str " } "); \
|
||||||
|
InsituStringStream s(json); \
|
||||||
|
NumbersAsStringsHandler h(str); \
|
||||||
|
Reader reader; \
|
||||||
|
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag|kParseNanAndInfFlag>(s, h)); \
|
||||||
|
free(json); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#define TEST_NAN_INF_ERROR(errorCode, str, errorOffset) \
|
||||||
|
{ \
|
||||||
|
int streamPos = errorOffset; \
|
||||||
|
char buffer[1001]; \
|
||||||
|
strncpy(buffer, str, 1000); \
|
||||||
|
InsituStringStream s(buffer); \
|
||||||
|
BaseReaderHandler<> h; \
|
||||||
|
Reader reader; \
|
||||||
|
EXPECT_FALSE(reader.Parse<kParseNanAndInfFlag>(s, h)); \
|
||||||
|
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
|
||||||
|
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
|
||||||
|
EXPECT_EQ(streamPos, s.Tell());\
|
||||||
|
}
|
||||||
|
|
||||||
|
double nan = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
double inf = std::numeric_limits<double>::infinity();
|
||||||
|
|
||||||
|
TEST_NAN_INF("NaN", nan);
|
||||||
|
TEST_NAN_INF("-NaN", nan);
|
||||||
|
TEST_NAN_INF("Inf", inf);
|
||||||
|
TEST_NAN_INF("Infinity", inf);
|
||||||
|
TEST_NAN_INF("-Inf", -inf);
|
||||||
|
TEST_NAN_INF("-Infinity", -inf);
|
||||||
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "nan", 1);
|
||||||
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1);
|
||||||
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1);
|
||||||
|
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-Infinty", 6);
|
||||||
|
|
||||||
|
#undef TEST_NAN_INF_ERROR
|
||||||
|
#undef TEST_NAN_INF
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
RAPIDJSON_DIAG_POP
|
RAPIDJSON_DIAG_POP
|
||||||
#endif
|
#endif
|
||||||
|
@ -446,9 +446,15 @@ TEST(Writer, NaN) {
|
|||||||
double nan = zero / zero;
|
double nan = zero / zero;
|
||||||
EXPECT_TRUE(internal::Double(nan).IsNan());
|
EXPECT_TRUE(internal::Double(nan).IsNan());
|
||||||
StringBuffer buffer;
|
StringBuffer buffer;
|
||||||
Writer<StringBuffer> writer(buffer);
|
{
|
||||||
EXPECT_FALSE(writer.Double(nan));
|
Writer<StringBuffer> writer(buffer);
|
||||||
|
EXPECT_FALSE(writer.Double(nan));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
|
||||||
|
EXPECT_TRUE(writer.Double(nan));
|
||||||
|
EXPECT_STREQ("NaN", buffer.GetString());
|
||||||
|
}
|
||||||
GenericStringBuffer<UTF16<> > buffer2;
|
GenericStringBuffer<UTF16<> > buffer2;
|
||||||
Writer<GenericStringBuffer<UTF16<> > > writer2(buffer2);
|
Writer<GenericStringBuffer<UTF16<> > > writer2(buffer2);
|
||||||
EXPECT_FALSE(writer2.Double(nan));
|
EXPECT_FALSE(writer2.Double(nan));
|
||||||
@ -460,12 +466,21 @@ TEST(Writer, Inf) {
|
|||||||
StringBuffer buffer;
|
StringBuffer buffer;
|
||||||
{
|
{
|
||||||
Writer<StringBuffer> writer(buffer);
|
Writer<StringBuffer> writer(buffer);
|
||||||
EXPECT_FALSE(writer.Double(inf));
|
EXPECT_FALSE(writer.Double(inf));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Writer<StringBuffer> writer(buffer);
|
Writer<StringBuffer> writer(buffer);
|
||||||
EXPECT_FALSE(writer.Double(-inf));
|
EXPECT_FALSE(writer.Double(-inf));
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
|
||||||
|
EXPECT_TRUE(writer.Double(inf));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
|
||||||
|
EXPECT_TRUE(writer.Double(-inf));
|
||||||
|
}
|
||||||
|
EXPECT_STREQ("Infinity-Infinity", buffer.GetString());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Writer, RawValue) {
|
TEST(Writer, RawValue) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user