Skip to content

Commit 645cd04

Browse files
baylesjdota17
authored andcommitted
Number fixes (#1053)
* cleaning up the logic for parsing numbers * Add Testcases for new Reader in jsontestrunner
1 parent ff58fdc commit 645cd04

File tree

115 files changed

+124
-76
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+124
-76
lines changed

meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ project(
1515
'cpp_std=c++11',
1616
'warning_level=1'],
1717
license : 'Public Domain',
18-
meson_version : '>= 0.50.0')
18+
meson_version : '>= 0.49.0')
1919

2020

2121
jsoncpp_headers = [

src/jsontestrunner/main.cpp

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515

1616
#include <algorithm> // sort
1717
#include <cstdio>
18+
#include <iostream>
1819
#include <json/json.h>
20+
#include <memory>
1921
#include <sstream>
2022

2123
struct Options {
@@ -126,19 +128,45 @@ static int parseAndSaveValueTree(const Json::String& input,
126128
const Json::String& actual,
127129
const Json::String& kind,
128130
const Json::Features& features, bool parseOnly,
129-
Json::Value* root) {
130-
Json::Reader reader(features);
131-
bool parsingSuccessful =
132-
reader.parse(input.data(), input.data() + input.size(), *root);
133-
if (!parsingSuccessful) {
134-
printf("Failed to parse %s file: \n%s\n", kind.c_str(),
135-
reader.getFormattedErrorMessages().c_str());
136-
return 1;
131+
Json::Value* root, bool use_legacy) {
132+
if (!use_legacy) {
133+
Json::CharReaderBuilder builder;
134+
135+
builder.settings_["allowComments"] = features.allowComments_;
136+
builder.settings_["strictRoot"] = features.strictRoot_;
137+
builder.settings_["allowDroppedNullPlaceholders"] =
138+
features.allowDroppedNullPlaceholders_;
139+
builder.settings_["allowNumericKeys"] = features.allowNumericKeys_;
140+
141+
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
142+
Json::String errors;
143+
const bool parsingSuccessful =
144+
reader->parse(input.data(), input.data() + input.size(), root, &errors);
145+
146+
if (!parsingSuccessful) {
147+
std::cerr << "Failed to parse " << kind << " file: " << std::endl
148+
<< errors << std::endl;
149+
return 1;
150+
}
151+
152+
// We may instead check the legacy implementation (to ensure it doesn't
153+
// randomly get broken).
154+
} else {
155+
Json::Reader reader(features);
156+
const bool parsingSuccessful =
157+
reader.parse(input.data(), input.data() + input.size(), *root);
158+
if (!parsingSuccessful) {
159+
std::cerr << "Failed to parse " << kind << " file: " << std::endl
160+
<< reader.getFormatedErrorMessages() << std::endl;
161+
return 1;
162+
}
137163
}
164+
138165
if (!parseOnly) {
139166
FILE* factual = fopen(actual.c_str(), "wt");
140167
if (!factual) {
141-
printf("Failed to create %s actual file.\n", kind.c_str());
168+
std::cerr << "Failed to create '" << kind << "' actual file."
169+
<< std::endl;
142170
return 2;
143171
}
144172
printValueTree(factual, *root);
@@ -172,7 +200,7 @@ static int rewriteValueTree(const Json::String& rewritePath,
172200
*rewrite = write(root);
173201
FILE* fout = fopen(rewritePath.c_str(), "wt");
174202
if (!fout) {
175-
printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
203+
std::cerr << "Failed to create rewrite file: " << rewritePath << std::endl;
176204
return 2;
177205
}
178206
fprintf(fout, "%s\n", rewrite->c_str());
@@ -193,14 +221,15 @@ static Json::String removeSuffix(const Json::String& path,
193221
static void printConfig() {
194222
// Print the configuration used to compile JsonCpp
195223
#if defined(JSON_NO_INT64)
196-
printf("JSON_NO_INT64=1\n");
224+
std::cout << "JSON_NO_INT64=1" << std::endl;
197225
#else
198-
printf("JSON_NO_INT64=0\n");
226+
std::cout << "JSON_NO_INT64=0" << std::endl;
199227
#endif
200228
}
201229

202230
static int printUsage(const char* argv[]) {
203-
printf("Usage: %s [--strict] input-json-file", argv[0]);
231+
std::cout << "Usage: " << argv[0] << " [--strict] input-json-file"
232+
<< std::endl;
204233
return 3;
205234
}
206235

@@ -230,7 +259,7 @@ static int parseCommandLine(int argc, const char* argv[], Options* opts) {
230259
} else if (writerName == "BuiltStyledStreamWriter") {
231260
opts->write = &useBuiltStyledStreamWriter;
232261
} else {
233-
printf("Unknown '--json-writer %s'\n", writerName.c_str());
262+
std::cerr << "Unknown '--json-writer' " << writerName << std::endl;
234263
return 4;
235264
}
236265
}
@@ -240,19 +269,20 @@ static int parseCommandLine(int argc, const char* argv[], Options* opts) {
240269
opts->path = argv[index];
241270
return 0;
242271
}
243-
static int runTest(Options const& opts) {
272+
273+
static int runTest(Options const& opts, bool use_legacy) {
244274
int exitCode = 0;
245275

246276
Json::String input = readInputTestFile(opts.path.c_str());
247277
if (input.empty()) {
248-
printf("Failed to read input or empty input: %s\n", opts.path.c_str());
278+
std::cerr << "Invalid input file: " << opts.path << std::endl;
249279
return 3;
250280
}
251281

252282
Json::String basePath = removeSuffix(opts.path, ".json");
253283
if (!opts.parseOnly && basePath.empty()) {
254-
printf("Bad input path. Path does not end with '.expected':\n%s\n",
255-
opts.path.c_str());
284+
std::cerr << "Bad input path '" << opts.path
285+
<< "'. Must end with '.expected'" << std::endl;
256286
return 3;
257287
}
258288

@@ -262,34 +292,47 @@ static int runTest(Options const& opts) {
262292

263293
Json::Value root;
264294
exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features,
265-
opts.parseOnly, &root);
295+
opts.parseOnly, &root, use_legacy);
266296
if (exitCode || opts.parseOnly) {
267297
return exitCode;
268298
}
299+
269300
Json::String rewrite;
270301
exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
271302
if (exitCode) {
272303
return exitCode;
273304
}
305+
274306
Json::Value rewriteRoot;
275307
exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite",
276-
opts.features, opts.parseOnly, &rewriteRoot);
277-
if (exitCode) {
278-
return exitCode;
279-
}
280-
return 0;
308+
opts.features, opts.parseOnly, &rewriteRoot,
309+
use_legacy);
310+
311+
return exitCode;
281312
}
313+
282314
int main(int argc, const char* argv[]) {
283315
Options opts;
284316
try {
285317
int exitCode = parseCommandLine(argc, argv, &opts);
286318
if (exitCode != 0) {
287-
printf("Failed to parse command-line.");
319+
std::cerr << "Failed to parse command-line." << std::endl;
288320
return exitCode;
289321
}
290-
return runTest(opts);
322+
323+
const int modern_return_code = runTest(opts, false);
324+
if (modern_return_code) {
325+
return modern_return_code;
326+
}
327+
328+
const std::string filename =
329+
opts.path.substr(opts.path.find_last_of("\\/") + 1);
330+
const bool should_run_legacy = (filename.rfind("legacy_", 0) == 0);
331+
if (should_run_legacy) {
332+
return runTest(opts, true);
333+
}
291334
} catch (const std::exception& e) {
292-
printf("Unhandled exception:\n%s\n", e.what());
335+
std::cerr << "Unhandled exception:" << std::endl << e.what() << std::endl;
293336
return 1;
294337
}
295338
}

src/lib_json/json_reader.cpp

Lines changed: 53 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,19 +1540,45 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
15401540
// larger than the maximum supported value of an integer then
15411541
// we decode the number as a double.
15421542
Location current = token.start_;
1543-
bool isNegative = *current == '-';
1544-
if (isNegative)
1543+
const bool isNegative = *current == '-';
1544+
if (isNegative) {
15451545
++current;
1546+
}
15461547

1547-
static constexpr auto positive_threshold = Value::maxLargestUInt / 10;
1548-
static constexpr auto positive_last_digit = Value::maxLargestUInt % 10;
1549-
static constexpr auto negative_threshold =
1550-
Value::LargestUInt(Value::minLargestInt) / 10;
1551-
static constexpr auto negative_last_digit =
1552-
Value::LargestUInt(Value::minLargestInt) % 10;
1553-
1554-
const auto threshold = isNegative ? negative_threshold : positive_threshold;
1555-
const auto last_digit =
1548+
// We assume we can represent the largest and smallest integer types as
1549+
// unsigned integers with separate sign. This is only true if they can fit
1550+
// into an unsigned integer.
1551+
static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1552+
"Int must be smaller than UInt");
1553+
1554+
// We need to convert minLargestInt into a positive number. The easiest way
1555+
// to do this conversion is to assume our "threshold" value of minLargestInt
1556+
// divided by 10 can fit in maxLargestInt when absolute valued. This should
1557+
// be a safe assumption.
1558+
static_assert(Value::minLargestInt <= -Value::maxLargestInt,
1559+
"The absolute value of minLargestInt must be greater than or "
1560+
"equal to maxLargestInt");
1561+
static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
1562+
"The absolute value of minLargestInt must be only 1 magnitude "
1563+
"larger than maxLargest Int");
1564+
1565+
static constexpr Value::LargestUInt positive_threshold =
1566+
Value::maxLargestUInt / 10;
1567+
static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1568+
1569+
// For the negative values, we have to be more careful. Since typically
1570+
// -Value::minLargestInt will cause an overflow, we first divide by 10 and
1571+
// then take the inverse. This assumes that minLargestInt is only a single
1572+
// power of 10 different in magnitude, which we check above. For the last
1573+
// digit, we take the modulus before negating for the same reason.
1574+
static constexpr Value::LargestUInt negative_threshold =
1575+
Value::LargestUInt(-(Value::minLargestInt / 10));
1576+
static constexpr Value::UInt negative_last_digit =
1577+
Value::UInt(-(Value::minLargestInt % 10));
1578+
1579+
const Value::LargestUInt threshold =
1580+
isNegative ? negative_threshold : positive_threshold;
1581+
const Value::UInt max_last_digit =
15561582
isNegative ? negative_last_digit : positive_last_digit;
15571583

15581584
Value::LargestUInt value = 0;
@@ -1561,26 +1587,30 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
15611587
if (c < '0' || c > '9')
15621588
return decodeDouble(token, decoded);
15631589

1564-
const auto digit(static_cast<Value::UInt>(c - '0'));
1590+
const Value::UInt digit(static_cast<Value::UInt>(c - '0'));
15651591
if (value >= threshold) {
15661592
// We've hit or exceeded the max value divided by 10 (rounded down). If
15671593
// a) we've only just touched the limit, meaing value == threshold,
15681594
// b) this is the last digit, or
15691595
// c) it's small enough to fit in that rounding delta, we're okay.
15701596
// Otherwise treat this number as a double to avoid overflow.
1571-
if (value > threshold || current != token.end_ || digit > last_digit) {
1597+
if (value > threshold || current != token.end_ ||
1598+
digit > max_last_digit) {
15721599
return decodeDouble(token, decoded);
15731600
}
15741601
}
15751602
value = value * 10 + digit;
15761603
}
15771604

1578-
if (isNegative)
1579-
decoded = -Value::LargestInt(value);
1580-
else if (value <= Value::LargestUInt(Value::maxLargestInt))
1605+
if (isNegative) {
1606+
// We use the same magnitude assumption here, just in case.
1607+
const Value::UInt last_digit = static_cast<Value::UInt>(value % 10);
1608+
decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
1609+
} else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
15811610
decoded = Value::LargestInt(value);
1582-
else
1611+
} else {
15831612
decoded = value;
1613+
}
15841614

15851615
return true;
15861616
}
@@ -1597,37 +1627,12 @@ bool OurReader::decodeDouble(Token& token) {
15971627

15981628
bool OurReader::decodeDouble(Token& token, Value& decoded) {
15991629
double value = 0;
1600-
const int bufferSize = 32;
1601-
int count;
1602-
ptrdiff_t const length = token.end_ - token.start_;
1603-
1604-
// Sanity check to avoid buffer overflow exploits.
1605-
if (length < 0) {
1606-
return addError("Unable to parse token length", token);
1607-
}
1608-
auto const ulength = static_cast<size_t>(length);
1609-
1610-
// Avoid using a string constant for the format control string given to
1611-
// sscanf, as this can cause hard to debug crashes on OS X. See here for more
1612-
// info:
1613-
//
1614-
// http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
1615-
char format[] = "%lf";
1616-
1617-
if (length <= bufferSize) {
1618-
Char buffer[bufferSize + 1];
1619-
memcpy(buffer, token.start_, ulength);
1620-
buffer[length] = 0;
1621-
fixNumericLocaleInput(buffer, buffer + length);
1622-
count = sscanf(buffer, format, &value);
1623-
} else {
1624-
String buffer(token.start_, token.end_);
1625-
count = sscanf(buffer.c_str(), format, &value);
1626-
}
1627-
1628-
if (count != 1)
1630+
const String buffer(token.start_, token.end_);
1631+
IStringStream is(buffer);
1632+
if (!(is >> value)) {
16291633
return addError(
16301634
"'" + String(token.start_, token.end_) + "' is not a number.", token);
1635+
}
16311636
decoded = value;
16321637
return true;
16331638
}
@@ -1649,9 +1654,9 @@ bool OurReader::decodeString(Token& token, String& decoded) {
16491654
Location end = token.end_ - 1; // do not include '"'
16501655
while (current != end) {
16511656
Char c = *current++;
1652-
if (c == '"')
1657+
if (c == '"') {
16531658
break;
1654-
else if (c == '\\') {
1659+
} else if (c == '\\') {
16551660
if (current == end)
16561661
return addError("Empty escape sequence in string", token, current);
16571662
Char escape = *current++;
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)