fast_float/include/fast_float/ascii_number.h

1091 lines
38 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef FASTFLOAT_ASCII_NUMBER_H
#define FASTFLOAT_ASCII_NUMBER_H
#include <cctype>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <limits>
#include <type_traits>
#include "float_common.h"
#if FASTFLOAT_X86_SIMD
#include <immintrin.h>
#elif FASTFLOAT_ARM_NEON
#include <arm_neon.h>
#endif
namespace fast_float {
template <typename UC>
fastfloat_inline FASTFLOAT_CONSTEVAL bool has_simd_opt() noexcept {
#ifdef FASTFLOAT_USE_SIMD
return std::is_same<UC, char16_t>::value;
#else
return false;
#endif
}
// Next function can be micro-optimized, but compilers are entirely
// able to optimize it well.
template <typename UC>
fastfloat_inline constexpr bool is_integer(UC c) noexcept {
// UC can be signed (wchar_t is a signed int on Linux and macOS), and the
// subtraction is then promoted to int and overflows for code units near the
// bottom of the range. Subtracting in the unsigned type wraps instead, which
// selects the same code units, as ch_to_digit already does.
using UnsignedUC = typename std::make_unsigned<UC>::type;
return static_cast<UnsignedUC>(static_cast<UnsignedUC>(c) -
static_cast<UnsignedUC>(UC('0'))) <= 9u;
}
#if FASTFLOAT_IS_BIG_ENDIAN
#if FASTFLOAT_HAS_BYTESWAP
namespace {
using std::byteswap;
}
#else
fastfloat_inline constexpr uint64_t byteswap(uint64_t val) noexcept {
return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
(val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
(val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
(val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
}
fastfloat_inline constexpr uint32_t byteswap(uint32_t val) noexcept {
return (val >> 24) | ((val >> 8) & 0x0000FF00u) | ((val << 8) & 0x00FF0000u) |
(val << 24);
}
#endif
#endif
// Read UCs into an unsigned integer. Truncates UC if not char.
template <typename T, typename UC>
fastfloat_inline FASTFLOAT_CONSTEXPR20 T
read_chars_to_unsigned(UC const *chars) noexcept {
if (is_constant_evaluated() || !std::is_same<UC, char>::value) {
T val = 0;
for (uint_fast8_t i = 0; i != sizeof(T); ++i) {
val |= T(uint8_t(*chars)) << (i * 8);
++chars;
}
return val;
}
T val;
std::memcpy(&val, chars, sizeof(T));
#if FASTFLOAT_IS_BIG_ENDIAN
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
return val;
}
#if FASTFLOAT_USE_SIMD
#if FASTFLOAT_X86_SIMD
fastfloat_inline uint64_t simd_read8(__m128i const data) {
// _mm_packus_epi16 is SSE2, converts 8×u16 → 8×u8
__m128i const packed = _mm_packus_epi16(data, data);
#if FASTFLOAT_64BIT
return static_cast<uint64_t>(_mm_cvtsi128_si64(packed));
#else
uint64_t value;
// Visual Studio + older versions of GCC don't support _mm_storeu_si64
_mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed);
return value;
#endif
}
fastfloat_inline uint64_t simd_read8(char16_t const *chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
// unaligned SIMD instruction -> all fine.
return simd_read8(_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
#elif FASTFLOAT_ARM_NEON
fastfloat_inline uint64_t simd_read8(uint16x8_t const &data) {
uint8x8_t utf8_packed = vmovn_u16(data);
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
}
fastfloat_inline uint64_t simd_read8(char16_t const *chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
return simd_read8(vld1q_u16(reinterpret_cast<uint16_t const *>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
#endif // FASTFLOAT_X86_SIMD
#endif
// MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC>
#else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif
// dummy for compile
uint64_t simd_read8(UC const *) {
return 0;
}
// credit @aqrit
fastfloat_inline FASTFLOAT_CONSTEXPR14 uint32_t
parse_8_digits(uint64_t val) noexcept {
uint64_t const mask = 0x000000FF000000FF;
uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
uint64_t const 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;
return static_cast<uint32_t>(val);
}
// Call this if chars are definitely 8 digits.
template <typename UC>
fastfloat_inline FASTFLOAT_CONSTEXPR20 uint32_t
parse_8_digits(UC const *chars) noexcept {
if (is_constant_evaluated() || !has_simd_opt<UC>()) {
return parse_8_digits(
read_chars_to_unsigned<uint64_t>(chars)); // truncation okay
}
return parse_8_digits(simd_read8(chars));
}
// credit @aqrit
fastfloat_inline constexpr bool is_made_of_8_digits(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080));
}
fastfloat_inline constexpr bool is_made_of_4_digits(uint32_t val) noexcept {
return !((((val + 0x46464646) | (val - 0x30303030)) & 0x80808080));
}
fastfloat_inline FASTFLOAT_CONSTEXPR14 uint32_t
parse_4_digits(uint32_t val) noexcept {
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
#if FASTFLOAT_USE_SIMD
#if FASTFLOAT_X86_SIMD
#if FASTFLOAT_X86_SIMD >= 31
// credit @hedgehoginthecpp
fastfloat_inline __m128i parse_4x4_digits(__m128i data) noexcept {
// 1. convert from ASCII '0' .. '9' to numbers 0 .. 9
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i t0 = _mm_subs_epu8(data, ascii0);
// 2. convert to 2-digit numbers
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i t1 = _mm_maddubs_epi16(t0, mul_1_10);
// 3. convert to 4-digit numbers
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
return _mm_madd_epi16(t1, mul_1_100);
}
#endif
// credit @hedgehoginthecpp
fastfloat_inline uint64_t convert_4x4_to_16_digits(__m128i data) noexcept {
// 4. convert to 16-digit number
// v[0] * 10^12 + v[1] * 10^8 + v[2] * 10^4 + v[3]
const uint64_t lo = static_cast<uint32_t>(_mm_cvtsi128_si32(data));
const uint64_t hi =
static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_srli_si128(data, 4)));
const uint64_t a = lo * 10000ULL + hi;
const uint64_t lo2 =
static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_srli_si128(data, 8)));
const uint64_t hi2 =
static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_srli_si128(data, 12)));
const uint64_t b = lo2 * 10000ULL + hi2;
return a * 100000000ULL + b;
}
#if FASTFLOAT_X86_SIMD >= 42
// credit @hedgehoginthecpp
fastfloat_inline bool parse_if_16_digits(char const *chars,
uint64_t &value) noexcept {
FASTFLOAT_SIMD_DISABLE_WARNINGS
const __m128i data =
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
FASTFLOAT_SIMD_RESTORE_WARNINGS
/*
* PCMPxSTRI range comparison.
*
* First operand:
*
* ['0','9']
*
* Second operand:
*
* 16 input bytes
*
* Negative polarity asks for the first byte which is
* NOT inside the digit range.
*/
const __m128i ranges =
_mm_setr_epi8('0', '9', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
const auto numbers =
_mm_cmpestri(ranges, 2, data, 16,
_SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
_SIDD_NEGATIVE_POLARITY | _SIDD_LEAST_SIGNIFICANT);
if (numbers != 16)
return false;
value = value * 10000000000000000ULL +
convert_4x4_to_16_digits(parse_4x4_digits(data));
return true;
}
#endif
#if FASTFLOAT_X86_SIMD >= 31
// credit @hedgehoginthecpp
fastfloat_inline uint64_t parse_16_digits(char const *p) noexcept {
const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
return convert_4x4_to_16_digits(parse_4x4_digits(data));
}
#endif
// credit @hedgehoginthecpp
fastfloat_inline FASTFLOAT_CONSTEXPR20 void
parse_digits_until_19(char const *&p, char const *pend, am_mant_t &mantissa) {
#if FASTFLOAT_X86_SIMD >= 31
if (!is_constant_evaluated()) {
// If mantissa < 10^2, a 16-digit block is guaranteed < 10^18 - 1.
while (std::distance(p, pend) >= 16 && mantissa < 100) {
auto const value = parse_16_digits(p);
mantissa = mantissa * 10000000000000000ULL + value;
p += 16;
}
}
#endif
// If mantissa < 10^10, a 8-digit block is guaranteed < 10^18 - 1.
while (std::distance(p, pend) >= 8 && mantissa < 10000000000ULL) {
auto const value = parse_8_digits(p);
mantissa = mantissa * 100000000ULL + value;
p += 8;
}
// If mantissa < 10^12, a 4-digit block is guaranteed < 10^18 - 1.
if (std::distance(p, pend) >= 4 && mantissa < 1000000000000ULL) {
auto const value = read_chars_to_unsigned<uint32_t>(p);
mantissa = mantissa * 10000 + parse_4_digits(value);
p += 4;
}
// If mantissa < 10^19, we should parse digits one by one.
while (p != pend && mantissa < minimal_nineteen_digit_integer) {
mantissa = mantissa * 10 + static_cast<uint8_t>(*p - '0');
++p;
}
// While mantissa >= 10^19, we should stop parsing digits.
}
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
fastfloat_inline FASTFLOAT_CONSTEXPR20 void
parse_digits_until_19(UC const *&p, UC const *pend,
am_mant_t &mantissa) noexcept {
do {
mantissa = mantissa * 10 + static_cast<uint8_t>(*p - UC('0'));
} while (++p != pend && mantissa < minimal_nineteen_digit_integer);
}
#else
template <typename UC>
fastfloat_inline FASTFLOAT_CONSTEXPR20 void
parse_digits_until_19(UC const *&p, UC const *pend,
am_mant_t &mantissa) noexcept {
do {
mantissa = mantissa * 10 + static_cast<uint8_t>(*p - UC('0'));
} while (++p != pend && mantissa < minimal_nineteen_digit_integer);
}
#endif
// Call this if chars might not be 8 digits.
// Using this style (instead of is_made_of_8_digits() then
// parse_8_digits()) ensures we don't load SIMD registers twice.
fastfloat_inline FASTFLOAT_CONSTEXPR20 bool
simd_parse_if_8_digits(char16_t const *chars, uint64_t &i) noexcept {
if (is_constant_evaluated()) {
return false;
}
#if FASTFLOAT_X86_SIMD
FASTFLOAT_SIMD_DISABLE_WARNINGS
// Load 8 UTF-16 characters (16 bytes)
// unaligned SIMD instruction -> all fine.
__m128i const data =
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
FASTFLOAT_SIMD_RESTORE_WARNINGS
// Branchless "are all digits?" trick from Lemire:
// (x - '0') <= 9 <=> (x + 32720) <= 32729
// encoded as signed comparison: (x + 32720) > -32759 ? not digit : digit
// http://0x80.pl/articles/simd-parsing-int-sequences.html
__m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
__m128i const mask = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
// If mask == 0 → all digits valid.
if (_mm_movemask_epi8(mask) == 0) {
i = i * 100000000 + parse_8_digits(simd_read8(data));
return true;
}
#elif FASTFLOAT_ARM_NEON
FASTFLOAT_SIMD_DISABLE_WARNINGS
uint16x8_t const data = vld1q_u16(reinterpret_cast<uint16_t const *>(chars));
FASTFLOAT_SIMD_RESTORE_WARNINGS
// (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html
uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0'));
uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
if (vminvq_u16(mask) == 0xFFFF) {
i = i * 100000000 + parse_8_digits(simd_read8(data));
return true;
}
#else
(void)chars;
(void)i;
#endif
return false;
}
#else
template <typename UC>
fastfloat_inline FASTFLOAT_CONSTEXPR20 void
parse_digits_until_19(UC const *&p, UC const *pend,
am_mant_t &mantissa) noexcept {
do {
mantissa = mantissa * 10 + static_cast<uint8_t>(*p - UC('0'));
} while (++p != pend && mantissa < minimal_nineteen_digit_integer);
}
#endif
// MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC>
#else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif
// dummy for compiler
constexpr bool simd_parse_if_8_digits(UC const *, uint64_t &) {
return false;
}
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
fastfloat_inline FASTFLOAT_CONSTEXPR20 void
loop_parse_if_digits(UC const *&p, UC const *const pend, uint64_t &i) noexcept {
if (!is_constant_evaluated()) {
if FASTFLOAT_CONSTEXPR17 (has_simd_opt<UC>()) {
while (std::distance(p, pend) >= 8 &&
simd_parse_if_8_digits(p, i)) { // may overflow, that's ok
p += 8;
}
}
}
// Finalizer
while (p != pend && is_integer(*p)) {
i = i * 10 + static_cast<uint8_t>(*p - UC('0')); // may overflow, that's ok
++p;
}
}
fastfloat_inline FASTFLOAT_CONSTEXPR20 void
loop_parse_if_digits(char const *&p, char const *const pend,
uint64_t &i) noexcept {
#if FASTFLOAT_USE_SIMD && FASTFLOAT_X86_SIMD >= 42
if (!is_constant_evaluated()) {
// SSE4.2 handles 16 bytes at once.
while (std::distance(p, pend) >= 16)
if (parse_if_16_digits(p, i)) {
p += 16;
} else {
break;
}
}
#endif
// Optimizes better than parse_if_eight_digits_unrolled() for char.
while (std::distance(p, pend) >= 8 /*sizeof(uint64_t)*/) {
auto const val = read_chars_to_unsigned<uint64_t>(p);
if (is_made_of_8_digits(val)) {
i = i * 100000000 + parse_8_digits(val); // may overflow, that's ok
p += sizeof(uint64_t);
} else {
break;
}
}
// Consume a remaining 4-7 digit run in a single SWAR step instead of
// byte-by-byte (reuses the existing 4-digit helpers). The parsed result is
// identical either way. Historically gated to clang because gcc regressed on
// short remainders, but that verdict predates the span-elision restructure;
// with the leaner hot path the 4-digit step now wins on gcc as well.
if (std::distance(p, pend) >= 4 /*sizeof(uint32_t)*/) {
auto const val = read_chars_to_unsigned<uint32_t>(p);
if (is_made_of_4_digits(val)) {
i = i * 10000 + parse_4_digits(val); // may overflow, that's ok
p += sizeof(uint32_t);
}
}
// Finalizer
while (p != pend && is_integer(*p)) {
i = i * 10 + static_cast<uint8_t>(*p - '0'); // may overflow, that's ok
++p;
}
}
enum class parse_error : uint_fast8_t {
no_error,
// A sign must be followed by an integer or dot.
missing_integer_or_dot_after_sign,
// The mantissa must have at least one digit.
no_digits_in_mantissa,
// Scientific notation requires an exponential part.
missing_exponential_part,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// [JavaScript-only, sloppy mode] The integer part is a legacy octal literal
// (a leading zero followed by octal digits only), which is not a decimal
// number. Parse it in base 8 instead.
legacy_octal_integer_part,
// [JSON/JavaScript-only] The integer part must not have leading zeros.
leading_zeros_in_integer_part,
// [JSON-only] The minus sign must be followed by an integer.
missing_integer_after_sign,
// [JSON-only] The integer part must have at least one digit.
no_digits_in_integer_part,
// [JSON-only] If there is a decimal point, there must be digits in the
// fractional part.
no_digits_in_fractional_part,
#endif
};
template <typename UC> struct parsed_number_string_t {
FASTFLOAT_NO_UNIQUE_ADDRESS am_mant_t mantissa;
FASTFLOAT_NO_UNIQUE_ADDRESS am_pow_t exponent;
FASTFLOAT_NO_UNIQUE_ADDRESS UC const *lastmatch;
// The field order matters: placing 'error' next to the booleans avoids
// padding, keeping the struct at 64 bytes on 64-bit systems instead of 72.
// See https://github.com/fastfloat/fast_float/issues/418
FASTFLOAT_NO_UNIQUE_ADDRESS parse_error error;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
FASTFLOAT_NO_UNIQUE_ADDRESS bool negative;
#endif
FASTFLOAT_NO_UNIQUE_ADDRESS bool invalid;
FASTFLOAT_NO_UNIQUE_ADDRESS bool too_many_digits;
// contains the range of the significant digits, fully optional, only matters
// if it's length > 0
FASTFLOAT_NO_UNIQUE_ADDRESS span<UC const> integer;
FASTFLOAT_NO_UNIQUE_ADDRESS span<UC const> fraction;
};
using byte_span = span<char const>;
using parsed_number_string = parsed_number_string_t<char>;
// Helper for error creating
template <typename UC>
fastfloat_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC> &
report_parse_error(parsed_number_string_t<UC> &answer, UC const *p,
parse_error error) noexcept {
answer.invalid = true;
answer.lastmatch = p;
answer.error = error;
return answer;
}
// Assuming that you use no more than 19 digits, this will
// parse an ASCII string.
//
// store_spans is a *runtime* flag (not a template parameter, deliberately: a
// template would create a second instantiation of this whole function and the
// extra icache pressure wipes out the gain). When false, the integer/fraction
// spans (read only by the rare digit_comp slow path) are not materialized,
// which keeps the fat parsed_number_string_t off the hot path. The caller
// re-parses with store_spans=true if the slow path is actually reached.
template <bool json_fmt, bool javascript_fmt, typename UC>
fastfloat_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
parse_number_string_impl(UC const *p, UC const *pend,
parse_options_t<UC> const options,
bool const store_spans) noexcept {
parsed_number_string_t<UC> answer{};
FASTFLOAT_ASSUME(p < pend); // so dereference without checks
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
answer.negative = *p == UC('-');
if (answer.negative ||
(!json_fmt &&
chars_format_t(options.format & chars_format::allow_leading_plus) &&
*p == UC('+'))) {
++p;
if fastfloat_unlikely (p == pend) {
return report_parse_error<UC>(
answer, p, parse_error::missing_integer_or_dot_after_sign);
}
if (json_fmt) {
if fastfloat_unlikely (!is_integer(*p)) {
// A sign must be followed by an integer
return report_parse_error<UC>(answer, p,
parse_error::missing_integer_after_sign);
}
} else if fastfloat_unlikely (!is_integer(*p) &&
*p != options.decimal_point) {
// A sign must be followed by an integer or the dot
return report_parse_error<UC>(
answer, p, parse_error::missing_integer_or_dot_after_sign);
}
}
#endif
auto const *const start_digits = p;
// HedgehogInTheCPP: compiler generate much better code in any mode without
// manual unroling because it's less branching and also it's allow more
// inlining which is better. A multiplication by 10 is cheaper than an
// arbitrary integer multiplication. might overflow, handled later
#if !defined(FASTFLOAT_ISNOT_CHECKED_BOUNDS) && \
!defined(FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN)
while (p != pend && is_integer(*p)) {
answer.mantissa = static_cast<fast_float::am_mant_t>(
answer.mantissa * 10 + static_cast<uint8_t>(*p - UC('0')));
++p;
}
#else
// External parser already check that this is num and it's exist
do {
answer.mantissa = static_cast<fast_float::am_mant_t>(
answer.mantissa * 10 + static_cast<uint8_t>(*p - UC('0')));
++p;
} while (p != pend && is_integer(*p));
#endif
UC const *const end_of_integer_part = p;
auto digit_count = static_cast<am_digits>(end_of_integer_part - start_digits);
if fastfloat_unlikely (store_spans) {
answer.integer = span<UC const>(start_digits, digit_count);
}
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if fastfloat_unlikely (json_fmt && digit_count == 0) {
// At least 1 digit in integer part
return report_parse_error<UC>(answer, p,
parse_error::no_digits_in_integer_part);
}
if (json_fmt || javascript_fmt) {
// ECMAScript DecimalIntegerLiteral is "0" or a non-zero digit followed by
// digits: no leading zeros. Unlike JSON, the integer part may be empty
// (".5"); the no_digits_in_mantissa check below still rejects ".".
if fastfloat_unlikely (start_digits[0] == UC('0') && digit_count > 1) {
if fastfloat_unlikely (json_fmt ||
!chars_format_t(options.format &
detail::javascript_sloppy_fmt)) {
return report_parse_error<UC>(
answer, start_digits, parse_error::leading_zeros_in_integer_part);
}
if (javascript_fmt) {
// Sloppy mode (Annex B): a NonOctalDecimalIntegerLiteral has a leading
// zero and at least one digit that is 8 or 9 ("08.5" is 8.5). With
// octal digits only, it is a LegacyOctalIntegerLiteral ("0775"), which
// is not a decimal number: report it so the caller can parse it in
// base 8.
bool has_non_octal_digit = false;
for (UC const *q = start_digits; q != end_of_integer_part; ++q) {
has_non_octal_digit |= (*q >= UC('8'));
}
if fastfloat_unlikely (!has_non_octal_digit) {
return report_parse_error<UC>(answer, start_digits,
parse_error::legacy_octal_integer_part);
}
}
}
}
#endif
// We can now parse the fraction part of the mantissa.
bool const has_decimal_point = p != pend && *p == options.decimal_point;
if (has_decimal_point) {
++p;
auto const *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_digits(p, pend, answer.mantissa);
answer.exponent = static_cast<am_pow_t>(before - p);
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if fastfloat_unlikely (json_fmt && answer.exponent == 0) {
// At least 1 digit in fractional part
return report_parse_error<UC>(answer, p,
parse_error::no_digits_in_fractional_part);
}
#endif
if fastfloat_unlikely (store_spans) {
answer.fraction =
span<UC const>(before, static_cast<am_digits>(p - before));
}
digit_count -= static_cast<am_digits>(answer.exponent);
}
if fastfloat_unlikely (digit_count == 0) {
// We must have encountered at least one integer!
return report_parse_error<UC>(answer, p,
parse_error::no_digits_in_mantissa);
}
// We have now parsed the integer and the fraction part of the mantissa.
// Now we can parse the explicit exponential part.
am_pow_t exp_number = 0; // explicit exponential part
if (p != pend &&
((chars_format_t(options.format & chars_format::scientific) &&
(UC('e') == *p || UC('E') == *p))
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|| (chars_format_t(options.format & detail::fortran_fmt) &&
(UC('+') == *p || UC('-') == *p || UC('d') == *p || UC('D') == *p))
#endif
)) {
auto const *location_of_e = p;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (UC('e') == *p || UC('E') == *p || UC('d') == *p || UC('D') == *p) {
++p;
}
#else
++p;
#endif
bool neg_exp = false;
if (p != pend) {
if (UC('-') == *p) {
neg_exp = true;
++p;
} else if (UC('+') == *p) {
// '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p;
}
}
// We have now parsed the sign of the exponent.
if fastfloat_unlikely (p == pend || !is_integer(*p)) {
if fastfloat_unlikely (!chars_format_t(options.format &
chars_format::fixed)) {
// The exponential part is invalid for scientific notation, so it must
// be a trailing token for fixed notation. However, fixed notation is
// disabled, so report a scientific notation error.
return report_parse_error<UC>(answer, p,
parse_error::missing_exponential_part);
}
// Otherwise, we will be ignoring the 'e'.
p = location_of_e;
} else {
// Now let's parse the explicit exponent.
while (p != pend && is_integer(*p)) {
if fastfloat_likely (exp_number < am_bias_limit) {
// check for exponent overflow if we have big explicit exponent.
auto const digit = static_cast<uint8_t>(*p - UC('0'));
exp_number = 10 * exp_number + digit;
}
++p;
}
if (neg_exp) {
exp_number = -exp_number;
}
answer.exponent += exp_number;
}
} else if fastfloat_unlikely (chars_format_t(options.format &
chars_format::scientific) &&
!chars_format_t(options.format &
chars_format::fixed)) {
// If it scientific and not fixed, we have to bail out.
return report_parse_error<UC>(answer, p,
parse_error::missing_exponential_part);
}
// We sucessfully parsed all parts of the number, let's save progress.
answer.lastmatch = p;
// Now we can check for errors.
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon.
//
// We can deal with up to 19 digits.
if fastfloat_unlikely (digit_count > 19) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000.
auto const *start = start_digits;
do {
if (*start == UC('0')) {
--digit_count;
} else if (*start != options.decimal_point) {
break;
}
} while (++start != pend);
// We have to check if number has more than 19 significant digits.
if fastfloat_unlikely (digit_count > 19) {
answer.too_many_digits = true;
// The truncation recompute below reads the integer/fraction spans. When
// store_spans is false we didn't materialize them, so just flag
// too_many_digits; the caller re-parses with store_spans=true to obtain
// the corrected mantissa/exponent before taking the slow path.
if fastfloat_unlikely (store_spans) {
// Let us start again, this time, avoiding overflows.
// We don't need to call if is_integer, since we use the
// pre-tokenized spans from above.
answer.mantissa = 0;
p = answer.integer.ptr;
parse_digits_until_19(p, p + answer.integer.len(), answer.mantissa);
if (answer.mantissa >= minimal_nineteen_digit_integer) {
// We have a big integers, so skip the fraction part completely.
answer.exponent = am_pow_t(end_of_integer_part - p) + exp_number;
} else if (answer.fraction.len()) {
// We have a value with a significant fractional component.
p = answer.fraction.ptr;
parse_digits_until_19(p, p + answer.fraction.len(), answer.mantissa);
answer.exponent = am_pow_t(answer.fraction.ptr - p) + exp_number;
}
// We have now corrected both exponent and mantissa, to a truncated
// value
}
}
}
return answer;
}
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// Cold instantiation of the parser: the ECMAScript integer-part rule costs two
// error returns plus the Annex B octal scan, and parse_number_string_impl is
// force-inlined, so an inlined javascript body would enlarge the frame of every
// caller that never asks for it. Out of line, it costs those callers nothing.
template <typename UC>
fastfloat_noinline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
parse_number_string_javascript(UC const *p, UC const *pend,
parse_options_t<UC> const options,
bool const store_spans) noexcept {
return parse_number_string_impl<false, true, UC>(p, pend, options,
store_spans);
}
// Public entry point, behaviour unchanged: chars_format::javascript is still
// honoured, it is just selected here once instead of being re-tested inside the
// parser loop. Callers that have already ruled the format out (see
// from_chars_float_advanced) should call parse_number_string_impl directly so
// that not even this test reaches their hot path.
template <bool basic_json_fmt, typename UC>
fastfloat_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
parse_number_string(UC const *p, UC const *pend,
parse_options_t<UC> const options,
bool const store_spans = true) noexcept {
// JSON and JavaScript are mutually exclusive, so only the non-JSON
// instantiation has to look at the flag.
if (!basic_json_fmt) {
if fastfloat_unlikely (chars_format_t(options.format &
detail::javascript_fmt)) {
return parse_number_string_javascript<UC>(p, pend, options, store_spans);
}
}
return parse_number_string_impl<basic_json_fmt, false, UC>(p, pend, options,
store_spans);
}
#endif
template <typename T, typename UC>
fastfloat_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
parse_int_string(UC const *p, UC const *pend, T &value,
parse_options_t<UC> const options) noexcept {
FASTFLOAT_ASSUME(p < pend); // so dereference without checks
from_chars_result_t<UC> answer;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
auto const *const first = p;
// Read sign
auto const negative = (*p == UC('-'));
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
#pragma warning(disable : 4127)
#endif
if (!std::is_signed<T>::value && negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop)
#endif
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
if (negative ||
(chars_format_t(options.format & chars_format::allow_leading_plus) &&
(*p == UC('+')))) {
++p;
}
#endif
auto const *const start_num = p;
// Skip leading zeros
#if !defined(FASTFLOAT_ISNOT_CHECKED_BOUNDS) && \
!defined(FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN)
while (p != pend && *p == UC('0')) {
++p;
}
#else
// External parser already check that this is num and it's exist
do {
if (*p == UC('0')) {
++p;
} else {
break;
}
} while (p != pend);
#endif
auto const has_leading_zeros = p > start_num;
auto const *const start_digits = p;
if (options.base == 10) {
auto const len = static_cast<am_digits>(pend - p);
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
// External parser already check that this is num and it's exist
if (len == 0) {
#endif
if (has_leading_zeros) {
value = 0;
answer.ec = std::errc();
answer.ptr = p;
return answer;
}
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
#endif
if FASTFLOAT_CONSTEXPR17 (std::is_same<T, std::uint8_t>::value &&
sizeof(UC) == 1) {
uint32_t digits;
if (len >= sizeof(uint32_t)) {
digits = read_chars_to_unsigned<uint32_t>(p);
} else {
uint32_t const b0 = static_cast<uint8_t>(p[0]);
uint32_t const b1 = (len > 1) ? static_cast<uint8_t>(p[1]) : 0x00u;
uint32_t const b2 = (len > 2) ? static_cast<uint8_t>(p[2]) : 0x00u;
uint32_t const b3 = 0x00u;
digits = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
}
uint32_t const magic =
((digits + 0x46464646u) | (digits - 0x30303030u)) & 0x80808080u;
auto const tz = countr_zero_32(magic); // 7, 15, 23, 31, or 32
auto nd = static_cast<am_digits>(tz >> 3);
nd = nd < len ? nd : len;
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
// External parser already check that this is num and it's exist
if (nd == 0) {
#endif
if (has_leading_zeros) {
value = 0;
answer.ec = std::errc();
answer.ptr = p;
return answer;
}
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
#endif
if (nd > 3) {
const UC *q = p + nd;
am_digits rem = len - nd;
while (rem) {
if (*q < UC('0') || *q > UC('9')) {
break;
}
++q;
--rem;
}
answer.ec = std::errc::result_out_of_range;
answer.ptr = q;
return answer;
}
digits ^= 0x30303030u;
digits <<= ((4 - nd) * 8);
uint32_t const check = ((digits >> 24) & 0xff) |
((digits >> 8) & 0xff00) |
((digits << 8) & 0xff0000);
if (check > 0x00020505) {
answer.ec = std::errc::result_out_of_range;
answer.ptr = p + nd;
return answer;
}
value = static_cast<uint8_t>((0x640a01 * digits) >> 24);
answer.ec = std::errc();
answer.ptr = p + nd;
return answer;
}
if FASTFLOAT_CONSTEXPR17 (std::is_same<T, std::uint16_t>::value &&
sizeof(UC) == 1) {
if (len >= sizeof(uint32_t)) {
auto const digits = read_chars_to_unsigned<uint32_t>(p);
if (is_made_of_4_digits(digits)) {
auto v = parse_4_digits(digits);
if (len >= 5 && is_integer(p[4])) {
v = v * 10 + static_cast<uint8_t>(p[4] - '0');
if (len >= 6 && is_integer(p[5])) {
const UC *q = p + 5;
while (q != pend && is_integer(*q)) {
++q;
}
answer.ec = std::errc::result_out_of_range;
answer.ptr = q;
return answer;
}
if (v > std::numeric_limits<uint16_t>::max()) {
answer.ec = std::errc::result_out_of_range;
answer.ptr = p + 5;
return answer;
}
value = static_cast<uint16_t>(v);
answer.ec = std::errc();
answer.ptr = p + 5;
return answer;
}
// 4 digits
value = static_cast<uint16_t>(v);
answer.ec = std::errc();
answer.ptr = p + 4;
return answer;
}
}
}
}
// Parse digits
am_mant_t i = 0;
if (options.base == 10) {
loop_parse_if_digits(p, pend, i); // use SIMD if possible
} else
while (p != pend) {
auto const digit = ch_to_digit(*p);
if (digit >= options.base) {
break;
}
i = am_mant_t(options.base) * i +
digit; // might overflow, check this later
++p;
}
auto const digit_count = static_cast<am_digits>(p - start_digits);
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
// parser already check that this is num and it's exist
if (digit_count == 0) {
#endif
if (has_leading_zeros) {
value = 0;
answer.ec = std::errc();
answer.ptr = p;
return answer;
}
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
#endif
answer.ptr = p;
// check u64 overflow
auto const max_digits = max_digits_u64(options.base);
if (digit_count > max_digits) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
// this check can be eliminated for all other types, but they will all require
// a max_digits(base) equivalent
if (digit_count == max_digits) {
// At the max_digits boundary the accumulator `i` may have wrapped around
// 2^64. A plain `i < min_safe_u64(base)` test is not sufficient: for any
// base whose max_digits-length range exceeds 2^64 (base 10 reaches
// ~5.4 * 2^64 at 20 digits) the value can wrap a whole multiple of 2^64 and
// land back above min_safe, slipping through. Decide exactly in O(1) using
// the leading digit, following the approach used in simdjson:
// ms == min_safe_u64(base) == base^(max_digits-1), the smallest
// max_digits-length value.
// dmax == the largest leading digit whose number can still fit in u64.
// The leading-digit band [d*ms, (d+1)*ms) has width ms < 2^64, so within
// the single band where d == dmax the value straddles 2^64 at most once,
// and a single threshold separates wrapped from non-wrapped values. A
// leading digit above dmax always overflows; below dmax always fits.
uint64_t const ms = min_safe_u64(options.base);
uint64_t const dmax = std::numeric_limits<uint64_t>::max() / ms;
uint64_t const lead = ch_to_digit(*start_digits);
if (lead > dmax || (lead == dmax && i < dmax * ms)) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
}
// check other types overflow
if (!std::is_same<T, am_mant_t>::value) {
if (i > am_mant_t(std::numeric_limits<T>::max())
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
+ uint8_t(negative)
#endif
) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
}
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
value = T(i);
#else
if (negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
#pragma warning(disable : 4146)
#pragma warning(disable : 4804)
#endif
// this weird workaround is required because:
// - converting unsigned to signed when its value is greater than signed max
// is UB pre-C++23.
// - reinterpret_casting (~i + 1) would work, but it is not constexpr
// this is always optimized into a neg instruction (note: T is an integer
// type)
value = T(-std::numeric_limits<T>::max() -
T(i - am_mant_t(std::numeric_limits<T>::max())));
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop)
#endif
} else {
value = T(i);
}
#endif
answer.ec = std::errc();
return answer;
}
} // namespace fast_float
#endif