Skip to content

Commit e6895ec

Browse files
committed
Merge pull request #641 from m7thon/allow_nan_and_inf
Allow options for writing and parsing NaN/Infinity
2 parents 2a3fbda + 135da7a commit e6895ec

File tree

5 files changed

+146
-10
lines changed

5 files changed

+146
-10
lines changed

doc/dom.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ Parse flags | Meaning
118118
`kParseCommentsFlag` | Allow one-line `// ...` and multi-line `/* ... */` comments (relaxed JSON syntax).
119119
`kParseNumbersAsStringsFlag` | Parse numerical type values as strings.
120120
`kParseTrailingCommasFlag` | Allow trailing commas at the end of objects and arrays (relaxed JSON syntax).
121+
`kParseNanAndInfFlag` | Allow parsing `NaN`, `Inf`, `Infinity`, `-Inf` and `-Infinity` as `double` values (relaxed JSON syntax).
121122
122123
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.
123124

include/rapidjson/reader.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "internal/meta.h"
2424
#include "internal/stack.h"
2525
#include "internal/strtod.h"
26+
#include <limits>
2627

2728
#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
2829
#include <intrin.h>
@@ -150,6 +151,7 @@ enum ParseFlag {
150151
kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments.
151152
kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings.
152153
kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
154+
kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles.
153155
kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS
154156
};
155157

@@ -1137,6 +1139,8 @@ class GenericReader {
11371139
(parseFlags & kParseInsituFlag) == 0> s(*this, copy.s);
11381140

11391141
size_t startOffset = s.Tell();
1142+
double d = 0.0;
1143+
bool useNanOrInf = false;
11401144

11411145
// Parse minus
11421146
bool minus = Consume(s, '-');
@@ -1178,12 +1182,26 @@ class GenericReader {
11781182
significandDigit++;
11791183
}
11801184
}
1185+
// Parse NaN or Infinity here
1186+
else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) {
1187+
useNanOrInf = true;
1188+
if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) {
1189+
d = std::numeric_limits<double>::quiet_NaN();
1190+
}
1191+
else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) {
1192+
d = (minus ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity());
1193+
if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n')
1194+
&& Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y'))))
1195+
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
1196+
}
1197+
else
1198+
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
1199+
}
11811200
else
11821201
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
11831202

