X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FFormat-inl.h;h=deb8bdaa30db9ffc293eab1cd7ffac1a48e4f28b;hb=d1af84b686a8eb9a360c28bedac3cee2a0a3f5f0;hp=1603423eef1c921016b4656cd23bfde05e755b79;hpb=ab794fb62edf575ae5985db55d9b4df815d96fc3;p=folly.git diff --git a/folly/Format-inl.h b/folly/Format-inl.h index 1603423e..deb8bdaa 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,8 +18,17 @@ #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 @@ -29,6 +38,9 @@ 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]; @@ -151,55 +163,10 @@ BaseFormatter::BaseFormatter(StringPiece str, "Exactly one argument required in container mode"); } -template -void BaseFormatter::handleFormatStrError() - const { - if (crashOnError_) { - LOG(FATAL) << "folly::format: bad format string \"" << str_ << "\": " << - folly::exceptionStr(std::current_exception()); - } - throw; -} - template template void BaseFormatter::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 BaseFormatter::appendOutput(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 "}" @@ -207,7 +174,7 @@ void BaseFormatter::appendOutput(Output& out) 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; @@ -230,7 +197,7 @@ void BaseFormatter::appendOutput(Output& out) 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; @@ -250,7 +217,7 @@ void BaseFormatter::appendOutput(Output& out) } // 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 '}'"); } @@ -260,6 +227,8 @@ void BaseFormatter::appendOutput(Output& out) int argIndex = 0; 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; @@ -269,12 +238,25 @@ void BaseFormatter::appendOutput(Output& out) } } 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 = 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 = 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"); @@ -287,7 +269,7 @@ void BaseFormatter::appendOutput(Output& out) "folly::format: may not have both default and explicit arg indexes"); } - doFormat(argIndex, arg, out); + doFormat(size_t(argIndex), arg, out); } } @@ -320,7 +302,7 @@ void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) { if (arg.precision != FormatArg::kDefaultPrecision && val.size() > static_cast(arg.precision)) { - val.reset(val.data(), arg.precision); + val.reset(val.data(), size_t(arg.precision)); } constexpr int padBufSize = 128; @@ -330,7 +312,7 @@ void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) { 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; } }; @@ -340,7 +322,7 @@ void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) { val.size() < static_cast(arg.width)) { char fill = arg.fill == FormatArg::kDefaultFill ? ' ' : arg.fill; int padChars = static_cast (arg.width - val.size()); - memset(padBuf, fill, std::min(padBufSize, padChars)); + memset(padBuf, fill, size_t(std::min(padBufSize, padChars))); switch (arg.align) { case FormatArg::Align::DEFAULT: @@ -377,8 +359,8 @@ void formatNumber(StringPiece val, int prefixLen, FormatArg& arg, 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); @@ -406,7 +388,7 @@ void formatFormatter( 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()) { @@ -437,6 +419,11 @@ class FormatValue< { public: explicit FormatValue(T val) : val_(val) { } + + T getValue() const { + return val_; + } + template void format(FormatArg& arg, FormatCallback& cb) const { arg.validate(FormatArg::Type::INTEGER); @@ -457,7 +444,7 @@ 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_); @@ -474,7 +461,7 @@ class FormatValue< } } } else { - uval = val_; + uval = static_cast(val_); sign = '\0'; arg.enforce(arg.sign == FormatArg::Sign::DEFAULT, @@ -493,27 +480,44 @@ 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 '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 + valBufEnd = valBufBegin + uint64ToBufferUnsafe(uval, valBufBegin); if (arg.thousandsSeparator) { - useSprintf("%'ju"); - } else { - // Use uintToBuffer, faster than sprintf - valBufBegin = valBuf + 3; - valBufEnd = valBufBegin + uint64ToBufferUnsafe(uval, valBufBegin); + detail::insertThousandsGroupingUnsafe(valBufBegin, &valBufEnd); } break; case 'c': @@ -624,135 +628,15 @@ class FormatValue { 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, static_cast (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; - }; - - auto flags = - DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN | - (arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT - : 0); - - double val = val_; - switch (arg.presentation) { - case '%': - val *= 100; - case 'f': - case 'F': - { - if (arg.precision > - DoubleToStringConverter::kMaxFixedDigitsAfterPoint) { - arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint; - } - DoubleToStringConverter conv(flags, - 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(flags, - 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(flags, - 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_; }; @@ -792,7 +676,7 @@ class FormatValue< "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); } } @@ -880,7 +764,9 @@ 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"); } }; @@ -923,45 +809,6 @@ class FormatValue< 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 && size_t(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> @@ -980,18 +827,6 @@ struct IndexableTraits> : public IndexableTraitsSeq> { }; -// fbvector -template -struct IndexableTraits> - : public IndexableTraitsSeq> { -}; - -// small_vector -template -struct IndexableTraits> - : public IndexableTraitsSeq> { -}; - // std::map with integral keys template struct IndexableTraits< @@ -1200,15 +1035,15 @@ class FormatValue> { 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: static constexpr size_t valueCount = std::tuple_size::value; template - typename std::enable_if::type - doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { + typename std::enable_if::type doFormatFrom( + size_t i, FormatArg& arg, Callback& /* cb */) const { arg.enforce("tuple index out of range, max=", i); }