Skip to content

Commit 2084563

Browse files
committed
Floating-point NaN or Infinity values should be allowed as a feature open-source-parsers#209
Introduce 'allowSpecialFloats' for readers and 'useSpecialFloats' for writers, use consistent macro snprintf definition for writers and readers, provide new unit tests for open-source-parsers#209
1 parent 6329975 commit 2084563

File tree

5 files changed

+167
-44
lines changed

5 files changed

+167
-44
lines changed

include/json/reader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ class JSON_API CharReaderBuilder : public CharReader::Factory {
321321
the JSON value in the input string.
322322
- `"rejectDupKeys": false or true`
323323
- If true, `parse()` returns false when a key is duplicated within an object.
324+
- `"allowSpecialFloats": false or true`
325+
- If true, special float values (NaNs and infinities) are allowed
326+
and their values are lossfree restorable.
324327
325328
You can examine 'settings_` yourself
326329
to see the defaults. You can also write and read them just like any

include/json/writer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
9999
Strictly speaking, this is not valid JSON. But when the output is being
100100
fed to a browser's Javascript, it makes for smaller output and the
101101
browser can handle the output just fine.
102+
- "useSpecialFloats": false or true
103+
- If true, outputs non-finite floating point values in the following way:
104+
NaN values as "NaN", positive infinity as "Infinity", and negative infinity
105+
as "-Infinity".
102106
103107
You can examine 'settings_` yourself
104108
to see the defaults. You can also write and read them just like any

src/lib_json/json_reader.cpp

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,21 @@
1717
#include <sstream>
1818
#include <memory>
1919
#include <set>
20+
#include <limits>
2021

21-
#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
22+
#if defined(_MSC_VER)
23+
#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
24+
#define snprintf sprintf_s
25+
#elif _MSC_VER >= 1900 // VC++ 14.0 and above
26+
#define snprintf std::snprintf
27+
#else
2228
#define snprintf _snprintf
2329
#endif
30+
#elif defined(__ANDROID__)
31+
#define snprintf snprintf
32+
#elif __cplusplus >= 201103L
33+
#define snprintf std::snprintf
34+
#endif
2435

2536
#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
2637
// Disable warning about strdup being deprecated.
@@ -795,15 +806,7 @@ std::string Reader::getLocationLineAndColumn(Location location) const {
795806
int line, column;
796807
getLocationLineAndColumn(location, line, column);
797808
char buffer[18 + 16 + 16 + 1];
798-
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
799-
#if defined(WINCE)
800-
_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
801-
#else
802-
sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
803-
#endif
804-
#else
805809
snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
806-
#endif
807810
return buffer;
808811
}
809812

@@ -894,6 +897,7 @@ class OurFeatures {
894897
bool allowSingleQuotes_;
895898
bool failIfExtra_;
896899
bool rejectDupKeys_;
900+
bool allowSpecialFloats_;
897901
int stackLimit_;
898902
}; // OurFeatures
899903

@@ -905,6 +909,7 @@ OurFeatures::OurFeatures()
905909
, allowDroppedNullPlaceholders_(false), allowNumericKeys_(false)
906910
, allowSingleQuotes_(false)
907911
, failIfExtra_(false)
912+
, allowSpecialFloats_(false)
908913
{
909914
}
910915

@@ -950,6 +955,9 @@ class OurReader {
950955
tokenTrue,
951956
tokenFalse,
952957
tokenNull,
958+
tokenNaN,
959+
tokenPosInf,
960+
tokenNegInf,
953961
tokenArraySeparator,
954962
tokenMemberSeparator,
955963
tokenComment,
@@ -980,7 +988,7 @@ class OurReader {
980988
bool readCppStyleComment();
981989
bool readString();
982990
bool readStringSingleQuote();
983-
void readNumber();
991+
bool readNumber();
984992
bool readValue();
985993
bool readObject(Token& token);
986994
bool readArray(Token& token);
@@ -1134,6 +1142,30 @@ bool OurReader::readValue() {
11341142
currentValue().setOffsetLimit(token.end_ - begin_);
11351143
}
11361144
break;
1145+
case tokenNaN:
1146+
{
1147+
Value v(std::numeric_limits<double>::quiet_NaN());
1148+
currentValue().swapPayload(v);
1149+
currentValue().setOffsetStart(token.start_ - begin_);
1150+
currentValue().setOffsetLimit(token.end_ - begin_);
1151+
}
1152+
break;
1153+
case tokenPosInf:
1154+
{
1155+
Value v(std::numeric_limits<double>::infinity());
1156+
currentValue().swapPayload(v);
1157+
currentValue().setOffsetStart(token.start_ - begin_);
1158+
currentValue().setOffsetLimit(token.end_ - begin_);
1159+
}
1160+
break;
1161+
case tokenNegInf:
1162+
{
1163+
Value v(-std::numeric_limits<double>::infinity());
1164+
currentValue().swapPayload(v);
1165+
currentValue().setOffsetStart(token.start_ - begin_);
1166+
currentValue().setOffsetLimit(token.end_ - begin_);
1167+
}
1168+
break;
11371169
case tokenArraySeparator:
11381170
case tokenObjectEnd:
11391171
case tokenArrayEnd:
@@ -1215,8 +1247,12 @@ bool OurReader::readToken(Token& token) {
12151247
case '8':
12161248
case '9':
12171249
case '-':
1218-
token.type_ = tokenNumber;
1219-
readNumber();
1250+
if (readNumber()) {
1251+
token.type_ = tokenNumber;
1252+
} else {
1253+
token.type_ = tokenNegInf;
1254+
ok = features_.allowSpecialFloats_ && match("nfinity", 7);
1255+
}
12201256
break;
12211257
case 't':
12221258
token.type_ = tokenTrue;
@@ -1230,6 +1266,22 @@ bool OurReader::readToken(Token& token) {
12301266
token.type_ = tokenNull;
12311267
ok = match("ull", 3);
12321268
break;
1269+
case 'N':
1270+
if (features_.allowSpecialFloats_) {
1271+
token.type_ = tokenNaN;
1272+
ok = match("aN", 2);
1273+
} else {
1274+
ok = false;
1275+
}
1276+
break;
1277+
case 'I':
1278+
if (features_.allowSpecialFloats_) {
1279+
token.type_ = tokenPosInf;
1280+
ok = match("nfinity", 7);
1281+
} else {
1282+
ok = false;
1283+
}
1284+
break;
12331285
case ',':
12341286
token.type_ = tokenArraySeparator;
12351287
break;
@@ -1330,8 +1382,12 @@ bool OurReader::readCppStyleComment() {
13301382
return true;
13311383
}
13321384

1333-
void OurReader::readNumber() {
1385+
bool OurReader::readNumber() {
13341386
const char *p = current_;
1387+
if (p != end_ && *p == 'I') {
1388+
current_ = ++p;
1389+
return false;
1390+
}
13351391
char c = '0'; // stopgap for already consumed character
13361392
// integral part
13371393
while (c >= '0' && c <= '9')
@@ -1350,6 +1406,7 @@ void OurReader::readNumber() {
13501406
while (c >= '0' && c <= '9')
13511407
c = (current_ = p) < end_ ? *p++ : 0;
13521408
}
1409+
return true;
13531410
}
13541411
bool OurReader::readString() {
13551412
Char c = 0;
@@ -1758,15 +1815,7 @@ std::string OurReader::getLocationLineAndColumn(Location location) const {
17581815
int line, column;
17591816
getLocationLineAndColumn(location, line, column);
17601817
char buffer[18 + 16 + 16 + 1];
1761-
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
1762-
#if defined(WINCE)
1763-
_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1764-
#else
1765-
sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1766-
#endif
1767-
#else
17681818
snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1769-
#endif
17701819
return buffer;
17711820
}
17721821

@@ -1880,6 +1929,7 @@ CharReader* CharReaderBuilder::newCharReader() const
18801929
features.stackLimit_ = settings_["stackLimit"].asInt();
18811930
features.failIfExtra_ = settings_["failIfExtra"].asBool();
18821931
features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
1932+
features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
18831933
return new OurCharReader(collectComments, features);
18841934
}
18851935
static void getValidReaderKeys(std::set<std::string>* valid_keys)
@@ -1894,6 +1944,7 @@ static void getValidReaderKeys(std::set<std::string>* valid_keys)
18941944
valid_keys->insert("stackLimit");
18951945
valid_keys->insert("failIfExtra");
18961946
valid_keys->insert("rejectDupKeys");
1947+
valid_keys->insert("allowSpecialFloats");
18971948
}
18981949
bool CharReaderBuilder::validate(Json::Value* invalid) const
18991950
{
@@ -1927,6 +1978,7 @@ void CharReaderBuilder::strictMode(Json::Value* settings)
19271978
(*settings)["allowSingleQuotes"] = false;
19281979
(*settings)["failIfExtra"] = true;
19291980
(*settings)["rejectDupKeys"] = true;
1981+
(*settings)["allowSpecialFloats"] = false;
19301982
//! [CharReaderBuilderStrictMode]
19311983
}
19321984
// static
@@ -1942,6 +1994,7 @@ void CharReaderBuilder::setDefaults(Json::Value* settings)
19421994
(*settings)["stackLimit"] = 1000;
19431995
(*settings)["failIfExtra"] = false;
19441996
(*settings)["rejectDupKeys"] = false;
1997+
(*settings)["allowSpecialFloats"] = false;
19451998
//! [CharReaderBuilderDefaults]
19461999
}
19472000

src/lib_json/json_writer.cpp

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,14 @@
2727
#define isfinite std::isfinite
2828
#endif
2929

30-
#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
30+
#if defined(_MSC_VER)
31+
#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
32+
#define snprintf sprintf_s
33+
#elif _MSC_VER >= 1900 // VC++ 14.0 and above
34+
#define snprintf std::snprintf
35+
#else
3136
#define snprintf _snprintf
37+
#endif
3238
#elif defined(__ANDROID__)
3339
#define snprintf snprintf
3440
#elif __cplusplus >= 201103L
@@ -108,43 +114,35 @@ std::string valueToString(UInt value) {
108114

109115
#endif // # if defined(JSON_HAS_INT64)
110116

111-
std::string valueToString(double value) {
117+
std::string valueToString(double value, bool useSpecialFloats) {
112118
// Allocate a buffer that is more than large enough to store the 16 digits of
113119
// precision requested below.
114120
char buffer[32];
115121
int len = -1;
116122

117-
// Print into the buffer. We need not request the alternative representation
118-
// that always has a decimal point because JSON doesn't distingish the
119-
// concepts of reals and integers.
120-
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
121-
// visual studio 2005 to
122-
// avoid warning.
123-
#if defined(WINCE)
124-
len = _snprintf(buffer, sizeof(buffer), "%.17g", value);
125-
#else
126-
len = sprintf_s(buffer, sizeof(buffer), "%.17g", value);
127-
#endif
128-
#else
123+
// Print into the buffer. We need not request the alternative representation
124+
// that always has a decimal point because JSON doesn't distingish the
125+
// concepts of reals and integers.
129126
if (isfinite(value)) {
130127
len = snprintf(buffer, sizeof(buffer), "%.17g", value);
131128
} else {
132129
// IEEE standard states that NaN values will not compare to themselves
133130
if (value != value) {
134-
len = snprintf(buffer, sizeof(buffer), "null");
131+
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
135132
} else if (value < 0) {
136-
len = snprintf(buffer, sizeof(buffer), "-1e+9999");
133+
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
137134
} else {
138-
len = snprintf(buffer, sizeof(buffer), "1e+9999");
135+
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999");
139136
}
140137
// For those, we do not need to call fixNumLoc, but it is fast.
141138
}
142-
#endif
143139
assert(len >= 0);
144140
fixNumericLocale(buffer, buffer + len);
145141
return buffer;
146142
}
147143

144+
std::string valueToString(double value) { return valueToString(value, false); }
145+
148146
std::string valueToString(bool value) { return value ? "true" : "false"; }
149147

150148
std::string valueToQuotedString(const char* value) {
@@ -816,7 +814,8 @@ struct BuiltStyledStreamWriter : public StreamWriter
816814
CommentStyle::Enum cs,
817815
std::string const& colonSymbol,
818816
std::string const& nullSymbol,
819-
std::string const& endingLineFeedSymbol);
817+
std::string const& endingLineFeedSymbol,
818+
bool useSpecialFloats);
820819
virtual int write(Value const& root, std::ostream* sout);
821820
private:
822821
void writeValue(Value const& value);
@@ -843,13 +842,15 @@ struct BuiltStyledStreamWriter : public StreamWriter
843842
std::string endingLineFeedSymbol_;
844843
bool addChildValues_ : 1;
845844
bool indented_ : 1;
845+
bool useSpecialFloats_ : 1;
846846
};
847847
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
848848
std::string const& indentation,
849849
CommentStyle::Enum cs,
850850
std::string const& colonSymbol,
851851
std::string const& nullSymbol,
852-
std::string const& endingLineFeedSymbol)
852+
std::string const& endingLineFeedSymbol,
853+
bool useSpecialFloats)
853854
: rightMargin_(74)
854855
, indentation_(indentation)
855856
, cs_(cs)
@@ -858,6 +859,7 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter(
858859
, endingLineFeedSymbol_(endingLineFeedSymbol)
859860
, addChildValues_(false)
860861
, indented_(false)
862+
, useSpecialFloats_(useSpecialFloats)
861863
{
862864
}
863865
int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)
@@ -887,7 +889,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
887889
pushValue(valueToString(value.asLargestUInt()));
888890
break;
889891
case realValue:
890-
pushValue(valueToString(value.asDouble()));
892+
pushValue(valueToString(value.asDouble(), useSpecialFloats_));
891893
break;
892894
case stringValue:
893895
{
@@ -1102,6 +1104,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
11021104
std::string cs_str = settings_["commentStyle"].asString();
11031105
bool eyc = settings_["enableYAMLCompatibility"].asBool();
11041106
bool dnp = settings_["dropNullPlaceholders"].asBool();
1107+
bool usf = settings_["useSpecialFloats"].asBool();
11051108
CommentStyle::Enum cs = CommentStyle::All;
11061109
if (cs_str == "All") {
11071110
cs = CommentStyle::All;
@@ -1123,7 +1126,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
11231126
std::string endingLineFeedSymbol = "";
11241127
return new BuiltStyledStreamWriter(
11251128
indentation, cs,
1126-
colonSymbol, nullSymbol, endingLineFeedSymbol);
1129+
colonSymbol, nullSymbol, endingLineFeedSymbol, usf);
11271130
}
11281131
static void getValidWriterKeys(std::set<std::string>* valid_keys)
11291132
{
@@ -1132,6 +1135,7 @@ static void getValidWriterKeys(std::set<std::string>* valid_keys)
11321135
valid_keys->insert("commentStyle");
11331136
valid_keys->insert("enableYAMLCompatibility");
11341137
valid_keys->insert("dropNullPlaceholders");
1138+
valid_keys->insert("useSpecialFloats");
11351139
}
11361140
bool StreamWriterBuilder::validate(Json::Value* invalid) const
11371141
{
@@ -1162,6 +1166,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings)
11621166
(*settings)["indentation"] = "\t";
11631167
(*settings)["enableYAMLCompatibility"] = false;
11641168
(*settings)["dropNullPlaceholders"] = false;
1169+
(*settings)["useSpecialFloats"] = false;
11651170
//! [StreamWriterBuilderDefaults]
11661171
}
11671172

0 commit comments

Comments
 (0)