fix: #213 allow long binary integer

This commit is contained in:
ToruNiina 2023-02-12 23:20:09 +09:00
commit 51e5d845b0
2 changed files with 59 additions and 10 deletions

View File

@ -84,6 +84,22 @@ BOOST_AUTO_TEST_CASE(test_bin_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "0b010000", value(16));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "0b01_00_00", value(16));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "0b111111", value(63));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"0b1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000",
// 1 0 0 0
// 0 C 8 4
value(0x0888888888888888));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"0b01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111",
// 1 0 0 0
// 0 C 8 4
value(0x7FFFFFFFFFFFFFFF));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"0b00000000_01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111",
// 1 0 0 0
// 0 C 8 4
value(0x7FFFFFFFFFFFFFFF));
}
BOOST_AUTO_TEST_CASE(test_integer_overflow)
@ -91,7 +107,8 @@ BOOST_AUTO_TEST_CASE(test_integer_overflow)
std::istringstream dec_overflow(std::string("dec-overflow = 9223372036854775808"));
std::istringstream hex_overflow(std::string("hex-overflow = 0x1_00000000_00000000"));
std::istringstream oct_overflow(std::string("oct-overflow = 0o1_000_000_000_000_000_000_000"));
std::istringstream bin_overflow(std::string("bin-overflow = 0b1_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"));
// 64 56 48 40 32 24 16 8
std::istringstream bin_overflow(std::string("bin-overflow = 0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"));
BOOST_CHECK_THROW(toml::parse(dec_overflow), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(hex_overflow), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(oct_overflow), toml::syntax_error);

View File

@ -57,20 +57,52 @@ parse_binary_integer(location& loc)
{
auto str = token.unwrap().str();
assert(str.size() > 2); // minimum -> 0b1
if(64 <= str.size())
{
assert(str.at(0) == '0' && str.at(1) == 'b');
// skip all the zeros and `_` locating at the MSB
str.erase(str.begin(), std::find_if(
str.begin() + 2, // to skip prefix `0b`
str.end(),
[](const char c) { return c == '1'; })
);
assert(str.empty() || str.front() == '1');
// since toml11 uses int64_t, 64bit (unsigned) input cannot be read.
const auto max_length = 63 + std::count(str.begin(), str.end(), '_');
if(static_cast<std::string::size_type>(max_length) < str.size())
{
loc.reset(first);
return err(format_underline("toml::parse_binary_integer:",
return err(format_underline("toml::parse_binary_integer: "
"only signed 64bit integer is available",
{{source_location(loc), "too large input (> int64_t)"}}));
}
integer retval(0), base(1);
for(auto i(str.rbegin()), e(str.rend() - 2); i!=e; ++i)
for(auto i(str.rbegin()), e(str.rend()); i!=e; ++i)
{
if (*i == '1'){retval += base; base *= 2;}
else if(*i == '0'){base *= 2;}
else if(*i == '_'){/* do nothing. */}
else // internal error.
assert(base != 0); // means overflow, checked in the above code
if(*i == '1')
{
retval += base;
if( (std::numeric_limits<integer>::max)() / 2 < base )
{
base = 0;
}
base *= 2;
}
else if(*i == '0')
{
if( (std::numeric_limits<integer>::max)() / 2 < base )
{
base = 0;
}
base *= 2;
}
else if(*i == '_')
{
// do nothing.
}
else // should be detected by lex_bin_int. [[unlikely]]
{
throw internal_error(format_underline(
"toml::parse_binary_integer: internal error",