Skip to content

Commit 11a6232

Browse files
committed
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.
1 parent 321d3a7 commit 11a6232

File tree

1 file changed

+35
-8
lines changed

1 file changed

+35
-8
lines changed

Diff for: include/fast_float/ascii_number.h

+35-8
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
3232
// able to optimize it well.
3333
template <typename UC>
3434
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
35-
return !(c > UC('9') || c < UC('0'));
35+
return static_cast<uint8_t>(c - '0') < 10;
3636
}
3737

3838
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,
232232
}
233233
}
234234

235+
// credit @realtimechris
236+
fastfloat_really_inline constexpr bool
237+
parse_if_eight_digits_unrolled(const char *&string, size_t &value) {
238+
constexpr size_t byte_mask = ~size_t(0) / 255ull;
239+
constexpr size_t msb_mask = byte_mask * 128ull;
240+
constexpr size_t threshold_byte_mask = byte_mask * (127ull - 9ull);
241+
constexpr size_t mask = 0x000000FF000000FFull;
242+
constexpr size_t mul1 = 0x000F424000000064ull;
243+
constexpr size_t mul2 = 0x0000271000000001ull;
244+
size_t value_new = read8_to_u64(string) - 0x3030303030303030;
245+
if (!(((value_new + threshold_byte_mask) | value_new) & msb_mask)) {
246+
value_new = (value_new * 10) + (value_new >> 8);
247+
value =
248+
value * 100000000 +
249+
((((value_new & mask) * mul1) + (((value_new >> 16) & mask) * mul2)) >>
250+
32);
251+
string += 8;
252+
return true;
253+
}
254+
return false;
255+
}
256+
257+
fastfloat_really_inline constexpr void
258+
loop_parse_if_digits(const char *&p, const char *const pend,
259+
size_t &i) noexcept {
260+
while (pend - p >= 8 && parse_if_eight_digits_unrolled(p, i)) {
261+
}
262+
while (p < pend && is_integer(*p)) {
263+
i = i * 10 + static_cast<uint8_t>(*p - '0');
264+
++p;
265+
}
266+
}
267+
235268
enum class parse_error {
236269
no_error,
237270
// [JSON-only] The minus sign must be followed by an integer.
@@ -347,13 +380,7 @@ parse_number_string(UC const *p, UC const *pend,
347380
UC const *before = p;
348381
// can occur at most twice without overflowing, but let it occur more, since
349382
// for integers with many digits, digit parsing is the primary bottleneck.
350-
loop_parse_if_eight_digits(p, pend, i);
351-
352-
while ((p != pend) && is_integer(*p)) {
353-
uint8_t digit = uint8_t(*p - UC('0'));
354-
++p;
355-
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
356-
}
383+
loop_parse_if_digits(p, pend, i);
357384
exponent = before - p;
358385
answer.fraction = span<UC const>(before, size_t(p - before));
359386
digit_count -= exponent;

0 commit comments

Comments
 (0)