X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FConv.h;h=76b9385cfe6c242872c21168c9b0a533abdd9de9;hb=9b4749fd47c605c27b6973f317d7c4f3a0e101e9;hp=9ecc43c8e4940c3e1659a3c7d84f0b6091691b36;hpb=56e0ec4fe2db38106311b09b88820a99860664f3;p=folly.git diff --git a/folly/Conv.h b/folly/Conv.h index 9ecc43c8..76b9385c 100644 --- a/folly/Conv.h +++ b/folly/Conv.h @@ -24,10 +24,10 @@ #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 @@ -241,6 +241,14 @@ 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 */ @@ -265,6 +273,37 @@ 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. */ @@ -329,6 +368,22 @@ toAppend(unsigned __int128 value, Tgt * result) { 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 /** @@ -353,6 +408,19 @@ 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. */ @@ -365,6 +433,15 @@ toAppend(Src value, Tgt * result) { 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. @@ -380,6 +457,19 @@ toAppend(Src value, Tgt * result) { toAppend(static_cast(value), result); } +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)); +} + #if defined(__clang__) || __GNUC_PREREQ(4, 7) // std::underlying_type became available by gcc 4.7.0 @@ -394,6 +484,14 @@ toAppend(Src value, Tgt * result) { static_cast::type>(value), result); } +template +typename std::enable_if< + std::is_enum::value, size_t>::type +estimateSpaceNeeded(Src value) { + return estimateSpaceNeeded( + static_cast::type>(value)); +} + #else /** @@ -418,6 +516,25 @@ toAppend(Src value, Tgt * result) { } } +template +typename std::enable_if< + std::is_enum::value, size_t>::type +estimateSpaceNeeded(Src value) { + /* static */ if (Src(-1) < 0) { + /* static */ if (sizeof(Src) <= sizeof(int)) { + return estimateSpaceNeeded(static_cast(value)); + } else { + return estimateSpaceNeeded(static_cast(value)); + } + } else { + /* static */ if (sizeof(Src) <= sizeof(int)) { + return estimateSpaceNeeded(static_cast(value)); + } else { + return estimateSpaceNeeded(static_cast(value)); + } + } +} + #endif // gcc 4.7 onwards /******************************************************************************* @@ -474,17 +591,141 @@ toAppend(Src value, Tgt * result) { } /** - * Variadic conversion to string. Appends each element in turn. + * Very primitive, lets say its our best effort + */ +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 +} + +/** + * 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 + && !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 && IsSomeString< typename std::remove_pointer< typename detail::last_element::type >::type>::value>::type -toAppend(const T& v, const Ts&... vs) { +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(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 will reserve + * the space for them (at least we will try). + */ +template +typename std::enable_if= 3 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppend(const Ts&... vs) { + detail::reserveInTarget(vs...); + detail::toAppendStrImpl(vs...); } /** @@ -515,15 +756,28 @@ toAppendDelim(const Delimiter& delim, const T& v, Tgt* tgt) { /** * Append to string with a delimiter in between elements. */ -template -typename std::enable_if= 2 +template +typename std::enable_if= 3 && IsSomeString< typename std::remove_pointer< typename detail::last_element::type >::type>::value>::type -toAppendDelim(const Delimiter& delim, const T& v, const Ts&... vs) { - toAppend(v, delim, detail::getLastElement(vs...)); - toAppendDelim(delim, vs...); +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. + */ +template +typename std::enable_if< + IsSomeString::value && std::is_same::value, + Tgt>::type +to(const Src & value) { + return value; } /** @@ -531,19 +785,38 @@ toAppendDelim(const Delimiter& delim, const T& v, const Ts&... vs) { * 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); 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); @@ -596,7 +869,7 @@ namespace detail { // still not overflow uint16_t. constexpr int32_t OOR = 10000; -__attribute__((aligned(16))) constexpr uint16_t shift1[] = { +__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 @@ -625,7 +898,7 @@ __attribute__((aligned(16))) constexpr uint16_t shift1[] = { OOR, OOR, OOR, OOR, OOR, OOR // 250 }; -__attribute__((aligned(16))) constexpr uint16_t shift10[] = { +__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 @@ -654,7 +927,7 @@ __attribute__((aligned(16))) constexpr uint16_t shift10[] = { OOR, OOR, OOR, OOR, OOR, OOR // 250 }; -__attribute__((aligned(16))) constexpr uint16_t shift100[] = { +__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 @@ -683,7 +956,7 @@ __attribute__((aligned(16))) constexpr uint16_t shift100[] = { OOR, OOR, OOR, OOR, OOR, OOR // 250 }; -__attribute__((aligned(16))) constexpr uint16_t shift1000[] = { +__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