X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FConv.cpp;h=6af0f90fbecd51754c6dcf9b05a82ca2d1d9aa4a;hp=6dfede7ce6487d020974d56ed530ee0cc57662ea;hb=8a56900338f3fccf82a35f5e4de9e9680abb05ad;hpb=5c77fedbef46995a71ffa268c9fcaf49efddd01b diff --git a/folly/Conv.cpp b/folly/Conv.cpp index 6dfede7c..6af0f90f 100644 --- a/folly/Conv.cpp +++ b/folly/Conv.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,20 +13,43 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define FOLLY_CONV_INTERNAL -#include "folly/Conv.h" +#include +#include namespace folly { namespace detail { -extern const char digit1[101] = - "00000000001111111111222222222233333333334444444444" - "55555555556666666666777777777788888888889999999999"; -extern const char digit2[101] = - "01234567890123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789"; +namespace { + +/** + * Finds the first non-digit in a string. The number of digits + * searched depends on the precision of the Tgt integral. Assumes the + * string starts with NO whitespace and NO sign. + * + * The semantics of the routine is: + * for (;; ++b) { + * if (b >= e || !isdigit(*b)) return b; + * } + * + * Complete unrolling marks bottom-line (i.e. entire conversion) + * improvements of 20%. + */ +inline const char* findFirstNonDigit(const char* b, const char* e) { + for (; b < e; ++b) { + auto const c = static_cast(*b) - '0'; + if (c >= 10) { + break; + } + } + return b; +} + +// Maximum value of number when represented as a string +template +struct MaxString { + static const char* const value; +}; -template <> const char *const MaxString::value = "true"; template <> const char *const MaxString::value = "255"; template <> const char *const MaxString::value = "65535"; template <> const char *const MaxString::value = "4294967295"; @@ -46,13 +69,182 @@ static_assert(sizeof(unsigned long long) >= 8, "Wrong value for MaxString::value" ", please update."); +#if FOLLY_HAVE_INT128_T +template <> const char *const MaxString<__uint128_t>::value = + "340282366920938463463374607431768211455"; +#endif + +/* + * Lookup tables that converts from a decimal character value to an integral + * binary value, shifted by a decimal "shift" multiplier. + * For all character values in the range '0'..'9', the table at those + * index locations returns the actual decimal value shifted by the multiplier. + * For all other values, the lookup table returns an invalid OOR value. + */ +// Out-of-range flag value, larger than the largest value that can fit in +// four decimal bytes (9999), but four of these added up together should +// still not overflow uint16_t. +constexpr int32_t OOR = 10000; + +FOLLY_ALIGNED(16) constexpr uint16_t shift1[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40 + 1, 2, 3, 4, 5, 6, 7, 8, 9, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +FOLLY_ALIGNED(16) constexpr uint16_t shift10[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40 + 10, 20, 30, 40, 50, 60, 70, 80, 90, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +FOLLY_ALIGNED(16) constexpr uint16_t shift100[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40 + 100, 200, 300, 400, 500, 600, 700, 800, 900, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +FOLLY_ALIGNED(16) constexpr uint16_t shift1000[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40 + 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +struct ErrorString { + const char* string; + bool quote; +}; + +// Keep this in sync with ConversionCode in Conv.h +constexpr const std::array< + ErrorString, + static_cast(ConversionCode::NUM_ERROR_CODES)> + kErrorStrings{{ + {"Success", true}, + {"Empty input string", true}, + {"No digits found in input string", true}, + {"Integer overflow when parsing bool (must be 0 or 1)", true}, + {"Invalid value for bool", true}, + {"Non-digit character found", true}, + {"Invalid leading character", true}, + {"Overflow during conversion", true}, + {"Negative overflow during conversion", true}, + {"Unable to convert string to floating point value", true}, + {"Non-whitespace character found after end of conversion", true}, + {"Overflow during arithmetic conversion", false}, + {"Negative overflow during arithmetic conversion", false}, + {"Loss of precision during arithmetic conversion", false}, + }}; + +// Check if ASCII is really ASCII +using IsAscii = std:: + integral_constant; + +// The code in this file that uses tolower() really only cares about +// 7-bit ASCII characters, so we can take a nice shortcut here. +inline char tolower_ascii(char in) { + return IsAscii::value ? in | 0x20 : char(std::tolower(in)); +} + inline bool bool_str_cmp(const char** b, size_t len, const char* value) { // Can't use strncasecmp, since we want to ensure that the full value matches const char* p = *b; const char* e = *b + len; const char* v = value; while (*v != '\0') { - if (p == e || tolower(*p) != *v) { // value is already lowercase + if (p == e || tolower_ascii(*p) != *v) { // value is already lowercase return false; } ++p; @@ -63,27 +255,31 @@ inline bool bool_str_cmp(const char** b, size_t len, const char* value) { return true; } -bool str_to_bool(StringPiece* src) { +} // anonymous namespace + +Expected str_to_bool(StringPiece* src) noexcept { auto b = src->begin(), e = src->end(); for (;; ++b) { - FOLLY_RANGE_CHECK(b < e, - "No non-whitespace characters found in input string"); - if (!isspace(*b)) break; + if (b >= e) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + if (!std::isspace(*b)) { + break; + } } bool result; - size_t len = e - b; + size_t len = size_t(e - b); switch (*b) { case '0': case '1': { - // Attempt to parse the value as an integer - StringPiece tmp(*src); - uint8_t value = to(&tmp); - // Only accept 0 or 1 - FOLLY_RANGE_CHECK(value <= 1, - "Integer overflow when parsing bool: must be 0 or 1"); - b = tmp.begin(); - result = (value == 1); + result = false; + for (; b < e && isdigit(*b); ++b) { + if (result || (*b != '0' && *b != '1')) { + return makeUnexpected(ConversionCode::BOOL_OVERFLOW); + } + result = (*b == '1'); + } break; } case 'y': @@ -121,16 +317,449 @@ bool str_to_bool(StringPiece* src) { } else if (bool_str_cmp(&b, len, "off")) { result = false; } else { - FOLLY_RANGE_CHECK(false, "Invalid value for bool"); + return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); } break; default: - FOLLY_RANGE_CHECK(false, "Invalid value for bool"); + return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); } src->assign(b, e); + return result; } +/** + * StringPiece to double, with progress information. Alters the + * StringPiece parameter to munch the already-parsed characters. + */ +template +Expected str_to_floating(StringPiece* src) noexcept { + using namespace double_conversion; + static StringToDoubleConverter + conv(StringToDoubleConverter::ALLOW_TRAILING_JUNK + | StringToDoubleConverter::ALLOW_LEADING_SPACES, + 0.0, + // return this for junk input string + std::numeric_limits::quiet_NaN(), + nullptr, nullptr); + + if (src->empty()) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + + int length; + auto result = conv.StringToDouble(src->data(), + static_cast(src->size()), + &length); // processed char count + + if (!std::isnan(result)) { + // If we get here with length = 0, the input string is empty. + // If we get here with result = 0.0, it's either because the string + // contained only whitespace, or because we had an actual zero value + // (with potential trailing junk). If it was only whitespace, we + // want to raise an error; length will point past the last character + // that was processed, so we need to check if that character was + // whitespace or not. + if (length == 0 || + (result == 0.0 && std::isspace((*src)[size_t(length) - 1]))) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + src->advance(size_t(length)); + return Tgt(result); + } + + auto* e = src->end(); + auto* b = + std::find_if_not(src->begin(), e, [](char c) { return std::isspace(c); }); + + // There must be non-whitespace, otherwise we would have caught this above + assert(b < e); + size_t size = size_t(e - b); + + bool negative = false; + if (*b == '-') { + negative = true; + ++b; + --size; + } + + result = 0.0; + + switch (tolower_ascii(*b)) { + case 'i': + if (size >= 3 && tolower_ascii(b[1]) == 'n' && + tolower_ascii(b[2]) == 'f') { + if (size >= 8 && tolower_ascii(b[3]) == 'i' && + tolower_ascii(b[4]) == 'n' && tolower_ascii(b[5]) == 'i' && + tolower_ascii(b[6]) == 't' && tolower_ascii(b[7]) == 'y') { + b += 8; + } else { + b += 3; + } + result = std::numeric_limits::infinity(); + } + break; + + case 'n': + if (size >= 3 && tolower_ascii(b[1]) == 'a' && + tolower_ascii(b[2]) == 'n') { + b += 3; + result = std::numeric_limits::quiet_NaN(); + } + break; + + default: + break; + } + + if (result == 0.0) { + // All bets are off + return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR); + } + + if (negative) { + result = -result; + } + + src->assign(b, e); + + return Tgt(result); +} + +template Expected str_to_floating( + StringPiece* src) noexcept; +template Expected str_to_floating( + StringPiece* src) noexcept; + +/** + * This class takes care of additional processing needed for signed values, + * like leading sign character and overflow checks. + */ +template ::value> +class SignedValueHandler; + +template +class SignedValueHandler { + public: + ConversionCode init(const char*& b) { + negative_ = false; + if (!std::isdigit(*b)) { + if (*b == '-') { + negative_ = true; + } else if (UNLIKELY(*b != '+')) { + return ConversionCode::INVALID_LEADING_CHAR; + } + ++b; + } + return ConversionCode::SUCCESS; + } + + ConversionCode overflow() { + return negative_ ? ConversionCode::NEGATIVE_OVERFLOW + : ConversionCode::POSITIVE_OVERFLOW; + } + + template + Expected finalize(U value) { + T rv; + if (negative_) { + rv = T(-value); + if (UNLIKELY(rv > 0)) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + } else { + rv = T(value); + if (UNLIKELY(rv < 0)) { + return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); + } + } + return rv; + } + + private: + bool negative_; +}; + +// For unsigned types, we don't need any extra processing +template +class SignedValueHandler { + public: + ConversionCode init(const char*&) { + return ConversionCode::SUCCESS; + } + + ConversionCode overflow() { + return ConversionCode::POSITIVE_OVERFLOW; + } + + Expected finalize(T value) { + return value; + } +}; + +/** + * String represented as a pair of pointers to char to signed/unsigned + * integrals. Assumes NO whitespace before or after, and also that the + * string is composed entirely of digits (and an optional sign only for + * signed types). String may be empty, in which case digits_to returns + * an appropriate error. + */ +template +inline Expected digits_to( + const char* b, + const char* const e) noexcept { + using UT = typename std::make_unsigned::type; + assert(b <= e); + + SignedValueHandler sgn; + + auto err = sgn.init(b); + if (UNLIKELY(err != ConversionCode::SUCCESS)) { + return makeUnexpected(err); + } + + size_t size = size_t(e - b); + + /* Although the string is entirely made of digits, we still need to + * check for overflow. + */ + if (size > std::numeric_limits::digits10) { + // Leading zeros? + if (b < e && *b == '0') { + for (++b;; ++b) { + if (b == e) { + return Tgt(0); // just zeros, e.g. "0000" + } + if (*b != '0') { + size = size_t(e - b); + break; + } + } + } + if (size > std::numeric_limits::digits10 && + (size != std::numeric_limits::digits10 + 1 || + strncmp(b, MaxString::value, size) > 0)) { + return makeUnexpected(sgn.overflow()); + } + } + + // Here we know that the number won't overflow when + // converted. Proceed without checks. + + UT result = 0; + + for (; e - b >= 4; b += 4) { + result *= static_cast(10000); + const int32_t r0 = shift1000[static_cast(b[0])]; + const int32_t r1 = shift100[static_cast(b[1])]; + const int32_t r2 = shift10[static_cast(b[2])]; + const int32_t r3 = shift1[static_cast(b[3])]; + const auto sum = r0 + r1 + r2 + r3; + if (sum >= OOR) { + goto outOfRange; + } + result += UT(sum); + } + + switch (e - b) { + case 3: { + const int32_t r0 = shift100[static_cast(b[0])]; + const int32_t r1 = shift10[static_cast(b[1])]; + const int32_t r2 = shift1[static_cast(b[2])]; + const auto sum = r0 + r1 + r2; + if (sum >= OOR) { + goto outOfRange; + } + result = UT(1000 * result + sum); + break; + } + case 2: { + const int32_t r0 = shift10[static_cast(b[0])]; + const int32_t r1 = shift1[static_cast(b[1])]; + const auto sum = r0 + r1; + if (sum >= OOR) { + goto outOfRange; + } + result = UT(100 * result + sum); + break; + } + case 1: { + const int32_t sum = shift1[static_cast(b[0])]; + if (sum >= OOR) { + goto outOfRange; + } + result = UT(10 * result + sum); + break; + } + default: + assert(b == e); + if (size == 0) { + return makeUnexpected(ConversionCode::NO_DIGITS); + } + break; + } + + return sgn.finalize(result); + +outOfRange: + return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); +} + +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; + +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; + +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; + +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; + +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected +digits_to(const char*, const char*) noexcept; + +#if FOLLY_HAVE_INT128_T +template Expected<__int128, ConversionCode> digits_to<__int128>( + const char*, + const char*) noexcept; +template Expected +digits_to(const char*, const char*) noexcept; +#endif + +/** + * StringPiece to integrals, with progress information. Alters the + * StringPiece parameter to munch the already-parsed characters. + */ +template +Expected str_to_integral(StringPiece* src) noexcept { + using UT = typename std::make_unsigned::type; + + auto b = src->data(), past = src->data() + src->size(); + + for (;; ++b) { + if (UNLIKELY(b >= past)) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + if (!std::isspace(*b)) { + break; + } + } + + SignedValueHandler sgn; + auto err = sgn.init(b); + + if (UNLIKELY(err != ConversionCode::SUCCESS)) { + return makeUnexpected(err); + } + if (std::is_signed::value && UNLIKELY(b >= past)) { + return makeUnexpected(ConversionCode::NO_DIGITS); + } + if (UNLIKELY(!isdigit(*b))) { + return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); + } + + auto m = findFirstNonDigit(b + 1, past); + + auto tmp = digits_to(b, m); + + if (UNLIKELY(!tmp.hasValue())) { + return makeUnexpected( + tmp.error() == ConversionCode::POSITIVE_OVERFLOW ? sgn.overflow() + : tmp.error()); + } + + auto res = sgn.finalize(tmp.value()); + + if (res.hasValue()) { + src->advance(size_t(m - src->data())); + } + + return res; +} + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected +str_to_integral(StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected +str_to_integral(StringPiece* src) noexcept; + +#if FOLLY_HAVE_INT128_T +template Expected<__int128, ConversionCode> str_to_integral<__int128>( + StringPiece* src) noexcept; +template Expected +str_to_integral(StringPiece* src) noexcept; +#endif + } // namespace detail + +ConversionError makeConversionError(ConversionCode code, StringPiece input) { + using namespace detail; + static_assert( + std::is_unsigned::type>::value, + "ConversionCode should be unsigned"); + assert((std::size_t)code < kErrorStrings.size()); + const ErrorString& err = kErrorStrings[(std::size_t)code]; + if (code == ConversionCode::EMPTY_INPUT_STRING && input.empty()) { + return {err.string, code}; + } + std::string tmp(err.string); + tmp.append(": "); + if (err.quote) { + tmp.append(1, '"'); + } + if (input.size() > 0) { + tmp.append(input.data(), input.size()); + } + if (err.quote) { + tmp.append(1, '"'); + } + return {tmp, code}; +} + } // namespace folly