/* * Copyright 2016 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include namespace folly { namespace detail { 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 = "255"; template <> const char *const MaxString::value = "65535"; template <> const char *const MaxString::value = "4294967295"; #if __SIZEOF_LONG__ == 4 template <> const char *const MaxString::value = "4294967295"; #else template <> const char *const MaxString::value = "18446744073709551615"; #endif static_assert(sizeof(unsigned long) >= 4, "Wrong value for MaxString::value," " please update."); template <> const char *const MaxString::value = "18446744073709551615"; 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 : 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_ascii(*p) != *v) { // value is already lowercase return false; } ++p; ++v; } *b = p; return true; } } // anonymous namespace Expected str_to_bool(StringPiece* src) noexcept { auto b = src->begin(), e = src->end(); for (;; ++b) { if (b >= e) { return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); } if (!std::isspace(*b)) { break; } } bool result; size_t len = e - b; switch (*b) { case '0': case '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': case 'Y': result = true; if (!bool_str_cmp(&b, len, "yes")) { ++b; // accept the single 'y' character } break; case 'n': case 'N': result = false; if (!bool_str_cmp(&b, len, "no")) { ++b; } break; case 't': case 'T': result = true; if (!bool_str_cmp(&b, len, "true")) { ++b; } break; case 'f': case 'F': result = false; if (!bool_str_cmp(&b, len, "false")) { ++b; } break; case 'o': case 'O': if (bool_str_cmp(&b, len, "on")) { result = true; } else if (bool_str_cmp(&b, len, "off")) { result = false; } else { return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); } break; default: 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)[length - 1]))) { return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); } src->advance(length); return 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 = 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 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 = -value; if (UNLIKELY(rv > 0)) { return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); } } else { rv = 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 = 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 = 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 *= 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 += 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 = 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 = 100 * result + sum; break; } case 1: { const int32_t sum = shift1[static_cast(b[0])]; if (sum >= OOR) { goto outOfRange; } result = 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(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