diff --git a/tests/test_parse_integer.cpp b/tests/test_parse_integer.cpp index 423961f..4a86226 100644 --- a/tests/test_parse_integer.cpp +++ b/tests/test_parse_integer.cpp @@ -84,6 +84,22 @@ BOOST_AUTO_TEST_CASE(test_bin_value) TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b010000", value(16)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b01_00_00", value(16)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b111111", value(63)); + + TOML11_TEST_PARSE_EQUAL_VALUE(parse_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, + "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, + "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); diff --git a/toml/parser.hpp b/toml/parser.hpp index 06e0324..8cdd2ce 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -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(max_length) < str.size()) { - // since toml11 uses int64_t, 64bit (unsigned) input cannot be read. loc.reset(first); - return err(format_underline("toml::parse_binary_integer:", - {{source_location(loc), "too large input (> int64_t)"}})); + 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::max)() / 2 < base ) + { + base = 0; + } + base *= 2; + } + else if(*i == '0') + { + if( (std::numeric_limits::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",