LCOV - code coverage report
Current view: top level - json/detail/charconv/detail/fast_float - parse_number.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 42.9 % 63 27
Test Date: 2026-02-13 18:42:28 Functions: 66.7 % 3 2

            Line data    Source code
       1              : // Copyright 2020-2023 Daniel Lemire
       2              : // Copyright 2023 Matt Borland
       3              : // Distributed under the Boost Software License, Version 1.0.
       4              : // https://www.boost.org/LICENSE_1_0.txt
       5              : //
       6              : // Derivative of: https://github.com/fastfloat/fast_float
       7              : 
       8              : #ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_PARSE_NUMBER_HPP
       9              : #define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_PARSE_NUMBER_HPP
      10              : 
      11              : #include <boost/json/detail/charconv/detail/fast_float/ascii_number.hpp>
      12              : #include <boost/json/detail/charconv/detail/fast_float/decimal_to_binary.hpp>
      13              : #include <boost/json/detail/charconv/detail/fast_float/digit_comparison.hpp>
      14              : #include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
      15              : 
      16              : #include <cmath>
      17              : #include <cstring>
      18              : #include <limits>
      19              : #include <system_error>
      20              : 
      21              : namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
      22              : 
      23              : 
      24              : namespace detail {
      25              : /**
      26              :  * Special case +inf, -inf, nan, infinity, -infinity.
      27              :  * The case comparisons could be made much faster given that we know that the
      28              :  * strings a null-free and fixed.
      29              :  **/
      30              : 
      31              : #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
      32              : # pragma GCC diagnostic push
      33              : # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
      34              : #endif
      35              : 
      36              : template <typename T, typename UC>
      37              : from_chars_result_t<UC> BOOST_JSON_CXX14_CONSTEXPR
      38            0 : parse_infnan(UC const * first, UC const * last, T &value)  noexcept  {
      39            0 :   from_chars_result_t<UC> answer{};
      40            0 :   answer.ptr = first;
      41            0 :   answer.ec = std::errc(); // be optimistic
      42            0 :   bool minusSign = false;
      43            0 :   if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
      44            0 :       minusSign = true;
      45            0 :       ++first;
      46              :   }
      47            0 :   if (last - first >= 3) {
      48            0 :     if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
      49            0 :       answer.ptr = (first += 3);
      50            0 :       value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
      51              :       // 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).
      52            0 :       if(first != last && *first == UC('(')) {
      53            0 :         for(UC const * ptr = first + 1; ptr != last; ++ptr) {
      54            0 :           if (*ptr == UC(')')) {
      55            0 :             answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
      56            0 :             break;
      57              :           }
      58            0 :           else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
      59            0 :             break; // forbidden char, not nan(n-char-seq-opt)
      60              :         }
      61              :       }
      62            0 :       return answer;
      63              :     }
      64            0 :     if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
      65            0 :       if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
      66            0 :         answer.ptr = first + 8;
      67              :       } else {
      68            0 :         answer.ptr = first + 3;
      69              :       }
      70            0 :       value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
      71            0 :       return answer;
      72              :     }
      73              :   }
      74            0 :   answer.ec = std::errc::invalid_argument;
      75            0 :   return answer;
      76              : }
      77              : 
      78              : #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
      79              : # pragma GCC diagnostic pop
      80              : #endif
      81              : 
      82              : /**
      83              :  * Returns true if the floating-pointing rounding mode is to 'nearest'.
      84              :  * It is the default on most system. This function is meant to be inexpensive.
      85              :  * Credit : @mwalcott3
      86              :  */
      87              : BOOST_FORCEINLINE bool rounds_to_nearest() noexcept {
      88              :   // https://lemire.me/blog/2020/06/26/gcc-not-nearest/
      89              : #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
      90              :   return false;
      91              : #endif
      92              :   // See
      93              :   // A fast function to check your floating-point rounding mode
      94              :   // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
      95              :   //
      96              :   // This function is meant to be equivalent to :
      97              :   // prior: #include <cfenv>
      98              :   //  return fegetround() == FE_TONEAREST;
      99              :   // However, it is expected to be much faster than the fegetround()
     100              :   // function call.
     101              :   //
     102              :   // The volatile keywoard prevents the compiler from computing the function
     103              :   // at compile-time.
     104              :   // There might be other ways to prevent compile-time optimizations (e.g., asm).
     105              :   // The value does not need to be std::numeric_limits<float>::min(), any small
     106              :   // value so that 1 + x should round to 1 would do (after accounting for excess
     107              :   // precision, as in 387 instructions).
     108              :   static volatile float fmin = (std::numeric_limits<float>::min)();
     109         5895 :   float fmini = fmin; // we copy it so that it gets loaded at most once.
     110              :   //
     111              :   // Explanation:
     112              :   // Only when fegetround() == FE_TONEAREST do we have that
     113              :   // fmin + 1.0f == 1.0f - fmin.
     114              :   //
     115              :   // FE_UPWARD:
     116              :   //  fmin + 1.0f > 1
     117              :   //  1.0f - fmin == 1
     118              :   //
     119              :   // FE_DOWNWARD or  FE_TOWARDZERO:
     120              :   //  fmin + 1.0f == 1
     121              :   //  1.0f - fmin < 1
     122              :   //
     123              :   // Note: This may fail to be accurate if fast-math has been
     124              :   // enabled, as rounding conventions may not apply.
     125              :   #ifdef BOOST_JSON_FASTFLOAT_VISUAL_STUDIO
     126              :   #   pragma warning(push)
     127              :   //  todo: is there a VS warning?
     128              :   //  see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
     129              :   #elif defined(__clang__)
     130              :   #   pragma clang diagnostic push
     131              :   #   pragma clang diagnostic ignored "-Wfloat-equal"
     132              :   #elif defined(__GNUC__)
     133              :   #   pragma GCC diagnostic push
     134              :   #   pragma GCC diagnostic ignored "-Wfloat-equal"
     135              :   #endif
     136         5895 :   return (fmini + 1.0f == 1.0f - fmini);
     137              :   #ifdef BOOST_JSON_FASTFLOAT_VISUAL_STUDIO
     138              :   #   pragma warning(pop)
     139              :   #elif defined(__clang__)
     140              :   #   pragma clang diagnostic pop
     141              :   #elif defined(__GNUC__)
     142              :   #   pragma GCC diagnostic pop
     143              :   #endif
     144              : }
     145              : 
     146              : } // namespace detail
     147              : 
     148              : template<typename T, typename UC>
     149              : BOOST_JSON_FASTFLOAT_CONSTEXPR20
     150      1009310 : from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
     151              :                              T &value, chars_format fmt /*= chars_format::general*/)  noexcept  {
     152      1009310 :   return from_chars_advanced(first, last, value, parse_options_t<UC>{fmt});
     153              : }
     154              : 
     155              : template<typename T, typename UC>
     156              : BOOST_JSON_FASTFLOAT_CONSTEXPR20
     157      1009310 : from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
     158              :                                       T &value, parse_options_t<UC> options)  noexcept  {
     159              : 
     160              :   static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
     161              :   static_assert (std::is_same<UC, char>::value ||
     162              :                  std::is_same<UC, wchar_t>::value ||
     163              :                  std::is_same<UC, char16_t>::value ||
     164              :                  std::is_same<UC, char32_t>::value , "only char, wchar_t, char16_t and char32_t are supported");
     165              : 
     166              :   from_chars_result_t<UC> answer;
     167      1009310 :   if (first == last) {
     168            0 :     answer.ec = std::errc::invalid_argument;
     169            0 :     answer.ptr = first;
     170            0 :     return answer;
     171              :   }
     172      1009310 :   parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
     173      1009310 :   if (!pns.valid) {
     174            0 :     return detail::parse_infnan(first, last, value);
     175              :   }
     176      1009310 :   answer.ec = std::errc(); // be optimistic
     177      1009310 :   answer.ptr = pns.lastmatch;
     178              :   // The implementation of the Clinger's fast path is convoluted because
     179              :   // we want round-to-nearest in all cases, irrespective of the rounding mode
     180              :   // selected on the thread.
     181              :   // We proceed optimistically, assuming that detail::rounds_to_nearest() returns
     182              :   // true.
     183      1009310 :   if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
     184              :     // Unfortunately, the conventional Clinger's fast path is only possible
     185              :     // when the system rounds to the nearest float.
     186              :     //
     187              :     // We expect the next branch to almost always be selected.
     188              :     // We could check it first (before the previous branch), but
     189              :     // there might be performance advantages at having the check
     190              :     // be last.
     191        11790 :     if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest())  {
     192              :       // We have that fegetround() == FE_TONEAREST.
     193              :       // Next is Clinger's fast path.
     194         5895 :       if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
     195         5570 :         value = T(pns.mantissa);
     196         5570 :         if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
     197         1756 :         else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
     198         5570 :         if (pns.negative) { value = -value; }
     199         5570 :         return answer;
     200              :       }
     201              :     } else {
     202              :       // We do not have that fegetround() == FE_TONEAREST.
     203              :       // Next is a modified Clinger's fast path, inspired by Jakub JelĂ­nek's proposal
     204            0 :       if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
     205              : #if defined(__clang__)
     206              :         // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
     207              :         if(pns.mantissa == 0) {
     208              :           value = pns.negative ? -0. : 0.;
     209              :           return answer;
     210              :         }
     211              : #endif
     212            0 :         value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
     213            0 :         if (pns.negative) { value = -value; }
     214            0 :         return answer;
     215              :       }
     216              :     }
     217              :   }
     218      1003740 :   adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
     219      1003740 :   if(pns.too_many_digits && am.power2 >= 0) {
     220      2001424 :     if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
     221         5972 :       am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
     222              :     }
     223              :   }
     224              :   // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
     225              :   // then we need to go the long way around again. This is very uncommon.
     226      1003740 :   if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
     227      1003740 :   to_float(pns.negative, am, value);
     228              :   // Test for over/underflow.
     229      1003740 :   if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format<T>::infinite_power()) {
     230        30608 :     answer.ec = std::errc::result_out_of_range;
     231              :   }
     232      1003740 :   return answer;
     233              : }
     234              : 
     235              : }}}}}} // namespace fast_float
     236              : 
     237              : #endif
        

Generated by: LCOV version 2.3