LCOV - code coverage report
Current view: top level - third_party/fast_float - ascii_number.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 118 146 80.8 %
Date: 2024-11-21 22:18:42 Functions: 0 1 0.0 %

          Line data    Source code
       1             : #ifndef FASTFLOAT_ASCII_NUMBER_H
       2             : #define FASTFLOAT_ASCII_NUMBER_H
       3             : 
       4             : #include <cctype>
       5             : #include <cstdint>
       6             : #include <cstring>
       7             : #include <iterator>
       8             : #include <limits>
       9             : #include <type_traits>
      10             : 
      11             : #include "float_common.h"
      12             : 
      13             : #ifdef FASTFLOAT_SSE2
      14             : #include <emmintrin.h>
      15             : #endif
      16             : 
      17             : #ifdef FASTFLOAT_NEON
      18             : #include <arm_neon.h>
      19             : #endif
      20             : 
      21             : namespace fast_float {
      22             : 
      23             : template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
      24             : #ifdef FASTFLOAT_HAS_SIMD
      25           0 :   return std::is_same<UC, char16_t>::value;
      26             : #else
      27             :   return false;
      28             : #endif
      29             : }
      30             : 
      31             : // Next function can be micro-optimized, but compilers are entirely
      32             : // able to optimize it well.
      33             : template <typename UC>
      34             : fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
      35   105824000 :   return !(c > UC('9') || c < UC('0'));
      36             : }
      37             : 
      38             : fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
      39             :   return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
      40             :          (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
      41             :          (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
      42             :          (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
      43             : }
      44             : 
      45             : // Read 8 UC into a u64. Truncates UC if not char.
      46             : template <typename UC>
      47             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
      48             : read8_to_u64(const UC *chars) {
      49     1543680 :   if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
      50           0 :     uint64_t val = 0;
      51           0 :     for (int i = 0; i < 8; ++i) {
      52           0 :       val |= uint64_t(uint8_t(*chars)) << (i * 8);
      53           0 :       ++chars;
      54             :     }
      55           0 :     return val;
      56             :   }
      57             :   uint64_t val;
      58     1543680 :   ::memcpy(&val, chars, sizeof(uint64_t));
      59             : #if FASTFLOAT_IS_BIG_ENDIAN == 1
      60             :   // Need to read as-if the number was in little-endian order.
      61             :   val = byteswap(val);
      62             : #endif
      63     1543680 :   return val;
      64             : }
      65             : 
      66             : #ifdef FASTFLOAT_SSE2
      67             : 
      68             : fastfloat_really_inline uint64_t simd_read8_to_u64(const __m128i data) {
      69             :   FASTFLOAT_SIMD_DISABLE_WARNINGS
      70             :   const __m128i packed = _mm_packus_epi16(data, data);
      71             : #ifdef FASTFLOAT_64BIT
      72             :   return uint64_t(_mm_cvtsi128_si64(packed));
      73             : #else
      74             :   uint64_t value;
      75             :   // Visual Studio + older versions of GCC don't support _mm_storeu_si64
      76             :   _mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed);
      77             :   return value;
      78             : #endif
      79             :   FASTFLOAT_SIMD_RESTORE_WARNINGS
      80             : }
      81             : 
      82             : fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
      83             :   FASTFLOAT_SIMD_DISABLE_WARNINGS
      84             :   return simd_read8_to_u64(
      85             :       _mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)));
      86             :   FASTFLOAT_SIMD_RESTORE_WARNINGS
      87             : }
      88             : 
      89             : #elif defined(FASTFLOAT_NEON)
      90             : 
      91             : fastfloat_really_inline uint64_t simd_read8_to_u64(const uint16x8_t data) {
      92             :   FASTFLOAT_SIMD_DISABLE_WARNINGS
      93             :   uint8x8_t utf8_packed = vmovn_u16(data);
      94             :   return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
      95             :   FASTFLOAT_SIMD_RESTORE_WARNINGS
      96             : }
      97             : 
      98             : fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
      99             :   FASTFLOAT_SIMD_DISABLE_WARNINGS
     100             :   return simd_read8_to_u64(
     101             :       vld1q_u16(reinterpret_cast<const uint16_t *>(chars)));
     102             :   FASTFLOAT_SIMD_RESTORE_WARNINGS
     103             : }
     104             : 
     105             : #endif // FASTFLOAT_SSE2
     106             : 
     107             : // MSVC SFINAE is broken pre-VS2017
     108             : #if defined(_MSC_VER) && _MSC_VER <= 1900
     109             : template <typename UC>
     110             : #else
     111             : template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
     112             : #endif
     113             : // dummy for compile
     114           0 : uint64_t simd_read8_to_u64(UC const *) {
     115           0 :   return 0;
     116             : }
     117             : 
     118             : // credit  @aqrit
     119             : fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
     120             : parse_eight_digits_unrolled(uint64_t val) {
     121      723914 :   const uint64_t mask = 0x000000FF000000FF;
     122      723914 :   const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
     123      723914 :   const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
     124      723914 :   val -= 0x3030303030303030;
     125      723914 :   val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
     126      723914 :   val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
     127           0 :   return uint32_t(val);
     128             : }
     129             : 
     130             : // Call this if chars are definitely 8 digits.
     131             : template <typename UC>
     132             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
     133             : parse_eight_digits_unrolled(UC const *chars) noexcept {
     134           0 :   if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
     135           0 :     return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
     136             :   }
     137           0 :   return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
     138             : }
     139             : 
     140             : // credit @aqrit
     141             : fastfloat_really_inline constexpr bool
     142             : is_made_of_eight_digits_fast(uint64_t val) noexcept {
     143      819768 :   return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
     144      819768 :             0x8080808080808080));
     145             : }
     146             : 
     147             : #ifdef FASTFLOAT_HAS_SIMD
     148             : 
     149             : // Call this if chars might not be 8 digits.
     150             : // Using this style (instead of is_made_of_eight_digits_fast() then
     151             : // parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice.
     152             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
     153             : simd_parse_if_eight_digits_unrolled(const char16_t *chars,
     154             :                                     uint64_t &i) noexcept {
     155             :   if (cpp20_and_in_constexpr()) {
     156             :     return false;
     157             :   }
     158             : #ifdef FASTFLOAT_SSE2
     159             :   FASTFLOAT_SIMD_DISABLE_WARNINGS
     160             :   const __m128i data =
     161             :       _mm_loadu_si128(reinterpret_cast<const __m128i *>(chars));
     162             : 
     163             :   // (x - '0') <= 9
     164             :   // http://0x80.pl/articles/simd-parsing-int-sequences.html
     165             :   const __m128i t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
     166             :   const __m128i t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
     167             : 
     168             :   if (_mm_movemask_epi8(t1) == 0) {
     169             :     i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
     170             :     return true;
     171             :   } else
     172             :     return false;
     173             :   FASTFLOAT_SIMD_RESTORE_WARNINGS
     174             : #elif defined(FASTFLOAT_NEON)
     175             :   FASTFLOAT_SIMD_DISABLE_WARNINGS
     176             :   const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t *>(chars));
     177             : 
     178             :   // (x - '0') <= 9
     179             :   // http://0x80.pl/articles/simd-parsing-int-sequences.html
     180             :   const uint16x8_t t0 = vsubq_u16(data, vmovq_n_u16('0'));
     181             :   const uint16x8_t mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
     182             : 
     183             :   if (vminvq_u16(mask) == 0xFFFF) {
     184             :     i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
     185             :     return true;
     186             :   } else
     187             :     return false;
     188             :   FASTFLOAT_SIMD_RESTORE_WARNINGS
     189             : #else
     190             :   (void)chars;
     191             :   (void)i;
     192             :   return false;
     193             : #endif // FASTFLOAT_SSE2
     194             : }
     195             : 
     196             : #endif // FASTFLOAT_HAS_SIMD
     197             : 
     198             : // MSVC SFINAE is broken pre-VS2017
     199             : #if defined(_MSC_VER) && _MSC_VER <= 1900
     200             : template <typename UC>
     201             : #else
     202             : template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
     203             : #endif
     204             : // dummy for compile
     205             : bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
     206             :   return 0;
     207             : }
     208             : 
     209             : template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
     210             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
     211             : loop_parse_if_eight_digits(const UC *&p, const UC *const pend, uint64_t &i) {
     212             :   if (!has_simd_opt<UC>()) {
     213             :     return;
     214             :   }
     215             :   while ((std::distance(p, pend) >= 8) &&
     216             :          simd_parse_if_eight_digits_unrolled(
     217             :              p, i)) { // in rare cases, this will overflow, but that's ok
     218             :     p += 8;
     219             :   }
     220             : }
     221             : 
     222             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
     223             : loop_parse_if_eight_digits(const char *&p, const char *const pend,
     224             :                            uint64_t &i) {
     225             :   // optimizes better than parse_if_eight_digits_unrolled() for UC = char.
     226    15403700 :   while ((std::distance(p, pend) >= 8) &&
     227     2459300 :          is_made_of_eight_digits_fast(read8_to_u64(p))) {
     228     1447830 :     i = i * 100000000 +
     229     1447830 :         parse_eight_digits_unrolled(read8_to_u64(
     230             :             p)); // in rare cases, this will overflow, but that's ok
     231      723914 :     p += 8;
     232             :   }
     233    13860000 : }
     234             : 
     235             : enum class parse_error {
     236             :   no_error,
     237             :   // [JSON-only] The minus sign must be followed by an integer.
     238             :   missing_integer_after_sign,
     239             :   // A sign must be followed by an integer or dot.
     240             :   missing_integer_or_dot_after_sign,
     241             :   // [JSON-only] The integer part must not have leading zeros.
     242             :   leading_zeros_in_integer_part,
     243             :   // [JSON-only] The integer part must have at least one digit.
     244             :   no_digits_in_integer_part,
     245             :   // [JSON-only] If there is a decimal point, there must be digits in the
     246             :   // fractional part.
     247             :   no_digits_in_fractional_part,
     248             :   // The mantissa must have at least one digit.
     249             :   no_digits_in_mantissa,
     250             :   // Scientific notation requires an exponential part.
     251             :   missing_exponential_part,
     252             : };
     253             : 
     254             : template <typename UC> struct parsed_number_string_t {
     255             :   int64_t exponent{0};
     256             :   uint64_t mantissa{0};
     257             :   UC const *lastmatch{nullptr};
     258             :   bool negative{false};
     259             :   bool valid{false};
     260             :   bool too_many_digits{false};
     261             :   // contains the range of the significant digits
     262             :   span<const UC> integer{};  // non-nullable
     263             :   span<const UC> fraction{}; // nullable
     264             :   parse_error error{parse_error::no_error};
     265             : };
     266             : 
     267             : using byte_span = span<const char>;
     268             : using parsed_number_string = parsed_number_string_t<char>;
     269             : 
     270             : template <typename UC>
     271             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
     272             : report_parse_error(UC const *p, parse_error error) {
     273         662 :   parsed_number_string_t<UC> answer;
     274         662 :   answer.valid = false;
     275         662 :   answer.lastmatch = p;
     276         662 :   answer.error = error;
     277         662 :   return answer;
     278             : }
     279             : 
     280             : // Assuming that you use no more than 19 digits, this will
     281             : // parse an ASCII string.
     282             : template <typename UC>
     283             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
     284             : parse_number_string(UC const *p, UC const *pend,
     285             :                     parse_options_t<UC> options) noexcept {
     286    15234800 :   chars_format const fmt = options.format;
     287    15234800 :   UC const decimal_point = options.decimal_point;
     288             : 
     289    15234800 :   parsed_number_string_t<UC> answer;
     290    15234800 :   answer.valid = false;
     291    15234800 :   answer.too_many_digits = false;
     292    15234800 :   answer.negative = (*p == UC('-'));
     293             : #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
     294             :   if ((*p == UC('-')) || (!(fmt & FASTFLOAT_JSONFMT) && *p == UC('+'))) {
     295             : #else
     296    15234800 :   if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
     297             : #endif
     298     6542820 :     ++p;
     299     6542820 :     if (p == pend) {
     300         241 :       return report_parse_error<UC>(
     301         241 :           p, parse_error::missing_integer_or_dot_after_sign);
     302             :     }
     303     6542580 :     if (fmt & FASTFLOAT_JSONFMT) {
     304           0 :       if (!is_integer(*p)) { // a sign must be followed by an integer
     305           0 :         return report_parse_error<UC>(p,
     306           0 :                                       parse_error::missing_integer_after_sign);
     307             :       }
     308             :     } else {
     309    13085200 :       if (!is_integer(*p) &&
     310          13 :           (*p !=
     311             :            decimal_point)) { // a sign must be followed by an integer or the dot
     312           5 :         return report_parse_error<UC>(
     313           5 :             p, parse_error::missing_integer_or_dot_after_sign);
     314             :       }
     315             :     }
     316             :   }
     317    15234600 :   UC const *const start_digits = p;
     318             : 
     319    15234600 :   uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
     320             : 
     321   120403000 :   while ((p != pend) && is_integer(*p)) {
     322             :     // a multiplication by 10 is cheaper than an arbitrary integer
     323             :     // multiplication
     324    45653700 :     i = 10 * i +
     325    45653700 :         uint64_t(*p -
     326             :                  UC('0')); // might overflow, we will handle the overflow later
     327    45653700 :     ++p;
     328             :   }
     329    15234600 :   UC const *const end_of_integer_part = p;
     330    15234600 :   int64_t digit_count = int64_t(end_of_integer_part - start_digits);
     331    15234600 :   answer.integer = span<const UC>(start_digits, size_t(digit_count));
     332    15234600 :   if (fmt & FASTFLOAT_JSONFMT) {
     333             :     // at least 1 digit in integer part, without leading zeros
     334           0 :     if (digit_count == 0) {
     335           0 :       return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
     336             :     }
     337           0 :     if ((start_digits[0] == UC('0') && digit_count > 1)) {
     338             :       return report_parse_error<UC>(start_digits,
     339           0 :                                     parse_error::leading_zeros_in_integer_part);
     340             :     }
     341             :   }
     342             : 
     343    15234600 :   int64_t exponent = 0;
     344    15234600 :   const bool has_decimal_point = (p != pend) && (*p == decimal_point);
     345    15234600 :   if (has_decimal_point) {
     346    13860000 :     ++p;
     347    13860000 :     UC const *before = p;
     348             :     // can occur at most twice without overflowing, but let it occur more, since
     349             :     // for integers with many digits, digit parsing is the primary bottleneck.
     350             :     loop_parse_if_eight_digits(p, pend, i);
     351             : 
     352    92647400 :     while ((p != pend) && is_integer(*p)) {
     353    39339600 :       uint8_t digit = uint8_t(*p - UC('0'));
     354    39339600 :       ++p;
     355    39339600 :       i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
     356             :     }
     357    13860000 :     exponent = before - p;
     358    13860000 :     answer.fraction = span<const UC>(before, size_t(p - before));
     359    13860000 :     digit_count -= exponent;
     360             :   }
     361    15234600 :   if (fmt & FASTFLOAT_JSONFMT) {
     362             :     // at least 1 digit in fractional part
     363           0 :     if (has_decimal_point && exponent == 0) {
     364           0 :       return report_parse_error<UC>(p,
     365           0 :                                     parse_error::no_digits_in_fractional_part);
     366             :     }
     367    15234600 :   } else if (digit_count ==
     368             :              0) { // we must have encountered at least one integer!
     369         832 :     return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
     370             :   }
     371    15234200 :   int64_t exp_number = 0; // explicit exponential part
     372    15234200 :   if (((fmt & chars_format::scientific) && (p != pend) &&
     373      108853 :        ((UC('e') == *p) || (UC('E') == *p))) ||
     374    15126000 :       ((fmt & FASTFLOAT_FORTRANFMT) && (p != pend) &&
     375           0 :        ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
     376           0 :         (UC('D') == *p)))) {
     377      108206 :     UC const *location_of_e = p;
     378      108206 :     if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
     379           0 :         (UC('D') == *p)) {
     380      108206 :       ++p;
     381             :     }
     382      108206 :     bool neg_exp = false;
     383      108206 :     if ((p != pend) && (UC('-') == *p)) {
     384        8560 :       neg_exp = true;
     385        8560 :       ++p;
     386       99646 :     } else if ((p != pend) &&
     387       99646 :                (UC('+') ==
     388             :                 *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
     389       99443 :       ++p;
     390             :     }
     391      216409 :     if ((p == pend) || !is_integer(*p)) {
     392           4 :       if (!(fmt & chars_format::fixed)) {
     393             :         // The exponential part is invalid for scientific notation, so it must
     394             :         // be a trailing token for fixed notation. However, fixed notation is
     395             :         // disabled, so report a scientific notation error.
     396           0 :         return report_parse_error<UC>(p, parse_error::missing_exponential_part);
     397             :       }
     398             :       // Otherwise, we will be ignoring the 'e'.
     399           4 :       p = location_of_e;
     400             :     } else {
     401      529750 :       while ((p != pend) && is_integer(*p)) {
     402      210774 :         uint8_t digit = uint8_t(*p - UC('0'));
     403      210774 :         if (exp_number < 0x10000000) {
     404      210774 :           exp_number = 10 * exp_number + digit;
     405             :         }
     406      210774 :         ++p;
     407             :       }
     408      108202 :       if (neg_exp) {
     409        8560 :         exp_number = -exp_number;
     410             :       }
     411      108202 :       exponent += exp_number;
     412      108206 :     }
     413             :   } else {
     414             :     // If it scientific and not fixed, we have to bail out.
     415    15126000 :     if ((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) {
     416           0 :       return report_parse_error<UC>(p, parse_error::missing_exponential_part);
     417             :     }
     418             :   }
     419    15234200 :   answer.lastmatch = p;
     420    15234200 :   answer.valid = true;
     421             : 
     422             :   // If we frequently had to deal with long strings of digits,
     423             :   // we could extend our code by using a 128-bit integer instead
     424             :   // of a 64-bit integer. However, this is uncommon.
     425             :   //
     426             :   // We can deal with up to 19 digits.
     427    15234200 :   if (digit_count > 19) { // this is uncommon
     428             :     // It is possible that the integer had an overflow.
     429             :     // We have to handle the case where we have 0.0000somenumber.
     430             :     // We need to be mindful of the case where we only have zeroes...
     431             :     // E.g., 0.000000000...000.
     432       63011 :     UC const *start = start_digits;
     433       67904 :     while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
     434        4893 :       if (*start == UC('0')) {
     435        4651 :         digit_count--;
     436             :       }
     437        4893 :       start++;
     438             :     }
     439             : 
     440       63011 :     if (digit_count > 19) {
     441       62591 :       answer.too_many_digits = true;
     442             :       // Let us start again, this time, avoiding overflows.
     443             :       // We don't need to check if is_integer, since we use the
     444             :       // pre-tokenized spans from above.
     445       62591 :       i = 0;
     446       62591 :       p = answer.integer.ptr;
     447       62591 :       UC const *int_end = p + answer.integer.len();
     448       62591 :       const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
     449      471495 :       while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
     450      408904 :         i = i * 10 + uint64_t(*p - UC('0'));
     451      408904 :         ++p;
     452             :       }
     453       62591 :       if (i >= minimal_nineteen_digit_integer) { // We have a big integers
     454           0 :         exponent = end_of_integer_part - p + exp_number;
     455             :       } else { // We have a value with a fractional component.
     456       62591 :         p = answer.fraction.ptr;
     457       62591 :         UC const *frac_end = p + answer.fraction.len();
     458      842918 :         while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
     459      780327 :           i = i * 10 + uint64_t(*p - UC('0'));
     460      780327 :           ++p;
     461             :         }
     462       62591 :         exponent = answer.fraction.ptr - p + exp_number;
     463             :       }
     464             :       // We have now corrected both exponent and i, to a truncated value
     465             :     }
     466             :   }
     467    15234200 :   answer.exponent = exponent;
     468    15234200 :   answer.mantissa = i;
     469    15234200 :   return answer;
     470             : }
     471             : 
     472             : template <typename T, typename UC>
     473             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
     474             : parse_int_string(UC const *p, UC const *pend, T &value, int base) {
     475             :   from_chars_result_t<UC> answer;
     476             : 
     477             :   UC const *const first = p;
     478             : 
     479             :   bool negative = (*p == UC('-'));
     480             :   if (!std::is_signed<T>::value && negative) {
     481             :     answer.ec = std::errc::invalid_argument;
     482             :     answer.ptr = first;
     483             :     return answer;
     484             :   }
     485             : #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
     486             :   if ((*p == UC('-')) || (*p == UC('+'))) {
     487             : #else
     488             :   if (*p == UC('-')) {
     489             : #endif
     490             :     ++p;
     491             :   }
     492             : 
     493             :   UC const *const start_num = p;
     494             : 
     495             :   while (p != pend && *p == UC('0')) {
     496             :     ++p;
     497             :   }
     498             : 
     499             :   const bool has_leading_zeros = p > start_num;
     500             : 
     501             :   UC const *const start_digits = p;
     502             : 
     503             :   uint64_t i = 0;
     504             :   if (base == 10) {
     505             :     loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
     506             :   }
     507             :   while (p != pend) {
     508             :     uint8_t digit = ch_to_digit(*p);
     509             :     if (digit >= base) {
     510             :       break;
     511             :     }
     512             :     i = uint64_t(base) * i + digit; // might overflow, check this later
     513             :     p++;
     514             :   }
     515             : 
     516             :   size_t digit_count = size_t(p - start_digits);
     517             : 
     518             :   if (digit_count == 0) {
     519             :     if (has_leading_zeros) {
     520             :       value = 0;
     521             :       answer.ec = std::errc();
     522             :       answer.ptr = p;
     523             :     } else {
     524             :       answer.ec = std::errc::invalid_argument;
     525             :       answer.ptr = first;
     526             :     }
     527             :     return answer;
     528             :   }
     529             : 
     530             :   answer.ptr = p;
     531             : 
     532             :   // check u64 overflow
     533             :   size_t max_digits = max_digits_u64(base);
     534             :   if (digit_count > max_digits) {
     535             :     answer.ec = std::errc::result_out_of_range;
     536             :     return answer;
     537             :   }
     538             :   // this check can be eliminated for all other types, but they will all require
     539             :   // a max_digits(base) equivalent
     540             :   if (digit_count == max_digits && i < min_safe_u64(base)) {
     541             :     answer.ec = std::errc::result_out_of_range;
     542             :     return answer;
     543             :   }
     544             : 
     545             :   // check other types overflow
     546             :   if (!std::is_same<T, uint64_t>::value) {
     547             :     if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) {
     548             :       answer.ec = std::errc::result_out_of_range;
     549             :       return answer;
     550             :     }
     551             :   }
     552             : 
     553             :   if (negative) {
     554             : #ifdef FASTFLOAT_VISUAL_STUDIO
     555             : #pragma warning(push)
     556             : #pragma warning(disable : 4146)
     557             : #endif
     558             :     // this weird workaround is required because:
     559             :     // - converting unsigned to signed when its value is greater than signed max
     560             :     // is UB pre-C++23.
     561             :     // - reinterpret_casting (~i + 1) would work, but it is not constexpr
     562             :     // this is always optimized into a neg instruction (note: T is an integer
     563             :     // type)
     564             :     value = T(-std::numeric_limits<T>::max() -
     565             :               T(i - uint64_t(std::numeric_limits<T>::max())));
     566             : #ifdef FASTFLOAT_VISUAL_STUDIO
     567             : #pragma warning(pop)
     568             : #endif
     569             :   } else {
     570             :     value = T(i);
     571             :   }
     572             : 
     573             :   answer.ec = std::errc();
     574             :   return answer;
     575             : }
     576             : 
     577             : } // namespace fast_float
     578             : 
     579             : #endif

Generated by: LCOV version 1.14