// Distributed under the Boost Software License Version 1.0 https://www.boost.org/LICENSE_1_0.txt // Copyright Gero Peterhoff #ifndef BOOST_CHARCONV2_HPP #define BOOST_CHARCONV2_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(BOOST_NO_CXX23_HDR_STDFLOAT) #include #endif #if !defined(BOOST_NO_CXX17_HDR_CHARCONV) #define BOOST_CHARCONV2_CXX17 #include #endif #if (BOOST_VERSION >= 108500L) #define BOOST_CHARCONV2_CXX11 #include #endif #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) #include #endif #if defined(BOOST_CHARCONV2_CXX17) || defined(BOOST_CHARCONV2_CXX11) // std::charconv or boost::charconv is available #else static_assert(false, "boost::charconv2 requires std::charconv or boost::charconv"); #endif namespace std { // coming soon https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113260 #if defined(BOOST_HAS_FLOAT128) && defined(BOOST_CHARCONV2_CXX17) constexpr from_chars_result from_chars(const char*const first, const char*const last, boost::float128_type& value, const chars_format format = chars_format::general) noexcept; constexpr to_chars_result to_chars(char*const first, char*const last, const boost::float128_type value, const chars_format format, const int precision) noexcept; constexpr to_chars_result to_chars(char*const first, char*const last, const boost::float128_type value, const chars_format format) noexcept; #endif // defined(BOOST_HAS_FLOAT128) && defined(BOOST_CHARCONV2_CXX17) } // std namespace boost { namespace charconv2 { #if defined(BOOST_CHARCONV2_CXX17) typedef std::from_chars_result from_chars_result; typedef std::to_chars_result to_chars_result; typedef std::chars_format chars_format; #else typedef boost::charconv::from_chars_result from_chars_result; typedef boost::charconv::to_chars_result to_chars_result; typedef boost::charconv::chars_format chars_format; #endif enum class binary_style : typename std::underlying_type::type { standard = 0, showpoint = std::ios_base::showpoint }; enum class nonfinite_style : unsigned { standard = 0, explicite_nan = 1 }; namespace detail { BOOST_STATIC_CONSTEXPR boost::core::string_view inf{"inf"}, inf_neg{"-inf"}, nan{"nan"}, nan_neg{"-nan"}, qnan{"qnan"}, qnan_neg{"-qnan"}, snan{"snan"}, snan_neg{"-snan"}; template class bitarray { protected: using limits = boost::math::math_limits; using elem_type = uint8_t; using array_type = std::array; BOOST_STATIC_CONSTEXPR size_t padding = boost::is_endian_little::value ? (sizeof(Type) - (limits::digits2() / CHAR_BIT)) : 0, separator_integer = 8; array_type elems{}; inline constexpr size_t index_elem(const size_t idx) const noexcept { return boost::is_endian_little::value ? ((elems.size() - 1) - (padding + (idx / CHAR_BIT))) : (idx / CHAR_BIT); } inline constexpr size_t index_bit(const size_t idx) const noexcept { return boost::is_endian_little::value ? ((CHAR_BIT - 1) - (idx % CHAR_BIT)) : (idx % CHAR_BIT); } public: inline constexpr bitarray() noexcept = default; inline constexpr size_t size() const noexcept { return limits::digits2(); } inline constexpr bool get(const size_t idx) const noexcept { return (elems[index_elem(idx)] >> index_bit(idx)) & elem_type{1}; } inline constexpr void set(const size_t idx) noexcept { elems[index_elem(idx)] |= (elem_type{1} << index_bit(idx)); } inline constexpr size_t points(const binary_style style) const noexcept { return (style == binary_style::showpoint) ? ((std::is_floating_point::value) ? 2 : (limits::digits2()/separator_integer - 1)) : 0; } inline constexpr bool is_point(const size_t idx) const noexcept { BOOST_IF_CONSTEXPR (std::is_floating_point::value) { const int pos = (boost::is_endian_little::value ? ((limits::digits2() - 1) - idx) : idx) + 1; return (pos == limits::pos_sign()) || (pos == limits::pos_exponent()); } else { return (idx > 0) && ((idx % separator_integer) == 0); } } }; inline constexpr bool is_valid_range(const char*const first, const char*const last) noexcept { return BOOST_LIKELY(first!=nullptr && last!=nullptr) ? (first < last) : false; } template inline constexpr void set_string(Result& result, char*const first, const size_t size, const boost::core::string_view& view) noexcept { if (BOOST_LIKELY(view.size() <= size)) { std::copy(view.data(), view.data()+view.size(), first); result = {first + view.size(), std::errc{}}; } } template inline constexpr void set_value(Result& result, Type& value, const Type number, const boost::core::string_view& view) noexcept { result = {view.data()+view.size(), std::errc{}}; value = number; } template inline constexpr void from_chars_nonfinite(Result& result, const char*const first, const char*const last, Type& value, const nonfinite_style style) noexcept { static_assert(boost::is_floating_point::value, "invalid type"); using limits = std::numeric_limits; const boost::core::string_view view{first, size_t(last - first)}; // inf if (view.starts_with(inf)) set_value(result, value, limits::infinity(), inf); else if (view.starts_with(inf_neg)) set_value(result, value, -limits::infinity(), inf_neg); // nan else if (view.starts_with(nan)) set_value(result, value, limits::quiet_NaN(), nan); else if (view.starts_with(nan_neg)) set_value(result, value, -limits::quiet_NaN(), nan_neg); // qnan/snan else if (BOOST_UNLIKELY(style == nonfinite_style::explicite_nan)) { // qnan if (view.starts_with(qnan)) set_value(result, value, limits::quiet_NaN(), qnan); else if (view.starts_with(qnan_neg)) set_value(result, value, -limits::quiet_NaN(), qnan_neg); // snan else if (view.starts_with(snan)) set_value(result, value, limits::signaling_NaN(), snan); else if (view.starts_with(snan_neg)) set_value(result, value, -limits::signaling_NaN(), snan_neg); } } template inline constexpr void to_chars_nonfinite(Result& result, char*const first, char*const last, const Type value, const nonfinite_style style) noexcept { static_assert(boost::is_floating_point::value, "invalid type"); const size_t size = size_t(last - first); const bool is_negativ = std::signbit(value); if (std::isinf(value)) { // inf set_string(result, first, size, is_negativ ? inf_neg : inf); } else { // nan if (BOOST_LIKELY(style == nonfinite_style::standard)) { set_string(result, first, size, is_negativ ? nan_neg : nan); } else { if (std::issignaling(value)) { set_string(result, first, size, is_negativ ? snan_neg : snan); } else { set_string(result, first, size, is_negativ ? qnan_neg : qnan); } } } } #if defined(BOOST_CHARCONV2_CXX17) && defined(BOOST_CHARCONV2_CXX11) inline constexpr boost::charconv::chars_format to_boost_format(const std::chars_format format) noexcept { return (format == std::chars_format::scientific) ? boost::charconv::chars_format::scientific : (format == std::chars_format::fixed) ? boost::charconv::chars_format::fixed : (format == std::chars_format::hex) ? boost::charconv::chars_format::hex : boost::charconv::chars_format::general; } #endif // defined(BOOST_CHARCONV2_CXX17) && defined(BOOST_CHARCONV2_CXX11) } // detail // integer template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const char*const first, const char*const last, Type& value, const int base = 10) noexcept { from_chars_result result{first, std::errc::invalid_argument}; if (BOOST_LIKELY(detail::is_valid_range(first, last))) { #if defined(BOOST_CHARCONV2_CXX17) result = std::from_chars(first, last, value, base); #else result = boost::charconv::from_chars(first, last, value, base); #endif } return result; } template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const std::string& view, Type& value, const int base = 10) noexcept { return from_chars(view.data(), view.data()+view.size(), value, base); } #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const std::string_view& view, Type& value, const int base = 10) noexcept { return from_chars(view.data(), view.size(), value, base); } #endif template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const boost::core::string_view& view, Type& value, const int base = 10) noexcept { return from_chars(view.data(), view.size(), value, base); } template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const boost::string_view& view, Type& value, const int base = 10) noexcept { return from_chars(view.data(), view.size(), value, base); } template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const char*const view, Type& value, const int base = 10) noexcept { return BOOST_LIKELY(view != nullptr) ? from_chars(boost::core::string_view{view}, value, base) : from_chars_result{view, std::errc::invalid_argument}; } template constexpr typename std::enable_if::value, to_chars_result>::type to_chars(char*const first, char*const last, const Type value, const int base = 10) noexcept { to_chars_result result{last, std::errc::value_too_large}; if (BOOST_LIKELY(detail::is_valid_range(first, last))) { #if defined(BOOST_CHARCONV2_CXX17) result = std::to_chars(first, last, value, base); #else result = boost::charconv::to_chars(first, last, value, base); #endif } return result; } // float template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const char*const first, const char*const last, Type& value, const chars_format format = chars_format::general, const nonfinite_style style = nonfinite_style::standard) noexcept { from_chars_result result{first, std::errc::invalid_argument}; if (BOOST_LIKELY(detail::is_valid_range(first, last))) { detail::from_chars_nonfinite(result, first, last, value, style); if (BOOST_LIKELY(result.ec != std::errc{})) { // no non-finite found #if defined(BOOST_CHARCONV2_CXX17) result = std::from_chars(first, last, value, format); #else result = boost::charconv::from_chars(first, last, value, format); #endif } } return result; } template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const std::string& view, Type& value, const chars_format format = chars_format::general, const nonfinite_style style = nonfinite_style::standard) noexcept { return from_chars(view.data(), view.size(), value, format, style); } #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const std::string_view& view, Type& value, const chars_format format = chars_format::general, const nonfinite_style style = nonfinite_style::standard) noexcept { return from_chars(view.data(), view.size(), value, format, style); } #endif template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const boost::core::string_view& view, Type& value, const chars_format format = chars_format::general, const nonfinite_style style = nonfinite_style::standard) noexcept { return from_chars(view.data(), view.size(), value, format, style); } template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const boost::string_view& view, Type& value, const chars_format format = chars_format::general, const nonfinite_style style = nonfinite_style::standard) noexcept { return from_chars(view.data(), view.size(), value, format, style); } template constexpr typename std::enable_if::value, from_chars_result>::type from_chars(const char*const view, Type& value, const chars_format format = chars_format::general, const nonfinite_style style = nonfinite_style::standard) noexcept { return BOOST_LIKELY(view != nullptr) ? from_chars(boost::core::string_view{view}, value, format, style) : from_chars_result{view, std::errc::invalid_argument}; } template constexpr typename std::enable_if::value, to_chars_result>::type to_chars(char*const first, char*const last, const Type value, const chars_format format, const int precision, const nonfinite_style style = nonfinite_style::standard) noexcept { to_chars_result result{last, std::errc::value_too_large}; if (BOOST_LIKELY(detail::is_valid_range(first, last))) { if (BOOST_LIKELY(std::isfinite(value))) { // finite #if defined(BOOST_CHARCONV2_CXX17) result = std::to_chars(first, last, value, format, precision); #else result = boost::charconv::to_chars(first, last, value, format, precision); #endif } else { // non-finite inf/nan detail::to_chars_nonfinite(result, first, last, value, style); } } return result; } template constexpr typename std::enable_if::value, to_chars_result>::type to_chars(char*const first, char*const last, const Type value, const chars_format format, const nonfinite_style style = nonfinite_style::standard) noexcept { to_chars_result result{last, std::errc::value_too_large}; if (BOOST_LIKELY(detail::is_valid_range(first, last))) { if (BOOST_LIKELY(std::isfinite(value))) { // finite #if defined(BOOST_CHARCONV2_CXX17) result = std::to_chars(first, last, value, format); #else result = boost::charconv::to_chars(first, last, value, format); #endif } else { // non-finite inf/nan detail::to_chars_nonfinite(result, first, last, value, style); } } return result; } // binary template constexpr typename std::enable_if::value, from_chars_result>::type from_chars_binary(const char*const first, const char*const last, Type& value, const binary_style style = binary_style::standard) noexcept { static_assert ( (boost::is_endian_little::value || boost::is_endian_big::value) && (CHAR_BIT == 8), "from_chars_binary is not supported on this platform" ); using array_type = detail::bitarray; from_chars_result result{first, std::errc::invalid_argument}; if (BOOST_LIKELY(detail::is_valid_range(first, last))) { array_type array{}; if (BOOST_LIKELY(size_t(last - first) >= (array.size() + array.points(style)))) { for (size_t idx=0, pnt=0; idx(array); result = {first + array.size(), std::errc{}}; } } } return result; } template constexpr typename std::enable_if::value, from_chars_result>::type from_chars_binary(const std::string& view, Type& value, const binary_style style = binary_style::standard) noexcept { return from_chars_binary(view.data(), view.data()+view.size(), value, style); } #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) template constexpr typename std::enable_if::value, from_chars_result>::type from_chars_binary(const std::string_view& view, Type& value, const binary_style style = binary_style::standard) noexcept { return from_chars_binary(view.data(), view.data()+view.size(), value, style); } #endif template constexpr typename std::enable_if::value, from_chars_result>::type from_chars_binary(const boost::core::string_view& view, Type& value, const binary_style style = binary_style::standard) noexcept { return from_chars_binary(view.data(), view.data()+view.size(), value, style); } template constexpr typename std::enable_if::value, from_chars_result>::type from_chars_binary(const boost::string_view& view, Type& value, const binary_style style = binary_style::standard) noexcept { return from_chars_binary(view.data(), view.data()+view.size(), value, style); } template constexpr typename std::enable_if::value, from_chars_result>::type from_chars_binary(const char*const view, Type& value, const binary_style style = binary_style::standard) noexcept { return BOOST_LIKELY(view != nullptr) ? from_chars_binary(boost::core::string_view{view}, value, style) : from_chars_result{view, std::errc::invalid_argument}; } template constexpr typename std::enable_if::value, to_chars_result>::type to_chars_binary(char*const first, char*const last, const Type value, const binary_style style = binary_style::standard) noexcept { static_assert ( (boost::is_endian_little::value || boost::is_endian_big::value) && (CHAR_BIT == 8), "to_chars_binary is not supported on this platform" ); using array_type = detail::bitarray; to_chars_result result{last, std::errc::value_too_large}; if (BOOST_LIKELY(detail::is_valid_range(first, last))) { const array_type array = boost::core::bit_cast(value); const size_t size = array.size() + array.points(style); if (BOOST_LIKELY(size_t(last - first) >= size)) { for (size_t idx=0, pnt=0; idx(value), format); #else const boost::charconv::from_chars_result res = boost::charconv::from_chars(first, last, value, boost::charconv2::detail::to_boost_format(format)); result = {res.ptr, res.ec}; #endif } } return result; } constexpr to_chars_result to_chars(char*const first, char*const last, const boost::float128_type value, const chars_format format, const int precision) noexcept { to_chars_result result{last, std::errc::value_too_large}; if (BOOST_LIKELY(boost::charconv2::detail::is_valid_range(first, last))) { #if defined(__STDCPP_FLOAT128_T__) result = to_chars(first, last, float128_t(value), format, precision); #else boost::charconv::to_chars_result res{result.ptr, result.ec}; if (BOOST_LIKELY(isfinite(value))) { // finite res = boost::charconv::to_chars(first, last, value, boost::charconv2::detail::to_boost_format(format), precision); } else { // non-finite inf/nan boost::charconv2::detail::to_chars_nonfinite(res, first, last, value, boost::charconv2::nonfinite_style::standard); } result = {res.ptr, res.ec}; #endif } return result; } constexpr to_chars_result to_chars(char*const first, char*const last, const boost::float128_type value, const chars_format format) noexcept { to_chars_result result{last, std::errc::value_too_large}; if (BOOST_LIKELY(boost::charconv2::detail::is_valid_range(first, last))) { #if defined(__STDCPP_FLOAT128_T__) result = to_chars(first, last, float128_t(value), format); #else boost::charconv::to_chars_result res{result.ptr, result.ec}; if (BOOST_LIKELY(isfinite(value))) { // finite res = boost::charconv::to_chars(first, last, value, boost::charconv2::detail::to_boost_format(format)); } else { // non-finite inf/nan boost::charconv2::detail::to_chars_nonfinite(res, first, last, value, boost::charconv2::nonfinite_style::standard); } result = {res.ptr, res.ec}; #endif } return result; } #endif // defined(BOOST_HAS_FLOAT128) && defined(BOOST_CHARCONV2_CXX17) } // std #undef BOOST_CHARCONV2_CXX17 #undef BOOST_CHARCONV2_CXX11 #endif // BOOST_CHARCONV2_HPP