X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FFormat-inl.h;h=940224c2bf60e39a2cad2eda016d681d454b550e;hb=02fe20e3434fd6400ecf2ad92c7e1231b8f17108;hp=625233f5f49c6a97e1045761a8bcae88f2a626b1;hpb=2c378ec80fa59964ec7c143ec2a9b53fc6a1fd15;p=folly.git diff --git a/folly/Format-inl.h b/folly/Format-inl.h index 625233f5..940224c2 100644 --- a/folly/Format-inl.h +++ b/folly/Format-inl.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 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. @@ -18,17 +18,29 @@ #error This file may only be included from Format.h. #endif +#include +#include +#include +#include +#include +#include + #include +#include #include +#include // Ignore -Wformat-nonliteral warnings within this file -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" +FOLLY_PUSH_WARNING +FOLLY_GCC_DISABLE_WARNING("-Wformat-nonliteral") namespace folly { namespace detail { +// Updates the end of the buffer after the comma separators have been added. +void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer); + extern const char formatHexUpper[256][2]; extern const char formatHexLower[256][2]; extern const char formatOctal[512][3]; @@ -48,8 +60,8 @@ const size_t kMaxBinaryLength = 8 * sizeof(uintmax_t); * [buf+begin, buf+bufLen). */ template -size_t uintToHex(char* buffer, size_t bufLen, Uint v, - const char (&repr)[256][2]) { +size_t +uintToHex(char* buffer, size_t bufLen, Uint v, const char (&repr)[256][2]) { // 'v >>= 7, v >>= 1' is no more than a work around to get rid of shift size // warning when Uint = uint8_t (it's false as v >= 256 implies sizeof(v) > 1). for (; !less_than(v); v >>= 7, v >>= 1) { @@ -139,74 +151,26 @@ size_t uintToBinary(char* buffer, size_t bufLen, Uint v) { return bufLen; } -} // namespace detail +} // namespace detail +template +BaseFormatter::BaseFormatter( + StringPiece str, + Args&&... args) + : str_(str), values_(std::forward(args)...) {} -template -Formatter::Formatter(StringPiece str, Args&&... args) - : str_(str), - values_(FormatValue::type>( - std::forward(args))...) { - static_assert(!containerMode || sizeof...(Args) == 1, - "Exactly one argument required in container mode"); -} - -template -void Formatter::handleFormatStrError() const { - if (crashOnError_) { - LOG(FATAL) << "folly::format: bad format string \"" << str_ << "\": " << - folly::exceptionStr(std::current_exception()); - } - throw; -} - -template +template template -void Formatter::operator()(Output& out) const { - // Catch BadFormatArg and range_error exceptions, and call - // handleFormatStrError(). - // - // These exception types indicate a problem with the format string. Most - // format strings are string literals specified by the programmer. If they - // have a problem, this is usually a programmer bug. We want to crash to - // ensure that these are found early on during development. - // - // BadFormatArg is thrown by the Format.h code, while range_error is thrown - // by Conv.h, which is used in several places in our format string - // processing. - // - // (Note: This behavior is slightly dangerous. If the Output object throws a - // BadFormatArg or a range_error, we will also crash the program, even if it - // wasn't an issue with the format string. This seems highly unlikely - // though, and none of our current Output objects can throw these errors.) - // - // We also throw out_of_range errors if the format string references an - // argument that isn't present (or a key that isn't present in one of the - // argument containers). However, at the moment we don't crash on these - // errors, as it is likely that the container is dynamic at runtime. - try { - appendOutput(out); - } catch (const BadFormatArg& ex) { - handleFormatStrError(); - } catch (const std::range_error& ex) { - handleFormatStrError(); - } -} - -template -template -void Formatter::appendOutput(Output& out) const { - auto p = str_.begin(); - auto end = str_.end(); - +void BaseFormatter::operator()( + Output& out) const { // Copy raw string (without format specifiers) to output; // not as simple as we'd like, as we still need to translate "}}" to "}" // and throw if we see any lone "}" - auto outputString = [&out] (StringPiece s) { + auto outputString = [&out](StringPiece s) { auto p = s.begin(); auto end = s.end(); while (p != end) { - auto q = static_cast(memchr(p, '}', end - p)); + auto q = static_cast(memchr(p, '}', size_t(end - p))); if (!q) { out(StringPiece(p, end)); break; @@ -216,17 +180,20 @@ void Formatter::appendOutput(Output& out) const { p = q; if (p == end || *p != '}') { - throw BadFormatArg("folly::format: single '}' in format string"); + throwBadFormatArg("folly::format: single '}' in format string"); } ++p; } }; + auto p = str_.begin(); + auto end = str_.end(); + int nextArg = 0; bool hasDefaultArgIndex = false; bool hasExplicitArgIndex = false; while (p != end) { - auto q = static_cast(memchr(p, '{', end - p)); + auto q = static_cast(memchr(p, '{', size_t(end - p))); if (!q) { outputString(StringPiece(p, end)); break; @@ -235,7 +202,7 @@ void Formatter::appendOutput(Output& out) const { p = q + 1; if (p == end) { - throw BadFormatArg("folly::format: '}' at end of format string"); + throwBadFormatArg("folly::format: '}' at end of format string"); } // "{{" -> "{" @@ -246,16 +213,19 @@ void Formatter::appendOutput(Output& out) const { } // Format string - q = static_cast(memchr(p, '}', end - p)); + q = static_cast(memchr(p, '}', size_t(end - p))); if (q == nullptr) { - throw BadFormatArg("folly::format: missing ending '}'"); + throwBadFormatArg("folly::format: missing ending '}'"); } FormatArg arg(StringPiece(p, q)); p = q + 1; int argIndex = 0; - auto piece = arg.splitKey(); // empty key component is okay - if (containerMode) { // static + auto piece = arg.splitKey(); // empty key component is okay + if (containerMode) { // static + arg.enforce( + arg.width != FormatArg::kDynamicWidth, + "dynamic field width not supported in vformat()"); if (piece.empty()) { arg.setNextIntKey(nextArg++); hasDefaultArgIndex = true; @@ -265,12 +235,27 @@ void Formatter::appendOutput(Output& out) const { } } else { if (piece.empty()) { + if (arg.width == FormatArg::kDynamicWidth) { + arg.enforce( + arg.widthIndex == FormatArg::kNoIndex, + "cannot provide width arg index without value arg index"); + int sizeArg = nextArg++; + arg.width = asDerived().getSizeArg(size_t(sizeArg), arg); + } + argIndex = nextArg++; hasDefaultArgIndex = true; } else { + if (arg.width == FormatArg::kDynamicWidth) { + arg.enforce( + arg.widthIndex != FormatArg::kNoIndex, + "cannot provide value arg index without width arg index"); + arg.width = asDerived().getSizeArg(size_t(arg.widthIndex), arg); + } + try { argIndex = to(piece); - } catch (const std::out_of_range& e) { + } catch (const std::out_of_range&) { arg.error("argument index must be integer"); } arg.enforce(argIndex >= 0, "argument index must be non-negative"); @@ -279,18 +264,20 @@ void Formatter::appendOutput(Output& out) const { } if (hasDefaultArgIndex && hasExplicitArgIndex) { - throw BadFormatArg( + throwBadFormatArg( "folly::format: may not have both default and explicit arg indexes"); } - doFormat(argIndex, arg, out); + asDerived().doFormat(size_t(argIndex), arg, out); } } -template -void writeTo(FILE* fp, const Formatter& formatter) { - auto writer = [fp] (StringPiece sp) { - ssize_t n = fwrite(sp.data(), 1, sp.size(), fp); +template +void writeTo( + FILE* fp, + const BaseFormatter& formatter) { + auto writer = [fp](StringPiece sp) { + size_t n = fwrite(sp.data(), 1, sp.size(), fp); if (n < sp.size()) { throwSystemError("Formatter writeTo", "fwrite failed"); } @@ -302,45 +289,53 @@ namespace format_value { template void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) { + if (arg.width != FormatArg::kDefaultWidth && arg.width < 0) { + throwBadFormatArg("folly::format: invalid width"); + } + if (arg.precision != FormatArg::kDefaultPrecision && arg.precision < 0) { + throwBadFormatArg("folly::format: invalid precision"); + } + if (arg.precision != FormatArg::kDefaultPrecision && - val.size() > arg.precision) { - val.reset(val.data(), arg.precision); + val.size() > static_cast(arg.precision)) { + val.reset(val.data(), static_cast(arg.precision)); } constexpr int padBufSize = 128; char padBuf[padBufSize]; // Output padding, no more than padBufSize at once - auto pad = [&padBuf, &cb, padBufSize] (int chars) { + auto pad = [&padBuf, &cb, padBufSize](int chars) { while (chars) { int n = std::min(chars, padBufSize); - cb(StringPiece(padBuf, n)); + cb(StringPiece(padBuf, size_t(n))); chars -= n; } }; int padRemaining = 0; - if (arg.width != FormatArg::kDefaultWidth && val.size() < arg.width) { + if (arg.width != FormatArg::kDefaultWidth && + val.size() < static_cast(arg.width)) { char fill = arg.fill == FormatArg::kDefaultFill ? ' ' : arg.fill; - int padChars = arg.width - val.size(); - memset(padBuf, fill, std::min(padBufSize, padChars)); + int padChars = static_cast(arg.width - val.size()); + memset(padBuf, fill, size_t(std::min(padBufSize, padChars))); switch (arg.align) { - case FormatArg::Align::DEFAULT: - case FormatArg::Align::LEFT: - padRemaining = padChars; - break; - case FormatArg::Align::CENTER: - pad(padChars / 2); - padRemaining = padChars - padChars / 2; - break; - case FormatArg::Align::RIGHT: - case FormatArg::Align::PAD_AFTER_SIGN: - pad(padChars); - break; - default: - abort(); - break; + case FormatArg::Align::DEFAULT: + case FormatArg::Align::LEFT: + padRemaining = padChars; + break; + case FormatArg::Align::CENTER: + pad(padChars / 2); + padRemaining = padChars - padChars / 2; + break; + case FormatArg::Align::RIGHT: + case FormatArg::Align::PAD_AFTER_SIGN: + pad(padChars); + break; + default: + abort(); + break; } } @@ -352,40 +347,49 @@ void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) { } template -void formatNumber(StringPiece val, int prefixLen, FormatArg& arg, - FormatCallback& cb) { +void formatNumber( + StringPiece val, + int prefixLen, + FormatArg& arg, + FormatCallback& cb) { // precision means something different for numbers arg.precision = FormatArg::kDefaultPrecision; if (arg.align == FormatArg::Align::DEFAULT) { arg.align = FormatArg::Align::RIGHT; } else if (prefixLen && arg.align == FormatArg::Align::PAD_AFTER_SIGN) { // Split off the prefix, then do any padding if necessary - cb(val.subpiece(0, prefixLen)); - val.advance(prefixLen); + cb(val.subpiece(0, size_t(prefixLen))); + val.advance(size_t(prefixLen)); arg.width = std::max(arg.width - prefixLen, 0); } format_value::formatString(val, arg, cb); } -template -void formatFormatter(const Formatter& formatter, - FormatArg& arg, - FormatCallback& cb) { +template < + class FormatCallback, + class Derived, + bool containerMode, + class... Args> +void formatFormatter( + const BaseFormatter& formatter, + FormatArg& arg, + FormatCallback& cb) { if (arg.width == FormatArg::kDefaultWidth && arg.precision == FormatArg::kDefaultPrecision) { // nothing to do formatter(cb); - } else if (arg.align != FormatArg::Align::LEFT && - arg.align != FormatArg::Align::DEFAULT) { + } else if ( + arg.align != FormatArg::Align::LEFT && + arg.align != FormatArg::Align::DEFAULT) { // We can only avoid creating a temporary string if we align left, // as we'd need to know the size beforehand otherwise format_value::formatString(formatter.fbstr(), arg, cb); } else { - auto fn = [&arg, &cb] (StringPiece sp) mutable { + auto fn = [&arg, &cb](StringPiece sp) mutable { int sz = static_cast(sp.size()); if (arg.precision != FormatArg::kDefaultPrecision) { sz = std::min(arg.precision, sz); - sp.reset(sp.data(), sz); + sp.reset(sp.data(), size_t(sz)); arg.precision -= sz; } if (!sp.empty()) { @@ -403,19 +407,23 @@ void formatFormatter(const Formatter& formatter, } } -} // namespace format_value +} // namespace format_value // Definitions for default FormatValue classes // Integral types (except bool) template class FormatValue< - T, typename std::enable_if< - std::is_integral::value && - !std::is_same::value>::type> - { + T, + typename std::enable_if< + std::is_integral::value && !std::is_same::value>::type> { public: - explicit FormatValue(T val) : val_(val) { } + explicit FormatValue(T val) : val_(val) {} + + T getValue() const { + return val_; + } + template void format(FormatArg& arg, FormatCallback& cb) const { arg.validate(FormatArg::Type::INTEGER); @@ -436,28 +444,29 @@ class FormatValue< char sign; if (std::is_signed::value) { if (folly::is_negative(val_)) { - uval = static_cast(-val_); + uval = UT(-static_cast(val_)); sign = '-'; } else { uval = static_cast(val_); switch (arg.sign) { - case FormatArg::Sign::PLUS_OR_MINUS: - sign = '+'; - break; - case FormatArg::Sign::SPACE_OR_MINUS: - sign = ' '; - break; - default: - sign = '\0'; - break; + case FormatArg::Sign::PLUS_OR_MINUS: + sign = '+'; + break; + case FormatArg::Sign::SPACE_OR_MINUS: + sign = ' '; + break; + default: + sign = '\0'; + break; } } } else { - uval = val_; + uval = static_cast(val_); sign = '\0'; - arg.enforce(arg.sign == FormatArg::Sign::DEFAULT, - "sign specifications not allowed for unsigned values"); + arg.enforce( + arg.sign == FormatArg::Sign::DEFAULT, + "sign specifications not allowed for unsigned values"); } // max of: @@ -472,94 +481,133 @@ class FormatValue< char* valBufBegin = nullptr; char* valBufEnd = nullptr; - // Defer to sprintf - auto useSprintf = [&] (const char* format) mutable { - valBufBegin = valBuf + 3; // room for sign and base prefix - valBufEnd = valBufBegin + sprintf(valBufBegin, format, - static_cast(uval)); - }; - int prefixLen = 0; - switch (presentation) { - case 'n': // TODO(tudorb): locale awareness? - case 'd': - arg.enforce(!arg.basePrefix, - "base prefix not allowed with '", presentation, - "' specifier"); - if (arg.thousandsSeparator) { - useSprintf("%'ju"); - } else { + case 'n': { + arg.enforce( + !arg.basePrefix, + "base prefix not allowed with '", + presentation, + "' specifier"); + + arg.enforce( + !arg.thousandsSeparator, + "cannot use ',' with the '", + presentation, + "' specifier"); + + valBufBegin = valBuf + 3; // room for sign and base prefix +#if defined(__ANDROID__) + int len = snprintf( + valBufBegin, + (valBuf + valBufSize) - valBufBegin, + "%" PRIuMAX, + static_cast(uval)); +#else + int len = snprintf( + valBufBegin, + size_t((valBuf + valBufSize) - valBufBegin), + "%ju", + static_cast(uval)); +#endif + // valBufSize should always be big enough, so this should never + // happen. + assert(len < valBuf + valBufSize - valBufBegin); + valBufEnd = valBufBegin + len; + break; + } + case 'd': + arg.enforce( + !arg.basePrefix, + "base prefix not allowed with '", + presentation, + "' specifier"); + valBufBegin = valBuf + 3; // room for sign and base prefix + // Use uintToBuffer, faster than sprintf - valBufBegin = valBuf + 3; valBufEnd = valBufBegin + uint64ToBufferUnsafe(uval, valBufBegin); - } - break; - case 'c': - arg.enforce(!arg.basePrefix, - "base prefix not allowed with '", presentation, - "' specifier"); - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufBegin = valBuf + 3; - *valBufBegin = static_cast(uval); - valBufEnd = valBufBegin + 1; - break; - case 'o': - case 'O': - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufEnd = valBuf + valBufSize - 1; - valBufBegin = valBuf + detail::uintToOctal(valBuf, valBufSize - 1, uval); - if (arg.basePrefix) { - *--valBufBegin = '0'; - prefixLen = 1; - } - break; - case 'x': - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufEnd = valBuf + valBufSize - 1; - valBufBegin = valBuf + detail::uintToHexLower(valBuf, valBufSize - 1, - uval); - if (arg.basePrefix) { - *--valBufBegin = 'x'; - *--valBufBegin = '0'; - prefixLen = 2; - } - break; - case 'X': - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufEnd = valBuf + valBufSize - 1; - valBufBegin = valBuf + detail::uintToHexUpper(valBuf, valBufSize - 1, - uval); - if (arg.basePrefix) { - *--valBufBegin = 'X'; - *--valBufBegin = '0'; - prefixLen = 2; - } - break; - case 'b': - case 'B': - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufEnd = valBuf + valBufSize - 1; - valBufBegin = valBuf + detail::uintToBinary(valBuf, valBufSize - 1, - uval); - if (arg.basePrefix) { - *--valBufBegin = presentation; // 0b or 0B - *--valBufBegin = '0'; - prefixLen = 2; - } - break; - default: - arg.error("invalid specifier '", presentation, "'"); + if (arg.thousandsSeparator) { + detail::insertThousandsGroupingUnsafe(valBufBegin, &valBufEnd); + } + break; + case 'c': + arg.enforce( + !arg.basePrefix, + "base prefix not allowed with '", + presentation, + "' specifier"); + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufBegin = valBuf + 3; + *valBufBegin = static_cast(uval); + valBufEnd = valBufBegin + 1; + break; + case 'o': + case 'O': + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufEnd = valBuf + valBufSize - 1; + valBufBegin = + valBuf + detail::uintToOctal(valBuf, valBufSize - 1, uval); + if (arg.basePrefix) { + *--valBufBegin = '0'; + prefixLen = 1; + } + break; + case 'x': + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufEnd = valBuf + valBufSize - 1; + valBufBegin = + valBuf + detail::uintToHexLower(valBuf, valBufSize - 1, uval); + if (arg.basePrefix) { + *--valBufBegin = 'x'; + *--valBufBegin = '0'; + prefixLen = 2; + } + break; + case 'X': + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufEnd = valBuf + valBufSize - 1; + valBufBegin = + valBuf + detail::uintToHexUpper(valBuf, valBufSize - 1, uval); + if (arg.basePrefix) { + *--valBufBegin = 'X'; + *--valBufBegin = '0'; + prefixLen = 2; + } + break; + case 'b': + case 'B': + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufEnd = valBuf + valBufSize - 1; + valBufBegin = + valBuf + detail::uintToBinary(valBuf, valBufSize - 1, uval); + if (arg.basePrefix) { + *--valBufBegin = presentation; // 0b or 0B + *--valBufBegin = '0'; + prefixLen = 2; + } + break; + default: + arg.error("invalid specifier '", presentation, "'"); } if (sign) { @@ -567,8 +615,8 @@ class FormatValue< ++prefixLen; } - format_value::formatNumber(StringPiece(valBufBegin, valBufEnd), prefixLen, - arg, cb); + format_value::formatNumber( + StringPiece(valBufBegin, valBufEnd), prefixLen, arg, cb); } private: @@ -579,14 +627,14 @@ class FormatValue< template <> class FormatValue { public: - explicit FormatValue(bool val) : val_(val) { } + explicit FormatValue(bool val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { if (arg.presentation == FormatArg::kDefaultPresentation) { arg.validate(FormatArg::Type::OTHER); format_value::formatString(val_ ? "true" : "false", arg, cb); - } else { // number + } else { // number FormatValue(val_).format(arg, cb); } } @@ -599,131 +647,19 @@ class FormatValue { template <> class FormatValue { public: - explicit FormatValue(double val) : val_(val) { } + explicit FormatValue(double val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { - using ::double_conversion::DoubleToStringConverter; - using ::double_conversion::StringBuilder; - - arg.validate(FormatArg::Type::FLOAT); - - if (arg.presentation == FormatArg::kDefaultPresentation) { - arg.presentation = 'g'; - } - - const char* infinitySymbol = isupper(arg.presentation) ? "INF" : "inf"; - const char* nanSymbol = isupper(arg.presentation) ? "NAN" : "nan"; - char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e'; - - if (arg.precision == FormatArg::kDefaultPrecision) { - arg.precision = 6; - } - - // 2+: for null terminator and optional sign shenanigans. - char buf[2 + std::max({ - (2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint + - DoubleToStringConverter::kMaxFixedDigitsAfterPoint), - (8 + DoubleToStringConverter::kMaxExponentialDigits), - (7 + DoubleToStringConverter::kMaxPrecisionDigits)})]; - StringBuilder builder(buf + 1, sizeof(buf) - 1); - - char plusSign; - switch (arg.sign) { - case FormatArg::Sign::PLUS_OR_MINUS: - plusSign = '+'; - break; - case FormatArg::Sign::SPACE_OR_MINUS: - plusSign = ' '; - break; - default: - plusSign = '\0'; - break; - }; - - double val = val_; - switch (arg.presentation) { - case '%': - val *= 100; - case 'f': - case 'F': - { - if (arg.precision > - DoubleToStringConverter::kMaxFixedDigitsAfterPoint) { - arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint; - } - DoubleToStringConverter conv( - DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, - infinitySymbol, - nanSymbol, - exponentSymbol, - -4, arg.precision, - 0, 0); - arg.enforce(conv.ToFixed(val, arg.precision, &builder), - "fixed double conversion failed"); - } - break; - case 'e': - case 'E': - { - if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) { - arg.precision = DoubleToStringConverter::kMaxExponentialDigits; - } - - DoubleToStringConverter conv( - DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, - infinitySymbol, - nanSymbol, - exponentSymbol, - -4, arg.precision, - 0, 0); - arg.enforce(conv.ToExponential(val, arg.precision, &builder)); - } - break; - case 'n': // should be locale-aware, but isn't - case 'g': - case 'G': - { - if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) { - arg.precision = DoubleToStringConverter::kMinPrecisionDigits; - } else if (arg.precision > - DoubleToStringConverter::kMaxPrecisionDigits) { - arg.precision = DoubleToStringConverter::kMaxPrecisionDigits; - } - DoubleToStringConverter conv( - DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, - infinitySymbol, - nanSymbol, - exponentSymbol, - -4, arg.precision, - 0, 0); - arg.enforce(conv.ToShortest(val, &builder)); - } - break; - default: - arg.error("invalid specifier '", arg.presentation, "'"); - } - - int len = builder.position(); - builder.Finalize(); - DCHECK_GT(len, 0); - - // Add '+' or ' ' sign if needed - char* p = buf + 1; - // anything that's neither negative nor nan - int prefixLen = 0; - if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) { - *--p = plusSign; - ++len; - prefixLen = 1; - } else if (*p == '-') { - prefixLen = 1; - } - - format_value::formatNumber(StringPiece(p, len), prefixLen, arg, cb); + fbstring piece; + int prefixLen; + formatHelper(piece, prefixLen, arg); + format_value::formatNumber(piece, prefixLen, arg, cb); } private: + void formatHelper(fbstring& piece, int& prefixLen, FormatArg& arg) const; + double val_; }; @@ -731,7 +667,7 @@ class FormatValue { template <> class FormatValue { public: - explicit FormatValue(float val) : val_(val) { } + explicit FormatValue(float val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { @@ -742,28 +678,33 @@ class FormatValue { float val_; }; -// Sring-y types (implicitly convertible to StringPiece, except char*) +// String-y types (implicitly convertible to StringPiece, except char*) template class FormatValue< - T, typename std::enable_if< - (!std::is_pointer::value || - !std::is_same::type>::type>::value) && - std::is_convertible::value>::type> - { + T, + typename std::enable_if< + (!std::is_pointer::value || + !std::is_same< + char, + typename std::decay::type>::type>:: + value) && + std::is_convertible::value>::type> { public: - explicit FormatValue(StringPiece val) : val_(val) { } + explicit FormatValue(StringPiece val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { if (arg.keyEmpty()) { arg.validate(FormatArg::Type::OTHER); - arg.enforce(arg.presentation == FormatArg::kDefaultPresentation || - arg.presentation == 's', - "invalid specifier '", arg.presentation, "'"); + arg.enforce( + arg.presentation == FormatArg::kDefaultPresentation || + arg.presentation == 's', + "invalid specifier '", + arg.presentation, + "'"); format_value::formatString(val_, arg, cb); } else { - FormatValue(val_.at(arg.splitIntKey())).format(arg, cb); + FormatValue(val_.at(size_t(arg.splitIntKey()))).format(arg, cb); } } @@ -775,13 +716,16 @@ class FormatValue< template <> class FormatValue { public: - explicit FormatValue(std::nullptr_t) { } + explicit FormatValue(std::nullptr_t) {} template void format(FormatArg& arg, FormatCallback& cb) const { arg.validate(FormatArg::Type::OTHER); - arg.enforce(arg.presentation == FormatArg::kDefaultPresentation, - "invalid specifier '", arg.presentation, "'"); + arg.enforce( + arg.presentation == FormatArg::kDefaultPresentation, + "invalid specifier '", + arg.presentation, + "'"); format_value::formatString("(null)", arg, cb); } }; @@ -789,12 +733,11 @@ class FormatValue { // Partial specialization of FormatValue for char* template class FormatValue< - T*, - typename std::enable_if< - std::is_same::type>::value>::type> - { + T*, + typename std::enable_if< + std::is_same::type>::value>::type> { public: - explicit FormatValue(T* val) : val_(val) { } + explicit FormatValue(T* val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { @@ -805,8 +748,8 @@ class FormatValue< FormatValue(val_).format(arg, cb); } } else { - FormatValue::type>( - val_[arg.splitIntKey()]).format(arg, cb); + FormatValue::type>(val_[arg.splitIntKey()]) + .format(arg, cb); } } @@ -817,12 +760,11 @@ class FormatValue< // Partial specialization of FormatValue for void* template class FormatValue< - T*, - typename std::enable_if< - std::is_same::type>::value>::type> - { + T*, + typename std::enable_if< + std::is_same::type>::value>::type> { public: - explicit FormatValue(T* val) : val_(val) { } + explicit FormatValue(T* val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { @@ -831,15 +773,18 @@ class FormatValue< } else { // Print as a pointer, in hex. arg.validate(FormatArg::Type::OTHER); - arg.enforce(arg.presentation == FormatArg::kDefaultPresentation, - "invalid specifier '", arg.presentation, "'"); + arg.enforce( + arg.presentation == FormatArg::kDefaultPresentation, + "invalid specifier '", + arg.presentation, + "'"); arg.basePrefix = true; arg.presentation = 'x'; if (arg.align == FormatArg::Align::DEFAULT) { arg.align = FormatArg::Align::LEFT; } - FormatValue( - reinterpret_cast(val_)).doFormat(arg, cb); + FormatValue(reinterpret_cast(val_)) + .doFormat(arg, cb); } } @@ -851,17 +796,17 @@ template class TryFormatValue { public: template - static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) { + static void + formatOrFail(T& /* value */, FormatArg& arg, FormatCallback& /* cb */) { arg.error("No formatter available for this type"); } }; template class TryFormatValue< - T, - typename std::enable_if< - 0 < sizeof(FormatValue::type>)>::type> - { + T, + typename std::enable_if< + 0 < sizeof(FormatValue::type>)>::type> { public: template static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) { @@ -872,13 +817,12 @@ class TryFormatValue< // Partial specialization of FormatValue for other pointers template class FormatValue< - T*, - typename std::enable_if< - !std::is_same::type>::value && - !std::is_same::type>::value>::type> - { + T*, + typename std::enable_if< + !std::is_same::type>::value && + !std::is_same::type>::value>::type> { public: - explicit FormatValue(T* val) : val_(val) { } + explicit FormatValue(T* val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { @@ -888,113 +832,56 @@ class FormatValue< TryFormatValue::formatOrFail(val_[arg.splitIntKey()], arg, cb); } } + private: T* val_; }; namespace detail { -// Shortcut, so we don't have to use enable_if everywhere -struct FormatTraitsBase { - typedef void enabled; -}; - -// Traits that define enabled, value_type, and at() for anything -// indexable with integral keys: pointers, arrays, vectors, and maps -// with integral keys -template struct IndexableTraits; - -// Base class for sequences (vectors, deques) -template -struct IndexableTraitsSeq : public FormatTraitsBase { - typedef C container_type; - typedef typename C::value_type value_type; - static const value_type& at(const C& c, int idx) { - return c.at(idx); - } - - static const value_type& at(const C& c, int idx, - const value_type& dflt) { - return (idx >= 0 && idx < c.size()) ? c.at(idx) : dflt; - } -}; - -// Base class for associative types (maps) -template -struct IndexableTraitsAssoc : public FormatTraitsBase { - typedef typename C::value_type::second_type value_type; - static const value_type& at(const C& c, int idx) { - return c.at(static_cast(idx)); - } - static const value_type& at(const C& c, int idx, - const value_type& dflt) { - auto pos = c.find(static_cast(idx)); - return pos != c.end() ? pos->second : dflt; - } -}; - // std::array template struct IndexableTraits> - : public IndexableTraitsSeq> { -}; + : public IndexableTraitsSeq> {}; // std::vector template struct IndexableTraits> - : public IndexableTraitsSeq> { -}; + : public IndexableTraitsSeq> {}; // std::deque template struct IndexableTraits> - : public IndexableTraitsSeq> { -}; - -// fbvector -template -struct IndexableTraits> - : public IndexableTraitsSeq> { -}; - -// small_vector -template -struct IndexableTraits> - : public IndexableTraitsSeq> { -}; + : public IndexableTraitsSeq> {}; // std::map with integral keys template struct IndexableTraits< - std::map, - typename std::enable_if::value>::type> - : public IndexableTraitsAssoc> { -}; + std::map, + typename std::enable_if::value>::type> + : public IndexableTraitsAssoc> {}; // std::unordered_map with integral keys template struct IndexableTraits< - std::unordered_map, - typename std::enable_if::value>::type> - : public IndexableTraitsAssoc> { -}; + std::unordered_map, + typename std::enable_if::value>::type> + : public IndexableTraitsAssoc> {}; -} // namespace detail +} // namespace detail // Partial specialization of FormatValue for integer-indexable containers template -class FormatValue< - T, - typename detail::IndexableTraits::enabled> { +class FormatValue::enabled> { public: - explicit FormatValue(const T& val) : val_(val) { } + explicit FormatValue(const T& val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { FormatValue::value_type>::type>( - detail::IndexableTraits::at( - val_, arg.splitIntKey())).format(arg, cb); + typename detail::IndexableTraits::value_type>::type>( + detail::IndexableTraits::at(val_, arg.splitIntKey())) + .format(arg, cb); } private: @@ -1003,20 +890,19 @@ class FormatValue< template class FormatValue< - detail::DefaultValueWrapper, - typename detail::IndexableTraits::enabled> { + detail::DefaultValueWrapper, + typename detail::IndexableTraits::enabled> { public: explicit FormatValue(const detail::DefaultValueWrapper& val) - : val_(val) { } + : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { FormatValue::value_type>::type>( - detail::IndexableTraits::at( - val_.container, - arg.splitIntKey(), - val_.defaultValue)).format(arg, cb); + typename detail::IndexableTraits::value_type>::type>( + detail::IndexableTraits::at( + val_.container, arg.splitIntKey(), val_.defaultValue)) + .format(arg, cb); } private: @@ -1027,7 +913,8 @@ namespace detail { // Define enabled, key_type, convert from StringPiece to the key types // that we support -template struct KeyFromStringPiece; +template +struct KeyFromStringPiece; // std::string template <> @@ -1058,14 +945,15 @@ struct KeyFromStringPiece : public FormatTraitsBase { }; // Base class for associative types keyed by strings -template struct KeyableTraitsAssoc : public FormatTraitsBase { +template +struct KeyableTraitsAssoc : public FormatTraitsBase { typedef typename T::key_type key_type; typedef typename T::value_type::second_type value_type; static const value_type& at(const T& map, StringPiece key) { return map.at(KeyFromStringPiece::convert(key)); } - static const value_type& at(const T& map, StringPiece key, - const value_type& dflt) { + static const value_type& + at(const T& map, StringPiece key, const value_type& dflt) { auto pos = map.find(KeyFromStringPiece::convert(key)); return pos != map.end() ? pos->second : dflt; } @@ -1073,40 +961,37 @@ template struct KeyableTraitsAssoc : public FormatTraitsBase { // Define enabled, key_type, value_type, at() for supported string-keyed // types -template struct KeyableTraits; +template +struct KeyableTraits; // std::map with string key template struct KeyableTraits< - std::map, - typename KeyFromStringPiece::enabled> - : public KeyableTraitsAssoc> { -}; + std::map, + typename KeyFromStringPiece::enabled> + : public KeyableTraitsAssoc> {}; // std::unordered_map with string key template struct KeyableTraits< - std::unordered_map, - typename KeyFromStringPiece::enabled> - : public KeyableTraitsAssoc> { -}; + std::unordered_map, + typename KeyFromStringPiece::enabled> + : public KeyableTraitsAssoc> {}; -} // namespace detail +} // namespace detail // Partial specialization of FormatValue for string-keyed containers template -class FormatValue< - T, - typename detail::KeyableTraits::enabled> { +class FormatValue::enabled> { public: - explicit FormatValue(const T& val) : val_(val) { } + explicit FormatValue(const T& val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { FormatValue::value_type>::type>( - detail::KeyableTraits::at( - val_, arg.splitKey())).format(arg, cb); + typename detail::KeyableTraits::value_type>::type>( + detail::KeyableTraits::at(val_, arg.splitKey())) + .format(arg, cb); } private: @@ -1115,20 +1000,19 @@ class FormatValue< template class FormatValue< - detail::DefaultValueWrapper, - typename detail::KeyableTraits::enabled> { + detail::DefaultValueWrapper, + typename detail::KeyableTraits::enabled> { public: explicit FormatValue(const detail::DefaultValueWrapper& val) - : val_(val) { } + : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { FormatValue::value_type>::type>( - detail::KeyableTraits::at( - val_.container, - arg.splitKey(), - val_.defaultValue)).format(arg, cb); + typename detail::KeyableTraits::value_type>::type>( + detail::KeyableTraits::at( + val_.container, arg.splitKey(), val_.defaultValue)) + .format(arg, cb); } private: @@ -1139,20 +1023,20 @@ class FormatValue< template class FormatValue> { public: - explicit FormatValue(const std::pair& val) : val_(val) { } + explicit FormatValue(const std::pair& val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { int key = arg.splitIntKey(); switch (key) { - case 0: - FormatValue::type>(val_.first).format(arg, cb); - break; - case 1: - FormatValue::type>(val_.second).format(arg, cb); - break; - default: - arg.error("invalid index for pair"); + case 0: + FormatValue::type>(val_.first).format(arg, cb); + break; + case 1: + FormatValue::type>(val_.second).format(arg, cb); + break; + default: + arg.error("invalid index for pair"); } } @@ -1164,14 +1048,15 @@ class FormatValue> { template class FormatValue> { typedef std::tuple Tuple; + public: - explicit FormatValue(const Tuple& val) : val_(val) { } + explicit FormatValue(const Tuple& val) : val_(val) {} template void format(FormatArg& arg, FormatCallback& cb) const { int key = arg.splitIntKey(); arg.enforce(key >= 0, "tuple index must be non-negative"); - doFormat(key, arg, cb); + doFormat(size_t(key), arg, cb); } private: @@ -1179,7 +1064,7 @@ class FormatValue> { template typename std::enable_if::type - doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { + doFormatFrom(size_t i, FormatArg& arg, Callback& /* cb */) const { arg.enforce("tuple index out of range, max=", i); } @@ -1188,10 +1073,10 @@ class FormatValue> { doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { if (i == K) { FormatValue::type>::type>( - std::get(val_)).format(arg, cb); + typename std::tuple_element::type>::type>(std::get(val_)) + .format(arg, cb); } else { - doFormatFrom(i, arg, cb); + doFormatFrom(i, arg, cb); } } @@ -1204,16 +1089,21 @@ class FormatValue> { }; // Partial specialization of FormatValue for nested Formatters -template -class FormatValue, void> { - typedef Formatter FormatterValue; +template class F> +class FormatValue< + F, + typename std::enable_if< + detail::IsFormatter>::value>::type> { + typedef typename F::BaseType FormatterValue; + public: - explicit FormatValue(const FormatterValue& f) : f_(f) { } + explicit FormatValue(const FormatterValue& f) : f_(f) {} template void format(FormatArg& arg, FormatCallback& cb) const { format_value::formatFormatter(f_, arg, cb); } + private: const FormatterValue& f_; }; @@ -1222,13 +1112,13 @@ class FormatValue, void> { * Formatter objects can be appended to strings, and therefore they're * compatible with folly::toAppend and folly::to. */ -template -typename std::enable_if< - IsSomeString::value>::type -toAppend(const Formatter& value, Tgt * result) { +template +typename std::enable_if::value>::type toAppend( + const BaseFormatter& value, + Tgt* result) { value.appendTo(*result); } -} // namespace folly +} // namespace folly -#pragma GCC diagnostic pop +FOLLY_POP_WARNING