From 11a6232927572bda678747329e13cd6011e88cbb Mon Sep 17 00:00:00 2001 From: RealTimeChris <40668522+RealTimeChris@users.noreply.github.com> Date: Sun, 24 Nov 2024 12:00:55 -0500 Subject: [PATCH 1/7] Adding new loop_parse_if_digits function. [skip ci] We can eliminate one of the subtractions from within is_made_of_eight_digits_fast and parse_eight_digits_unrolled by instead moving it out into when the value is collected. Additionally, we can also save some overhead by not collecting the value twice for each iteration of the loop within loop_parse_if_eight_digits. --- include/fast_float/ascii_number.h | 43 +++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 25ac40d..edca0f2 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -32,7 +32,7 @@ template fastfloat_really_inline constexpr bool has_simd_opt() { // 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 - '0') < 10; } fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { @@ -232,6 +232,39 @@ loop_parse_if_eight_digits(char const *&p, char const *const pend, } } +// credit @realtimechris +fastfloat_really_inline constexpr bool +parse_if_eight_digits_unrolled(const char *&string, size_t &value) { + constexpr size_t byte_mask = ~size_t(0) / 255ull; + constexpr size_t msb_mask = byte_mask * 128ull; + constexpr size_t threshold_byte_mask = byte_mask * (127ull - 9ull); + constexpr size_t mask = 0x000000FF000000FFull; + constexpr size_t mul1 = 0x000F424000000064ull; + constexpr size_t mul2 = 0x0000271000000001ull; + size_t value_new = read8_to_u64(string) - 0x3030303030303030; + if (!(((value_new + threshold_byte_mask) | value_new) & msb_mask)) { + value_new = (value_new * 10) + (value_new >> 8); + value = + value * 100000000 + + ((((value_new & mask) * mul1) + (((value_new >> 16) & mask) * mul2)) >> + 32); + string += 8; + return true; + } + return false; +} + +fastfloat_really_inline constexpr void +loop_parse_if_digits(const char *&p, const char *const pend, + size_t &i) noexcept { + while (pend - p >= 8 && parse_if_eight_digits_unrolled(p, i)) { + } + while (p < pend && is_integer(*p)) { + i = i * 10 + static_cast(*p - '0'); + ++p; + } +} + enum class parse_error { no_error, // [JSON-only] The minus sign must be followed by an integer. @@ -347,13 +380,7 @@ 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); - - 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 - } + loop_parse_if_digits(p, pend, i); exponent = before - p; answer.fraction = span(before, size_t(p - before)); digit_count -= exponent; From d4c573de0b5a1c934f719e6a929396746d3849e6 Mon Sep 17 00:00:00 2001 From: RealTimeChris <40668522+RealTimeChris@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:43:47 -0500 Subject: [PATCH 2/7] Update include/fast_float/ascii_number.h Co-authored-by: Anders Dalvander --- include/fast_float/ascii_number.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index edca0f2..4ad2340 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -32,7 +32,7 @@ template fastfloat_really_inline constexpr bool has_simd_opt() { // able to optimize it well. template fastfloat_really_inline constexpr bool is_integer(UC c) noexcept { - return static_cast(c - '0') < 10; + return static_cast(c - UC('0')) < 10; } fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { From 08108bff7b082bcbf0dcc953050e3d6460667aa7 Mon Sep 17 00:00:00 2001 From: RealTimeChris <40668522+RealTimeChris@users.noreply.github.com> Date: Tue, 26 Nov 2024 00:58:42 -0500 Subject: [PATCH 3/7] Updating to fix some issues. --- include/fast_float/ascii_number.h | 83 +++++++++++-------- include/fast_float/constexpr_feature_detect.h | 6 ++ 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 4ad2340..f76b25b 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -118,9 +118,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 +144,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 +224,10 @@ 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()) { + FASTFLOAT_IF_CONSTEXPR(!has_simd_opt()) { return; } - while ((std::distance(p, pend) >= 8) && + 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 +238,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,36 +246,26 @@ loop_parse_if_eight_digits(char const *&p, char const *const pend, } } -// credit @realtimechris -fastfloat_really_inline constexpr bool -parse_if_eight_digits_unrolled(const char *&string, size_t &value) { - constexpr size_t byte_mask = ~size_t(0) / 255ull; - constexpr size_t msb_mask = byte_mask * 128ull; - constexpr size_t threshold_byte_mask = byte_mask * (127ull - 9ull); - constexpr size_t mask = 0x000000FF000000FFull; - constexpr size_t mul1 = 0x000F424000000064ull; - constexpr size_t mul2 = 0x0000271000000001ull; - size_t value_new = read8_to_u64(string) - 0x3030303030303030; - if (!(((value_new + threshold_byte_mask) | value_new) & msb_mask)) { - value_new = (value_new * 10) + (value_new >> 8); - value = - value * 100000000 + - ((((value_new & mask) * mul1) + (((value_new >> 16) & mask) * mul2)) >> - 32); - string += 8; - return true; +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; } - return false; } -fastfloat_really_inline constexpr void -loop_parse_if_digits(const char *&p, const char *const pend, - size_t &i) noexcept { - while (pend - p >= 8 && parse_if_eight_digits_unrolled(p, i)) { - } - while (p < pend && is_integer(*p)) { - i = i * 10 + static_cast(*p - '0'); - ++p; +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; } } @@ -381,6 +385,13 @@ parse_number_string(UC const *p, UC const *pend, // 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_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; @@ -473,7 +484,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; @@ -605,4 +616,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..d1b3195 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 +#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 From c23929fc66b1f94338f7971d28aba94d0ebba0b5 Mon Sep 17 00:00:00 2001 From: RealTimeChris <40668522+RealTimeChris@users.noreply.github.com> Date: Tue, 26 Nov 2024 03:45:28 -0500 Subject: [PATCH 4/7] Modifying feature detection macro. --- include/fast_float/constexpr_feature_detect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fast_float/constexpr_feature_detect.h b/include/fast_float/constexpr_feature_detect.h index d1b3195..22e892a 100644 --- a/include/fast_float/constexpr_feature_detect.h +++ b/include/fast_float/constexpr_feature_detect.h @@ -14,7 +14,7 @@ #define FASTFLOAT_CONSTEXPR14 #endif -#if __cplusplus >= 201703L +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) #define FASTFLOAT_IF_CONSTEXPR if constexpr #else #define FASTFLOAT_IF_CONSTEXPR if From ee79fe6c7e0a0aa5fda8e77de57dbd6e839d51fc Mon Sep 17 00:00:00 2001 From: RealTimeChris <40668522+RealTimeChris@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:18:10 -0500 Subject: [PATCH 5/7] Running clang-format. --- include/fast_float/ascii_number.h | 8 ++------ include/fast_float/constexpr_feature_detect.h | 2 +- include/fast_float/float_common.h | 8 ++++++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index f76b25b..9b5befe 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -224,9 +224,7 @@ 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) { - FASTFLOAT_IF_CONSTEXPR(!has_simd_opt()) { - return; - } + 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 @@ -249,9 +247,7 @@ 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; - } + 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 diff --git a/include/fast_float/constexpr_feature_detect.h b/include/fast_float/constexpr_feature_detect.h index 22e892a..d56a1ef 100644 --- a/include/fast_float/constexpr_feature_detect.h +++ b/include/fast_float/constexpr_feature_detect.h @@ -15,7 +15,7 @@ #endif #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -#define FASTFLOAT_IF_CONSTEXPR if constexpr +#define FASTFLOAT_IF_CONSTEXPR if constexpr #else #define FASTFLOAT_IF_CONSTEXPR if #endif diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index ebe7cfd..ca1fd96 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -189,12 +189,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 From d88299dd4c3fe84a1a44af2afc4eb09669224db5 Mon Sep 17 00:00:00 2001 From: RealTimeChris <40668522+RealTimeChris@users.noreply.github.com> Date: Wed, 27 Nov 2024 07:41:08 -0500 Subject: [PATCH 6/7] Adding fix for is_integer overflow. This should avoid using unnecessarily sized unsigned types, while also avoiding overflow. --- include/fast_float/ascii_number.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 9b5befe..a70db6e 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 = std::conditional_t< + sizeof(value_type) == 4, uint32_t, + std::conditional_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 static_cast(c - UC('0')) < 10; + return static_cast>(c - UC('0')) < 10; } fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { From 1f032e3afdb57caed171b5cfdbc8579c7838ffc0 Mon Sep 17 00:00:00 2001 From: RealTimeChris <40668522+RealTimeChris@users.noreply.github.com> Date: Wed, 27 Nov 2024 07:41:08 -0500 Subject: [PATCH 7/7] Adding conditional_t for pre-CPP14. --- include/fast_float/ascii_number.h | 4 ++-- include/fast_float/float_common.h | 34 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index a70db6e..f5200f7 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -29,9 +29,9 @@ template fastfloat_really_inline constexpr bool has_simd_opt() { } template struct get_equal_sized_uint { - using type = std::conditional_t< + using type = FASTFLOAT_CONDITIONAL_T( sizeof(value_type) == 4, uint32_t, - std::conditional_t>; + FASTFLOAT_CONDITIONAL_T(sizeof(value_type) == 2, uint16_t, uint8_t)); }; template diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index ca1fd96..064e4b0 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -883,6 +883,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) {