in folly/Conv.cpp [345:458]
Expected<Tgt, ConversionCode> 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<Tgt>::quiet_NaN(),
nullptr,
nullptr);
if (src->empty()) {
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
}
int length; // processed char count
auto result = std::is_same<Tgt, float>::value
? conv.StringToFloat(src->data(), static_cast<int>(src->size()), &length)
: static_cast<Tgt>(conv.StringToDouble(
src->data(), static_cast<int>(src->size()), &length));
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);
}
if (length >= 2) {
const char* suffix = src->data() + length - 1;
// double_conversion doesn't update length correctly when there is an
// incomplete exponent specifier. Converting "12e-f-g" shouldn't consume
// any more than "12", but it will consume "12e-".
// "123-" should only parse "123"
if (*suffix == '-' || *suffix == '+') {
--suffix;
--length;
}
// "12e-f-g" or "12euro" should only parse "12"
if (*suffix == 'e' || *suffix == 'E') {
--length;
}
}
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); });
if (b == e) {
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
}
auto size = size_t(e - b);
bool negative = false;
if (*b == '-') {
negative = true;
++b;
--size;
if (size == 0) {
return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR);
}
}
assert(size > 0);
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<Tgt>::infinity();
}
break;
case 'n':
if (size >= 3 && tolower_ascii(b[1]) == 'a' &&
tolower_ascii(b[2]) == 'n') {
b += 3;
result = std::numeric_limits<Tgt>::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);
}