diff --git a/include/boost/charconv/detail/from_chars_integer_impl.hpp b/include/boost/charconv/detail/from_chars_integer_impl.hpp index 94e3e665..a069fd96 100644 --- a/include/boost/charconv/detail/from_chars_integer_impl.hpp +++ b/include/boost/charconv/detail/from_chars_integer_impl.hpp @@ -42,6 +42,47 @@ static constexpr unsigned char uchar_values[] = static_assert(sizeof(uchar_values) == 256, "uchar_values should represent all 256 values of unsigned char"); +static constexpr double log_2_table[] = +{ + 0.0, + 0.0, + 1.0, + 0.630929753571, + 0.5, + 0.430676558073, + 0.386852807235, + 0.356207187108, + 0.333333333333, + 0.315464876786, + 0.301029995664, + 0.289064826318, + 0.278942945651, + 0.270238154427, + 0.262649535037, + 0.255958024810, + 0.25, + 0.244650542118, + 0.239812466568, + 0.235408913367, + 0.231378213160, + 0.227670248697, + 0.224243824218, + 0.221064729458, + 0.218104291986, + 0.215338279037, + 0.212746053553, + 0.210309917857, + 0.208014597677, + 0.205846832460, + 0.203795047091, + 0.201849086582, + 0.2, + 0.198239863171, + 0.196561632233, + 0.194959021894, + 0.193426403617 +}; + // Convert characters for 0-9, A-Z, a-z to 0-35. Anything else is 255 constexpr unsigned char digit_from_char(char val) noexcept { @@ -177,13 +218,15 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs // In non-GNU mode on GCC numeric limits may not be specialized #if defined(BOOST_CHARCONV_HAS_INT128) && !defined(__GLIBCXX_TYPE_INT_N_0) - constexpr std::ptrdiff_t nd = std::is_same::value ? 38 : - std::is_same::value ? 38 : - std::numeric_limits::digits10; + constexpr std::ptrdiff_t nd_2 = std::is_same::value ? 127 : + std::is_same::value ? 128 : + std::numeric_limits::digits10; #else - constexpr std::ptrdiff_t nd = std::numeric_limits::digits10; + constexpr std::ptrdiff_t nd_2 = std::numeric_limits::digits; #endif + const auto nd = static_cast(nd_2 * log_2_table[static_cast(unsigned_base)]); + { // Check that the first character is valid before proceeding const unsigned char first_digit = digit_from_char(*next); diff --git a/test/Jamfile b/test/Jamfile index f4fb26c5..6ef328ee 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -65,3 +65,4 @@ run github_issue_154.cpp ; run github_issue_158.cpp ; run github_issue_166.cpp ; run github_issue_186.cpp ; +run github_issue_212.cpp ; diff --git a/test/from_chars.cpp b/test/from_chars.cpp index aaf97764..ed7048f1 100644 --- a/test/from_chars.cpp +++ b/test/from_chars.cpp @@ -13,6 +13,12 @@ #include #include +#if defined(__has_include) +# if __has_include() +# include +# endif +#endif + #ifdef BOOST_CHARCONV_HAS_INT128 template void test_128bit_int() @@ -206,6 +212,15 @@ void invalid_argument_test() auto r11 = boost::charconv::from_chars(buffer11, buffer11 + 2, o); BOOST_TEST(r11); BOOST_TEST_EQ(o, static_cast(3)); + + // https://github.com/boostorg/charconv/issues/213 + #if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L + T segment_result {12}; + std::string_view input{"/c"}; + auto r12 = boost::charconv::from_chars(input.data(), input.data() + input.size(), segment_result); + BOOST_TEST(r12.ec == std::errc::invalid_argument); + BOOST_TEST_EQ(segment_result, static_cast(12)); + #endif } // No overflows, negative numbers, locales, etc. @@ -272,6 +287,7 @@ int main() invalid_argument_test(); invalid_argument_test(); + invalid_argument_test(); overflow_test(); overflow_test(); diff --git a/test/github_issue_212.cpp b/test/github_issue_212.cpp new file mode 100644 index 00000000..110774e2 --- /dev/null +++ b/test/github_issue_212.cpp @@ -0,0 +1,52 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include + +template +void test() +{ + const char* str = "ffffffff1"; + + for (int i = 11; i < 36; ++i) + { + T segment_result {42U}; + const auto r = boost::charconv::from_chars(str, str + std::strlen(str), segment_result, 16); + BOOST_TEST(r.ec == std::errc::result_out_of_range); + BOOST_TEST_EQ(segment_result, UINT32_C(42)); + } +} + +template +void test_64() +{ + const char* str = "ffffffffffffffff1"; + + for (int i = 11; i < 36; ++i) + { + T segment_result {42U}; + const auto r = boost::charconv::from_chars(str, str + std::strlen(str), segment_result, 16); + BOOST_TEST(r.ec == std::errc::result_out_of_range); + BOOST_TEST_EQ(segment_result, UINT32_C(42)); + } +} + +int main() +{ + test(); + test(); + test(); + test(); + + test_64(); + test_64(); + test_64(); + test_64(); + + return boost::report_errors(); +}