Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding new loop_parse_if_digits function #291

Closed
wants to merge 9 commits into from
69 changes: 56 additions & 13 deletions include/fast_float/ascii_number.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,20 @@ template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
#endif
}

template <typename value_type> 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 <typename value_type>
using get_equal_sized_uint_t = typename get_equal_sized_uint<value_type>::type;

// Next function can be micro-optimized, but compilers are entirely
// able to optimize it well.
template <typename UC>
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
return !(c > UC('9') || c < UC('0'));
return static_cast<get_equal_sized_uint_t<UC>>(c - UC('0')) < 10;
}

fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
Expand Down Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -209,10 +233,8 @@ bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::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<UC>()) {
return;
}
while ((std::distance(p, pend) >= 8) &&
FASTFLOAT_IF_CONSTEXPR(!has_simd_opt<UC>()) { 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;
Expand All @@ -223,15 +245,35 @@ 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
p += 8;
}
}

template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::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<UC>()) { 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.
Expand Down Expand Up @@ -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);
RealTimeChris marked this conversation as resolved.
Show resolved Hide resolved

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<UC const>(before, size_t(p - before));
digit_count -= exponent;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -578,4 +621,4 @@ parse_int_string(UC const *p, UC const *pend, T &value,

} // namespace fast_float

#endif
#endif
6 changes: 6 additions & 0 deletions include/fast_float/constexpr_feature_detect.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 40 additions & 2 deletions include/fast_float/float_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,16 @@ using parse_options = parse_options_t<char>;

#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
Expand Down Expand Up @@ -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<condition, true_t, false_t>
#else
template <bool condition, typename true_t, typename false_t>
struct conditional {
using type = false_t;
};

template <typename true_t, typename false_t>
struct conditional<true, true_t, false_t> {
using type = true_t;
};

template <bool condition, typename true_t, typename false_t>
using conditional_t = typename conditional<condition, true_t, false_t>::type;

#define FASTFLOAT_CONDITIONAL_T(condition, true_t, false_t) \
conditional_t<condition, true_t, false_t>
#endif

namespace detail {
// adjust for deprecated feature macros
constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
Expand Down
Loading