-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Number fixes #1053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Number fixes #1053
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
bc1f4f4
Fix issues with signed/unsigned overflows
baylesj 8fb4506
Add tests
baylesj 4df3140
Delete duplicate tests.
baylesj 74816ce
In progress
baylesj 9050dc3
Finish fixing up json reader
baylesj 836d954
Move tests to legacy_ prepend
baylesj 729abaf
clang formatize
baylesj 1c4763b
Pushback meson version dependency so I can devbox
baylesj ac1b55c
Fixup cmake errors/warnings
baylesj 7e1a02d
Fixup clang format
baylesj 9ad6e8b
Change OurReader to use string stream
baylesj f4903f2
run clang-format (#1089)
dota17 ea0fc9a
fix conflict (#1090)
dota17 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1524,19 +1524,45 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { | |
// larger than the maximum supported value of an integer then | ||
// we decode the number as a double. | ||
Location current = token.start_; | ||
bool isNegative = *current == '-'; | ||
if (isNegative) | ||
const bool isNegative = *current == '-'; | ||
if (isNegative) { | ||
++current; | ||
} | ||
|
||
static constexpr auto positive_threshold = Value::maxLargestUInt / 10; | ||
static constexpr auto positive_last_digit = Value::maxLargestUInt % 10; | ||
static constexpr auto negative_threshold = | ||
Value::LargestUInt(Value::minLargestInt) / 10; | ||
static constexpr auto negative_last_digit = | ||
Value::LargestUInt(Value::minLargestInt) % 10; | ||
|
||
const auto threshold = isNegative ? negative_threshold : positive_threshold; | ||
const auto last_digit = | ||
// We assume we can represent the largest and smallest integer types as | ||
// unsigned integers with separate sign. This is only true if they can fit | ||
// into an unsigned integer. | ||
static_assert(Value::maxLargestInt <= Value::maxLargestUInt, | ||
"Int must be smaller than UInt"); | ||
|
||
// We need to convert minLargestInt into a positive number. The easiest way | ||
// to do this conversion is to assume our "threshold" value of minLargestInt | ||
// divided by 10 can fit in maxLargestInt when absolute valued. This should | ||
// be a safe assumption. | ||
static_assert(Value::minLargestInt <= -Value::maxLargestInt, | ||
"The absolute value of minLargestInt must be greater than or " | ||
"equal to maxLargestInt"); | ||
static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt, | ||
"The absolute value of minLargestInt must be only 1 magnitude " | ||
"larger than maxLargest Int"); | ||
|
||
static constexpr Value::LargestUInt positive_threshold = | ||
Value::maxLargestUInt / 10; | ||
static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10; | ||
|
||
// For the negative values, we have to be more careful. Since typically | ||
// -Value::minLargestInt will cause an overflow, we first divide by 10 and | ||
// then take the inverse. This assumes that minLargestInt is only a single | ||
// power of 10 different in magnitude, which we check above. For the last | ||
// digit, we take the modulus before negating for the same reason. | ||
static constexpr Value::LargestUInt negative_threshold = | ||
Value::LargestUInt(-(Value::minLargestInt / 10)); | ||
static constexpr Value::UInt negative_last_digit = | ||
Value::UInt(-(Value::minLargestInt % 10)); | ||
|
||
const Value::LargestUInt threshold = | ||
isNegative ? negative_threshold : positive_threshold; | ||
const Value::UInt max_last_digit = | ||
isNegative ? negative_last_digit : positive_last_digit; | ||
|
||
Value::LargestUInt value = 0; | ||
|
@@ -1545,26 +1571,30 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { | |
if (c < '0' || c > '9') | ||
return decodeDouble(token, decoded); | ||
|
||
const auto digit(static_cast<Value::UInt>(c - '0')); | ||
const Value::UInt digit(static_cast<Value::UInt>(c - '0')); | ||
if (value >= threshold) { | ||
// We've hit or exceeded the max value divided by 10 (rounded down). If | ||
// a) we've only just touched the limit, meaing value == threshold, | ||
// b) this is the last digit, or | ||
// c) it's small enough to fit in that rounding delta, we're okay. | ||
// Otherwise treat this number as a double to avoid overflow. | ||
if (value > threshold || current != token.end_ || digit > last_digit) { | ||
if (value > threshold || current != token.end_ || | ||
digit > max_last_digit) { | ||
return decodeDouble(token, decoded); | ||
} | ||
} | ||
value = value * 10 + digit; | ||
} | ||
|
||
if (isNegative) | ||
decoded = -Value::LargestInt(value); | ||
else if (value <= Value::LargestUInt(Value::maxLargestInt)) | ||
if (isNegative) { | ||
// We use the same magnitude assumption here, just in case. | ||
const Value::UInt last_digit = static_cast<Value::UInt>(value % 10); | ||
decoded = -Value::LargestInt(value / 10) * 10 - last_digit; | ||
} else if (value <= Value::LargestUInt(Value::maxLargestInt)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh! This was a bug? Good catch. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just wonder the bug, is there a example code to reproduce the bug? |
||
decoded = Value::LargestInt(value); | ||
else | ||
} else { | ||
decoded = value; | ||
} | ||
|
||
return true; | ||
} | ||
|
@@ -1581,37 +1611,12 @@ bool OurReader::decodeDouble(Token& token) { | |
|
||
bool OurReader::decodeDouble(Token& token, Value& decoded) { | ||
double value = 0; | ||
const int bufferSize = 32; | ||
int count; | ||
ptrdiff_t const length = token.end_ - token.start_; | ||
|
||
// Sanity check to avoid buffer overflow exploits. | ||
if (length < 0) { | ||
return addError("Unable to parse token length", token); | ||
} | ||
auto const ulength = static_cast<size_t>(length); | ||
|
||
// Avoid using a string constant for the format control string given to | ||
// sscanf, as this can cause hard to debug crashes on OS X. See here for more | ||
// info: | ||
// | ||
// http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html | ||
char format[] = "%lf"; | ||
|
||
if (length <= bufferSize) { | ||
Char buffer[bufferSize + 1]; | ||
memcpy(buffer, token.start_, ulength); | ||
buffer[length] = 0; | ||
fixNumericLocaleInput(buffer, buffer + length); | ||
count = sscanf(buffer, format, &value); | ||
} else { | ||
String buffer(token.start_, token.end_); | ||
count = sscanf(buffer.c_str(), format, &value); | ||
} | ||
|
||
if (count != 1) | ||
const String buffer(token.start_, token.end_); | ||
IStringStream is(buffer); | ||
if (!(is >> value)) { | ||
return addError( | ||
"'" + String(token.start_, token.end_) + "' is not a number.", token); | ||
} | ||
decoded = value; | ||
return true; | ||
} | ||
|
@@ -1633,9 +1638,9 @@ bool OurReader::decodeString(Token& token, String& decoded) { | |
Location end = token.end_ - 1; // do not include '"' | ||
while (current != end) { | ||
Char c = *current++; | ||
if (c == '"') | ||
if (c == '"') { | ||
break; | ||
else if (c == '\\') { | ||
} else if (c == '\\') { | ||
if (current == end) | ||
return addError("Empty escape sequence in string", token, current); | ||
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.
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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.