mirror of
https://github.com/fastfloat/fast_float.git
synced 2026-06-15 00:16:11 +08:00
detect uint64 overflow that wraps past min_safe in parse_int_string
This commit is contained in:
parent
8234a89623
commit
632cc97b5b
@ -781,9 +781,28 @@ parse_int_string(UC const *p, UC const *pend, T &value,
|
|||||||
}
|
}
|
||||||
// this check can be eliminated for all other types, but they will all require
|
// this check can be eliminated for all other types, but they will all require
|
||||||
// a max_digits(base) equivalent
|
// a max_digits(base) equivalent
|
||||||
if (digit_count == max_digits && i < min_safe_u64(base)) {
|
if (digit_count == max_digits) {
|
||||||
answer.ec = std::errc::result_out_of_range;
|
// A value that wrapped below the smallest max_digits-length value has
|
||||||
return answer;
|
// certainly overflowed.
|
||||||
|
if (i < min_safe_u64(base)) {
|
||||||
|
answer.ec = std::errc::result_out_of_range;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
// i >= min_safe_u64(base) is still not proof that it fits: for any base
|
||||||
|
// whose max_digits-length range exceeds 2^64 (base 10 reaches ~5.4 * 2^64
|
||||||
|
// at 20 digits) the accumulator can wrap a whole multiple of 2^64 and land
|
||||||
|
// back above min_safe, so the test above lets that overflow through. Re-run
|
||||||
|
// the parsed digits with a checked multiply-add to decide exactly.
|
||||||
|
uint64_t overflow_check = 0;
|
||||||
|
for (UC const *q = start_digits; q != p; ++q) {
|
||||||
|
uint8_t const digit = ch_to_digit(*q);
|
||||||
|
if (overflow_check >
|
||||||
|
(std::numeric_limits<uint64_t>::max() - digit) / uint64_t(base)) {
|
||||||
|
answer.ec = std::errc::result_out_of_range;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
overflow_check = uint64_t(base) * overflow_check + digit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check other types overflow
|
// check other types overflow
|
||||||
|
|||||||
@ -821,6 +821,61 @@ int main() {
|
|||||||
++base_unsigned;
|
++base_unsigned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unsigned out of range error base test, multi-wrap (64 bit)
|
||||||
|
// These values overflow uint64_t, but the accumulator wraps a whole multiple
|
||||||
|
// of 2^64 and lands back at or above the smallest max_digits-length value, so
|
||||||
|
// a single comparison against that bound does not catch the overflow. Bases
|
||||||
|
// 2, 4 and 16 are excluded because their max_digits-length range fits within
|
||||||
|
// a single 2^64 span.
|
||||||
|
std::vector<int> const unsigned_multiwrap_base{
|
||||||
|
3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20,
|
||||||
|
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36};
|
||||||
|
std::vector<std::string_view> const unsigned_multiwrap_base_test{
|
||||||
|
"22222222222222222222222222222222222222222",
|
||||||
|
"4400000000000000000000000000",
|
||||||
|
"5555555555555555555555555",
|
||||||
|
"66666666666666666666666",
|
||||||
|
"7777777777777777777777",
|
||||||
|
"888888888888888888888",
|
||||||
|
"46893488147419103233",
|
||||||
|
"AAAAAAAAAAAAAAAAAAA",
|
||||||
|
"BBBBBBBBBBBBBBBBBB",
|
||||||
|
"427772311192C9BAAB",
|
||||||
|
"DDDDDDDDDDDDDDDDD",
|
||||||
|
"532C82996D3A44919",
|
||||||
|
"GGGGGGGGGGGGGGGG",
|
||||||
|
"HHHHHHHHHHHHHHHH",
|
||||||
|
"3835GEGDF36622EG",
|
||||||
|
"JJJJJJJJJJJJJJJ",
|
||||||
|
"KKKKKKKKKKKKKKK",
|
||||||
|
"LLLLLLLLLLLLLLL",
|
||||||
|
"444BGHB4EG5DA2D",
|
||||||
|
"NNNNNNNNNNNNNN",
|
||||||
|
"JE5H4MNDLJGNLO",
|
||||||
|
"PPPPPPPPPPPPPP",
|
||||||
|
"QQQQQQQQQQQQQQ",
|
||||||
|
"RRRRRRRRRRRRRR",
|
||||||
|
"4H7QS52310IHQK",
|
||||||
|
"TTTTTTTTTTTTTT",
|
||||||
|
"UUUUUUUUUUUUU",
|
||||||
|
"VVVVVVVVVVVVV",
|
||||||
|
"WWWWWWWWWWWWW",
|
||||||
|
"XXXXXXXXXXXXX",
|
||||||
|
"YYYYYYYYYYYYY",
|
||||||
|
"6U831JL976P6O"};
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < unsigned_multiwrap_base_test.size(); ++i) {
|
||||||
|
auto const &f = unsigned_multiwrap_base_test[i];
|
||||||
|
uint64_t result;
|
||||||
|
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
|
||||||
|
unsigned_multiwrap_base[i]);
|
||||||
|
if (answer.ec != std::errc::result_out_of_range) {
|
||||||
|
std::cerr << "expected error for should be 'result_out_of_range': \"" << f
|
||||||
|
<< "\"" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// just within range base test (64 bit)
|
// just within range base test (64 bit)
|
||||||
std::vector<std::string_view> const int_within_range_base_test{
|
std::vector<std::string_view> const int_within_range_base_test{
|
||||||
"111111111111111111111111111111111111111111111111111111111111111",
|
"111111111111111111111111111111111111111111111111111111111111111",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user