diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 25ac40d..f5200f7 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -28,11 +28,20 @@ template fastfloat_really_inline constexpr bool has_simd_opt() { #endif } +template struct get_equal_sized_uint { + using type = FASTFLOAT_CONDITIONAL_T( + sizeof(value_type) == 4, uint32_t, + FASTFLOAT_CONDITIONAL_T(sizeof(value_type) == 2, uint16_t, uint8_t)); +}; + +template +using get_equal_sized_uint_t = typename get_equal_sized_uint::type; + // Next function can be micro-optimized, but compilers are entirely // able to optimize it well. template fastfloat_really_inline constexpr bool is_integer(UC c) noexcept { - return !(c > UC('9') || c < UC('0')); + return static_cast>(c - UC('0')) < 10; } fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { @@ -118,9 +127,9 @@ uint64_t simd_read8_to_u64(UC const *) { // credit @aqrit fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t parse_eight_digits_unrolled(uint64_t val) { - uint64_t const mask = 0x000000FF000000FF; - uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) - uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + constexpr uint64_t mask = 0x000000FF000000FF; + constexpr uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + constexpr uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) val -= 0x3030303030303030; val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; @@ -144,6 +153,21 @@ is_made_of_eight_digits_fast(uint64_t val) noexcept { 0x8080808080808080)); } +fastfloat_really_inline constexpr bool +is_made_of_eight_digits_no_sub(uint64_t val) noexcept { + return !((((val + 0x7676767676767676) | (val)) & 0x8080808080808080)); +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t +parse_eight_digits_unrolled_no_sub(uint64_t val) { + constexpr uint64_t mask = 0x000000FF000000FF; + constexpr uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + constexpr uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return uint32_t(val); +} + #ifdef FASTFLOAT_HAS_SIMD // Call this if chars might not be 8 digits. @@ -209,10 +233,8 @@ bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) { template ::value) = 0> fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) { - if (!has_simd_opt()) { - return; - } - while ((std::distance(p, pend) >= 8) && + FASTFLOAT_IF_CONSTEXPR(!has_simd_opt()) { return; } + while (((pend - p) >= 8) && simd_parse_if_eight_digits_unrolled( p, i)) { // in rare cases, this will overflow, but that's ok p += 8; @@ -223,8 +245,7 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void loop_parse_if_eight_digits(char const *&p, char const *const pend, uint64_t &i) { // optimizes better than parse_if_eight_digits_unrolled() for UC = char. - while ((std::distance(p, pend) >= 8) && - is_made_of_eight_digits_fast(read8_to_u64(p))) { + while (((pend - p) >= 8) && is_made_of_eight_digits_fast(read8_to_u64(p))) { i = i * 100000000 + parse_eight_digits_unrolled(read8_to_u64( p)); // in rare cases, this will overflow, but that's ok @@ -232,6 +253,27 @@ loop_parse_if_eight_digits(char const *&p, char const *const pend, } } +template ::value) = 0> +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void +loop_parse_if_digits(UC const *&p, UC const *const pend, uint64_t &i) { + FASTFLOAT_IF_CONSTEXPR(!has_simd_opt()) { return; } + while (((pend - p) >= 8) && + simd_parse_if_eight_digits_unrolled( + p, i)) { // in rare cases, this will overflow, but that's ok + p += 8; + } +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void +loop_parse_if_digits(char const *&p, char const *const pend, uint64_t &i) { + uint64_t val; + while ((pend - p) >= 8 && (val = read8_to_u64(p) - 0x3030303030303030, + is_made_of_eight_digits_no_sub(val))) { + i = i * 100000000 + parse_eight_digits_unrolled_no_sub(val); + p += 8; + } +} + enum class parse_error { no_error, // [JSON-only] The minus sign must be followed by an integer. @@ -347,13 +389,14 @@ parse_number_string(UC const *p, UC const *pend, UC const *before = p; // can occur at most twice without overflowing, but let it occur more, since // for integers with many digits, digit parsing is the primary bottleneck. - loop_parse_if_eight_digits(p, pend, i); + loop_parse_if_digits(p, pend, i); while ((p != pend) && is_integer(*p)) { uint8_t digit = uint8_t(*p - UC('0')); ++p; i = i * 10 + digit; // in rare cases, this will overflow, but that's ok } + exponent = before - p; answer.fraction = span(before, size_t(p - before)); digit_count -= exponent; @@ -446,7 +489,7 @@ parse_number_string(UC const *p, UC const *pend, i = 0; p = answer.integer.ptr; UC const *int_end = p + answer.integer.len(); - uint64_t const minimal_nineteen_digit_integer{1000000000000000000}; + constexpr uint64_t minimal_nineteen_digit_integer{1000000000000000000}; while ((i < minimal_nineteen_digit_integer) && (p != int_end)) { i = i * 10 + uint64_t(*p - UC('0')); ++p; @@ -578,4 +621,4 @@ parse_int_string(UC const *p, UC const *pend, T &value, } // namespace fast_float -#endif +#endif \ No newline at end of file diff --git a/include/fast_float/constexpr_feature_detect.h b/include/fast_float/constexpr_feature_detect.h index 7624bea..d56a1ef 100644 --- a/include/fast_float/constexpr_feature_detect.h +++ b/include/fast_float/constexpr_feature_detect.h @@ -14,6 +14,12 @@ #define FASTFLOAT_CONSTEXPR14 #endif +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define FASTFLOAT_IF_CONSTEXPR if constexpr +#else +#define FASTFLOAT_IF_CONSTEXPR if +#endif + #if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L #define FASTFLOAT_HAS_BIT_CAST 1 #else diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 38f7759..aacdcad 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -191,12 +191,16 @@ using parse_options = parse_options_t; #ifndef FASTFLOAT_ASSERT #define FASTFLOAT_ASSERT(x) \ - { ((void)(x)); } + { \ + ((void)(x)); \ + } #endif #ifndef FASTFLOAT_DEBUG_ASSERT #define FASTFLOAT_DEBUG_ASSERT(x) \ - { ((void)(x)); } + { \ + ((void)(x)); \ + } #endif // rust style `try!()` macro, or `?` operator @@ -932,6 +936,40 @@ operator^=(chars_format &lhs, chars_format rhs) noexcept { return lhs = (lhs ^ rhs); } +#if defined(_MSC_VER) +#if _MSC_VER >= 1900 +#if _MSC_VER >= 1910 +#else +#define CPP14_NOT_SUPPORTED +#endif +#else +#define CPP14_NOT_SUPPORTED +#endif +#elif __cplusplus < 201402L +#define CPP14_NOT_SUPPORTED +#endif + +#ifndef CPP14_NOT_SUPPORTED +#define FASTFLOAT_CONDITIONAL_T(condition, true_t, false_t) \ + std::conditional_t +#else +template +struct conditional { + using type = false_t; +}; + +template +struct conditional { + using type = true_t; +}; + +template +using conditional_t = typename conditional::type; + +#define FASTFLOAT_CONDITIONAL_T(condition, true_t, false_t) \ + conditional_t +#endif + namespace detail { // adjust for deprecated feature macros constexpr chars_format adjust_for_feature_macros(chars_format fmt) {