diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 9fbe9ac..c8a051f 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -9,7 +9,7 @@ #include "float_common.h" -#if FASTFLOAT_SSE2 +#ifdef FASTFLOAT_SSE2 #include #endif @@ -20,7 +20,7 @@ namespace fast_float { // able to optimize it well. template fastfloat_really_inline constexpr bool is_integer(CharT c) noexcept { - return c >= static_cast('0') && c <= static_cast('9'); + return c >= CharT('0') && c <= CharT('9'); } fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { @@ -42,9 +42,10 @@ uint64_t fast_read_u64(const char* chars) { } // https://quick-bench.com/q/fk6Y07KDGu8XZ9iUtQD8QJTc3Hg +// todo: add support for char32_t fastfloat_really_inline uint64_t fast_read_u64(const char16_t* chars) { -#if FASTFLOAT_SSE2 +#ifdef FASTFLOAT_SSE2 FASTFLOAT_SIMD_DISABLE_WARNINGS static const char16_t masks[] = {0xff, 0xff, 0xff, 0xff}; const __m128i m_masks = _mm_loadu_si128(reinterpret_cast(masks)); @@ -65,6 +66,7 @@ FASTFLOAT_SIMD_RESTORE_WARNINGS for (int i = 0; i < 8; ++i) bytes[i] = (unsigned char)chars[i]; + // bit-cast uint64_t val; ::memcpy(&val, bytes, sizeof(uint64_t)); return val; @@ -77,7 +79,7 @@ uint64_t read_u64(const CharT *chars) { if (cpp20_and_in_constexpr()) { uint64_t val = 0; for(int i = 0; i < 8; ++i) { - val |= uint64_t(*chars) << (i*8); + val |= uint64_t(char(*chars)) << (i*8); ++chars; } return val; @@ -121,7 +123,7 @@ uint32_t parse_eight_digits_unrolled(uint64_t val) { } // http://0x80.pl/articles/simd-parsing-int-sequences.html -#if FASTFLOAT_SSE2 +#ifdef FASTFLOAT_SSE2 fastfloat_really_inline uint32_t parse_eight_digits_unrolled_c16(const __m128i val) { // x - '0' @@ -152,13 +154,15 @@ uint32_t parse_eight_digits_unrolled(const char* chars) noexcept { return parse_eight_digits_unrolled(read_u64(chars)); } +// Call this if you know chars are only digits +//todo: add support for char32_t fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t parse_eight_digits_unrolled(const char16_t* chars) noexcept { if (cpp20_and_in_constexpr() || !has_simd()) { return parse_eight_digits_unrolled(read_u64(chars)); } -#if !FASTFLOAT_HAS_SIMD - return 0; // never reaches here, satisfy compiler +#ifndef FASTFLOAT_HAS_SIMD + return 0; // never reaches here, remove warning #else FASTFLOAT_SIMD_DISABLE_WARNINGS return parse_eight_digits_unrolled_c16(_mm_loadu_si128(reinterpret_cast(chars))); @@ -173,7 +177,9 @@ bool parse_if_eight_digits_unrolled(const char* chars, std::uint64_t& i) noexcep return all; } +// Call this if you don't know whether chars are only digits // http://0x80.pl/articles/simd-parsing-int-sequences.html +//todo: add support for char32_t fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool parse_if_eight_digits_unrolled(const char16_t* chars, std::uint64_t& i) noexcept { if (cpp20_and_in_constexpr() || !has_simd()) { @@ -184,17 +190,16 @@ bool parse_if_eight_digits_unrolled(const char16_t* chars, std::uint64_t& i) noe i = i * 100000000 + parse_eight_digits_unrolled(read_u64(chars)); return true; } -#if !FASTFLOAT_HAS_SIMD - return false; // never reaches here, satisfy compiler +#ifndef FASTFLOAT_HAS_SIMD + return false; // never reaches here, remove warning #else FASTFLOAT_SIMD_DISABLE_WARNINGS const __m128i data = _mm_loadu_si128(reinterpret_cast(chars)); // (x - '0') <= 9 const __m128i t0 = _mm_sub_epi16(data, _mm_set1_epi16(80)); const __m128i t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-119)); - const bool is_digits = _mm_movemask_epi8(t1) == 0; - if (is_digits) { + if (_mm_movemask_epi8(t1) == 0) { i = i * 100000000 + parse_eight_digits_unrolled_c16(data); return true; } @@ -227,16 +232,16 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string parse_number_string(const CharT *p, const CharT *pend, parse_options options) noexcept { const chars_format fmt = options.format; const parse_rules rules = options.rules; - const CharT decimal_point = static_cast(options.decimal_point); + const CharT decimal_point = CharT(options.decimal_point); parsed_number_string answer; answer.valid = false; answer.too_many_digits = false; - answer.negative = (*p == static_cast('-')); + answer.negative = (*p == CharT('-')); #if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default - if ((*p == static_cast('-')) || (*p == static_cast('+'))) { + if ((*p == CharT('-')) || (*p == CharT('+'))) { #else - if (*p == static_cast('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + if (*p == CharT('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here #endif ++p; if (p == pend) { @@ -254,7 +259,7 @@ parsed_number_string parse_number_string(const CharT *p, const CharT *pen // a multiplication by 10 is cheaper than an arbitrary integer // multiplication i = 10 * i + - uint64_t(*p - static_cast('0')); // might overflow, we will handle the overflow later + uint64_t(*p - CharT('0')); // might overflow, we will handle the overflow later ++p; } const CharT *const end_of_integer_part = p; @@ -271,7 +276,7 @@ parsed_number_string parse_number_string(const CharT *p, const CharT *pen p += 8; } while ((p != pend) && is_integer(*p)) { - i = i * 10 + uint64_t(*p - static_cast('0')); // in rare cases, this will overflow, but that's ok + i = i * 10 + uint64_t(*p - CharT('0')); // in rare cases, this will overflow, but that's ok ++p; } exponent = before - p; @@ -283,14 +288,14 @@ parsed_number_string parse_number_string(const CharT *p, const CharT *pen return answer; } int64_t exp_number = 0; // explicit exponential part - if ((fmt & chars_format::scientific) && (p != pend) && ((static_cast('e') == *p) || (static_cast('E') == *p))) { + if ((fmt & chars_format::scientific) && (p != pend) && ((CharT('e') == *p) || (CharT('E') == *p))) { const CharT * location_of_e = p; ++p; bool neg_exp = false; - if ((p != pend) && (static_cast('-') == *p)) { + if ((p != pend) && (CharT('-') == *p)) { neg_exp = true; ++p; - } else if ((p != pend) && (static_cast('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) + } else if ((p != pend) && (CharT('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) ++p; } if ((p == pend) || !is_integer(*p)) { @@ -302,7 +307,7 @@ parsed_number_string parse_number_string(const CharT *p, const CharT *pen p = location_of_e; } else { while ((p != pend) && is_integer(*p)) { - uint8_t digit = uint8_t(*p - static_cast('0')); + uint8_t digit = uint8_t(*p - CharT('0')); if (exp_number < 0x10000000) { exp_number = 10 * exp_number + digit; } @@ -317,7 +322,7 @@ parsed_number_string parse_number_string(const CharT *p, const CharT *pen } // disallow leading zeros before the decimal point - if (rules == parse_rules::json_rules && start_digits[0] == static_cast('0') && digit_count >= 2 && is_integer(start_digits[1])) + if (rules == parse_rules::json_rules && start_digits[0] == CharT('0') && digit_count >= 2 && is_integer(start_digits[1])) return answer; answer.lastmatch = p; @@ -335,12 +340,13 @@ parsed_number_string parse_number_string(const CharT *p, const CharT *pen // We need to be mindful of the case where we only have zeroes... // E.g., 0.000000000...000. const CharT *start = start_digits; - while ((start != pend) && (*start == static_cast('0') || *start == decimal_point)) { - if(*start == static_cast('0')) { digit_count --; } + while ((start != pend) && (*start == CharT('0') || *start == decimal_point)) { + if(*start == CharT('0')) { digit_count --; } start++; } // exponent/mantissa must be truncated later + // this is unlikely, so don't inline truncation code with the rest of parse_number_string() answer.too_many_digits = digit_count > 19; } answer.exponent = exponent; @@ -350,7 +356,7 @@ parsed_number_string parse_number_string(const CharT *p, const CharT *pen template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 -void truncate_exponent_mantissa(parsed_number_string& ps) +void parse_truncated_number_string(parsed_number_string& ps) { // Let us start again, this time, avoiding overflows. // We don't need to check if is_integer, since we use the @@ -361,7 +367,7 @@ void truncate_exponent_mantissa(parsed_number_string& ps) const CharT* const int_end = p + ps.integer.len(); const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; while ((i < minimal_nineteen_digit_integer) && (p != int_end)) { - i = i * 10 + uint64_t(*p - static_cast('0')); + i = i * 10 + uint64_t(*p - CharT('0')); ++p; } if (i >= minimal_nineteen_digit_integer) { // We have a big integers @@ -371,7 +377,7 @@ void truncate_exponent_mantissa(parsed_number_string& ps) p = ps.fraction.ptr; const CharT* const frac_end = p + ps.fraction.len(); while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) { - i = i * 10 + uint64_t(*p - static_cast('0')); + i = i * 10 + uint64_t(*p - CharT('0')); ++p; } exponent = ps.fraction.ptr - p + ps.exp_number; diff --git a/include/fast_float/digit_comparison.h b/include/fast_float/digit_comparison.h index 5ba91af..73d6732 100644 --- a/include/fast_float/digit_comparison.h +++ b/include/fast_float/digit_comparison.h @@ -158,16 +158,18 @@ void round_down(adjusted_mantissa& am, int32_t shift) noexcept { template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void skip_zeros(const CharT*& first, const CharT* last) noexcept { - uint64_t val; - while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) { - val = fast_read_u64(first); - if (val != 0x3030303030303030) { - break; + if (std::is_same::value || has_simd()) { + uint64_t val; + while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) { + val = fast_read_u64(first); + if (val != 0x3030303030303030) { + break; + } + first += 8; } - first += 8; } while (first != last) { - if (*first != static_cast('0')) { + if (*first != CharT('0')) { break; } first++; @@ -179,17 +181,19 @@ void skip_zeros(const CharT*& first, const CharT* last) noexcept { template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool is_truncated(const CharT* first, const CharT* last) noexcept { - // do 8-bit optimizations, can just compare to 8 literal 0s. - uint64_t val; - while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) { - val = fast_read_u64(first); - if (val != 0x3030303030303030) { - return true; + if (std::is_same::value || has_simd()) { + // do 8-bit optimizations, can just compare to 8 literal 0s. + uint64_t val; + while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) { + val = fast_read_u64(first); + if (val != 0x3030303030303030) { + return true; + } + first += 8; } - first += 8; } while (first != last) { - if (*first != static_cast('0')) { + if (*first != CharT('0')) { return true; } first++; @@ -215,7 +219,7 @@ void parse_eight_digits(const CharT*& p, limb& value, size_t& counter, size_t& c template fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void parse_one_digit(const CharT*& p, limb& value, size_t& counter, size_t& count) noexcept { - value = value * 10 + limb(*p - static_cast('0')); + value = value * 10 + limb(*p - CharT('0')); p++; counter++; count++; diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 4fb03fc..020ae6b 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -78,11 +78,12 @@ #endif #endif -#if FASTFLOAT_SSE2 +#ifdef FASTFLOAT_SSE2 #define FASTFLOAT_HAS_SIMD (1) #endif #if defined(__GNUC__) +// disable -Wcast-align=strict (GCC only) #define FASTFLOAT_SIMD_DISABLE_WARNINGS \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"") @@ -141,7 +142,7 @@ inline FASTFLOAT_CONSTEXPR14 bool fastfloat_strncasecmp(const CharT *input1, const char *input2, size_t length) { char running_diff{0}; for (size_t i = 0; i < length; i++) { - running_diff |= (static_cast(input1[i]) ^ input2[i]); + running_diff |= (char(input1[i]) ^ input2[i]); } return (running_diff == 0) || (running_diff == 32); } diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index d5b266d..a407bfc 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -26,12 +26,12 @@ parse_infnan(const CharT *first, const CharT *last, T &value) noexcept { answer.ptr = first; answer.ec = std::errc(); // be optimistic bool minusSign = false; - if (*first == static_cast('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here + if (*first == CharT('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here minusSign = true; ++first; } #if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default - if (*first == static_cast('+')) { + if (*first == CharT('+')) { ++first; } #endif @@ -40,15 +40,15 @@ parse_infnan(const CharT *first, const CharT *last, T &value) noexcept { answer.ptr = (first += 3); value = minusSign ? -std::numeric_limits::quiet_NaN() : std::numeric_limits::quiet_NaN(); // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). - if(first != last && *first == static_cast('(')) { + if(first != last && *first == CharT('(')) { for(const CharT* ptr = first + 1; ptr != last; ++ptr) { - if (*ptr == static_cast(')')) { + if (*ptr == CharT(')')) { answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) break; } - else if(!((static_cast('a') <= *ptr && *ptr <= static_cast('z')) || - (static_cast('A') <= *ptr && *ptr <= static_cast('Z')) || - (static_cast('0') <= *ptr && *ptr <= static_cast('9')) || *ptr == static_cast('_'))) + else if(!((CharT('a') <= *ptr && *ptr <= CharT('z')) || + (CharT('A') <= *ptr && *ptr <= CharT('Z')) || + (CharT('0') <= *ptr && *ptr <= CharT('9')) || *ptr == CharT('_'))) break; // forbidden char, not nan(n-char-seq-opt) } } @@ -159,7 +159,7 @@ from_chars_result from_chars_preparsed(parsed_number_string pns, c } } if (pns.too_many_digits) - truncate_exponent_mantissa(pns); + parse_truncated_number_string(pns); answer.ec = std::errc(); // be optimistic answer.ptr = pns.lastmatch;