| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "absl/strings/internal/charconv_parse.h" |
| #include "absl/strings/charconv.h" |
|
|
| #include <cassert> |
| #include <cstdint> |
| #include <limits> |
|
|
| #include "absl/strings/internal/memutil.h" |
|
|
| namespace absl { |
| ABSL_NAMESPACE_BEGIN |
| namespace { |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| constexpr int kDecimalMantissaDigitsMax = 19; |
|
|
| static_assert(std::numeric_limits<uint64_t>::digits10 == |
| kDecimalMantissaDigitsMax, |
| "(a) above"); |
|
|
| |
| static_assert(std::numeric_limits<double>::is_iec559, "IEEE double assumed"); |
| static_assert(std::numeric_limits<double>::radix == 2, "IEEE double fact"); |
| static_assert(std::numeric_limits<double>::digits == 53, "IEEE double fact"); |
|
|
| |
| |
| static_assert(1000000000000000000u > (uint64_t{1} << (53 + 3)), "(b) above"); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| constexpr int kHexadecimalMantissaDigitsMax = 15; |
|
|
| |
| |
| |
| |
| constexpr int kGuaranteedHexadecimalMantissaBitPrecision = |
| 4 * kHexadecimalMantissaDigitsMax - 3; |
|
|
| static_assert(kGuaranteedHexadecimalMantissaBitPrecision > |
| std::numeric_limits<double>::digits + 2, |
| "kHexadecimalMantissaDigitsMax too small"); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| constexpr int kDecimalExponentDigitsMax = 9; |
| static_assert(std::numeric_limits<int>::digits10 >= kDecimalExponentDigitsMax, |
| "int type too small"); |
|
|
| |
| |
| |
| |
| constexpr int kDecimalDigitLimit = 50000000; |
|
|
| |
| |
| |
| constexpr int kHexadecimalDigitLimit = kDecimalDigitLimit / 4; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static_assert(999999999 + 2 * kDecimalDigitLimit < |
| std::numeric_limits<int>::max(), |
| "int type too small"); |
| static_assert(999999999 + 2 * (4 * kHexadecimalDigitLimit) < |
| std::numeric_limits<int>::max(), |
| "int type too small"); |
|
|
| |
| |
| bool AllowExponent(chars_format flags) { |
| bool fixed = (flags & chars_format::fixed) == chars_format::fixed; |
| bool scientific = |
| (flags & chars_format::scientific) == chars_format::scientific; |
| return scientific || !fixed; |
| } |
|
|
| |
| bool RequireExponent(chars_format flags) { |
| bool fixed = (flags & chars_format::fixed) == chars_format::fixed; |
| bool scientific = |
| (flags & chars_format::scientific) == chars_format::scientific; |
| return scientific && !fixed; |
| } |
|
|
| const int8_t kAsciiToInt[256] = { |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, |
| 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, -1, -1, -1, -1, -1, -1}; |
|
|
| |
| template <int base> |
| bool IsDigit(char ch); |
|
|
| |
| template <int base> |
| unsigned ToDigit(char ch); |
|
|
| |
| template <int base> |
| bool IsExponentCharacter(char ch); |
|
|
| |
| |
| template <int base> |
| constexpr int MantissaDigitsMax(); |
|
|
| |
| |
| template <int base> |
| constexpr int DigitLimit(); |
|
|
| |
| |
| |
| |
| template <int base> |
| constexpr int DigitMagnitude(); |
|
|
| template <> |
| bool IsDigit<10>(char ch) { |
| return ch >= '0' && ch <= '9'; |
| } |
| template <> |
| bool IsDigit<16>(char ch) { |
| return kAsciiToInt[static_cast<unsigned char>(ch)] >= 0; |
| } |
|
|
| template <> |
| unsigned ToDigit<10>(char ch) { |
| return static_cast<unsigned>(ch - '0'); |
| } |
| template <> |
| unsigned ToDigit<16>(char ch) { |
| return static_cast<unsigned>(kAsciiToInt[static_cast<unsigned char>(ch)]); |
| } |
|
|
| template <> |
| bool IsExponentCharacter<10>(char ch) { |
| return ch == 'e' || ch == 'E'; |
| } |
|
|
| template <> |
| bool IsExponentCharacter<16>(char ch) { |
| return ch == 'p' || ch == 'P'; |
| } |
|
|
| template <> |
| constexpr int MantissaDigitsMax<10>() { |
| return kDecimalMantissaDigitsMax; |
| } |
| template <> |
| constexpr int MantissaDigitsMax<16>() { |
| return kHexadecimalMantissaDigitsMax; |
| } |
|
|
| template <> |
| constexpr int DigitLimit<10>() { |
| return kDecimalDigitLimit; |
| } |
| template <> |
| constexpr int DigitLimit<16>() { |
| return kHexadecimalDigitLimit; |
| } |
|
|
| template <> |
| constexpr int DigitMagnitude<10>() { |
| return 1; |
| } |
| template <> |
| constexpr int DigitMagnitude<16>() { |
| return 4; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <int base, typename T> |
| int ConsumeDigits(const char* begin, const char* end, int max_digits, T* out, |
| bool* dropped_nonzero_digit) { |
| if (base == 10) { |
| assert(max_digits <= std::numeric_limits<T>::digits10); |
| } else if (base == 16) { |
| assert(max_digits * 4 <= std::numeric_limits<T>::digits); |
| } |
| const char* const original_begin = begin; |
|
|
| |
| |
| |
| while (!*out && end != begin && *begin == '0') ++begin; |
|
|
| T accumulator = *out; |
| const char* significant_digits_end = |
| (end - begin > max_digits) ? begin + max_digits : end; |
| while (begin < significant_digits_end && IsDigit<base>(*begin)) { |
| |
| |
| auto digit = static_cast<T>(ToDigit<base>(*begin)); |
| assert(accumulator * base >= accumulator); |
| accumulator *= base; |
| assert(accumulator + digit >= accumulator); |
| accumulator += digit; |
| ++begin; |
| } |
| bool dropped_nonzero = false; |
| while (begin < end && IsDigit<base>(*begin)) { |
| dropped_nonzero = dropped_nonzero || (*begin != '0'); |
| ++begin; |
| } |
| if (dropped_nonzero && dropped_nonzero_digit != nullptr) { |
| *dropped_nonzero_digit = true; |
| } |
| *out = accumulator; |
| return static_cast<int>(begin - original_begin); |
| } |
|
|
| |
| |
| bool IsNanChar(char v) { |
| return (v == '_') || (v >= '0' && v <= '9') || (v >= 'a' && v <= 'z') || |
| (v >= 'A' && v <= 'Z'); |
| } |
|
|
| |
| |
| bool ParseInfinityOrNan(const char* begin, const char* end, |
| strings_internal::ParsedFloat* out) { |
| if (end - begin < 3) { |
| return false; |
| } |
| switch (*begin) { |
| case 'i': |
| case 'I': { |
| |
| |
| if (strings_internal::memcasecmp(begin + 1, "nf", 2) != 0) { |
| return false; |
| } |
| out->type = strings_internal::FloatType::kInfinity; |
| if (end - begin >= 8 && |
| strings_internal::memcasecmp(begin + 3, "inity", 5) == 0) { |
| out->end = begin + 8; |
| } else { |
| out->end = begin + 3; |
| } |
| return true; |
| } |
| case 'n': |
| case 'N': { |
| |
| |
| |
| if (strings_internal::memcasecmp(begin + 1, "an", 2) != 0) { |
| return false; |
| } |
| out->type = strings_internal::FloatType::kNan; |
| out->end = begin + 3; |
| |
| |
| begin += 3; |
| if (begin < end && *begin == '(') { |
| const char* nan_begin = begin + 1; |
| while (nan_begin < end && IsNanChar(*nan_begin)) { |
| ++nan_begin; |
| } |
| if (nan_begin < end && *nan_begin == ')') { |
| |
| out->subrange_begin = begin + 1; |
| out->subrange_end = nan_begin; |
| out->end = nan_begin + 1; |
| } |
| } |
| return true; |
| } |
| default: |
| return false; |
| } |
| } |
| } |
|
|
| namespace strings_internal { |
|
|
| template <int base> |
| strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end, |
| chars_format format_flags) { |
| strings_internal::ParsedFloat result; |
|
|
| |
| if (begin == end) return result; |
|
|
| |
| if (ParseInfinityOrNan(begin, end, &result)) { |
| return result; |
| } |
|
|
| const char* const mantissa_begin = begin; |
| while (begin < end && *begin == '0') { |
| ++begin; |
| } |
| uint64_t mantissa = 0; |
|
|
| int exponent_adjustment = 0; |
| bool mantissa_is_inexact = false; |
| int pre_decimal_digits = ConsumeDigits<base>( |
| begin, end, MantissaDigitsMax<base>(), &mantissa, &mantissa_is_inexact); |
| begin += pre_decimal_digits; |
| int digits_left; |
| if (pre_decimal_digits >= DigitLimit<base>()) { |
| |
| return result; |
| } else if (pre_decimal_digits > MantissaDigitsMax<base>()) { |
| |
| |
| exponent_adjustment = |
| static_cast<int>(pre_decimal_digits - MantissaDigitsMax<base>()); |
| digits_left = 0; |
| } else { |
| digits_left = |
| static_cast<int>(MantissaDigitsMax<base>() - pre_decimal_digits); |
| } |
| if (begin < end && *begin == '.') { |
| ++begin; |
| if (mantissa == 0) { |
| |
| |
| const char* begin_zeros = begin; |
| while (begin < end && *begin == '0') { |
| ++begin; |
| } |
| int zeros_skipped = static_cast<int>(begin - begin_zeros); |
| if (zeros_skipped >= DigitLimit<base>()) { |
| |
| return result; |
| } |
| exponent_adjustment -= static_cast<int>(zeros_skipped); |
| } |
| int post_decimal_digits = ConsumeDigits<base>( |
| begin, end, digits_left, &mantissa, &mantissa_is_inexact); |
| begin += post_decimal_digits; |
|
|
| |
| |
| |
| |
| if (post_decimal_digits >= DigitLimit<base>()) { |
| |
| return result; |
| } else if (post_decimal_digits > digits_left) { |
| exponent_adjustment -= digits_left; |
| } else { |
| exponent_adjustment -= post_decimal_digits; |
| } |
| } |
| |
| if (mantissa_begin == begin) { |
| return result; |
| } |
| |
| if (begin - mantissa_begin == 1 && *mantissa_begin == '.') { |
| return result; |
| } |
|
|
| if (mantissa_is_inexact) { |
| |
| if (base == 10) { |
| |
| |
| result.subrange_begin = mantissa_begin; |
| result.subrange_end = begin; |
| } else if (base == 16) { |
| |
| |
| mantissa |= 1; |
| } |
| } |
| result.mantissa = mantissa; |
|
|
| const char* const exponent_begin = begin; |
| result.literal_exponent = 0; |
| bool found_exponent = false; |
| if (AllowExponent(format_flags) && begin < end && |
| IsExponentCharacter<base>(*begin)) { |
| bool negative_exponent = false; |
| ++begin; |
| if (begin < end && *begin == '-') { |
| negative_exponent = true; |
| ++begin; |
| } else if (begin < end && *begin == '+') { |
| ++begin; |
| } |
| const char* const exponent_digits_begin = begin; |
| |
| begin += ConsumeDigits<10>(begin, end, kDecimalExponentDigitsMax, |
| &result.literal_exponent, nullptr); |
| if (begin == exponent_digits_begin) { |
| |
| |
| found_exponent = false; |
| begin = exponent_begin; |
| } else { |
| found_exponent = true; |
| if (negative_exponent) { |
| result.literal_exponent = -result.literal_exponent; |
| } |
| } |
| } |
|
|
| if (!found_exponent && RequireExponent(format_flags)) { |
| |
| |
| return result; |
| } |
|
|
| |
| result.type = strings_internal::FloatType::kNumber; |
| if (result.mantissa > 0) { |
| result.exponent = result.literal_exponent + |
| (DigitMagnitude<base>() * exponent_adjustment); |
| } else { |
| result.exponent = 0; |
| } |
| result.end = begin; |
| return result; |
| } |
|
|
| template ParsedFloat ParseFloat<10>(const char* begin, const char* end, |
| chars_format format_flags); |
| template ParsedFloat ParseFloat<16>(const char* begin, const char* end, |
| chars_format format_flags); |
|
|
| } |
| ABSL_NAMESPACE_END |
| } |
|
|