11841203
// Parse 64bit int
11851204
bool useDouble = false;
1186-
double d = 0.0;
11871205
if (use64bit) {
11881206
if (minus)
11891207
while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
@@ -1346,6 +1364,9 @@ class GenericReader {
13461364

13471365
cont = handler.Double(minus ? -d : d);
13481366
}
1367+
else if (useNanOrInf) {
1368+
cont = handler.Double(d);
1369+
}
13491370
else {
13501371
if (use64bit) {
13511372
if (minus)

include/rapidjson/writer.h

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ RAPIDJSON_NAMESPACE_BEGIN
6262
enum WriteFlag {
6363
kWriteNoFlags = 0, //!< No flags are set.
6464
kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
65+
kWriteNanAndInfFlag = 2, //!< Allow writing of Inf, -Inf and NaN.
6566
kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
6667
};
6768

@@ -319,9 +320,25 @@ class Writer {
319320
}
320321

321322
bool WriteDouble(double d) {
322-
if (internal::Double(d).IsNanOrInf())
323-
return false;
324-
323+
if (internal::Double(d).IsNanOrInf()) {
324+
if (!(writeFlags & kWriteNanAndInfFlag))
325+
return false;
326+
if (internal::Double(d).IsNan()) {
327+
PutReserve(*os_, 3);
328+
PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
329+
return true;
330+
}
331+
if (internal::Double(d).Sign()) {
332+
PutReserve(*os_, 9);
333+
PutUnsafe(*os_, '-');
334+
}
335+
else
336+
PutReserve(*os_, 8);
337+
PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
338+
PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
339+
return true;
340+
}
341+
325342
char buffer[25];
326343
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
327344
PutReserve(*os_, static_cast<size_t>(end - buffer));
@@ -489,8 +506,25 @@ inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
489506

490507
template<>
491508
inline bool Writer<StringBuffer>::WriteDouble(double d) {
492-
if (internal::Double(d).IsNanOrInf())
493-
return false;
509+
if (internal::Double(d).IsNanOrInf()) {
510+
// Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag).
511+
if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
512+
return false;
513+
if (internal::Double(d).IsNan()) {
514+
PutReserve(*os_, 3);
515+
PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
516+
return true;
517+
}
518+
if (internal::Double(d).Sign()) {
519+
PutReserve(*os_, 9);
520+
PutUnsafe(*os_, '-');
521+
}
522+
else
523+
PutReserve(*os_, 8);
524+
PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
525+
PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
526+
return true;
527+
}
494528

495529
char *buffer = os_->Push(25);
496530
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);

test/unittest/readertest.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "rapidjson/internal/itoa.h"
2020
#include "rapidjson/memorystream.h"
2121

22+
#include <limits>
23+
2224
using namespace rapidjson;
2325

2426
#ifdef __GNUC__
@@ -1774,6 +1776,69 @@ TEST(Reader, TrailingCommaHandlerTerminationIterative) {
17741776
TestTrailingCommaHandlerTermination<kParseIterativeFlag>();
17751777
}
17761778

1779+
TEST(Reader, ParseNanAndInfinity) {
1780+
#define TEST_NAN_INF(str, x) \
1781+
{ \
1782+
{ \
1783+
StringStream s(str); \
1784+
ParseDoubleHandler h; \
1785+
Reader reader; \
1786+
ASSERT_EQ(kParseErrorNone, reader.Parse<kParseNanAndInfFlag>(s, h).Code()); \
1787+
EXPECT_EQ(1u, h.step_); \
1788+
internal::Double e(x), a(h.actual_); \
1789+
EXPECT_EQ(e.IsNan(), a.IsNan()); \
1790+
EXPECT_EQ(e.IsInf(), a.IsInf()); \
1791+
if (!e.IsNan()) \
1792+
EXPECT_EQ(e.Sign(), a.Sign()); \
1793+
} \
1794+
{ \
1795+
const char* json = "{ \"naninfdouble\": " str " } "; \
1796+
StringStream s(json); \
1797+
NumbersAsStringsHandler h(str); \
1798+
Reader reader; \
1799+
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag|kParseNanAndInfFlag>(s, h)); \
1800+
} \
1801+
{ \
1802+
char* json = StrDup("{ \"naninfdouble\": " str " } "); \
1803+
InsituStringStream s(json); \
1804+
NumbersAsStringsHandler h(str); \
1805+
Reader reader; \
1806+
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag|kParseNanAndInfFlag>(s, h)); \
1807+
free(json); \
1808+
} \
1809+
}
1810+
#define TEST_NAN_INF_ERROR(errorCode, str, errorOffset) \
1811+
{ \
1812+
int streamPos = errorOffset; \
1813+
char buffer[1001]; \
1814+
strncpy(buffer, str, 1000); \
1815+
InsituStringStream s(buffer); \
1816+
BaseReaderHandler<> h; \
1817+
Reader reader; \
1818+
EXPECT_FALSE(reader.Parse<kParseNanAndInfFlag>(s, h)); \
1819+
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
1820+
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
1821+
EXPECT_EQ(streamPos, s.Tell());\
1822+
}
1823+
1824+
double nan = std::numeric_limits<double>::quiet_NaN();
1825+
double inf = std::numeric_limits<double>::infinity();
1826+
1827+
TEST_NAN_INF("NaN", nan);
1828+
TEST_NAN_INF("-NaN", nan);
1829+
TEST_NAN_INF("Inf", inf);
1830+
TEST_NAN_INF("Infinity", inf);
1831+
TEST_NAN_INF("-Inf", -inf);
1832+
TEST_NAN_INF("-Infinity", -inf);
1833+
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "nan", 1);
1834+
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1);
1835+
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1);
1836+
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-Infinty", 6);
1837+
1838+
#undef TEST_NAN_INF_ERROR
1839+
#undef TEST_NAN_INF
1840+
}
1841+
17771842
#ifdef __GNUC__
17781843
RAPIDJSON_DIAG_POP
17791844
#endif

test/unittest/writertest.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,15 @@ TEST(Writer, NaN) {
446446
double nan = zero / zero;
447447
EXPECT_TRUE(internal::Double(nan).IsNan());
448448
StringBuffer buffer;
449-
Writer<StringBuffer> writer(buffer);
450-
EXPECT_FALSE(writer.Double(nan));
451-
449+
{
450+
Writer<StringBuffer> writer(buffer);
451+
EXPECT_FALSE(writer.Double(nan));
452+
}
453+
{
454+
Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
455+
EXPECT_TRUE(writer.Double(nan));
456+
EXPECT_STREQ("NaN", buffer.GetString());
457+
}
452458
GenericStringBuffer<UTF16<> > buffer2;
453459
Writer<GenericStringBuffer<UTF16<> > > writer2(buffer2);
454460
EXPECT_FALSE(writer2.Double(nan));
@@ -460,12 +466,21 @@ TEST(Writer, Inf) {
460466
StringBuffer buffer;
461467
{
462468
Writer<StringBuffer> writer(buffer);
463-
EXPECT_FALSE(writer.Double(inf));
469+
EXPECT_FALSE(writer.Double(inf));
464470
}
465471
{
466472
Writer<StringBuffer> writer(buffer);
467473
EXPECT_FALSE(writer.Double(-inf));
468474
}
475+
{
476+
Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
477+
EXPECT_TRUE(writer.Double(inf));
478+
}
479+
{
480+
Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
481+
EXPECT_TRUE(writer.Double(-inf));
482+
}
483+
EXPECT_STREQ("Infinity-Infinity", buffer.GetString());
469484
}
470485

471486
TEST(Writer, RawValue) {

0 commit comments

Comments
 (0)