Skip to content

Commit 32ffb93

Browse files
committed
Replaced the complex implementation of valueToString(double).
The previous one was confusing and prone to buffer overflows, and didn't work correctly with 16-decimal-digit numbers. The new one simply uses snprintf with a standard format string. The major change is that we don't always print a decimal point now. Fortunately, JSON doesn't distinguish between integers and reals.
1 parent bb53cd0 commit 32ffb93

2 files changed

Lines changed: 23 additions & 44 deletions

File tree

src/lib_json/json_writer.cpp

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -73,40 +73,19 @@ std::string valueToString( UInt value )
7373

7474
std::string valueToString( double value )
7575
{
76+
// Allocate a buffer that is more than large enough to store the 16 digits of
77+
// precision requested below.
7678
char buffer[32];
79+
80+
// Print into the buffer. We need not request the alternative representation
81+
// that always has a decimal point because JSON doesn't distingish the
82+
// concepts of reals and integers.
7783
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
78-
sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
79-
#else
80-
snprintf(buffer, sizeof(buffer), "%#.16g", value);
84+
sprintf_s(buffer, sizeof(buffer), "%.16g", value);
85+
#else
86+
snprintf(buffer, sizeof(buffer), "%.16g", value);
8187
#endif
82-
char* ch = buffer + strlen(buffer) - 1;
83-
if (*ch != '0') return buffer; // nothing to truncate, so save time
84-
while(ch > buffer && *ch == '0'){
85-
--ch;
86-
}
87-
char* last_nonzero = ch;
88-
while(ch >= buffer){
89-
switch(*ch){
90-
case '0':
91-
case '1':
92-
case '2':
93-
case '3':
94-
case '4':
95-
case '5':
96-
case '6':
97-
case '7':
98-
case '8':
99-
case '9':
100-
--ch;
101-
continue;
102-
case '.':
103-
// Truncate zeroes to save bytes in output, but keep one.
104-
*(last_nonzero+2) = '\0';
105-
return buffer;
106-
default:
107-
return buffer;
108-
}
109-
}
88+
11089
return buffer;
11190
}
11291

src/test_lib_json/main.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
462462
JSONTEST_ASSERT_EQUAL(0.0, val.asDouble());
463463
JSONTEST_ASSERT_EQUAL(0.0, val.asFloat());
464464
JSONTEST_ASSERT_EQUAL(false, val.asBool());
465-
JSONTEST_ASSERT_STRING_EQUAL("0.0", val.asString());
465+
JSONTEST_ASSERT_STRING_EQUAL("0", val.asString());
466466

467467
// Zero (signed constructor arg)
468468
val = Json::Value(0);
@@ -546,7 +546,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
546546
JSONTEST_ASSERT_EQUAL(0.0, val.asDouble());
547547
JSONTEST_ASSERT_EQUAL(0.0, val.asFloat());
548548
JSONTEST_ASSERT_EQUAL(false, val.asBool());
549-
JSONTEST_ASSERT_STRING_EQUAL("0.0", val.asString());
549+
JSONTEST_ASSERT_STRING_EQUAL("0", val.asString());
550550

551551
// 2^20 (signed constructor arg)
552552
val = Json::Value(1 << 20);
@@ -629,7 +629,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
629629
JSONTEST_ASSERT_EQUAL((1 << 20), val.asDouble());
630630
JSONTEST_ASSERT_EQUAL((1 << 20), val.asFloat());
631631
JSONTEST_ASSERT_EQUAL(true, val.asBool());
632-
JSONTEST_ASSERT_STRING_EQUAL("1048576.0", normalizeFloatingPointStr(val.asString()));
632+
JSONTEST_ASSERT_STRING_EQUAL("1048576", normalizeFloatingPointStr(val.asString()));
633633

634634
// -2^20
635635
val = Json::Value(-(1 << 20));
@@ -869,7 +869,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
869869
JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 40), val.asDouble());
870870
JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 40), val.asFloat());
871871
JSONTEST_ASSERT_EQUAL(true, val.asBool());
872-
JSONTEST_ASSERT_STRING_EQUAL("1099511627776.0", normalizeFloatingPointStr(val.asString()));
872+
JSONTEST_ASSERT_STRING_EQUAL("1099511627776", normalizeFloatingPointStr(val.asString()));
873873

874874
// -2^40
875875
val = Json::Value(-(Json::Int64(1) << 40));
@@ -1035,7 +1035,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
10351035
JSONTEST_ASSERT_EQUAL(1e19, val.asDouble());
10361036
JSONTEST_ASSERT_EQUAL(1e19, val.asFloat());
10371037
JSONTEST_ASSERT_EQUAL(true, val.asBool());
1038-
JSONTEST_ASSERT_STRING_EQUAL("1.000000000000000e+19", normalizeFloatingPointStr(val.asString()));
1038+
JSONTEST_ASSERT_STRING_EQUAL("1e+19", normalizeFloatingPointStr(val.asString()));
10391039

