X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FConv.h;h=4acb9c1e40aa995da9de2477a7ac8264b90cec5b;hb=59527d6ae3453d12190f38762637c0fd19b6abd7;hp=f5cc23c5cce939f960de63c4dcce232214de9062;hpb=6d5d48ac0610d5298ed21ab6882ac4dccb56d2b5;p=folly.git diff --git a/folly/Conv.h b/folly/Conv.h index f5cc23c5..4acb9c1e 100644 --- a/folly/Conv.h +++ b/folly/Conv.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 Facebook, Inc. + * Copyright 2015 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,12 +24,13 @@ #ifndef FOLLY_BASE_CONV_H_ #define FOLLY_BASE_CONV_H_ -#include "folly/FBString.h" -#include "folly/Likely.h" -#include "folly/Preprocessor.h" -#include "folly/Range.h" +#include +#include +#include +#include #include +#include #include #include #include @@ -39,19 +40,67 @@ #include -#include "double-conversion.h" // V8 JavaScript implementation +// V8 JavaScript implementation +#include -#define FOLLY_RANGE_CHECK(condition, message) \ - ((condition) ? (void)0 : throw std::range_error( \ - (__FILE__ "(" + std::to_string((long long int) __LINE__) + "): " \ - + (message)).c_str())) +#define FOLLY_RANGE_CHECK_STRINGIZE(x) #x +#define FOLLY_RANGE_CHECK_STRINGIZE2(x) FOLLY_RANGE_CHECK_STRINGIZE(x) + +// Android doesn't support std::to_string so just use a placeholder there. +#ifdef __ANDROID__ +#define FOLLY_RANGE_CHECK_TO_STRING(x) std::string("N/A") +#else +#define FOLLY_RANGE_CHECK_TO_STRING(x) std::to_string(x) +#endif + +#define FOLLY_RANGE_CHECK(condition, message, src) \ + ((condition) ? (void)0 : throw std::range_error( \ + (std::string(__FILE__ "(" FOLLY_RANGE_CHECK_STRINGIZE2(__LINE__) "): ") \ + + (message) + ": '" + (src) + "'").c_str())) + +#define FOLLY_RANGE_CHECK_BEGIN_END(condition, message, b, e) \ + FOLLY_RANGE_CHECK(condition, message, std::string((b), (e) - (b))) + +#define FOLLY_RANGE_CHECK_STRINGPIECE(condition, message, sp) \ + FOLLY_RANGE_CHECK(condition, message, std::string((sp).data(), (sp).size())) namespace folly { +/** + * The identity conversion function. + * to(T) returns itself for all types T. + */ +template +typename std::enable_if::value, Tgt>::type +to(const Src & value) { + return value; +} + +template +typename std::enable_if::value, Tgt>::type +to(Src && value) { + return std::move(value); +} + /******************************************************************************* * Integral to integral ******************************************************************************/ +/** + * Unchecked conversion from integral to boolean. This is different from the + * other integral conversions because we use the C convention of treating any + * non-zero value as true, instead of range checking. + */ +template +typename std::enable_if< + std::is_integral::value + && !std::is_same::value + && std::is_same::value, + Tgt>::type +to(const Src & value) { + return value != 0; +} + /** * Checked conversion from integral to integral. The checks are only * performed when meaningful, e.g. conversion from int to long goes @@ -59,22 +108,25 @@ namespace folly { */ template typename std::enable_if< - std::is_integral::value && std::is_integral::value, + std::is_integral::value + && !std::is_same::value + && !std::is_same::value + && std::is_integral::value, Tgt>::type to(const Src & value) { /* static */ if (std::numeric_limits::max() < std::numeric_limits::max()) { FOLLY_RANGE_CHECK( (!greater_than::max()>(value)), - "Overflow" - ); + "Overflow", + FOLLY_RANGE_CHECK_TO_STRING(value)); } /* static */ if (std::is_signed::value && (!std::is_signed::value || sizeof(Src) > sizeof(Tgt))) { FOLLY_RANGE_CHECK( (!less_than::min()>(value)), - "Negative overflow" - ); + "Negative overflow", + FOLLY_RANGE_CHECK_TO_STRING(value)); } return static_cast(value); } @@ -85,15 +137,19 @@ to(const Src & value) { template typename std::enable_if< - std::is_floating_point::value && std::is_floating_point::value, + std::is_floating_point::value + && std::is_floating_point::value + && !std::is_same::value, Tgt>::type to(const Src & value) { /* static */ if (std::numeric_limits::max() < std::numeric_limits::max()) { FOLLY_RANGE_CHECK(value <= std::numeric_limits::max(), - "Overflow"); + "Overflow", + FOLLY_RANGE_CHECK_TO_STRING(value)); FOLLY_RANGE_CHECK(value >= -std::numeric_limits::max(), - "Negative overflow"); + "Negative overflow", + FOLLY_RANGE_CHECK_TO_STRING(value)); } return boost::implicit_cast(value); } @@ -104,11 +160,6 @@ to(const Src & value) { namespace detail { -template struct IsSomeString { - enum { value = std::is_same::value - || std::is_same::value }; -}; - template const T& getLastElement(const T & v) { return v; @@ -118,10 +169,27 @@ template typename std::tuple_element< sizeof...(Ts), std::tuple >::type const& - getLastElement(const T& v, const Ts&... vs) { + getLastElement(const T&, const Ts&... vs) { return getLastElement(vs...); } +// This class exists to specialize away std::tuple_element in the case where we +// have 0 template arguments. Without this, Clang/libc++ will blow a +// static_assert even if tuple_element is protected by an enable_if. +template +struct last_element { + typedef typename std::enable_if< + sizeof...(Ts) >= 1, + typename std::tuple_element< + sizeof...(Ts) - 1, std::tuple + >::type>::type type; +}; + +template <> +struct last_element<> { + typedef void type; +}; + } // namespace detail /******************************************************************************* @@ -137,10 +205,10 @@ digitsEnough() { return ceil((double(sizeof(IntegerType) * CHAR_BIT) * M_LN2) / M_LN10); } -inline unsigned int -unsafeTelescope128(char * buffer, unsigned int room, unsigned __int128 x) { +inline size_t +unsafeTelescope128(char * buffer, size_t room, unsigned __int128 x) { typedef unsigned __int128 Usrc; - unsigned int p = room - 1; + size_t p = room - 1; while (x >= (Usrc(1) << 64)) { // Using 128-bit division while needed const auto y = x / 10; @@ -176,6 +244,42 @@ unsafeTelescope128(char * buffer, unsigned int room, unsigned __int128 x) { */ inline uint32_t digits10(uint64_t v) { +#ifdef __x86_64__ + + // For this arch we can get a little help from specialized CPU instructions + // which can count leading zeroes; 64 minus that is appx. log (base 2). + // Use that to approximate base-10 digits (log_10) and then adjust if needed. + + // 10^i, defined for i 0 through 19. + // This is 20 * 8 == 160 bytes, which fits neatly into 5 cache lines + // (assuming a cache line size of 64). + static const uint64_t powersOf10[20] FOLLY_ALIGNED(64) = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, + 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, + 1000000000000000, 10000000000000000, 100000000000000000, + 1000000000000000000, 10000000000000000000UL + }; + + // "count leading zeroes" operation not valid; for 0; special case this. + if UNLIKELY (! v) { + return 1; + } + + // bits is in the ballpark of log_2(v). + const uint8_t leadingZeroes = __builtin_clzll(v); + const auto bits = 63 - leadingZeroes; + + // approximate log_10(v) == log_10(2) * bits. + // Integer magic below: 77/256 is appx. 0.3010 (log_10(2)). + // The +1 is to make this the ceiling of the log_10 estimate. + const uint32_t minLength = 1 + ((bits * 77) >> 8); + + // return that log_10 lower bound, plus adjust if input >= 10^(that bound) + // in case there's a small error and we misjudged length. + return minLength + (uint32_t) (UNLIKELY (v >= powersOf10[minLength])); + +#else + uint32_t result = 1; for (;;) { if (LIKELY(v < 10)) return result; @@ -186,6 +290,8 @@ inline uint32_t digits10(uint64_t v) { v /= 10000U; result += 4; } + +#endif } /** @@ -228,13 +334,29 @@ void toAppend(char value, Tgt * result) { *result += value; } +template +constexpr typename std::enable_if< + std::is_same::value, + size_t>::type +estimateSpaceNeeded(T) { + return 1; +} + +/** + * Ubiquitous helper template for writing string appenders + */ +template struct IsSomeString { + enum { value = std::is_same::value + || std::is_same::value }; +}; + /** * Everything implicitly convertible to const char* gets appended. */ template typename std::enable_if< std::is_convertible::value - && detail::IsSomeString::value>::type + && IsSomeString::value>::type toAppend(Src value, Tgt * result) { // Treat null pointers like an empty string, as in: // operator<<(std::ostream&, const char*). @@ -244,12 +366,43 @@ toAppend(Src value, Tgt * result) { } } +template +typename std::enable_if< + std::is_convertible::value, + size_t>::type +estimateSpaceNeeded(Src value) { + const char *c = value; + if (c) { + return folly::StringPiece(value).size(); + }; + return 0; +} + +template +typename std::enable_if< + (std::is_convertible::value || + IsSomeString::value) && + !std::is_convertible::value, + size_t>::type +estimateSpaceNeeded(Src value) { + return folly::StringPiece(value).size(); +} + +template +typename std::enable_if< + std::is_pointer::value && + IsSomeString>::value, + size_t>::type +estimateSpaceNeeded(Src value) { + return value->size(); +} + /** * Strings get appended, too. */ template typename std::enable_if< - detail::IsSomeString::value && detail::IsSomeString::value>::type + IsSomeString::value && IsSomeString::value>::type toAppend(const Src& value, Tgt * result) { result->append(value); } @@ -259,7 +412,7 @@ toAppend(const Src& value, Tgt * result) { */ template typename std::enable_if< - detail::IsSomeString::value>::type + IsSomeString::value>::type toAppend(StringPiece value, Tgt * result) { result->append(value.data(), value.size()); } @@ -270,7 +423,7 @@ toAppend(StringPiece value, Tgt * result) { */ template typename std::enable_if< - detail::IsSomeString::value>::type + IsSomeString::value>::type toAppend(const fbstring& value, Tgt * result) { result->append(value.data(), value.size()); } @@ -285,7 +438,7 @@ void toAppend(__int128 value, Tgt * result) { typedef unsigned __int128 Usrc; char buffer[detail::digitsEnough() + 1]; - unsigned int p; + size_t p; if (value < 0) { p = detail::unsafeTelescope128(buffer, sizeof(buffer), Usrc(-value)); @@ -301,13 +454,29 @@ template void toAppend(unsigned __int128 value, Tgt * result) { char buffer[detail::digitsEnough()]; - unsigned int p; + size_t p; p = detail::unsafeTelescope128(buffer, sizeof(buffer), value); result->append(buffer + p, buffer + sizeof(buffer)); } +template +constexpr typename std::enable_if< + std::is_same::value, + size_t>::type +estimateSpaceNeeded(T) { + return detail::digitsEnough<__int128>(); +} + +template +constexpr typename std::enable_if< + std::is_same::value, + size_t>::type +estimateSpaceNeeded(T) { + return detail::digitsEnough(); +} + #endif /** @@ -321,7 +490,7 @@ toAppend(unsigned __int128 value, Tgt * result) { template typename std::enable_if< std::is_integral::value && std::is_signed::value && - detail::IsSomeString::value && sizeof(Src) >= 4>::type + IsSomeString::value && sizeof(Src) >= 4>::type toAppend(Src value, Tgt * result) { char buffer[20]; if (value < 0) { @@ -332,18 +501,40 @@ toAppend(Src value, Tgt * result) { } } +template +typename std::enable_if< + std::is_integral::value && std::is_signed::value + && sizeof(Src) >= 4 && sizeof(Src) < 16, + size_t>::type +estimateSpaceNeeded(Src value) { + if (value < 0) { + return 1 + digits10(static_cast(-value)); + } + + return digits10(static_cast(value)); +} + /** * As above, but for uint32_t and uint64_t. */ template typename std::enable_if< std::is_integral::value && !std::is_signed::value - && detail::IsSomeString::value && sizeof(Src) >= 4>::type + && IsSomeString::value && sizeof(Src) >= 4>::type toAppend(Src value, Tgt * result) { char buffer[20]; result->append(buffer, buffer + uint64ToBufferUnsafe(value, buffer)); } +template +typename std::enable_if< + std::is_integral::value && !std::is_signed::value + && sizeof(Src) >= 4 && sizeof(Src) < 16, + size_t>::type +estimateSpaceNeeded(Src value) { + return digits10(value); +} + /** * All small signed and unsigned integers to string go through 32-bit * types int32_t and uint32_t, respectively. @@ -351,7 +542,7 @@ toAppend(Src value, Tgt * result) { template typename std::enable_if< std::is_integral::value - && detail::IsSomeString::value && sizeof(Src) < 4>::type + && IsSomeString::value && sizeof(Src) < 4>::type toAppend(Src value, Tgt * result) { typedef typename std::conditional::value, int64_t, uint64_t>::type @@ -359,55 +550,52 @@ toAppend(Src value, Tgt * result) { toAppend(static_cast(value), result); } -#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) -// std::underlying_type became available by gcc 4.7.0 +template +typename std::enable_if< + std::is_integral::value + && sizeof(Src) < 4 + && !std::is_same::value, + size_t>::type +estimateSpaceNeeded(Src value) { + typedef typename + std::conditional::value, int64_t, uint64_t>::type + Intermediate; + return estimateSpaceNeeded(static_cast(value)); +} /** * Enumerated values get appended as integers. */ template typename std::enable_if< - std::is_enum::value && detail::IsSomeString::value>::type + std::is_enum::value && IsSomeString::value>::type toAppend(Src value, Tgt * result) { toAppend( static_cast::type>(value), result); } -#else - -/** - * Enumerated values get appended as integers. - */ -template +template typename std::enable_if< - std::is_enum::value && detail::IsSomeString::value>::type -toAppend(Src value, Tgt * result) { - /* static */ if (Src(-1) < 0) { - /* static */ if (sizeof(Src) <= sizeof(int)) { - toAppend(static_cast(value), result); - } else { - toAppend(static_cast(value), result); - } - } else { - /* static */ if (sizeof(Src) <= sizeof(int)) { - toAppend(static_cast(value), result); - } else { - toAppend(static_cast(value), result); - } - } + std::is_enum::value, size_t>::type +estimateSpaceNeeded(Src value) { + return estimateSpaceNeeded( + static_cast::type>(value)); } -#endif // gcc 4.7 onwards - /******************************************************************************* * Conversions from floating-point types to string types. ******************************************************************************/ +namespace detail { +constexpr int kConvMaxDecimalInShortestLow = -6; +constexpr int kConvMaxDecimalInShortestHigh = 21; +} // folly::detail + /** Wrapper around DoubleToStringConverter **/ template typename std::enable_if< std::is_floating_point::value - && detail::IsSomeString::value>::type + && IsSomeString::value>::type toAppend( Src value, Tgt * result, @@ -416,9 +604,9 @@ toAppend( using namespace double_conversion; DoubleToStringConverter conv(DoubleToStringConverter::NO_FLAGS, - "infinity", "NaN", 'E', - -6, // decimal in shortest low - 21, // decimal in shortest high + "Infinity", "NaN", 'E', + detail::kConvMaxDecimalInShortestLow, + detail::kConvMaxDecimalInShortestHigh, 6, // max leading padding zeros 1); // max trailing padding zeros char buffer[256]; @@ -446,32 +634,186 @@ toAppend( template typename std::enable_if< std::is_floating_point::value - && detail::IsSomeString::value>::type + && IsSomeString::value>::type toAppend(Src value, Tgt * result) { toAppend( value, result, double_conversion::DoubleToStringConverter::SHORTEST, 0); } /** - * Variadic conversion to string. Appends each element in turn. + * Upper bound of the length of the output from + * DoubleToStringConverter::ToShortest(double, StringBuilder*), + * as used in toAppend(double, string*). */ +template +typename std::enable_if< + std::is_floating_point::value, size_t>::type +estimateSpaceNeeded(Src value) { + // kBase10MaximalLength is 17. We add 1 for decimal point, + // e.g. 10.0/9 is 17 digits and 18 characters, including the decimal point. + constexpr int kMaxMantissaSpace = + double_conversion::DoubleToStringConverter::kBase10MaximalLength + 1; + // strlen("E-") + digits10(numeric_limits::max_exponent10) + constexpr int kMaxExponentSpace = 2 + 3; + static const int kMaxPositiveSpace = std::max({ + // E.g. 1.1111111111111111E-100. + kMaxMantissaSpace + kMaxExponentSpace, + // E.g. 0.000001.1111111111111111, if kConvMaxDecimalInShortestLow is -6. + kMaxMantissaSpace - detail::kConvMaxDecimalInShortestLow, + // If kConvMaxDecimalInShortestHigh is 21, then 1e21 is the smallest + // number > 1 which ToShortest outputs in exponential notation, + // so 21 is the longest non-exponential number > 1. + detail::kConvMaxDecimalInShortestHigh + }); + return kMaxPositiveSpace + (value < 0); // +1 for minus sign, if negative +} + +/** + * This can be specialized, together with adding specialization + * for estimateSpaceNeed for your type, so that we allocate + * as much as you need instead of the default + */ +template +struct HasLengthEstimator : std::false_type {}; + +template +constexpr typename std::enable_if< + !std::is_fundamental::value +#ifdef FOLLY_HAVE_INT128_T + // On OSX 10.10, is_fundamental<__int128> is false :-O + && !std::is_same<__int128, Src>::value + && !std::is_same::value +#endif + && !IsSomeString::value + && !std::is_convertible::value + && !std::is_convertible::value + && !std::is_enum::value + && !HasLengthEstimator::value, + size_t>::type +estimateSpaceNeeded(const Src&) { + return sizeof(Src) + 1; // dumbest best effort ever? +} + +namespace detail { + +inline size_t estimateSpaceToReserve(size_t sofar) { + return sofar; +} + +template +size_t estimateSpaceToReserve(size_t sofar, const T& v, const Ts&... vs) { + return estimateSpaceToReserve(sofar + estimateSpaceNeeded(v), vs...); +} + +template +size_t estimateSpaceToReserve(size_t sofar, const T& v) { + return sofar + estimateSpaceNeeded(v); +} + +template +void reserveInTarget(const Ts&...vs) { + getLastElement(vs...)->reserve(estimateSpaceToReserve(0, vs...)); +} + +template +void reserveInTargetDelim(const Delimiter& d, const Ts&...vs) { + static_assert(sizeof...(vs) >= 2, "Needs at least 2 args"); + size_t fordelim = (sizeof...(vs) - 2) * estimateSpaceToReserve(0, d); + getLastElement(vs...)->reserve(estimateSpaceToReserve(fordelim, vs...)); +} + +/** + * Variadic base case: append one element + */ +template +typename std::enable_if< + IsSomeString::type> + ::value>::type +toAppendStrImpl(const T& v, Tgt result) { + toAppend(v, result); +} + template typename std::enable_if= 2 - && detail::IsSomeString< + && IsSomeString< typename std::remove_pointer< - typename std::tuple_element< - sizeof...(Ts) - 1, std::tuple - >::type>::type>::value>::type -toAppend(const T& v, const Ts&... vs) { + typename detail::last_element::type + >::type>::value>::type +toAppendStrImpl(const T& v, const Ts&... vs) { + toAppend(v, getLastElement(vs...)); + toAppendStrImpl(vs...); +} + +template +typename std::enable_if< + IsSomeString::type> + ::value>::type +toAppendDelimStrImpl(const Delimiter& delim, const T& v, Tgt result) { + toAppend(v, result); +} + +template +typename std::enable_if= 2 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) { + // we are really careful here, calling toAppend with just one element does + // not try to estimate space needed (as we already did that). If we call + // toAppend(v, delim, ....) we would do unnecesary size calculation toAppend(v, detail::getLastElement(vs...)); + toAppend(delim, detail::getLastElement(vs...)); + toAppendDelimStrImpl(delim, vs...); +} +} // folly::detail + + +/** + * Variadic conversion to string. Appends each element in turn. + * If we have two or more things to append, we it will not reserve + * the space for them and will depend on strings exponential growth. + * If you just append once consider using toAppendFit which reserves + * the space needed (but does not have exponential as a result). + */ +template +typename std::enable_if= 3 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppend(const Ts&... vs) { + ::folly::detail::toAppendStrImpl(vs...); +} + +/** + * Special version of the call that preallocates exaclty as much memory + * as need for arguments to be stored in target. This means we are + * not doing exponential growth when we append. If you are using it + * in a loop you are aiming at your foot with a big perf-destroying + * bazooka. + * On the other hand if you are appending to a string once, this + * will probably save a few calls to malloc. + */ +template +typename std::enable_if< + IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppendFit(const Ts&... vs) { + ::folly::detail::reserveInTarget(vs...); toAppend(vs...); } +template +void toAppendFit(const Ts&) {} + /** * Variadic base case: do nothing. */ template -typename std::enable_if::value>::type +typename std::enable_if::value>::type toAppend(Tgt* result) { } @@ -479,7 +821,7 @@ toAppend(Tgt* result) { * Variadic base case: do nothing. */ template -typename std::enable_if::value>::type +typename std::enable_if::value>::type toAppendDelim(const Delimiter& delim, Tgt* result) { } @@ -487,47 +829,82 @@ toAppendDelim(const Delimiter& delim, Tgt* result) { * 1 element: same as toAppend. */ template -typename std::enable_if::value>::type +typename std::enable_if::value>::type toAppendDelim(const Delimiter& delim, const T& v, Tgt* tgt) { toAppend(v, tgt); } /** - * Append to string with a delimiter in between elements. + * Append to string with a delimiter in between elements. Check out + * comments for toAppend for details about memory allocation. */ -template -typename std::enable_if= 2 - && detail::IsSomeString< +template +typename std::enable_if= 3 + && IsSomeString< typename std::remove_pointer< - typename std::tuple_element< - sizeof...(Ts) - 1, std::tuple - >::type>::type>::value>::type -toAppendDelim(const Delimiter& delim, const T& v, const Ts&... vs) { - toAppend(v, delim, detail::getLastElement(vs...)); + typename detail::last_element::type + >::type>::value>::type +toAppendDelim(const Delimiter& delim, const Ts&... vs) { + detail::toAppendDelimStrImpl(delim, vs...); +} + +/** + * Detail in comment for toAppendFit + */ +template +typename std::enable_if< + IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppendDelimFit(const Delimiter& delim, const Ts&... vs) { + detail::reserveInTargetDelim(delim, vs...); toAppendDelim(delim, vs...); } +template +void toAppendDelimFit(const De&, const Ts&) {} + /** * to(v1, v2, ...) uses toAppend() (see below) as back-end * for all types. */ template -typename std::enable_if::value, Tgt>::type +typename std::enable_if< + IsSomeString::value && ( + sizeof...(Ts) != 1 || + !std::is_same::type>::value), + Tgt>::type to(const Ts&... vs) { Tgt result; - toAppend(vs..., &result); + toAppendFit(vs..., &result); return result; } +/** + * toDelim(SomeString str) returns itself. + */ +template +typename std::enable_if< + IsSomeString::value && std::is_same::value, + Tgt>::type +toDelim(const Delim& delim, const Src & value) { + return value; +} + /** * toDelim(delim, v1, v2, ...) uses toAppendDelim() as * back-end for all types. */ template -typename std::enable_if::value, Tgt>::type +typename std::enable_if< + IsSomeString::value && ( + sizeof...(Ts) != 1 || + !std::is_same::type>::value), + Tgt>::type toDelim(const Delim& delim, const Ts&... vs) { Tgt result; - toAppendDelim(delim, vs..., &result); + toAppendDelimFit(delim, vs..., &result); return result; } @@ -564,212 +941,25 @@ namespace detail { static const char*const value; }; + bool str_to_bool(StringPiece* src); -/* - * 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; - -__attribute__((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 -}; - -__attribute__((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 -}; - -__attribute__((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 -}; - -__attribute__((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 -}; - -/** - * String represented as a pair of pointers to char to unsigned - * integrals. Assumes NO whitespace before or after, and also that the - * string is composed entirely of digits. Tgt must be unsigned, and no - * sign is allowed in the string (even it's '+'). String may be empty, - * in which case digits_to throws. - */ template - Tgt digits_to(const char * b, const char * e) { - - static_assert(!std::is_signed::value, "Unsigned type expected"); - assert(b <= e); - - const 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 + 1) { - // Leading zeros? If so, recurse to keep things simple - if (b < e && *b == '0') { - for (++b;; ++b) { - if (b == e) return 0; // just zeros, e.g. "0000" - if (*b != '0') return digits_to(b, e); - } - } - FOLLY_RANGE_CHECK(size == std::numeric_limits::digits10 + 1 && - strncmp(b, detail::MaxString::value, size) <= 0, - "Numeric overflow upon conversion"); - } - - // Here we know that the number won't overflow when - // converted. Proceed without checks. - - Tgt 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; - assert(sum < OOR && "Assumption: string only has digits"); - 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; - assert(sum < OOR && "Assumption: string only has digits"); - return result * 1000 + sum; - } - 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; - assert(sum < OOR && "Assumption: string only has digits"); - return result * 100 + sum; - } - case 1: { - const int32_t sum = shift1[static_cast(b[0])]; - assert(sum < OOR && "Assumption: string only has digits"); - return result * 10 + sum; - } - } - - assert(b == e); - FOLLY_RANGE_CHECK(size > 0, "Found no digits to convert in input"); - return result; - } - - - bool str_to_bool(StringPiece * src); + Tgt digits_to(const char* b, const char* e); + + extern template unsigned char digits_to(const char* b, + const char* e); + extern template unsigned short digits_to(const char* b, + const char* e); + extern template unsigned int digits_to(const char* b, + const char* e); + extern template unsigned long digits_to(const char* b, + const char* e); + extern template unsigned long long digits_to( + const char* b, const char* e); +#if FOLLY_HAVE_INT128_T + extern template unsigned __int128 digits_to(const char* b, + const char* e); +#endif } // namespace detail @@ -796,18 +986,19 @@ typename std::enable_if< std::is_integral::value && std::is_signed::value, Tgt>::type to(const char * b, const char * e) { - FOLLY_RANGE_CHECK(b < e, "Empty input string in conversion to integral"); + FOLLY_RANGE_CHECK(b < e, "Empty input string in conversion to integral", + to("b: ", intptr_t(b), " e: ", intptr_t(e))); if (!isdigit(*b)) { if (*b == '-') { Tgt result = -to::type>(b + 1, e); - FOLLY_RANGE_CHECK(result <= 0, "Negative overflow."); + FOLLY_RANGE_CHECK_BEGIN_END(result <= 0, "Negative overflow.", b, e); return result; } - FOLLY_RANGE_CHECK(*b == '+', "Invalid lead character"); + FOLLY_RANGE_CHECK_BEGIN_END(*b == '+', "Invalid lead character", b, e); ++b; } Tgt result = to::type>(b, e); - FOLLY_RANGE_CHECK(result >= 0, "Overflow."); + FOLLY_RANGE_CHECK_BEGIN_END(result >= 0, "Overflow", b, e); return result; } @@ -830,7 +1021,8 @@ to(StringPiece * src) { auto b = src->data(), past = src->data() + src->size(); for (;; ++b) { - FOLLY_RANGE_CHECK(b < past, "No digits found in input string"); + FOLLY_RANGE_CHECK_STRINGPIECE(b < past, + "No digits found in input string", *src); if (!isspace(*b)) break; } @@ -843,15 +1035,16 @@ to(StringPiece * src) { if (*m == '-') { negative = true; } else { - FOLLY_RANGE_CHECK(*m == '+', "Invalid leading character in conversion" - " to integral"); + FOLLY_RANGE_CHECK_STRINGPIECE(*m == '+', "Invalid leading character in " + "conversion to integral", *src); } ++b; ++m; } } - FOLLY_RANGE_CHECK(m < past, "No digits found in input string"); - FOLLY_RANGE_CHECK(isdigit(*m), "Non-digit character found"); + FOLLY_RANGE_CHECK_STRINGPIECE(m < past, "No digits found in input string", + *src); + FOLLY_RANGE_CHECK_STRINGPIECE(isdigit(*m), "Non-digit character found", *src); m = detail::findFirstNonDigit(m + 1, past); Tgt result; @@ -861,10 +1054,11 @@ to(StringPiece * src) { auto t = detail::digits_to::type>(b, m); if (negative) { result = -t; - FOLLY_RANGE_CHECK(result <= 0, "Negative overflow"); + FOLLY_RANGE_CHECK_STRINGPIECE(is_non_positive(result), + "Negative overflow", *src); } else { result = t; - FOLLY_RANGE_CHECK(result >= 0, "Overflow"); + FOLLY_RANGE_CHECK_STRINGPIECE(is_non_negative(result), "Overflow", *src); } } src->advance(m - src->data()); @@ -890,7 +1084,9 @@ namespace detail { */ inline void enforceWhitespace(const char* b, const char* e) { for (; b != e; ++b) { - FOLLY_RANGE_CHECK(isspace(*b), to("Non-whitespace: ", *b)); + FOLLY_RANGE_CHECK_BEGIN_END(isspace(*b), + to("Non-whitespace: ", *b), + b, e); } } @@ -932,11 +1128,13 @@ to(StringPiece *const src) { std::numeric_limits::quiet_NaN(), nullptr, nullptr); - FOLLY_RANGE_CHECK(!src->empty(), "No digits found in input string"); + FOLLY_RANGE_CHECK_STRINGPIECE(!src->empty(), + "No digits found in input string", *src); int length; - auto result = conv.StringToDouble(src->data(), src->size(), - &length); // processed char count + auto result = conv.StringToDouble(src->data(), + static_cast(src->size()), + &length); // processed char count if (!std::isnan(result)) { src->advance(length); @@ -1044,7 +1242,12 @@ to(const Src & value) { if (value != witness) { throw std::range_error( to("to<>: loss of precision when converting ", value, - " to type ", typeid(Tgt).name()).c_str()); +#ifdef FOLLY_HAS_RTTI + " to type ", typeid(Tgt).name() +#else + " to other type" +#endif + ).c_str()); } return result; } @@ -1053,61 +1256,20 @@ to(const Src & value) { * Enum to anything and back ******************************************************************************/ -#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) -// std::underlying_type became available by gcc 4.7.0 - template -typename std::enable_if::value, Tgt>::type +typename std::enable_if< + std::is_enum::value && !std::is_same::value, Tgt>::type to(const Src & value) { return to(static_cast::type>(value)); } template -typename std::enable_if::value, Tgt>::type +typename std::enable_if< + std::is_enum::value && !std::is_same::value, Tgt>::type to(const Src & value) { return static_cast(to::type>(value)); } -#else - -template -typename std::enable_if::value, Tgt>::type -to(const Src & value) { - /* static */ if (Src(-1) < 0) { - /* static */ if (sizeof(Src) <= sizeof(int)) { - return to(static_cast(value)); - } else { - return to(static_cast(value)); - } - } else { - /* static */ if (sizeof(Src) <= sizeof(int)) { - return to(static_cast(value)); - } else { - return to(static_cast(value)); - } - } -} - -template -typename std::enable_if::value, Tgt>::type -to(const Src & value) { - /* static */ if (Tgt(-1) < 0) { - /* static */ if (sizeof(Tgt) <= sizeof(int)) { - return static_cast(to(value)); - } else { - return static_cast(to(value)); - } - } else { - /* static */ if (sizeof(Tgt) <= sizeof(int)) { - return static_cast(to(value)); - } else { - return static_cast(to(value)); - } - } -} - -#endif // gcc 4.7 onwards - } // namespace folly // FOLLY_CONV_INTERNAL is defined by Conv.cpp. Keep the FOLLY_RANGE_CHECK @@ -1115,6 +1277,10 @@ to(const Src & value) { // to avoid defining this global macro name in other files that include Conv.h. #ifndef FOLLY_CONV_INTERNAL #undef FOLLY_RANGE_CHECK +#undef FOLLY_RANGE_CHECK_BEGIN_END +#undef FOLLY_RANGE_CHECK_STRINGPIECE +#undef FOLLY_RANGE_CHECK_STRINGIZE +#undef FOLLY_RANGE_CHECK_STRINGIZE2 #endif #endif /* FOLLY_BASE_CONV_H_ */