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

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

+70-27
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

+53-48
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.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)