From b0b1954c871b9abf0bbc4f5dce3be5389a3dd35d Mon Sep 17 00:00:00 2001 From: IRainman Date: Wed, 5 Mar 2025 23:21:24 +0300 Subject: [PATCH] * Added a config macro FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN and FASTFLOAT_DISALLOW_NAN. This both allow to significantly reduce code size and speedup your code when you use fast_float as a part of your parser. --- include/fast_float/ascii_number.h | 16 ++++++++- include/fast_float/digit_comparison.h | 6 +++- include/fast_float/float_common.h | 29 ++++++++++++++-- include/fast_float/parse_number.h | 50 ++++++++++++++++++++++----- 4 files changed, 89 insertions(+), 12 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 9001016..0133c1a 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -255,7 +255,9 @@ template struct parsed_number_string_t { int64_t exponent{0}; uint64_t mantissa{0}; UC const *lastmatch{nullptr}; +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN bool negative{false}; +#endif bool valid{false}; bool too_many_digits{false}; // contains the range of the significant digits @@ -290,6 +292,7 @@ parse_number_string(UC const *p, UC const *pend, answer.valid = false; answer.too_many_digits = false; // assume p < pend, so dereference without checks; +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN answer.negative = (*p == UC('-')); // C++17 20.19.3.(7.1) explicitly forbids '+' sign here if ((*p == UC('-')) || @@ -314,6 +317,7 @@ parse_number_string(UC const *p, UC const *pend, } } } +#endif UC const *const start_digits = p; uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) @@ -481,6 +485,7 @@ parse_int_string(UC const *p, UC const *pend, T &value, UC const *const first = p; +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN bool const negative = (*p == UC('-')); #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(push) @@ -498,6 +503,7 @@ parse_int_string(UC const *p, UC const *pend, T &value, (uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { ++p; } +#endif UC const *const start_num = p; @@ -553,12 +559,17 @@ parse_int_string(UC const *p, UC const *pend, T &value, // check other types overflow if (!std::is_same::value) { - if (i > uint64_t(std::numeric_limits::max()) + uint64_t(negative)) { + if (i > uint64_t(std::numeric_limits::max()) +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + + uint64_t(negative) +#endif + ) { answer.ec = std::errc::result_out_of_range; return answer; } } +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN if (negative) { #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(push) @@ -576,8 +587,11 @@ parse_int_string(UC const *p, UC const *pend, T &value, #pragma warning(pop) #endif } else { +#endif value = T(i); +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN } +#endif answer.ec = std::errc(); return answer; diff --git a/include/fast_float/digit_comparison.h b/include/fast_float/digit_comparison.h index d7ef3d9..00b5dba 100644 --- a/include/fast_float/digit_comparison.h +++ b/include/fast_float/digit_comparison.h @@ -381,7 +381,11 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp( round(am_b, [](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); }); T b; - to_float(false, am_b, b); + to_float( +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + false, +#endif + am_b, b); adjusted_mantissa theor = to_extended_halfway(b); bigint theor_digits(theor.mantissa); int32_t theor_exp = theor.power2; diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 2d2afca..fe69bf4 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -51,8 +51,10 @@ enum class chars_format : uint64_t { json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific, fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific, general = fixed | scientific, +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN allow_leading_plus = 1 << 7, skip_white_space = 1 << 8, +#endif }; template struct from_chars_result_t { @@ -606,6 +608,8 @@ template <> inline constexpr int binary_format::infinite_power() { return 0xFF; } +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + template <> inline constexpr int binary_format::sign_index() { return 63; } @@ -614,6 +618,8 @@ template <> inline constexpr int binary_format::sign_index() { return 31; } +#endif + template <> inline constexpr int binary_format::max_exponent_fast_path() { return 22; @@ -979,13 +985,19 @@ binary_format::hidden_bit_mask() { template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void -to_float(bool negative, adjusted_mantissa am, T &value) { +to_float( +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + bool negative, +#endif + adjusted_mantissa am, T &value) { using equiv_uint = equiv_uint_t; equiv_uint word = equiv_uint(am.mantissa); word = equiv_uint(word | equiv_uint(am.power2) << binary_format::mantissa_explicit_bits()); +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN word = equiv_uint(word | equiv_uint(negative) << binary_format::sign_index()); +#endif #if FASTFLOAT_HAS_BIT_CAST value = std::bit_cast(word); #else @@ -993,6 +1005,8 @@ to_float(bool negative, adjusted_mantissa am, T &value) { #endif } +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + template struct space_lut { static constexpr bool value[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1018,6 +1032,8 @@ template constexpr bool is_space(UC c) { return c < 256 && space_lut<>::value[uint8_t(c)]; } +#endif + template static constexpr uint64_t int_cmp_zeros() { static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size"); @@ -1032,6 +1048,8 @@ template static constexpr int int_cmp_len() { return sizeof(uint64_t) / sizeof(UC); } +#ifndef FASTFLOAT_DISALLOW_NAN + template constexpr UC const *str_const_nan(); template <> constexpr char const *str_const_nan() { return "nan"; } @@ -1052,6 +1070,8 @@ template <> constexpr char8_t const *str_const_nan() { } #endif +#endif + template constexpr UC const *str_const_inf(); template <> constexpr char const *str_const_inf() { return "infinity"; } @@ -1223,7 +1243,12 @@ operator^=(chars_format &lhs, chars_format rhs) noexcept { namespace detail { // adjust for deprecated feature macros -constexpr chars_format adjust_for_feature_macros(chars_format fmt) { +fastfloat_really_inline constexpr chars_format adjust_for_feature_macros(chars_format fmt) { +#if defined(FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN) && \ + (defined(FASTFLOAT_ALLOWS_LEADING_PLUS) || \ + defined(FASTFLOAT_SKIP_WHITE_SPACE)) +#error "FASTFLOAT_ALLOWS_LEADING_PLUS and FASTFLOAT_SKIP_WHITE_SPACE require FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN to be undefined" +#endif return fmt #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS | chars_format::allow_leading_plus diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 0dbb3a1..1c45bcc 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -22,11 +22,16 @@ namespace detail { template from_chars_result_t FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, - T &value, chars_format fmt) noexcept { + T &value +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + , chars_format fmt +#endif + ) noexcept { from_chars_result_t answer{}; answer.ptr = first; answer.ec = std::errc(); // be optimistic // assume first < last, so dereference without checks; +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN bool const minusSign = (*first == UC('-')); // C++17 20.19.3.(7.1) explicitly forbids '+' sign here if ((*first == UC('-')) || @@ -34,11 +39,16 @@ from_chars_result_t (*first == UC('+')))) { ++first; } +#endif if (last - first >= 3) { +#ifndef FASTFLOAT_DISALLOW_NAN if (fastfloat_strncasecmp(first, str_const_nan(), 3)) { answer.ptr = (first += 3); - value = minusSign ? -std::numeric_limits::quiet_NaN() - : std::numeric_limits::quiet_NaN(); + value = +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + minusSign ? -std::numeric_limits::quiet_NaN() : +#endif + std::numeric_limits::quiet_NaN(); // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, // C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). if (first != last && *first == UC('(')) { @@ -54,6 +64,7 @@ from_chars_result_t } return answer; } +#endif if (fastfloat_strncasecmp(first, str_const_inf(), 3)) { if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf() + 3, 5)) { @@ -61,8 +72,11 @@ from_chars_result_t } else { answer.ptr = first + 3; } - value = minusSign ? -std::numeric_limits::infinity() - : std::numeric_limits::infinity(); + value = +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + minusSign ? -std::numeric_limits::infinity() : +#endif + std::numeric_limits::infinity(); return answer; } } @@ -231,9 +245,11 @@ from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { } else { value = value * binary_format::exact_power_of_ten(pns.exponent); } +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN if (pns.negative) { value = -value; } +#endif return answer; } } else { @@ -246,15 +262,21 @@ from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { #if defined(__clang__) || defined(FASTFLOAT_32BIT) // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD if (pns.mantissa == 0) { - value = pns.negative ? T(-0.) : T(0.); + value = +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + pns.negative ? T(-0.) : +#endif + T(0.); return answer; } #endif value = T(pns.mantissa) * binary_format::exact_power_of_ten(pns.exponent); +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN if (pns.negative) { value = -value; } +#endif return answer; } } @@ -272,7 +294,11 @@ from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { if (am.power2 < 0) { am = digit_comp(pns, am); } - to_float(pns.negative, am, value); + to_float( +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + pns.negative, +#endif + am, value); // Test for over/underflow. if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format::infinite_power()) { @@ -294,11 +320,13 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value, chars_format const fmt = detail::adjust_for_feature_macros(options.format); from_chars_result_t answer; +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN if (uint64_t(fmt & chars_format::skip_white_space)) { while ((first != last) && fast_float::is_space(*first)) { first++; } } +#endif if (first == last) { answer.ec = std::errc::invalid_argument; answer.ptr = first; @@ -312,7 +340,11 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value, answer.ptr = first; return answer; } else { - return detail::parse_infnan(first, last, value, fmt); + return detail::parse_infnan(first, last, value +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN + , fmt +#endif + ); } } @@ -348,11 +380,13 @@ from_chars_int_advanced(UC const *first, UC const *last, T &value, int const base = options.base; from_chars_result_t answer; +#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN if (uint64_t(fmt & chars_format::skip_white_space)) { while ((first != last) && fast_float::is_space(*first)) { first++; } } +#endif if (first == last || base < 2 || base > 36) { answer.ec = std::errc::invalid_argument; answer.ptr = first;