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