X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FConv.h;h=5a196463dd79df1ee6fbfd73375c65be3a23a39e;hb=6d079c1421885038d14a1f65e71c010836b6c94c;hp=76b9385cfe6c242872c21168c9b0a533abdd9de9;hpb=a42878e365ad97ef653a823ac5d5b62f91f63663;p=folly.git diff --git a/folly/Conv.h b/folly/Conv.h index 76b9385c..5a196463 100644 --- a/folly/Conv.h +++ b/folly/Conv.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -42,13 +43,32 @@ // 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__) + "): " \ +#define FOLLY_RANGE_CHECK_STRINGIZE(x) #x +#define FOLLY_RANGE_CHECK_STRINGIZE2(x) FOLLY_RANGE_CHECK_STRINGIZE(x) + +#define FOLLY_RANGE_CHECK(condition, message) \ + ((condition) ? (void)0 : throw std::range_error( \ + (std::string(__FILE__ "(" FOLLY_RANGE_CHECK_STRINGIZE2(__LINE__) "): ") \ + (message)).c_str())) 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 ******************************************************************************/ @@ -60,7 +80,9 @@ namespace folly { */ template typename std::enable_if< - std::is_integral::value && std::is_integral::value, + std::is_integral::value + && std::is_integral::value + && !std::is_same::value, Tgt>::type to(const Src & value) { /* static */ if (std::numeric_limits::max() @@ -86,7 +108,9 @@ 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() < @@ -150,10 +174,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; @@ -345,7 +369,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)); @@ -361,7 +385,7 @@ 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); @@ -541,6 +565,11 @@ estimateSpaceNeeded(Src value) { * 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< @@ -555,8 +584,8 @@ toAppend( DoubleToStringConverter conv(DoubleToStringConverter::NO_FLAGS, "infinity", "NaN", 'E', - -6, // decimal in shortest low - 21, // decimal in shortest high + detail::kConvMaxDecimalInShortestLow, + detail::kConvMaxDecimalInShortestHigh, 6, // max leading padding zeros 1); // max trailing padding zeros char buffer[256]; @@ -591,29 +620,31 @@ toAppend(Src value, Tgt * result) { } /** - * Very primitive, lets say its our best effort + * 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) { - size_t sofar = 0; - if (value < 0) { - ++sofar; - value = -value; - } - - if (value < 1) { - return sofar + 10; // lets assume 0 + '.' + 8 precision digits - } - - if (value < static_cast(std::numeric_limits::max())) { - sofar += digits10(static_cast(value)); - } else { - return 64; // give up, it will be more than 23 anyway - } - - return sofar + 10; // integral part + '.' + 8 precision digits + // 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 } /** @@ -627,6 +658,11 @@ 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 @@ -714,8 +750,10 @@ toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) { /** * Variadic conversion to string. Appends each element in turn. - * If we have two or more things to append, we will reserve - * the space for them (at least we will try). + * 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 @@ -724,10 +762,32 @@ typename std::enable_if= 3 typename detail::last_element::type >::type>::value>::type toAppend(const Ts&... vs) { - detail::reserveInTarget(vs...); 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) { + detail::reserveInTarget(vs...); + toAppend(vs...); +} + +template +void toAppendFit(const Ts&) {} + /** * Variadic base case: do nothing. */ @@ -754,7 +814,8 @@ toAppendDelim(const Delimiter& delim, const T& v, Tgt* 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= 3 @@ -763,23 +824,26 @@ typename std::enable_if= 3 typename detail::last_element::type >::type>::value>::type toAppendDelim(const Delimiter& delim, const Ts&... vs) { - detail::reserveInTargetDelim(delim, vs...); detail::toAppendDelimStrImpl(delim, vs...); } /** - * to(SomeString str) returns itself. As both std::string and - * folly::fbstring use Copy-on-Write, it's much more efficient by - * avoiding copying the underlying char array. + * Detail in comment for toAppendFit */ -template +template typename std::enable_if< - IsSomeString::value && std::is_same::value, - Tgt>::type -to(const Src & value) { - return value; + 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. @@ -792,7 +856,7 @@ typename std::enable_if< Tgt>::type to(const Ts&... vs) { Tgt result; - toAppend(vs..., &result); + toAppendFit(vs..., &result); return result; } @@ -819,7 +883,7 @@ typename std::enable_if< Tgt>::type toDelim(const Delim& delim, const Ts&... vs) { Tgt result; - toAppendDelim(delim, vs..., &result); + toAppendDelimFit(delim, vs..., &result); return result; } @@ -1227,8 +1291,9 @@ to(StringPiece *const src) { FOLLY_RANGE_CHECK(!src->empty(), "No digits found in input string"); 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); @@ -1349,13 +1414,15 @@ to(const Src & value) { // 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)); } @@ -1363,7 +1430,8 @@ to(const Src & value) { #else 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) { /* static */ if (Src(-1) < 0) { /* static */ if (sizeof(Src) <= sizeof(int)) { @@ -1381,7 +1449,8 @@ to(const Src & 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) { /* static */ if (Tgt(-1) < 0) { /* static */ if (sizeof(Tgt) <= sizeof(int)) { @@ -1407,6 +1476,8 @@ 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_STRINGIZE2 +#undef FOLLY_RANGE_CHECK_STRINGIZE #endif #endif /* FOLLY_BASE_CONV_H_ */