From f67e85b06953a02121b0912266f9cbc8ec6fb74e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 30 Jan 2024 11:44:47 +0100 Subject: [PATCH 01/15] Add boost::core::string_view integer overloads --- include/boost/charconv/from_chars.hpp | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/include/boost/charconv/from_chars.hpp b/include/boost/charconv/from_chars.hpp index e62ac392..dce1c86d 100644 --- a/include/boost/charconv/from_chars.hpp +++ b/include/boost/charconv/from_chars.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace boost { namespace charconv { @@ -75,6 +76,63 @@ BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, co } #endif +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, bool& value, int base = 10) noexcept = delete; +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, char& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, signed char& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, unsigned char& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, short& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, unsigned short& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, int& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, unsigned int& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, long& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, unsigned long& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, long long& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, unsigned long long& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} + +#ifdef BOOST_CHARCONV_HAS_INT128 +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, boost::int128_type& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, boost::uint128_type& value, int base = 10) noexcept +{ + return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); +} +#endif + //---------------------------------------------------------------------------------------------------------------------- // Floating Point //---------------------------------------------------------------------------------------------------------------------- From d049605d411da4ed38e2292b94e21f237536440e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 30 Jan 2024 11:45:12 +0100 Subject: [PATCH 02/15] Add string_view integer tests --- test/Jamfile | 1 + test/from_chars_string_view.cpp | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 test/from_chars_string_view.cpp diff --git a/test/Jamfile b/test/Jamfile index c40e7d6d..30f71917 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -56,3 +56,4 @@ run test_float128.cpp : : : [ check-target-builds ../config//has_float128 "GCC l run P2497.cpp ; run github_issue_110.cpp ; run github_issue_122.cpp ; +run from_chars_string_view.cpp ; diff --git a/test/from_chars_string_view.cpp b/test/from_chars_string_view.cpp new file mode 100644 index 00000000..4262c57f --- /dev/null +++ b/test/from_chars_string_view.cpp @@ -0,0 +1,67 @@ +// 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 +#include +#include + +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) +# include +#endif + +static std::mt19937_64 rng(42); +constexpr std::size_t N = 1024; + +template +void test_int() +{ + std::uniform_int_distribution dist((std::numeric_limits::min)(), (std::numeric_limits::max)()); + + for (std::size_t i = 0; i < N; ++i) + { + const auto value = dist(rng); + std::string str_value = std::to_string(value); + StringViewType sv = str_value; + + T v = 0; + auto r = boost::charconv::from_chars(sv, v); + BOOST_TEST(r); + BOOST_TEST_EQ(v, value); + } +} + +int main() +{ + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + + #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + + #endif + + return boost::report_errors(); +} From d0c56db38cffd79e366f87b71e09ee8655938b92 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 30 Jan 2024 11:51:06 +0100 Subject: [PATCH 03/15] Fuzz the string_view interface --- fuzzing/fuzz_from_chars_int.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fuzzing/fuzz_from_chars_int.cpp b/fuzzing/fuzz_from_chars_int.cpp index 7846924e..2ab60633 100644 --- a/fuzzing/fuzz_from_chars_int.cpp +++ b/fuzzing/fuzz_from_chars_int.cpp @@ -3,6 +3,7 @@ // https://www.boost.org/LICENSE_1_0.txt #include +#include #include #include @@ -11,32 +12,41 @@ extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, std::size_t size try { auto c_data = reinterpret_cast(data); + boost::core::string_view sv {c_data, size}; for (int base = 2; base < 36; ++base) { char c_val; boost::charconv::from_chars(c_data, c_data + size, c_val, base); + boost::charconv::from_chars(sv, c_val, base); int i_val; boost::charconv::from_chars(c_data, c_data + size, i_val, base); + boost::charconv::from_chars(sv, i_val, base); long l_val; boost::charconv::from_chars(c_data, c_data + size, l_val, base); + boost::charconv::from_chars(sv, l_val, base); long long ll_val; boost::charconv::from_chars(c_data, c_data + size, ll_val, base); + boost::charconv::from_chars(sv, ll_val, base); unsigned char uc_val; boost::charconv::from_chars(c_data, c_data + size, uc_val, base); + boost::charconv::from_chars(sv, uc_val, base); unsigned int ui_val; boost::charconv::from_chars(c_data, c_data + size, ui_val, base); + boost::charconv::from_chars(sv, ui_val, base); unsigned long ul_val; boost::charconv::from_chars(c_data, c_data + size, ul_val, base); + boost::charconv::from_chars(sv, ul_val, base); unsigned long long ull_val; boost::charconv::from_chars(c_data, c_data + size, ull_val, base); + boost::charconv::from_chars(sv, ull_val, base); } } catch(...) From 534e35e4eb1cef49df40427847367f3328a22cab Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 30 Jan 2024 12:13:12 +0100 Subject: [PATCH 04/15] Fix 128-bit integer in non-gnu mode --- include/boost/charconv/from_chars.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/charconv/from_chars.hpp b/include/boost/charconv/from_chars.hpp index dce1c86d..7e5db82c 100644 --- a/include/boost/charconv/from_chars.hpp +++ b/include/boost/charconv/from_chars.hpp @@ -125,11 +125,11 @@ BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_v #ifdef BOOST_CHARCONV_HAS_INT128 BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, boost::int128_type& value, int base = 10) noexcept { - return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); + return detail::from_chars128(sv.data(), sv.data() + sv.size(), value, base); } BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, boost::uint128_type& value, int base = 10) noexcept { - return detail::from_chars(sv.data(), sv.data() + sv.size(), value, base); + return detail::from_chars128(sv.data(), sv.data() + sv.size(), value, base); } #endif From 5bb2311a9bc7115019d263755e88b109d47c32e8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 30 Jan 2024 12:13:28 +0100 Subject: [PATCH 05/15] Add floating point overloads --- include/boost/charconv/from_chars.hpp | 48 +++++++++++ src/from_chars.cpp | 113 ++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/include/boost/charconv/from_chars.hpp b/include/boost/charconv/from_chars.hpp index 7e5db82c..459d4c8e 100644 --- a/include/boost/charconv/from_chars.hpp +++ b/include/boost/charconv/from_chars.hpp @@ -162,6 +162,31 @@ BOOST_CHARCONV_DECL from_chars_result from_chars_erange(const char* first, const BOOST_CHARCONV_DECL from_chars_result from_chars_erange(const char* first, const char* last, std::bfloat16_t& value, chars_format fmt = chars_format::general) noexcept; #endif +BOOST_CHARCONV_DECL from_chars_result from_chars_erange(boost::core::string_view sv, float& value, chars_format fmt = chars_format::general) noexcept; +BOOST_CHARCONV_DECL from_chars_result from_chars_erange(boost::core::string_view sv, double& value, chars_format fmt = chars_format::general) noexcept; +BOOST_CHARCONV_DECL from_chars_result from_chars_erange(boost::core::string_view sv, long double& value, chars_format fmt = chars_format::general) noexcept; + +#ifdef BOOST_CHARCONV_HAS_FLOAT128 +BOOST_CHARCONV_DECL from_chars_result from_chars_erange(boost::core::string_view sv, __float128& value, chars_format fmt = chars_format::general) noexcept; +#endif + +// types +#ifdef BOOST_CHARCONV_HAS_FLOAT16 +BOOST_CHARCONV_DECL from_chars_result from_chars_erange(boost::core::string_view sv, std::float16_t& value, chars_format fmt = chars_format::general) noexcept; +#endif +#ifdef BOOST_CHARCONV_HAS_FLOAT32 +BOOST_CHARCONV_DECL from_chars_result from_chars_erange(boost::core::string_view sv, std::float32_t& value, chars_format fmt = chars_format::general) noexcept; +#endif +#ifdef BOOST_CHARCONV_HAS_FLOAT64 +BOOST_CHARCONV_DECL from_chars_result from_chars_erange(boost::core::string_view sv, std::float64_t& value, chars_format fmt = chars_format::general) noexcept; +#endif +#if defined(BOOST_CHARCONV_HAS_STDFLOAT128) && defined(BOOST_CHARCONV_HAS_FLOAT128) +BOOST_CHARCONV_DECL from_chars_result from_chars_erange(boost::core::string_view sv, std::float128_t& value, chars_format fmt = chars_format::general) noexcept; +#endif +#ifdef BOOST_CHARCONV_HAS_BRAINFLOAT16 +BOOST_CHARCONV_DECL from_chars_result from_chars_erange(boost::core::string_view sv, std::bfloat16_t& value, chars_format fmt = chars_format::general) noexcept; +#endif + // The following adhere to the standard library definition with std::errc::result_out_of_range // Returns value unmodified // See: https://github.com/cppalliance/charconv/issues/110 @@ -189,6 +214,29 @@ BOOST_CHARCONV_DECL from_chars_result from_chars(const char* first, const char* BOOST_CHARCONV_DECL from_chars_result from_chars(const char* first, const char* last, std::bfloat16_t& value, chars_format fmt = chars_format::general) noexcept; #endif +BOOST_CHARCONV_DECL from_chars_result from_chars(boost::core::string_view sv, float& value, chars_format fmt = chars_format::general) noexcept; +BOOST_CHARCONV_DECL from_chars_result from_chars(boost::core::string_view sv, double& value, chars_format fmt = chars_format::general) noexcept; +BOOST_CHARCONV_DECL from_chars_result from_chars(boost::core::string_view sv, long double& value, chars_format fmt = chars_format::general) noexcept; + +#ifdef BOOST_CHARCONV_HAS_FLOAT128 +BOOST_CHARCONV_DECL from_chars_result from_chars(boost::core::string_view sv, __float128& value, chars_format fmt = chars_format::general) noexcept; +#endif +#ifdef BOOST_CHARCONV_HAS_FLOAT16 +BOOST_CHARCONV_DECL from_chars_result from_chars(boost::core::string_view sv, std::float16_t& value, chars_format fmt = chars_format::general) noexcept; +#endif +#ifdef BOOST_CHARCONV_HAS_FLOAT32 +BOOST_CHARCONV_DECL from_chars_result from_chars(boost::core::string_view sv, std::float32_t& value, chars_format fmt = chars_format::general) noexcept; +#endif +#ifdef BOOST_CHARCONV_HAS_FLOAT64 +BOOST_CHARCONV_DECL from_chars_result from_chars(boost::core::string_view sv, std::float64_t& value, chars_format fmt = chars_format::general) noexcept; +#endif +#if defined(BOOST_CHARCONV_HAS_STDFLOAT128) && defined(BOOST_CHARCONV_HAS_FLOAT128) +BOOST_CHARCONV_DECL from_chars_result from_chars(boost::core::string_view sv, std::float128_t& value, chars_format fmt = chars_format::general) noexcept; +#endif +#ifdef BOOST_CHARCONV_HAS_BRAINFLOAT16 +BOOST_CHARCONV_DECL from_chars_result from_chars(boost::core::string_view sv, std::bfloat16_t& value, chars_format fmt = chars_format::general) noexcept; +#endif + } // namespace charconv } // namespace boost diff --git a/src/from_chars.cpp b/src/from_chars.cpp index 02dbbf1a..9dc083cc 100644 --- a/src/from_chars.cpp +++ b/src/from_chars.cpp @@ -287,6 +287,62 @@ boost::charconv::from_chars_result boost::charconv::from_chars_erange(const char #endif // long double implementations +// String view overloads + +boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, float& value, boost::charconv::chars_format fmt) noexcept +{ + return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); +} + +boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, double & value, boost::charconv::chars_format fmt) noexcept +{ + return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); +} + +boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, long double& value, boost::charconv::chars_format fmt) noexcept +{ + return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); +} + +#ifdef BOOST_CHARCONV_HAS_FLOAT128 +boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, __float128& value, boost::charconv::chars_format fmt) noexcept +{ + return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif + +// types +#ifdef BOOST_CHARCONV_HAS_FLOAT16 +boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::float16_t& value, boost::charconv::chars_format fmt) noexcept +{ + return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif +#ifdef BOOST_CHARCONV_HAS_FLOAT32 +boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::float32_t& value, boost::charconv::chars_format fmt) noexcept +{ + return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif +#ifdef BOOST_CHARCONV_HAS_FLOAT64 +boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::float64_t& value, boost::charconv::chars_format fmt) noexcept +{ + return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif +#if defined(BOOST_CHARCONV_HAS_STDFLOAT128) && defined(BOOST_CHARCONV_HAS_FLOAT128) +boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::float128_t& value, boost::charconv::chars_format fmt) noexcept +{ + return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif +#ifdef BOOST_CHARCONV_HAS_BRAINFLOAT16 +boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::bfloat16_t& value, boost::charconv::chars_format fmt) noexcept +{ + return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif + namespace { // Adheres to the STL strictly as opposed to fixing the ERANGE problem (which pre-review was the library default behavior) @@ -362,3 +418,60 @@ boost::charconv::from_chars_result boost::charconv::from_chars(const char* first return from_chars_strict_impl(first, last, value, fmt); } #endif + +boost::charconv::from_chars_result boost::charconv::from_chars(boost::core::string_view sv, float& value, boost::charconv::chars_format fmt) noexcept +{ + return from_chars_strict_impl(sv.data(), sv.data() + sv.size(), value, fmt); +} + +boost::charconv::from_chars_result boost::charconv::from_chars(boost::core::string_view sv, double& value, boost::charconv::chars_format fmt) noexcept +{ + return from_chars_strict_impl(sv.data(), sv.data() + sv.size(), value, fmt); +} + +boost::charconv::from_chars_result boost::charconv::from_chars(boost::core::string_view sv, long double& value, boost::charconv::chars_format fmt) noexcept +{ + return from_chars_strict_impl(sv.data(), sv.data() + sv.size(), value, fmt); +} + +#ifdef BOOST_CHARCONV_HAS_FLOAT128 +boost::charconv::from_chars_result boost::charconv::from_chars(boost::core::string_view sv, __float128& value, boost::charconv::chars_format fmt) noexcept +{ + return from_chars_strict_impl(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif + +#ifdef BOOST_CHARCONV_HAS_FLOAT16 +boost::charconv::from_chars_result boost::charconv::from_chars(boost::core::string_view sv, std::float16_t& value, boost::charconv::chars_format fmt) noexcept +{ + return from_chars_strict_impl(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif + +#ifdef BOOST_CHARCONV_HAS_FLOAT32 +boost::charconv::from_chars_result boost::charconv::from_chars(boost::core::string_view sv, std::float32_t& value, boost::charconv::chars_format fmt) noexcept +{ + return from_chars_strict_impl(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif + +#ifdef BOOST_CHARCONV_HAS_FLOAT64 +boost::charconv::from_chars_result boost::charconv::from_chars(boost::core::string_view sv, std::float64_t& value, boost::charconv::chars_format fmt) noexcept +{ + return from_chars_strict_impl(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif + +#if defined(BOOST_CHARCONV_HAS_STDFLOAT128) && defined(BOOST_CHARCONV_HAS_FLOAT128) +boost::charconv::from_chars_result boost::charconv::from_chars(boost::core::string_view sv, std::float128_t& value, boost::charconv::chars_format fmt) noexcept +{ + return from_chars_strict_impl(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif + +#ifdef BOOST_CHARCONV_HAS_BRAINFLOAT16 +boost::charconv::from_chars_result boost::charconv::from_chars(boost::core::string_view sv, std::bfloat16_t& value, boost::charconv::chars_format fmt) noexcept +{ + return from_chars_strict_impl(sv.data(), sv.data() + sv.size(), value, fmt); +} +#endif From c79d8bbbbc376a75432150adaf8b9bacdc7f8671 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 30 Jan 2024 12:13:39 +0100 Subject: [PATCH 06/15] Add floating point testing --- test/from_chars_string_view.cpp | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/from_chars_string_view.cpp b/test/from_chars_string_view.cpp index 4262c57f..72a16f89 100644 --- a/test/from_chars_string_view.cpp +++ b/test/from_chars_string_view.cpp @@ -35,6 +35,29 @@ void test_int() } } +template +void test_float() +{ + std::uniform_real_distribution dist(T(-1e3), T(1e3)); + + for (std::size_t i = 0; i < N; ++i) + { + const auto value = dist(rng); + std::string str_value = std::to_string(value); + StringViewType sv = str_value; + + T v = 0; + auto r = boost::charconv::from_chars(sv, v); + BOOST_TEST(r); + + T v2 = 0; + auto r2 = boost::charconv::from_chars(str_value.data(), str_value.data() + str_value.size(), v2); + BOOST_TEST(r2); + + BOOST_TEST_EQ(v, v2); + } +} + int main() { test_int(); @@ -63,5 +86,31 @@ int main() #endif + test_float(); + test_float(); + test_float(); + + #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + + test_float(); + test_float(); + test_float(); + + #endif + + #ifdef BOOST_CHARCONV_HAS_FLOAT32 + + test_float(); + test_float(); + + #endif + + #ifdef BOOST_CHARCONV_HAS_FLOAT64 + + test_float(); + test_float(); + + #endif + return boost::report_errors(); } From ccdcb5690268f5f9b96468a5432c5fe4e8d49890 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 30 Jan 2024 12:20:28 +0100 Subject: [PATCH 07/15] Add fuzzing to floating point interface --- fuzzing/fuzz_from_chars_float.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fuzzing/fuzz_from_chars_float.cpp b/fuzzing/fuzz_from_chars_float.cpp index 0827c6ef..5fab9466 100644 --- a/fuzzing/fuzz_from_chars_float.cpp +++ b/fuzzing/fuzz_from_chars_float.cpp @@ -3,6 +3,7 @@ // https://www.boost.org/LICENSE_1_0.txt #include +#include #include #include @@ -15,16 +16,21 @@ extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, std::size_t size const auto formats = {boost::charconv::chars_format::general, boost::charconv::chars_format::fixed, boost::charconv::chars_format::scientific, boost::charconv::chars_format::hex}; + boost::core::string_view sv {c_data, size}; + for (const auto format : formats) { float f_val; boost::charconv::from_chars(c_data, c_data + size, f_val, format); + boost::charconv::from_chars(sv, f_val, format); double val; boost::charconv::from_chars(c_data, c_data + size, val, format); + boost::charconv::from_chars(sv, val, format); long double ld_val; boost::charconv::from_chars(c_data, c_data + size, ld_val, format); + boost::charconv::from_chars(sv, ld_val, format); } } catch(...) From 746a990ed9bf4c7e1e53e50737c3edf2c5b6267a Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 30 Jan 2024 12:40:52 +0100 Subject: [PATCH 08/15] Add test for std::string --- test/from_chars_string_view.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/from_chars_string_view.cpp b/test/from_chars_string_view.cpp index 72a16f89..63f3f1e4 100644 --- a/test/from_chars_string_view.cpp +++ b/test/from_chars_string_view.cpp @@ -71,6 +71,17 @@ int main() test_int(); test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + test_int(); + #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) test_int(); @@ -90,6 +101,10 @@ int main() test_float(); test_float(); + test_float(); + test_float(); + test_float(); + #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) test_float(); @@ -101,6 +116,7 @@ int main() #ifdef BOOST_CHARCONV_HAS_FLOAT32 test_float(); + test_float(); test_float(); #endif @@ -108,6 +124,7 @@ int main() #ifdef BOOST_CHARCONV_HAS_FLOAT64 test_float(); + test_float(); test_float(); #endif From cf01eea30b8d2118d4e91b93e605098c5cf450fb Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 30 Jan 2024 12:41:09 +0100 Subject: [PATCH 09/15] Add new interface and examples to docs --- doc/charconv/from_chars.adoc | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/charconv/from_chars.adoc b/doc/charconv/from_chars.adoc index 6c4dc047..2bc82208 100644 --- a/doc/charconv/from_chars.adoc +++ b/doc/charconv/from_chars.adoc @@ -32,21 +32,32 @@ struct from_chars_result template BOOST_CXX14_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, Integral& value, int base = 10) noexcept; +template +BOOST_CXX14_CONSTEXPR from_chars_result from_chars(boost::core::string_view sv, Integral& value, int base = 10) noexcept; + BOOST_CXX14_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, bool& value, int base) = delete; template from_chars_result from_chars(const char* first, const char* last, Real& value, chars_format fmt = chars_format::general) noexcept; +template +from_chars_result from_chars(boost::core::string_view sv, Real& value, chars_format fmt = chars_format::general) noexcept; + // See note below Usage notes for from_chars for floating point types template from_chars_result from_chars_erange(const char* first, const char* last, Real& value, chars_format fmt = chars_format::general) noexcept; +template +from_chars_result from_chars_erange(boost::core::string_view sv, Real& value, chars_format fmt = chars_format::general) noexcept; + }} // Namespace boost::charconv ---- == from_chars parameters * `first`, `last` - pointers to a valid range to parse +* `sv` - string view of a valid range to parse. +Compatible with boost::core::string_view, std::string, and std::string_view * `value` - where the output is stored upon successful parsing * `base` (integer only) - the integer base to use. Must be between 2 and 36 inclusive * `fmt` (floating point only) - The format of the buffer. See <> for description. @@ -111,7 +122,15 @@ from_chars_result r = boost::charconv::from_chars(buffer, buffer + std::strlen(b assert(r.ec == std::errc()); assert(r); // Same as above but less verbose. Added in C++26. assert(v == 42); + +std::string str_buffer (buffer); +boost::core::string_view sv(str_buffer); +int v2; +auto r2 = boost::charconv::from_chars(sv, v2); +assert(r); +assert(v2 == v); ---- + ==== Floating Point [source, c++] ---- @@ -121,6 +140,12 @@ auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v); assert(r.ec == std::errc()); assert(r); // Same as above but less verbose. Added in C++26. assert(v == 1.2345); + +std::string str_buffer(buffer); +double v2; +auto r2 = boost::charconv::from_chars(buffer, v2); +assert(r2); +assert(v == v2); ---- === Hexadecimal From e1aec37e7857277562287ae9616190b906cd049f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 31 Jan 2024 08:51:46 +0100 Subject: [PATCH 10/15] Disable (un)signed char tests on MSVC --- test/from_chars_string_view.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/from_chars_string_view.cpp b/test/from_chars_string_view.cpp index 63f3f1e4..361d00b5 100644 --- a/test/from_chars_string_view.cpp +++ b/test/from_chars_string_view.cpp @@ -60,8 +60,13 @@ void test_float() int main() { + // MSVC does not allow (un)signed char in uniform_int_distribution + // GCC and clang do + #ifndef _MSC_VER test_int(); test_int(); + #endif + test_int(); test_int(); test_int(); @@ -71,8 +76,11 @@ int main() test_int(); test_int(); + #ifndef _MSC_VER test_int(); test_int(); + #endif + test_int(); test_int(); test_int(); @@ -84,8 +92,11 @@ int main() #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + #ifndef _MSC_VER test_int(); test_int(); + #endif + test_int(); test_int(); test_int(); From c157bea6f98c70f2c8ab6c8558842a449bac2793 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 31 Jan 2024 09:30:40 +0100 Subject: [PATCH 11/15] Fix ambiguous overload errors --- test/limits.cpp | 24 +++++++++++++++++++++--- test/roundtrip.cpp | 2 +- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/test/limits.cpp b/test/limits.cpp index 125a61b5..5847dd3a 100644 --- a/test/limits.cpp +++ b/test/limits.cpp @@ -81,7 +81,13 @@ template void test_integral( T value ) T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2 ); - BOOST_TEST(r2.ec == std::errc()) && BOOST_TEST_EQ( v2, value ); + if( BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST( v2 == value ) ) + { + } + else + { + std::cerr << "... test failure for value=" << value << "; buffer='" << std::string( buffer, r.ptr ) << "'" << std::endl; // LCOV_EXCL_LINE + } } // base 10 @@ -93,7 +99,13 @@ template void test_integral( T value ) T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2, 10 ); - BOOST_TEST(r2.ec == std::errc()) && BOOST_TEST_EQ( v2, value ); + if( BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST( v2 == value ) ) + { + } + else + { + std::cerr << "... test failure for value=" << value << "; buffer='" << std::string( buffer, r.ptr ) << "'" << std::endl; // LCOV_EXCL_LINE + } } // any base @@ -106,7 +118,13 @@ template void test_integral( T value ) T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2, base ); - BOOST_TEST(r2.ec == std::errc()) && BOOST_TEST_EQ( v2, value ); + if( BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST( v2 == value ) ) + { + } + else + { + std::cerr << "... test failure for value=" << value << "; buffer='" << std::string( buffer, r.ptr ) << "'" << std::endl; // LCOV_EXCL_LINE + } } } diff --git a/test/roundtrip.cpp b/test/roundtrip.cpp index 99cd19a0..4944a30f 100644 --- a/test/roundtrip.cpp +++ b/test/roundtrip.cpp @@ -93,7 +93,7 @@ template void test_roundtrip( T value, int base ) T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2, base ); - if( BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST_EQ( v2, value ) ) + if( BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST( v2 == value ) ) { } else From 71062ffc225d636248cca39fac91cdb37dd82f5b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 31 Jan 2024 09:40:33 +0100 Subject: [PATCH 12/15] Add debug statement --- test/from_chars_string_view.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/from_chars_string_view.cpp b/test/from_chars_string_view.cpp index 361d00b5..c56554f3 100644 --- a/test/from_chars_string_view.cpp +++ b/test/from_chars_string_view.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) # include @@ -48,13 +49,18 @@ void test_float() T v = 0; auto r = boost::charconv::from_chars(sv, v); - BOOST_TEST(r); T v2 = 0; auto r2 = boost::charconv::from_chars(str_value.data(), str_value.data() + str_value.size(), v2); - BOOST_TEST(r2); - BOOST_TEST_EQ(v, v2); + if( BOOST_TEST( r.ec == std::errc() ) && BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST( v2 == value ) ) + { + } + else + { + std::cerr << std::setprecision(std::numeric_limits::digits10) + << "... test failure for value=" << value << "; buffer='" << str_value << "'; errc=" << static_cast(r.ec) << " and " << static_cast(r2.ec) << std::endl; // LCOV_EXCL_LINE + } } } From 58e3c6580d2a4278ce074b8f4f27585fe7d45507 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 31 Jan 2024 09:45:56 +0100 Subject: [PATCH 13/15] Fix values being compared --- test/from_chars_string_view.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/from_chars_string_view.cpp b/test/from_chars_string_view.cpp index c56554f3..f1bcaf84 100644 --- a/test/from_chars_string_view.cpp +++ b/test/from_chars_string_view.cpp @@ -53,13 +53,13 @@ void test_float() T v2 = 0; auto r2 = boost::charconv::from_chars(str_value.data(), str_value.data() + str_value.size(), v2); - if( BOOST_TEST( r.ec == std::errc() ) && BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST( v2 == value ) ) + if( BOOST_TEST( r.ec == std::errc() ) && BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST( v == v2 ) ) { } else { std::cerr << std::setprecision(std::numeric_limits::digits10) - << "... test failure for value=" << value << "; buffer='" << str_value << "'; errc=" << static_cast(r.ec) << " and " << static_cast(r2.ec) << std::endl; // LCOV_EXCL_LINE + << "... test failure for value=" << value << "; buffer='" << str_value << "'; errc=" << static_cast(r.ec) << "," << static_cast(r2.ec) << std::endl; // LCOV_EXCL_LINE } } } From 0c5e345bcfe198aa29dfabe7c031b9227e592ad3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 31 Jan 2024 11:05:08 +0100 Subject: [PATCH 14/15] Fix potential exponent overflow in 32-bit case --- 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 57b63ac2..3e1a16ce 100644 --- a/include/boost/charconv/detail/parser.hpp +++ b/include/boost/charconv/detail/parser.hpp @@ -273,7 +273,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, } if (dot_position != 0 || fractional) { - exponent = static_cast(dot_position - i) + extra_zeros + leading_zero_powers; + exponent = static_cast(dot_position) - static_cast(i) + extra_zeros + leading_zero_powers; } else { From aa93e3aa6e54efd0796e03cbfa2e8c36bc79a35d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 31 Jan 2024 11:28:55 +0100 Subject: [PATCH 15/15] Fix floating-point definitions --- src/from_chars.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/from_chars.cpp b/src/from_chars.cpp index 9dc083cc..4fe88298 100644 --- a/src/from_chars.cpp +++ b/src/from_chars.cpp @@ -138,7 +138,7 @@ boost::charconv::from_chars_result boost::charconv::from_chars_erange(const char boost::charconv::from_chars_result boost::charconv::from_chars_erange(const char* first, const char* last, std::float16_t& value, boost::charconv::chars_format fmt) noexcept { float f; - const auto r = boost::charconv::from_chars(first, last, f, fmt); + const auto r = boost::charconv::from_chars_erange(first, last, f, fmt); if (r.ec == std::errc()) { value = static_cast(f); @@ -156,7 +156,7 @@ boost::charconv::from_chars_result boost::charconv::from_chars_erange(const char float f; std::memcpy(&f, &value, sizeof(float)); - const auto r = boost::charconv::from_chars(first, last, f, fmt); + const auto r = boost::charconv::from_chars_erange(first, last, f, fmt); std::memcpy(&value, &f, sizeof(std::float32_t)); return r; } @@ -171,7 +171,7 @@ boost::charconv::from_chars_result boost::charconv::from_chars_erange(const char double d; std::memcpy(&d, &value, sizeof(double)); - const auto r = boost::charconv::from_chars(first, last, d, fmt); + const auto r = boost::charconv::from_chars_erange(first, last, d, fmt); std::memcpy(&value, &d, sizeof(std::float64_t)); return r; } @@ -181,7 +181,7 @@ boost::charconv::from_chars_result boost::charconv::from_chars_erange(const char boost::charconv::from_chars_result boost::charconv::from_chars_erange(const char* first, const char* last, std::bfloat16_t& value, boost::charconv::chars_format fmt) noexcept { float f; - const auto r = boost::charconv::from_chars(first, last, f, fmt); + const auto r = boost::charconv::from_chars_erange(first, last, f, fmt); if (r.ec == std::errc()) { value = static_cast(f); @@ -278,7 +278,7 @@ boost::charconv::from_chars_result boost::charconv::from_chars_erange(const char __float128 q; std::memcpy(&q, &value, sizeof(__float128)); - const auto r = boost::charconv::from_chars(first, last, q, fmt); + const auto r = boost::charconv::from_chars_erange(first, last, q, fmt); std::memcpy(&value, &q, sizeof(std::float128_t)); return r; @@ -291,23 +291,23 @@ boost::charconv::from_chars_result boost::charconv::from_chars_erange(const char boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, float& value, boost::charconv::chars_format fmt) noexcept { - return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); + return boost::charconv::from_chars_erange(sv.data(), sv.data() + sv.size(), value, fmt); } boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, double & value, boost::charconv::chars_format fmt) noexcept { - return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); + return boost::charconv::from_chars_erange(sv.data(), sv.data() + sv.size(), value, fmt); } boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, long double& value, boost::charconv::chars_format fmt) noexcept { - return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); + return boost::charconv::from_chars_erange(sv.data(), sv.data() + sv.size(), value, fmt); } #ifdef BOOST_CHARCONV_HAS_FLOAT128 boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, __float128& value, boost::charconv::chars_format fmt) noexcept { - return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); + return boost::charconv::from_chars_erange(sv.data(), sv.data() + sv.size(), value, fmt); } #endif @@ -315,31 +315,31 @@ boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::cor #ifdef BOOST_CHARCONV_HAS_FLOAT16 boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::float16_t& value, boost::charconv::chars_format fmt) noexcept { - return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); + return boost::charconv::from_chars_erange(sv.data(), sv.data() + sv.size(), value, fmt); } #endif #ifdef BOOST_CHARCONV_HAS_FLOAT32 boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::float32_t& value, boost::charconv::chars_format fmt) noexcept { - return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); + return boost::charconv::from_chars_erange(sv.data(), sv.data() + sv.size(), value, fmt); } #endif #ifdef BOOST_CHARCONV_HAS_FLOAT64 boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::float64_t& value, boost::charconv::chars_format fmt) noexcept { - return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); + return boost::charconv::from_chars_erange(sv.data(), sv.data() + sv.size(), value, fmt); } #endif #if defined(BOOST_CHARCONV_HAS_STDFLOAT128) && defined(BOOST_CHARCONV_HAS_FLOAT128) boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::float128_t& value, boost::charconv::chars_format fmt) noexcept { - return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); + return boost::charconv::from_chars_erange(sv.data(), sv.data() + sv.size(), value, fmt); } #endif #ifdef BOOST_CHARCONV_HAS_BRAINFLOAT16 boost::charconv::from_chars_result boost::charconv::from_chars_erange(boost::core::string_view sv, std::bfloat16_t& value, boost::charconv::chars_format fmt) noexcept { - return boost::charconv::from_chars(sv.data(), sv.data() + sv.size(), value, fmt); + return boost::charconv::from_chars_erange(sv.data(), sv.data() + sv.size(), value, fmt); } #endif