+ return sgn.finalize(result);
+
+outOfRange:
+ return makeUnexpected(ConversionCode::NON_DIGIT_CHAR);
+}
+
+template Expected<char, ConversionCode> digits_to<char>(
+ const char*,
+ const char*) noexcept;
+template Expected<signed char, ConversionCode> digits_to<signed char>(
+ const char*,
+ const char*) noexcept;
+template Expected<unsigned char, ConversionCode> digits_to<unsigned char>(
+ const char*,
+ const char*) noexcept;
+
+template Expected<short, ConversionCode> digits_to<short>(
+ const char*,
+ const char*) noexcept;
+template Expected<unsigned short, ConversionCode> digits_to<unsigned short>(
+ const char*,
+ const char*) noexcept;
+
+template Expected<int, ConversionCode> digits_to<int>(
+ const char*,
+ const char*) noexcept;
+template Expected<unsigned int, ConversionCode> digits_to<unsigned int>(
+ const char*,
+ const char*) noexcept;
+
+template Expected<long, ConversionCode> digits_to<long>(
+ const char*,
+ const char*) noexcept;
+template Expected<unsigned long, ConversionCode> digits_to<unsigned long>(
+ const char*,
+ const char*) noexcept;
+
+template Expected<long long, ConversionCode> digits_to<long long>(
+ const char*,
+ const char*) noexcept;
+template Expected<unsigned long long, ConversionCode>
+digits_to<unsigned long long>(const char*, const char*) noexcept;
+
+#if FOLLY_HAVE_INT128_T
+template Expected<__int128, ConversionCode> digits_to<__int128>(
+ const char*,
+ const char*) noexcept;
+template Expected<unsigned __int128, ConversionCode>
+digits_to<unsigned __int128>(const char*, const char*) noexcept;
+#endif
+
+/**
+ * StringPiece to integrals, with progress information. Alters the
+ * StringPiece parameter to munch the already-parsed characters.
+ */
+template <class Tgt>
+Expected<Tgt, ConversionCode> str_to_integral(StringPiece* src) noexcept {
+ using UT = typename std::make_unsigned<Tgt>::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<Tgt> sgn;
+ auto err = sgn.init(b);
+
+ if (UNLIKELY(err != ConversionCode::SUCCESS)) {
+ return makeUnexpected(err);
+ }
+ if (std::is_signed<Tgt>::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<UT>(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;