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: 106 129 82.2 %
Date: 2024-05-14 23:54:21 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 <type_traits>
       9             : 
      10             : #include "float_common.h"
      11             : 
      12             : #ifdef FASTFLOAT_SSE2
      13             : #include <emmintrin.h>
      14             : #endif
      15             : 
      16             : #ifdef FASTFLOAT_NEON
      17             : #include <arm_neon.h>
      18             : #endif
      19             : 
      20             : namespace fast_float {
      21             : 
      22             : template <typename UC>
      23             : 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    41267700 :   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
      40             :     | (val & 0x00FF000000000000) >> 40
      41             :     | (val & 0x0000FF0000000000) >> 24
      42             :     | (val & 0x000000FF00000000) >> 8
      43             :     | (val & 0x00000000FF000000) << 8
      44             :     | (val & 0x0000000000FF0000) << 24
      45             :     | (val & 0x000000000000FF00) << 40
      46             :     | (val & 0x00000000000000FF) << 56;
      47             : }
      48             : 
      49             : // Read 8 UC into a u64. Truncates UC if not char.
      50             : template <typename UC>
      51             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20
      52             : uint64_t read8_to_u64(const UC *chars) {
      53     1128290 :   if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
      54           0 :     uint64_t val = 0;
      55           0 :     for(int i = 0; i < 8; ++i) {
      56           0 :       val |= uint64_t(uint8_t(*chars)) << (i*8);
      57           0 :       ++chars;
      58             :     }
      59           0 :     return val;
      60             :   }
      61             :   uint64_t val;
      62     1128290 :   ::memcpy(&val, chars, sizeof(uint64_t));
      63             : #if FASTFLOAT_IS_BIG_ENDIAN == 1
      64             :   // Need to read as-if the number was in little-endian order.
      65             :   val = byteswap(val);
      66             : #endif
      67     1128290 :   return val;
      68             : }
      69             : 
      70             : #ifdef FASTFLOAT_SSE2
      71             : 
      72             : fastfloat_really_inline
      73             : uint64_t simd_read8_to_u64(const __m128i data) {
      74             : FASTFLOAT_SIMD_DISABLE_WARNINGS
      75             :   const __m128i packed = _mm_packus_epi16(data, data);
      76             : #ifdef FASTFLOAT_64BIT
      77             :   return uint64_t(_mm_cvtsi128_si64(packed));
      78             : #else
      79             :   uint64_t value;
      80             :   // Visual Studio + older versions of GCC don't support _mm_storeu_si64
      81             :   _mm_storel_epi64(reinterpret_cast<__m128i*>(&value), packed);
      82             :   return value;
      83             : #endif
      84             : FASTFLOAT_SIMD_RESTORE_WARNINGS
      85             : }
      86             : 
      87             : fastfloat_really_inline
      88             : uint64_t simd_read8_to_u64(const char16_t* chars) {
      89             : FASTFLOAT_SIMD_DISABLE_WARNINGS
      90             :   return simd_read8_to_u64(_mm_loadu_si128(reinterpret_cast<const __m128i*>(chars)));
      91             : FASTFLOAT_SIMD_RESTORE_WARNINGS
      92             : }
      93             : 
      94             : #elif defined(FASTFLOAT_NEON)
      95             : 
      96             : 
      97             : fastfloat_really_inline
      98             : uint64_t simd_read8_to_u64(const uint16x8_t data) {
      99             : FASTFLOAT_SIMD_DISABLE_WARNINGS
     100             :   uint8x8_t utf8_packed = vmovn_u16(data);
     101             :   return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
     102             : FASTFLOAT_SIMD_RESTORE_WARNINGS
     103             : }
     104             : 
     105             : fastfloat_really_inline
     106             : uint64_t simd_read8_to_u64(const char16_t* chars) {
     107             : FASTFLOAT_SIMD_DISABLE_WARNINGS
     108             :   return simd_read8_to_u64(vld1q_u16(reinterpret_cast<const uint16_t*>(chars)));
     109             : FASTFLOAT_SIMD_RESTORE_WARNINGS
     110             : }
     111             : 
     112             : #endif // FASTFLOAT_SSE2
     113             : 
     114             : // MSVC SFINAE is broken pre-VS2017
     115             : #if defined(_MSC_VER) && _MSC_VER <= 1900
     116             : template <typename UC>
     117             : #else
     118             : template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>())>
     119             : #endif
     120             : // dummy for compile
     121           0 : uint64_t simd_read8_to_u64(UC const*) {
     122           0 :   return 0;
     123             : }
     124             : 
     125             : 
     126             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20
     127             : void write_u64(uint8_t *chars, uint64_t val) {
     128             :   if (cpp20_and_in_constexpr()) {
     129             :     for(int i = 0; i < 8; ++i) {
     130             :       *chars = uint8_t(val);
     131             :       val >>= 8;
     132             :       ++chars;
     133             :     }
     134             :     return;
     135             :   }
     136             : #if FASTFLOAT_IS_BIG_ENDIAN == 1
     137             :   // Need to read as-if the number was in little-endian order.
     138             :   val = byteswap(val);
     139             : #endif
     140             :   ::memcpy(chars, &val, sizeof(uint64_t));
     141             : }
     142             : 
     143             : // credit  @aqrit
     144             : fastfloat_really_inline FASTFLOAT_CONSTEXPR14
     145             : uint32_t parse_eight_digits_unrolled(uint64_t val) {
     146      549944 :   const uint64_t mask = 0x000000FF000000FF;
     147      549944 :   const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
     148      549944 :   const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
     149      549944 :   val -= 0x3030303030303030;
     150      549944 :   val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
     151      549944 :   val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
     152           0 :   return uint32_t(val);
     153             : }
     154             : 
     155             : 
     156             : // Call this if chars are definitely 8 digits.
     157             : template <typename UC>
     158             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20
     159             : uint32_t parse_eight_digits_unrolled(UC const * chars)  noexcept {
     160           0 :   if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
     161           0 :     return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
     162             :   }
     163           0 :   return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
     164             : }
     165             : 
     166             : 
     167             : // credit @aqrit
     168             : fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val)  noexcept {
     169      578350 :   return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
     170      578350 :      0x8080808080808080));
     171             : }
     172             : 
     173             : 
     174             : #ifdef FASTFLOAT_HAS_SIMD
     175             : 
     176             : // Call this if chars might not be 8 digits.
     177             : // Using this style (instead of is_made_of_eight_digits_fast() then parse_eight_digits_unrolled())
     178             : // ensures we don't load SIMD registers twice.
     179             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20
     180             : bool simd_parse_if_eight_digits_unrolled(const char16_t* chars, uint64_t& i) noexcept {
     181             :   if (cpp20_and_in_constexpr()) {
     182             :     return false;
     183             :   }   
     184             : #ifdef FASTFLOAT_SSE2
     185             : FASTFLOAT_SIMD_DISABLE_WARNINGS
     186             :   const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(chars));
     187             : 
     188             :   // (x - '0') <= 9
     189             :   // http://0x80.pl/articles/simd-parsing-int-sequences.html
     190             :   const __m128i t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
     191             :   const __m128i t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
     192             : 
     193             :   if (_mm_movemask_epi8(t1) == 0) {
     194             :     i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
     195             :     return true;
     196             :   }
     197             :   else return false;
     198             : FASTFLOAT_SIMD_RESTORE_WARNINGS
     199             : #elif defined(FASTFLOAT_NEON)
     200             : FASTFLOAT_SIMD_DISABLE_WARNINGS
     201             :   const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t*>(chars));
     202             :   
     203             :   // (x - '0') <= 9
     204             :   // http://0x80.pl/articles/simd-parsing-int-sequences.html
     205             :   const uint16x8_t t0 = vsubq_u16(data, vmovq_n_u16('0'));
     206             :   const uint16x8_t mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
     207             : 
     208             :   if (vminvq_u16(mask) == 0xFFFF) {
     209             :     i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
     210             :     return true;
     211             :   }
     212             :   else return false;
     213             : FASTFLOAT_SIMD_RESTORE_WARNINGS
     214             : #else
     215             :   (void)chars; (void)i;
     216             :   return false;
     217             : #endif // FASTFLOAT_SSE2
     218             : }
     219             : 
     220             : #endif // FASTFLOAT_HAS_SIMD
     221             : 
     222             : // MSVC SFINAE is broken pre-VS2017
     223             : #if defined(_MSC_VER) && _MSC_VER <= 1900
     224             : template <typename UC>
     225             : #else
     226             : template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>())>
     227             : #endif
     228             : // dummy for compile
     229             : bool simd_parse_if_eight_digits_unrolled(UC const*, uint64_t&) {
     230             :   return 0;
     231             : }
     232             : 
     233             : 
     234             : template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value)>
     235             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20
     236             : void loop_parse_if_eight_digits(const UC*& p, const UC* const pend, uint64_t& i) {
     237             :   if (!has_simd_opt<UC>()) {
     238             :     return;
     239             :   }
     240             :   while ((std::distance(p, pend) >= 8) && simd_parse_if_eight_digits_unrolled(p, i)) { // in rare cases, this will overflow, but that's ok
     241             :     p += 8;
     242             :   }
     243             : }
     244             : 
     245             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20
     246             : void loop_parse_if_eight_digits(const char*& p, const char* const pend, uint64_t& i) {
     247             :   // optimizes better than parse_if_eight_digits_unrolled() for UC = char.
     248     8021700 :   while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(read8_to_u64(p))) {
     249     1099890 :     i = i * 100000000 + parse_eight_digits_unrolled(read8_to_u64(p)); // in rare cases, this will overflow, but that's ok
     250      549944 :     p += 8;
     251             :   }
     252     6315050 : }
     253             : 
     254             : template <typename UC>
     255             : struct parsed_number_string_t {
     256             :   int64_t exponent{0};
     257             :   uint64_t mantissa{0};
     258             :   UC const * lastmatch{nullptr};
     259             :   bool negative{false};
     260             :   bool valid{false};
     261             :   bool too_many_digits{false};
     262             :   // contains the range of the significant digits
     263             :   span<const UC> integer{};  // non-nullable
     264             :   span<const UC> fraction{}; // nullable
     265             : };
     266             : 
     267             : using byte_span = span<const char>;
     268             : using parsed_number_string = parsed_number_string_t<char>;
     269             : 
     270             : // Assuming that you use no more than 19 digits, this will
     271             : // parse an ASCII string.
     272             : template <typename UC>
     273             : fastfloat_really_inline FASTFLOAT_CONSTEXPR20
     274             : parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, parse_options_t<UC> options) noexcept {
     275     7423270 :   chars_format const fmt = options.format;
     276     7423270 :   UC const decimal_point = options.decimal_point;
     277             : 
     278     7423270 :   parsed_number_string_t<UC> answer;
     279     7423270 :   answer.valid = false;
     280     7423270 :   answer.too_many_digits = false;
     281     7423270 :   answer.negative = (*p == UC('-'));
     282             : #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
     283             :   if ((*p == UC('-')) || (!(fmt & FASTFLOAT_JSONFMT) && *p == UC('+'))) {
     284             : #else
     285     7423270 :   if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
     286             : #endif
     287     2968940 :     ++p;
     288     2968940 :     if (p == pend) {
     289         232 :       return answer;
     290             :     }
     291     2968710 :     if (fmt & FASTFLOAT_JSONFMT) {
     292           0 :       if (!is_integer(*p)) { // a sign must be followed by an integer
     293           0 :         return answer;
     294             :       }    
     295             :     } else {
     296     5937430 :       if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
     297           0 :         return answer;
     298             :       }
     299             :     }
     300             :   }
     301     7423040 :   UC const * const start_digits = p;
     302             : 
     303     7423040 :   uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
     304             : 
     305    57612800 :   while ((p != pend) && is_integer(*p)) {
     306             :     // a multiplication by 10 is cheaper than an arbitrary integer
     307             :     // multiplication
     308    21936800 :     i = 10 * i +
     309    21936800 :         uint64_t(*p - UC('0')); // might overflow, we will handle the overflow later
     310    21936800 :     ++p;
     311             :   }
     312     7423040 :   UC const * const end_of_integer_part = p;
     313     7423040 :   int64_t digit_count = int64_t(end_of_integer_part - start_digits);
     314     7423040 :   answer.integer = span<const UC>(start_digits, size_t(digit_count));
     315     7423040 :   if (fmt & FASTFLOAT_JSONFMT) {
     316             :     // at least 1 digit in integer part, without leading zeros
     317           0 :     if (digit_count == 0 || (start_digits[0] == UC('0') && digit_count > 1)) {
     318           0 :       return answer;
     319             :     }
     320             :   }
     321             : 
     322     7423040 :   int64_t exponent = 0;
     323     7423040 :   const bool has_decimal_point = (p != pend) && (*p == decimal_point);
     324     7423040 :   if (has_decimal_point) {
     325     6315050 :     ++p;
     326     6315050 :     UC const * before = p;
     327             :     // can occur at most twice without overflowing, but let it occur more, since
     328             :     // for integers with many digits, digit parsing is the primary bottleneck.
     329             :     loop_parse_if_eight_digits(p, pend, i);
     330             : 
     331    26137800 :     while ((p != pend) && is_integer(*p)) {
     332     9891440 :       uint8_t digit = uint8_t(*p - UC('0'));
     333     9891440 :       ++p;
     334     9891440 :       i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
     335             :     }
     336     6315050 :     exponent = before - p;
     337     6315050 :     answer.fraction = span<const UC>(before, size_t(p - before));
     338     6315050 :     digit_count -= exponent;
     339             :   }
     340     7423040 :   if (fmt & FASTFLOAT_JSONFMT) {
     341             :     // at least 1 digit in fractional part
     342           0 :     if (has_decimal_point && exponent == 0) {
     343           0 :       return answer;
     344             :     }
     345             :   } 
     346     7423040 :   else if (digit_count == 0) { // we must have encountered at least one integer!
     347         387 :     return answer;
     348             :   }
     349     7422650 :   int64_t exp_number = 0;            // explicit exponential part
     350     7422650 :   if ( ((fmt & chars_format::scientific) &&
     351     7422650 :         (p != pend) &&
     352       40711 :         ((UC('e') == *p) || (UC('E') == *p)))
     353     7382550 :        ||
     354     7382550 :        ((fmt & FASTFLOAT_FORTRANFMT) &&
     355           0 :         (p != pend) &&
     356           0 :         ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || (UC('D') == *p)))) {
     357       40103 :     UC const * location_of_e = p;
     358       40103 :     if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || (UC('D') == *p)) {
     359       40103 :       ++p;
     360             :     }
     361       40103 :     bool neg_exp = false;
     362       40103 :     if ((p != pend) && (UC('-') == *p)) {
     363        8409 :       neg_exp = true;
     364        8409 :       ++p;
     365       31694 :     } else if ((p != pend) && (UC('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
     366       31492 :       ++p;
     367             :     }
     368       80203 :     if ((p == pend) || !is_integer(*p)) {
     369           3 :       if(!(fmt & chars_format::fixed)) {
     370             :         // We are in error.
     371           0 :         return answer;
     372             :       }
     373             :       // Otherwise, we will be ignoring the 'e'.
     374           3 :       p = location_of_e;
     375             :     } else {
     376      189168 :       while ((p != pend) && is_integer(*p)) {
     377       74534 :         uint8_t digit = uint8_t(*p - UC('0'));
     378       74534 :         if (exp_number < 0x10000000) {
     379       74534 :           exp_number = 10 * exp_number + digit;
     380             :         }
     381       74534 :         ++p;
     382             :       }
     383       40100 :       if(neg_exp) { exp_number = - exp_number; }
     384       40100 :       exponent += exp_number;
     385       40103 :     }
     386             :   } else {
     387             :     // If it scientific and not fixed, we have to bail out.
     388     7382550 :     if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
     389             :   }
     390     7422650 :   answer.lastmatch = p;
     391     7422650 :   answer.valid = true;
     392             : 
     393             :   // If we frequently had to deal with long strings of digits,
     394             :   // we could extend our code by using a 128-bit integer instead
     395             :   // of a 64-bit integer. However, this is uncommon.
     396             :   //
     397             :   // We can deal with up to 19 digits.
     398     7422650 :   if (digit_count > 19) { // this is uncommon
     399             :     // It is possible that the integer had an overflow.
     400             :     // We have to handle the case where we have 0.0000somenumber.
     401             :     // We need to be mindful of the case where we only have zeroes...
     402             :     // E.g., 0.000000000...000.
     403       63420 :     UC const * start = start_digits;
     404       70193 :     while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
     405        6773 :       if(*start == UC('0')) { digit_count --; }
     406        6773 :       start++;
     407             :     }
     408             : 
     409       63420 :     if (digit_count > 19) {
     410       62516 :       answer.too_many_digits = true;
     411             :       // Let us start again, this time, avoiding overflows.
     412             :       // We don't need to check if is_integer, since we use the
     413             :       // pre-tokenized spans from above.
     414       62516 :       i = 0;
     415       62516 :       p = answer.integer.ptr;
     416       62516 :       UC const* int_end = p + answer.integer.len();
     417       62516 :       const uint64_t minimal_nineteen_digit_integer{ 1000000000000000000 };
     418      471114 :       while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
     419      408598 :         i = i * 10 + uint64_t(*p - UC('0'));
     420      408598 :         ++p;
     421             :       }
     422       62516 :       if (i >= minimal_nineteen_digit_integer) { // We have a big integers
     423           0 :         exponent = end_of_integer_part - p + exp_number;
     424             :       }
     425             :       else { // We have a value with a fractional component.
     426       62516 :         p = answer.fraction.ptr;
     427       62516 :         UC const* frac_end = p + answer.fraction.len();
     428      841724 :         while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
     429      779208 :           i = i * 10 + uint64_t(*p - UC('0'));
     430      779208 :           ++p;
     431             :         }
     432       62516 :         exponent = answer.fraction.ptr - p + exp_number;
     433             :       }
     434             :       // We have now corrected both exponent and i, to a truncated value
     435             :     }
     436             :   }
     437     7422650 :   answer.exponent = exponent;
     438     7422650 :   answer.mantissa = i;
     439     7422650 :   return answer;
     440             : }
     441             : 
     442             : } // namespace fast_float
     443             : 
     444             : #endif

Generated by: LCOV version 1.14