From d999d9284e44dd20c7d492ab3af0d45d4132ca08 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Fri, 27 Dec 2024 20:54:47 +0100 Subject: [PATCH 1/5] Add `from_chars_dispatch` overload for unsigned integers smaller than `uint64_t` Useful for testing. --- include/boost/charconv/detail/parser.hpp | 18 ++++++++++++++++++ test/test_parser.cpp | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/boost/charconv/detail/parser.hpp b/include/boost/charconv/detail/parser.hpp index 410c6735..34ee64b9 100644 --- a/include/boost/charconv/detail/parser.hpp +++ b/include/boost/charconv/detail/parser.hpp @@ -62,6 +62,24 @@ inline from_chars_result from_chars_dispatch(const char* first, const char* last } #endif +template +typename std::enable_if::value && + std::numeric_limits::is_integer && + sizeof(Unsigned_Integer) < sizeof(std::uint64_t), + from_chars_result>::type + from_chars_dispatch(const char* first, const char* last, Unsigned_Integer& value, int base) noexcept +{ + std::uint64_t tmp_value; + auto result = boost::charconv::detail::from_chars(first, last, tmp_value, base); + if (result) { + if (tmp_value > std::numeric_limits::max()) + result.ec = std::errc::result_out_of_range; + else + value = static_cast(tmp_value); + } + return result; +} + template inline from_chars_result parser(const char* first, const char* last, bool& sign, Unsigned_Integer& significand, Integer& exponent, chars_format fmt = chars_format::general) noexcept { diff --git a/test/test_parser.cpp b/test/test_parser.cpp index 281ef834..1493eab6 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -66,6 +66,22 @@ void test_integer() BOOST_TEST_EQ(exponent, 0); BOOST_TEST_EQ(significand, max_sig_v); } + + // Small significant type + std::uint8_t significand_8{}; + exponent = 0; + sign = false; + + const char* val3 = "255"; + auto r5 = boost::charconv::detail::parser(val3, val3 + std::strlen(val3), sign, significand_8, exponent); + BOOST_TEST(r5); + BOOST_TEST_EQ(sign, false); + BOOST_TEST_EQ(exponent, 0); + BOOST_TEST_EQ(significand_8, 255); + + const char* val4 = "256"; + auto r6 = boost::charconv::detail::parser(val4, val4 + std::strlen(val4), sign, significand_8, exponent); + BOOST_TEST(r6.ec == std::errc::result_out_of_range); } template From 53c2394b4ec560df5be708da16673ae38df3afeb Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Fri, 27 Dec 2024 21:45:44 +0100 Subject: [PATCH 2/5] Add test case for different zeroes --- test/test_parser.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/test_parser.cpp b/test/test_parser.cpp index 1493eab6..59e7fcfa 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -211,6 +211,29 @@ void test_hex_scientific() BOOST_TEST_EQ(significand, UINT64_C(80427)); } +void test_zeroes() +{ + for (const char* val : { + "0", "00", "000", "0000", + "0.", "00.", "000.", "0000.", + "0.0", "00.0", "000.0", "0000.0", + "0e0", "00e0", "000e0", "0000e0", + "0.e0", "00.e0", "000.e0", "0000.e0", + "0.0e0", "00.0e0", "000.0e0", "0000.0e0", + }) { + // Use small integer type to reduce input test string lengths + std::uint8_t significand = 1; + std::int64_t exponent = 1; + bool sign = (std::strlen(val) % 2) == 0; // Different initial values + + auto r1 = boost::charconv::detail::parser(val, val + std::strlen(val), sign, significand, exponent); + BOOST_TEST(r1); + BOOST_TEST_EQ(sign, false); + BOOST_TEST_EQ(significand, 0u); + BOOST_TEST_EQ(exponent, 0); + } +} + int main() { test_integer(); @@ -229,5 +252,6 @@ int main() test_hex_scientific(); test_hex_scientific(); + test_zeroes(); return boost::report_errors(); } From d961daf54795df1210951d246ffdb970be1a6ef7 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Fri, 27 Dec 2024 21:52:38 +0100 Subject: [PATCH 3/5] Fix uninitialized values when parsing zeroes `0.e0` will not initialize the significant reference argument. `00[..]0.[0...]` will not initialize exponent nor significant. --- include/boost/charconv/detail/parser.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/boost/charconv/detail/parser.hpp b/include/boost/charconv/detail/parser.hpp index 34ee64b9..fcbbe3ac 100644 --- a/include/boost/charconv/detail/parser.hpp +++ b/include/boost/charconv/detail/parser.hpp @@ -252,6 +252,8 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, if (next == last) { + significand = 0; + exponent = 0; return {last, std::errc()}; } } @@ -365,6 +367,8 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, significand += 1; } } + else + significand = 0; } else { From aa0b11c26827705e9fe0deb1e456b29096a0d249 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sat, 28 Dec 2024 14:27:31 +0100 Subject: [PATCH 4/5] Avoid -Wconversion warning for small significant types --- include/boost/charconv/detail/parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/charconv/detail/parser.hpp b/include/boost/charconv/detail/parser.hpp index fcbbe3ac..15149d25 100644 --- a/include/boost/charconv/detail/parser.hpp +++ b/include/boost/charconv/detail/parser.hpp @@ -364,7 +364,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, if (round) { - significand += 1; + significand = static_cast(significand + 1u); } } else From 685d4b1bfceeffc7adf80eaebcdbfe28557a3544 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Thu, 2 Jan 2025 16:43:26 +0100 Subject: [PATCH 5/5] Protect against `max` macro Co-authored-by: Matt Borland --- include/boost/charconv/detail/parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/charconv/detail/parser.hpp b/include/boost/charconv/detail/parser.hpp index 15149d25..660de688 100644 --- a/include/boost/charconv/detail/parser.hpp +++ b/include/boost/charconv/detail/parser.hpp @@ -72,7 +72,7 @@ typename std::enable_if::value && std::uint64_t tmp_value; auto result = boost::charconv::detail::from_chars(first, last, tmp_value, base); if (result) { - if (tmp_value > std::numeric_limits::max()) + if (tmp_value > (std::numeric_limits::max)()) result.ec = std::errc::result_out_of_range; else value = static_cast(tmp_value);