Skip to content

Commit 9297822

Browse files
committed
Merge pull request #349 from cdunn2001/special-floats
Merge pull request #339 from Dani-Hub/master
2 parents a7b80fe + a4354d7 commit 9297822

File tree

5 files changed

+205
-44
lines changed

5 files changed

+205
-44
lines changed

Diff for: include/json/reader.h

+3
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ class JSON_API CharReaderBuilder : public CharReader::Factory {
277277
the JSON value in the input string.
278278
- `"rejectDupKeys": false or true`
279279
- If true, `parse()` returns false when a key is duplicated within an object.
280+
- `"allowSpecialFloats": false or true`
281+
- If true, special float values (NaNs and infinities) are allowed
282+
and their values are lossfree restorable.
280283
281284
You can examine 'settings_` yourself
282285
to see the defaults. You can also write and read them just like any

Diff for: include/json/writer.h

+4
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

Diff for: src/lib_json/json_reader.cpp

+71-21
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.
@@ -753,15 +764,7 @@ std::string Reader::getLocationLineAndColumn(Location location) const {
753764
int line, column;
754765
getLocationLineAndColumn(location, line, column);
755766
char buffer[18 + 16 + 16 + 1];
756-
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
757-
#if defined(WINCE)
758-
_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
759-
#else
760-
sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
761-
#endif
762-
#else
763767
snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
764-
#endif
765768
return buffer;
766769
}
767770

@@ -801,6 +804,7 @@ class OurFeatures {
801804
bool allowSingleQuotes_;
802805
bool failIfExtra_;
803806
bool rejectDupKeys_;
807+
bool allowSpecialFloats_;
804808
int stackLimit_;
805809
}; // OurFeatures
806810

@@ -812,6 +816,7 @@ OurFeatures::OurFeatures()
812816
, allowDroppedNullPlaceholders_(false), allowNumericKeys_(false)
813817
, allowSingleQuotes_(false)
814818
, failIfExtra_(false)
819+
, allowSpecialFloats_(false)
815820
{
816821
}
817822

@@ -853,6 +858,9 @@ class OurReader {
853858
tokenTrue,
854859
tokenFalse,
855860
tokenNull,
861+
tokenNaN,
862+
tokenPosInf,
863+
tokenNegInf,
856864
tokenArraySeparator,
857865
tokenMemberSeparator,
858866
tokenComment,
@@ -883,7 +891,7 @@ class OurReader {
883891
bool readCppStyleComment();
884892
bool readString();
885893
bool readStringSingleQuote();
886-
void readNumber();
894+
bool readNumber(bool checkInf);
887895
bool readValue();
888896
bool readObject(Token& token);
889897
bool readArray(Token& token);
@@ -1029,6 +1037,24 @@ bool OurReader::readValue() {
10291037
currentValue().swapPayload(v);
10301038
}
10311039
break;
1040+
case tokenNaN:
1041+
{
1042+
Value v(std::numeric_limits<double>::quiet_NaN());
1043+
currentValue().swapPayload(v);
1044+
}
1045+
break;
1046+
case tokenPosInf:
1047+
{
1048+
Value v(std::numeric_limits<double>::infinity());
1049+
currentValue().swapPayload(v);
1050+
}
1051+
break;
1052+
case tokenNegInf:
1053+
{
1054+
Value v(-std::numeric_limits<double>::infinity());
1055+
currentValue().swapPayload(v);
1056+
}
1057+
break;
10321058
case tokenArraySeparator:
10331059
case tokenObjectEnd:
10341060
case tokenArrayEnd:
@@ -1105,9 +1131,16 @@ bool OurReader::readToken(Token& token) {
11051131
case '7':
11061132
case '8':
11071133
case '9':
1108-
case '-':
11091134
token.type_ = tokenNumber;
1110-
readNumber();
1135+
readNumber(false);
1136+
break;
1137+
case '-':
1138+
if (readNumber(true)) {
1139+
token.type_ = tokenNumber;
1140+
} else {
1141+
token.type_ = tokenNegInf;
1142+
ok = features_.allowSpecialFloats_ && match("nfinity", 7);
1143+
}
11111144
break;
11121145
case 't':
11131146
token.type_ = tokenTrue;
@@ -1121,6 +1154,22 @@ bool OurReader::readToken(Token& token) {
11211154
token.type_ = tokenNull;
11221155
ok = match("ull", 3);
11231156
break;
1157+
case 'N':
1158+
if (features_.allowSpecialFloats_) {
1159+
token.type_ = tokenNaN;
1160+
ok = match("aN", 2);
1161+
} else {
1162+
ok = false;
1163+
}
1164+
break;
1165+
case 'I':
1166+
if (features_.allowSpecialFloats_) {
1167+
token.type_ = tokenPosInf;
1168+
ok = match("nfinity", 7);
1169+
} else {
1170+
ok = false;
1171+
}
1172+
break;
11241173
case ',':
11251174
token.type_ = tokenArraySeparator;
11261175
break;
@@ -1221,8 +1270,12 @@ bool OurReader::readCppStyleComment() {
12211270
return true;
12221271
}
12231272

1224-
void OurReader::readNumber() {
1273+
bool OurReader::readNumber(bool checkInf) {
12251274
const char *p = current_;
1275+
if (checkInf && p != end_ && *p == 'I') {
1276+
current_ = ++p;
1277+
return false;
1278+
}
12261279
char c = '0'; // stopgap for already consumed character
12271280
// integral part
12281281
while (c >= '0' && c <= '9')
@@ -1241,6 +1294,7 @@ void OurReader::readNumber() {
12411294
while (c >= '0' && c <= '9')
12421295
c = (current_ = p) < end_ ? *p++ : 0;
12431296
}
1297+
return true;
12441298
}
12451299
bool OurReader::readString() {
12461300
Char c = 0;
@@ -1641,15 +1695,7 @@ std::string OurReader::getLocationLineAndColumn(Location location) const {
16411695
int line, column;
16421696
getLocationLineAndColumn(location, line, column);
16431697
char buffer[18 + 16 + 16 + 1];
1644-
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
1645-
#if defined(WINCE)
1646-
_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1647-
#else
1648-
sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1649-
#endif
1650-
#else
16511698
snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1652-
#endif
16531699
return buffer;
16541700
}
16551701

@@ -1709,6 +1755,7 @@ CharReader* CharReaderBuilder::newCharReader() const
17091755
features.stackLimit_ = settings_["stackLimit"].asInt();
17101756
features.failIfExtra_ = settings_["failIfExtra"].asBool();
17111757
features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
1758+
features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
17121759
return new OurCharReader(collectComments, features);
17131760
}
17141761
static void getValidReaderKeys(std::set<std::string>* valid_keys)
@@ -1723,6 +1770,7 @@ static void getValidReaderKeys(std::set<std::string>* valid_keys)
17231770
valid_keys->insert("stackLimit");
17241771
valid_keys->insert("failIfExtra");
17251772
valid_keys->insert("rejectDupKeys");
1773+
valid_keys->insert("allowSpecialFloats");
17261774
}
17271775
bool CharReaderBuilder::validate(Json::Value* invalid) const
17281776
{
@@ -1756,6 +1804,7 @@ void CharReaderBuilder::strictMode(Json::Value* settings)
17561804
(*settings)["allowSingleQuotes"] = false;
17571805
(*settings)["failIfExtra"] = true;
17581806
(*settings)["rejectDupKeys"] = true;
1807+
(*settings)["allowSpecialFloats"] = false;
17591808
//! [CharReaderBuilderStrictMode]
17601809
}
17611810
// static
@@ -1771,6 +1820,7 @@ void CharReaderBuilder::setDefaults(Json::Value* settings)
17711820
(*settings)["stackLimit"] = 1000;
17721821
(*settings)["failIfExtra"] = false;
17731822
(*settings)["rejectDupKeys"] = false;
1823+
(*settings)["allowSpecialFloats"] = false;
17741824
//! [CharReaderBuilderDefaults]
17751825
}
17761826

Diff for: src/lib_json/json_writer.cpp

+27-22
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
@@ -104,43 +110,35 @@ std::string valueToString(UInt value) {
104110

105111
#endif // # if defined(JSON_HAS_INT64)
106112

107-
std::string valueToString(double value) {
113+
std::string valueToString(double value, bool useSpecialFloats) {
108114
// Allocate a buffer that is more than large enough to store the 16 digits of
109115
// precision requested below.
110116
char buffer[32];
111117
int len = -1;
112118

113-
// Print into the buffer. We need not request the alternative representation
114-
// that always has a decimal point because JSON doesn't distingish the
115-
// concepts of reals and integers.
116-
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
117-
// visual studio 2005 to
118-
// avoid warning.
119-
#if defined(WINCE)
120-
len = _snprintf(buffer, sizeof(buffer), "%.17g", value);
121-
#else
122-
len = sprintf_s(buffer, sizeof(buffer), "%.17g", value);
123-
#endif
124-
#else
119+
// Print into the buffer. We need not request the alternative representation
120+
// that always has a decimal point because JSON doesn't distingish the
121+
// concepts of reals and integers.
125122
if (isfinite(value)) {
126123
len = snprintf(buffer, sizeof(buffer), "%.17g", value);
127124
} else {
128125
// IEEE standard states that NaN values will not compare to themselves
129126
if (value != value) {
130-
len = snprintf(buffer, sizeof(buffer), "null");
127+
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
131128
} else if (value < 0) {
132-
len = snprintf(buffer, sizeof(buffer), "-1e+9999");
129+
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
133130
} else {
134-
len = snprintf(buffer, sizeof(buffer), "1e+9999");
131+
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999");
135132
}
136133
// For those, we do not need to call fixNumLoc, but it is fast.
137134
}
138-
#endif
139135
assert(len >= 0);
140136
fixNumericLocale(buffer, buffer + len);
141137
return buffer;
142138
}
143139

140+
std::string valueToString(double value) { return valueToString(value, false); }
141+
144142
std::string valueToString(bool value) { return value ? "true" : "false"; }
145143

146144
std::string valueToQuotedString(const char* value) {
@@ -805,7 +803,8 @@ struct BuiltStyledStreamWriter : public StreamWriter
805803
CommentStyle::Enum cs,
806804
std::string const& colonSymbol,
807805
std::string const& nullSymbol,
808-
std::string const& endingLineFeedSymbol);
806+
std::string const& endingLineFeedSymbol,
807+
bool useSpecialFloats);
809808
virtual int write(Value const& root, std::ostream* sout);
810809
private:
811810
void writeValue(Value const& value);
@@ -832,13 +831,15 @@ struct BuiltStyledStreamWriter : public StreamWriter
832831
std::string endingLineFeedSymbol_;
833832
bool addChildValues_ : 1;
834833
bool indented_ : 1;
834+
bool useSpecialFloats_ : 1;
835835
};
836836
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
837837
std::string const& indentation,
838838
CommentStyle::Enum cs,
839839
std::string const& colonSymbol,
840840
std::string const& nullSymbol,
841-
std::string const& endingLineFeedSymbol)
841+
std::string const& endingLineFeedSymbol,
842+
bool useSpecialFloats)
842843
: rightMargin_(74)
843844
, indentation_(indentation)
844845
, cs_(cs)
@@ -847,6 +848,7 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter(
847848
, endingLineFeedSymbol_(endingLineFeedSymbol)
848849
, addChildValues_(false)
849850
, indented_(false)
851+
, useSpecialFloats_(useSpecialFloats)
850852
{
851853
}
852854
int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)
@@ -876,7 +878,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
876878
pushValue(valueToString(value.asLargestUInt()));
877879
break;
878880
case realValue:
879-
pushValue(valueToString(value.asDouble()));
881+
pushValue(valueToString(value.asDouble(), useSpecialFloats_));
880882
break;
881883
case stringValue:
882884
{
@@ -1091,6 +1093,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
10911093
std::string cs_str = settings_["commentStyle"].asString();
10921094
bool eyc = settings_["enableYAMLCompatibility"].asBool();
10931095
bool dnp = settings_["dropNullPlaceholders"].asBool();
1096+
bool usf = settings_["useSpecialFloats"].asBool();
10941097
CommentStyle::Enum cs = CommentStyle::All;
10951098
if (cs_str == "All") {
10961099
cs = CommentStyle::All;
@@ -1112,7 +1115,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
11121115
std::string endingLineFeedSymbol = "";
11131116
return new BuiltStyledStreamWriter(
11141117
indentation, cs,
1115-
colonSymbol, nullSymbol, endingLineFeedSymbol);
1118+
colonSymbol, nullSymbol, endingLineFeedSymbol, usf);
11161119
}
11171120
static void getValidWriterKeys(std::set<std::string>* valid_keys)
11181121
{
@@ -1121,6 +1124,7 @@ static void getValidWriterKeys(std::set<std::string>* valid_keys)
11211124
valid_keys->insert("commentStyle");
11221125
valid_keys->insert("enableYAMLCompatibility");
11231126
valid_keys->insert("dropNullPlaceholders");
1127+
valid_keys->insert("useSpecialFloats");
11241128
}
11251129
bool StreamWriterBuilder::validate(Json::Value* invalid) const
11261130
{
@@ -1151,6 +1155,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings)
11511155
(*settings)["indentation"] = "\t";
11521156
(*settings)["enableYAMLCompatibility"] = false;
11531157
(*settings)["dropNullPlaceholders"] = false;
1158+
(*settings)["useSpecialFloats"] = false;
11541159
//! [StreamWriterBuilderDefaults]
11551160
}
11561161

0 commit comments

Comments
 (0)