10401040
// uint64 max
10411041
val = Json::Value(Json::UInt64(kuint64max));
@@ -1114,7 +1114,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
11141114
JSONTEST_ASSERT_EQUAL(1, val.asUInt());
11151115
JSONTEST_ASSERT_EQUAL(1, val.asLargestUInt());
11161116
JSONTEST_ASSERT_EQUAL(true, val.asBool());
1117-
JSONTEST_ASSERT_EQUAL("1.50", val.asString());
1117+
JSONTEST_ASSERT_EQUAL("1.5", val.asString());
11181118

11191119
// Small negative number
11201120
val = Json::Value(-1.5);
@@ -1140,7 +1140,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
11401140
JSONTEST_ASSERT_EQUAL(-1, val.asInt());
11411141
JSONTEST_ASSERT_EQUAL(-1, val.asLargestInt());
11421142
JSONTEST_ASSERT_EQUAL(true, val.asBool());
1143-
JSONTEST_ASSERT_EQUAL("-1.50", val.asString());
1143+
JSONTEST_ASSERT_EQUAL("-1.5", val.asString());
11441144

11451145
// A bit over int32 max
11461146
val = Json::Value(kint32max + 0.5);
@@ -1169,7 +1169,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
11691169
JSONTEST_ASSERT_EQUAL(2147483647U, val.asLargestUInt());
11701170
#endif
11711171
JSONTEST_ASSERT_EQUAL(true, val.asBool());
1172-
JSONTEST_ASSERT_EQUAL("2147483647.50", normalizeFloatingPointStr(val.asString()));
1172+
JSONTEST_ASSERT_EQUAL("2147483647.5", normalizeFloatingPointStr(val.asString()));
11731173

11741174
// A bit under int32 min
11751175
val = Json::Value(kint32min - 0.5);
@@ -1196,7 +1196,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
11961196
JSONTEST_ASSERT_EQUAL(-Json::Int64(1)<< 31, val.asLargestInt());
11971197
#endif
11981198
JSONTEST_ASSERT_EQUAL(true, val.asBool());
1199-
JSONTEST_ASSERT_EQUAL("-2147483648.50", normalizeFloatingPointStr(val.asString()));
1199+
JSONTEST_ASSERT_EQUAL("-2147483648.5", normalizeFloatingPointStr(val.asString()));
12001200

12011201
// A bit over uint32 max
12021202
val = Json::Value(kuint32max + 0.5);
@@ -1224,15 +1224,15 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
12241224
JSONTEST_ASSERT_EQUAL((Json::UInt64(1) << 32)-Json::UInt64(1), val.asLargestUInt());
12251225
#endif
12261226
JSONTEST_ASSERT_EQUAL(true, val.asBool());
1227-
JSONTEST_ASSERT_EQUAL("4294967295.50", normalizeFloatingPointStr(val.asString()));
1227+
JSONTEST_ASSERT_EQUAL("4294967295.5", normalizeFloatingPointStr(val.asString()));
12281228

12291229
val = Json::Value(1.2345678901234);
1230-
JSONTEST_ASSERT_STRING_EQUAL( "1.23456789012340", normalizeFloatingPointStr(val.asString()));
1230+
JSONTEST_ASSERT_STRING_EQUAL( "1.2345678901234", normalizeFloatingPointStr(val.asString()));
12311231

12321232
// A 16-digit floating point number.
12331233
val = Json::Value(2199023255552000.0f);
12341234
JSONTEST_ASSERT_EQUAL(float(2199023255552000), val.asFloat());
1235-
JSONTEST_ASSERT_STRING_EQUAL("2199023255552000.", normalizeFloatingPointStr(val.asString()));
1235+
JSONTEST_ASSERT_STRING_EQUAL("2199023255552000", normalizeFloatingPointStr(val.asString()));
12361236

12371237
// A very large floating point number.
12381238
val = Json::Value(3.402823466385289e38);
@@ -1242,7 +1242,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
12421242
// An even larger floating point number.
12431243
val = Json::Value(1.2345678e300);
12441244
JSONTEST_ASSERT_EQUAL(double(1.2345678e300), val.asDouble());
1245-
JSONTEST_ASSERT_STRING_EQUAL("1.234567800000000e+300", normalizeFloatingPointStr(val.asString()));
1245+
JSONTEST_ASSERT_STRING_EQUAL("1.2345678e+300", normalizeFloatingPointStr(val.asString()));
12461246
}
12471247

12481248

0 commit comments

Comments
 (0)