X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FConv.cpp;h=6af0f90fbecd51754c6dcf9b05a82ca2d1d9aa4a;hp=e11d8693a4ff95540bb2f3c30be8ff3cf861e3fb;hb=55af3d190b31e6b09943bb6456dc1085688fc007;hpb=60699fefadef15f769f24bfe3b174a9c3f252cba diff --git a/folly/Conv.cpp b/folly/Conv.cpp index e11d8693..6af0f90f 100644 --- a/folly/Conv.cpp +++ b/folly/Conv.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 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. @@ -207,8 +207,10 @@ struct ErrorString { bool quote; }; -// Keep this in sync with ConversionError::Code in Conv.h -constexpr const std::array +// 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}, @@ -233,7 +235,7 @@ using IsAscii = std:: // 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); + return IsAscii::value ? in | 0x20 : char(std::tolower(in)); } inline bool bool_str_cmp(const char** b, size_t len, const char* value) { @@ -255,34 +257,11 @@ inline bool bool_str_cmp(const char** b, size_t len, const char* value) { } // anonymous namespace -ConversionError makeConversionError( - ConversionError::Code code, - const char* input, - size_t inputLen) { - assert(code >= 0 && code < kErrorStrings.size()); - const ErrorString& err = kErrorStrings[code]; - if (code == ConversionError::EMPTY_INPUT_STRING && inputLen == 0) { - return ConversionError(err.string, code); - } - std::string tmp(err.string); - tmp.append(": "); - if (err.quote) { - tmp.append(1, '"'); - } - if (input && inputLen > 0) { - tmp.append(input, inputLen); - } - if (err.quote) { - tmp.append(1, '"'); - } - return ConversionError(tmp, code); -} - -ConversionResult str_to_bool(StringPiece* src) { +Expected str_to_bool(StringPiece* src) noexcept { auto b = src->begin(), e = src->end(); for (;; ++b) { if (b >= e) { - return ConversionResult(ConversionError::EMPTY_INPUT_STRING); + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); } if (!std::isspace(*b)) { break; @@ -290,14 +269,14 @@ ConversionResult str_to_bool(StringPiece* src) { } bool result; - size_t len = e - b; + size_t len = size_t(e - b); switch (*b) { case '0': case '1': { result = false; for (; b < e && isdigit(*b); ++b) { if (result || (*b != '0' && *b != '1')) { - return ConversionResult(ConversionError::BOOL_OVERFLOW); + return makeUnexpected(ConversionCode::BOOL_OVERFLOW); } result = (*b == '1'); } @@ -338,16 +317,16 @@ ConversionResult str_to_bool(StringPiece* src) { } else if (bool_str_cmp(&b, len, "off")) { result = false; } else { - return ConversionResult(ConversionError::BOOL_INVALID_VALUE); + return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); } break; default: - return ConversionResult(ConversionError::BOOL_INVALID_VALUE); + return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); } src->assign(b, e); - return ConversionResult(result); + return result; } /** @@ -355,7 +334,7 @@ ConversionResult str_to_bool(StringPiece* src) { * StringPiece parameter to munch the already-parsed characters. */ template -ConversionResult str_to_floating(StringPiece* src) { +Expected str_to_floating(StringPiece* src) noexcept { using namespace double_conversion; static StringToDoubleConverter conv(StringToDoubleConverter::ALLOW_TRAILING_JUNK @@ -366,7 +345,7 @@ ConversionResult str_to_floating(StringPiece* src) { nullptr, nullptr); if (src->empty()) { - return ConversionResult(ConversionError::EMPTY_INPUT_STRING); + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); } int length; @@ -382,11 +361,12 @@ ConversionResult str_to_floating(StringPiece* src) { // 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 ConversionResult(ConversionError::EMPTY_INPUT_STRING); + if (length == 0 || + (result == 0.0 && std::isspace((*src)[size_t(length) - 1]))) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); } - src->advance(length); - return ConversionResult(result); + src->advance(size_t(length)); + return Tgt(result); } auto* e = src->end(); @@ -395,7 +375,7 @@ ConversionResult str_to_floating(StringPiece* src) { // There must be non-whitespace, otherwise we would have caught this above assert(b < e); - size_t size = e - b; + size_t size = size_t(e - b); bool negative = false; if (*b == '-') { @@ -435,7 +415,7 @@ ConversionResult str_to_floating(StringPiece* src) { if (result == 0.0) { // All bets are off - return ConversionResult(ConversionError::STRING_TO_FLOAT_ERROR); + return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR); } if (negative) { @@ -444,11 +424,13 @@ ConversionResult str_to_floating(StringPiece* src) { src->assign(b, e); - return ConversionResult(result); + return Tgt(result); } -template ConversionResult str_to_floating(StringPiece* src); -template ConversionResult str_to_floating(StringPiece* src); +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, @@ -460,39 +442,39 @@ class SignedValueHandler; template class SignedValueHandler { public: - ConversionError::Code init(const char*& b) { + ConversionCode init(const char*& b) { negative_ = false; if (!std::isdigit(*b)) { if (*b == '-') { negative_ = true; } else if (UNLIKELY(*b != '+')) { - return ConversionError::INVALID_LEADING_CHAR; + return ConversionCode::INVALID_LEADING_CHAR; } ++b; } - return ConversionError::SUCCESS; + return ConversionCode::SUCCESS; } - ConversionError::Code overflow() { - return negative_ ? ConversionError::NEGATIVE_OVERFLOW - : ConversionError::POSITIVE_OVERFLOW; + ConversionCode overflow() { + return negative_ ? ConversionCode::NEGATIVE_OVERFLOW + : ConversionCode::POSITIVE_OVERFLOW; } template - ConversionResult finalize(U value) { + Expected finalize(U value) { T rv; if (negative_) { - rv = -value; + rv = T(-value); if (UNLIKELY(rv > 0)) { - return ConversionResult(ConversionError::NEGATIVE_OVERFLOW); + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); } } else { - rv = value; + rv = T(value); if (UNLIKELY(rv < 0)) { - return ConversionResult(ConversionError::POSITIVE_OVERFLOW); + return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); } } - return ConversionResult(rv); + return rv; } private: @@ -503,16 +485,16 @@ class SignedValueHandler { template class SignedValueHandler { public: - ConversionError::Code init(const char*&) { - return ConversionError::SUCCESS; + ConversionCode init(const char*&) { + return ConversionCode::SUCCESS; } - ConversionError::Code overflow() { - return ConversionError::POSITIVE_OVERFLOW; + ConversionCode overflow() { + return ConversionCode::POSITIVE_OVERFLOW; } - ConversionResult finalize(T value) { - return ConversionResult(value); + Expected finalize(T value) { + return value; } }; @@ -524,18 +506,20 @@ class SignedValueHandler { * an appropriate error. */ template -inline ConversionResult digits_to(const char* b, const char* const e) { +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 != ConversionError::SUCCESS)) { - return ConversionResult(err); + if (UNLIKELY(err != ConversionCode::SUCCESS)) { + return makeUnexpected(err); } - size_t size = e - b; + size_t size = size_t(e - b); /* Although the string is entirely made of digits, we still need to * check for overflow. @@ -545,10 +529,10 @@ inline ConversionResult digits_to(const char* b, const char* const e) { if (b < e && *b == '0') { for (++b;; ++b) { if (b == e) { - return ConversionResult(Tgt(0)); // just zeros, e.g. "0000" + return Tgt(0); // just zeros, e.g. "0000" } if (*b != '0') { - size = e - b; + size = size_t(e - b); break; } } @@ -556,7 +540,7 @@ inline ConversionResult digits_to(const char* b, const char* const e) { if (size > std::numeric_limits::digits10 && (size != std::numeric_limits::digits10 + 1 || strncmp(b, MaxString::value, size) > 0)) { - return ConversionResult(sgn.overflow()); + return makeUnexpected(sgn.overflow()); } } @@ -566,7 +550,7 @@ inline ConversionResult digits_to(const char* b, const char* const e) { UT result = 0; for (; e - b >= 4; b += 4) { - result *= 10000; + 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])]; @@ -575,7 +559,7 @@ inline ConversionResult digits_to(const char* b, const char* const e) { if (sum >= OOR) { goto outOfRange; } - result += sum; + result += UT(sum); } switch (e - b) { @@ -587,7 +571,7 @@ inline ConversionResult digits_to(const char* b, const char* const e) { if (sum >= OOR) { goto outOfRange; } - result = 1000 * result + sum; + result = UT(1000 * result + sum); break; } case 2: { @@ -597,7 +581,7 @@ inline ConversionResult digits_to(const char* b, const char* const e) { if (sum >= OOR) { goto outOfRange; } - result = 100 * result + sum; + result = UT(100 * result + sum); break; } case 1: { @@ -605,13 +589,13 @@ inline ConversionResult digits_to(const char* b, const char* const e) { if (sum >= OOR) { goto outOfRange; } - result = 10 * result + sum; + result = UT(10 * result + sum); break; } default: assert(b == e); if (size == 0) { - return ConversionResult(ConversionError::NO_DIGITS); + return makeUnexpected(ConversionCode::NO_DIGITS); } break; } @@ -619,46 +603,52 @@ inline ConversionResult digits_to(const char* b, const char* const e) { return sgn.finalize(result); outOfRange: - return ConversionResult(ConversionError::NON_DIGIT_CHAR); + return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); } -template ConversionResult digits_to(const char*, const char*); -template ConversionResult digits_to( +template Expected digits_to( const char*, - const char*); -template ConversionResult digits_to( + const char*) noexcept; +template Expected digits_to( const char*, - const char*); - -template ConversionResult digits_to(const char*, const char*); -template ConversionResult digits_to( + const char*) noexcept; +template Expected digits_to( const char*, - const char*); + const char*) noexcept; -template ConversionResult digits_to(const char*, const char*); -template ConversionResult digits_to( +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( const char*, - const char*); + const char*) noexcept; -template ConversionResult digits_to(const char*, const char*); -template ConversionResult digits_to( +template Expected digits_to( const char*, - const char*); + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; -template ConversionResult digits_to( +template Expected digits_to( const char*, - const char*); -template ConversionResult digits_to( + const char*) noexcept; +template Expected digits_to( const char*, - const char*); + const char*) noexcept; -#if FOLLY_HAVE_INT128_T -template ConversionResult<__int128> digits_to<__int128>( +template Expected digits_to( const char*, - const char*); -template ConversionResult digits_to( + 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*); + const char*) noexcept; +template Expected +digits_to(const char*, const char*) noexcept; #endif /** @@ -666,14 +656,14 @@ template ConversionResult digits_to( * StringPiece parameter to munch the already-parsed characters. */ template -ConversionResult str_to_integral(StringPiece* src) { +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 ConversionResult(ConversionError::EMPTY_INPUT_STRING); + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); } if (!std::isspace(*b)) { break; @@ -683,63 +673,93 @@ ConversionResult str_to_integral(StringPiece* src) { SignedValueHandler sgn; auto err = sgn.init(b); - if (UNLIKELY(err != ConversionError::SUCCESS)) { - return ConversionResult(err); + if (UNLIKELY(err != ConversionCode::SUCCESS)) { + return makeUnexpected(err); } if (std::is_signed::value && UNLIKELY(b >= past)) { - return ConversionResult(ConversionError::NO_DIGITS); + return makeUnexpected(ConversionCode::NO_DIGITS); } if (UNLIKELY(!isdigit(*b))) { - return ConversionResult(ConversionError::NON_DIGIT_CHAR); + return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); } auto m = findFirstNonDigit(b + 1, past); auto tmp = digits_to(b, m); - if (UNLIKELY(!tmp.success())) { - return ConversionResult( - tmp.error == ConversionError::POSITIVE_OVERFLOW ? sgn.overflow() - : tmp.error); + if (UNLIKELY(!tmp.hasValue())) { + return makeUnexpected( + tmp.error() == ConversionCode::POSITIVE_OVERFLOW ? sgn.overflow() + : tmp.error()); } - auto res = sgn.finalize(tmp.value); + auto res = sgn.finalize(tmp.value()); - if (res.success()) { - src->advance(m - src->data()); + if (res.hasValue()) { + src->advance(size_t(m - src->data())); } return res; } -template ConversionResult str_to_integral(StringPiece* src); -template ConversionResult str_to_integral( - StringPiece* src); -template ConversionResult str_to_integral( - StringPiece* src); - -template ConversionResult str_to_integral(StringPiece* src); -template ConversionResult str_to_integral( - StringPiece* src); - -template ConversionResult str_to_integral(StringPiece* src); -template ConversionResult str_to_integral( - StringPiece* src); - -template ConversionResult str_to_integral(StringPiece* src); -template ConversionResult str_to_integral( - StringPiece* src); - -template ConversionResult str_to_integral( - StringPiece* src); -template ConversionResult -str_to_integral(StringPiece* src); +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 ConversionResult<__int128> str_to_integral<__int128>(StringPiece* src); -template ConversionResult str_to_integral( - StringPiece* src); +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