accept(): skip digits without computing their value
accept() needs the value of a number only for a float whose exponent may reach the largest value. skip_digits() finds the end of the digits without the multiply-adds of accumulate_digits(), and once an 8-byte word fails the digit test, the remaining loop needs no bounds check: a non-digit is in that word. The value is computed afterwards for the rare floats that need it. accept() of canada.json: -16 % on Apple M1, -22 % with GCC 15 and -21 % with Clang 21 on x86-64; files with many numbers -9 to -15 %. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
4 files changed
+103
-4
No files matched your search
@@ -646,7 +646,9 @@ done:
|
||||
}
|
||||
else if (i < avail && data[i] >= '1' && data[i] <= '9')
|
||||
{
|
||||
i = accumulate_digits(data, i, avail, w);
|
||||
// accept() needs the value only for a float with a large exponent,
|
||||
// which computes it below; its handler ignores the values
|
||||
i = keep_values() ? accumulate_digits(data, i, avail, w) : skip_digits(data, i, avail);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -664,7 +666,7 @@ done:
|
||||
return false;
|
||||
}
|
||||
const std::size_t fraction_begin = i;
|
||||
i = accumulate_digits(data, i, avail, w);
|
||||
i = keep_values() ? accumulate_digits(data, i, avail, w) : skip_digits(data, i, avail);
|
||||
fraction_digits = i - fraction_begin;
|
||||
digit_count += fraction_digits;
|
||||
}
|
||||
@@ -753,6 +755,16 @@ done:
|
||||
{
|
||||
return report(sax->number_float(value, number_text(data, data + i)));
|
||||
}
|
||||
if (!keep_values())
|
||||
{
|
||||
// the digits were skipped: their value now
|
||||
s.w = 0;
|
||||
const std::size_t integer_end = accumulate_digits(data, integer_begin, avail, s.w);
|
||||
if (dot_index != std::string::npos)
|
||||
{
|
||||
static_cast<void>(accumulate_digits(data, integer_end + 1, avail, s.w));
|
||||
}
|
||||
}
|
||||
value = decimal_to_float<number_float_t>(s);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -316,6 +316,30 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t accumulate_digits(const char* data, std::s
|
||||
return i;
|
||||
}
|
||||
|
||||
/// the index of the first byte at or after @a i that is not a digit: the
|
||||
/// digits of accumulate_digits() without their value
|
||||
JSON_HEDLEY_ALWAYS_INLINE std::size_t skip_digits(const char* data, std::size_t i, std::size_t avail) noexcept
|
||||
{
|
||||
while (avail - i >= 8)
|
||||
{
|
||||
if (!is_eight_digits(read_eight_bytes(data + i)))
|
||||
{
|
||||
// a byte of the word is not a digit: no bounds checks before it
|
||||
while (static_cast<unsigned char>(data[i] - '0') <= 9)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
i += 8;
|
||||
}
|
||||
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
||||
{
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/// whether [first, last) contains a digit other than '0'; a plain loop, as
|
||||
/// GCC calls std::any_of out of line even for the usual empty range
|
||||
JSON_HEDLEY_ALWAYS_INLINE bool has_nonzero_digit(const char* first, const char* last) noexcept
|
||||
|
||||
Reference in new issue
Block a user