X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FFormat-inl.h;h=64591d6af702e8dfadafffeb2737b87def6a5132;hb=a5a5daef1f43d6222a0261e856edd3a62f2c7b24;hp=59f4550a50f7cb31aaaaf726d338fa7d1ecdb5d9;hpb=27494a20393fa45072e7d526d358835f3abe312a;p=folly.git diff --git a/folly/Format-inl.h b/folly/Format-inl.h index 59f4550a..64591d6a 100644 --- a/folly/Format-inl.h +++ b/folly/Format-inl.h @@ -1,5 +1,5 @@ /* - * Copyright 2012 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. @@ -18,10 +18,27 @@ #error This file may only be included from Format.h. #endif +#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" + 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]; @@ -43,7 +60,9 @@ const size_t kMaxBinaryLength = 8 * sizeof(uintmax_t); template size_t uintToHex(char* buffer, size_t bufLen, Uint v, const char (&repr)[256][2]) { - for (; v >= 256; v >>= 8) { + // '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) { auto b = v & 0xff; bufLen -= 2; buffer[bufLen] = repr[b][0]; @@ -85,7 +104,9 @@ inline size_t uintToHexUpper(char* buffer, size_t bufLen, Uint v) { template size_t uintToOctal(char* buffer, size_t bufLen, Uint v) { auto& repr = formatOctal; - for (; v >= 512; v >>= 9) { + // 'v >>= 7, v >>= 2' is no more than a work around to get rid of shift size + // warning when Uint = uint8_t (it's false as v >= 512 implies sizeof(v) > 1). + for (; !less_than(v); v >>= 7, v >>= 2) { auto b = v & 0x1ff; bufLen -= 3; buffer[bufLen] = repr[b][0]; @@ -117,7 +138,7 @@ size_t uintToBinary(char* buffer, size_t bufLen, Uint v) { buffer[--bufLen] = '0'; return bufLen; } - for (; v; v >>= 8) { + for (; v; v >>= 7, v >>= 1) { auto b = v & 0xff; bufLen -= 8; memcpy(buffer + bufLen, &(repr[b][0]), 8); @@ -130,22 +151,20 @@ size_t uintToBinary(char* buffer, size_t bufLen, Uint v) { } // namespace detail - -template -Formatter::Formatter(StringPiece str, Args&&... args) - : str_(str), - values_(FormatValue::type>( - std::forward(args))...) { +template +BaseFormatter::BaseFormatter(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 +template template -void Formatter::operator()(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 "}" @@ -163,13 +182,15 @@ void Formatter::operator()(Output& out) const { p = q; if (p == end || *p != '}') { - throw std::invalid_argument( - "folly::format: single '}' in format string"); + throw BadFormatArg("folly::format: single '}' in format string"); } ++p; } }; + auto p = str_.begin(); + auto end = str_.end(); + int nextArg = 0; bool hasDefaultArgIndex = false; bool hasExplicitArgIndex = false; @@ -183,8 +204,7 @@ void Formatter::operator()(Output& out) const { p = q + 1; if (p == end) { - throw std::invalid_argument( - "folly::format: '}' at end of format string"); + throw BadFormatArg("folly::format: '}' at end of format string"); } // "{{" -> "{" @@ -196,8 +216,8 @@ void Formatter::operator()(Output& out) const { // Format string q = static_cast(memchr(p, '}', end - p)); - if (q == end) { - throw std::invalid_argument("folly::format: missing ending '}'"); + if (q == nullptr) { + throw BadFormatArg("folly::format: missing ending '}'"); } FormatArg arg(StringPiece(p, q)); p = q + 1; @@ -205,6 +225,8 @@ void Formatter::operator()(Output& out) const { 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; @@ -214,9 +236,22 @@ void Formatter::operator()(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 = getSizeArg(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(arg.widthIndex, arg); + } + try { argIndex = to(piece); } catch (const std::out_of_range& e) { @@ -228,7 +263,7 @@ void Formatter::operator()(Output& out) const { } if (hasDefaultArgIndex && hasExplicitArgIndex) { - throw std::invalid_argument( + throw BadFormatArg( "folly::format: may not have both default and explicit arg indexes"); } @@ -236,12 +271,35 @@ void Formatter::operator()(Output& out) const { } } +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"); + } + }; + formatter(writer); +} + namespace format_value { template void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) { + if (arg.width != FormatArg::kDefaultWidth && arg.width < 0) { + throw BadFormatArg("folly::format: invalid width"); + } + if (arg.precision != FormatArg::kDefaultPrecision && arg.precision < 0) { + throw BadFormatArg("folly::format: invalid precision"); + } + + // XXX: clang should be smart enough to not need the two static_cast + // uses below given the above checks. If clang ever becomes that smart, we + // should remove the otherwise unnecessary warts. + if (arg.precision != FormatArg::kDefaultPrecision && - val.size() > arg.precision) { + val.size() > static_cast(arg.precision)) { val.reset(val.data(), arg.precision); } @@ -258,9 +316,10 @@ void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) { }; 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(); + int padChars = static_cast (arg.width - val.size()); memset(padBuf, fill, std::min(padBufSize, padChars)); switch (arg.align) { @@ -305,10 +364,14 @@ void formatNumber(StringPiece val, int prefixLen, FormatArg& arg, format_value::formatString(val, arg, cb); } -template -void formatFormatter(const Formatter& formatter, - FormatArg& arg, - FormatCallback& cb) { +template +void formatFormatter( + const BaseFormatter& formatter, + FormatArg& arg, + FormatCallback& cb) { if (arg.width == FormatArg::kDefaultWidth && arg.precision == FormatArg::kDefaultPrecision) { // nothing to do @@ -354,6 +417,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); @@ -373,7 +441,7 @@ class FormatValue< UT uval; char sign; if (std::is_signed::value) { - if (val_ < 0) { + if (folly::is_negative(val_)) { uval = static_cast(-val_); sign = '-'; } else { @@ -410,28 +478,49 @@ 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 +#ifdef _MSC_VER + char valBuf2[valBufSize]; + snprintf(valBuf2, valBufSize, "%ju", static_cast(uval)); + int len = GetNumberFormat( + LOCALE_USER_DEFAULT, + 0, + valBuf2, + nullptr, + valBufBegin, + (int)((valBuf + valBufSize) - valBufBegin) + ); +#else + int len = snprintf(valBufBegin, (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 - valBufEnd = valBuf + valBufSize - 1; - valBufBegin = valBuf + detail::uintToBuffer(valBuf, valBufSize - 1, - uval); + detail::insertThousandsGroupingUnsafe(valBufBegin, &valBufEnd); } break; case 'c': @@ -542,129 +631,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; - } - - bool done = false; - - // 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); - CHECK(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); - CHECK(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_; }; @@ -788,6 +763,28 @@ class FormatValue< T* val_; }; +template +class TryFormatValue { + public: + template + 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> + { + public: + template + static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) { + FormatValue::type>(value).format(arg, cb); + } +}; + // Partial specialization of FormatValue for other pointers template class FormatValue< @@ -804,8 +801,7 @@ class FormatValue< if (arg.keyEmpty()) { FormatValue((void*)val_).format(arg, cb); } else { - FormatValue::type>( - val_[arg.splitIntKey()]).format(arg, cb); + TryFormatValue::formatOrFail(val_[arg.splitIntKey()], arg, cb); } } private: @@ -814,35 +810,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); - } -}; - -// 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)); - } -}; - // std::array template struct IndexableTraits> @@ -861,18 +828,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< @@ -911,6 +866,28 @@ class FormatValue< const T& val_; }; +template +class FormatValue< + detail::DefaultValueWrapper, + typename detail::IndexableTraits::enabled> { + public: + explicit FormatValue(const detail::DefaultValueWrapper& 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); + } + + private: + const detail::DefaultValueWrapper& val_; +}; + namespace detail { // Define enabled, key_type, convert from StringPiece to the key types @@ -952,6 +929,11 @@ template struct KeyableTraitsAssoc : public FormatTraitsBase { 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) { + auto pos = map.find(KeyFromStringPiece::convert(key)); + return pos != map.end() ? pos->second : dflt; + } }; // Define enabled, key_type, value_type, at() for supported string-keyed @@ -996,6 +978,28 @@ class FormatValue< const T& val_; }; +template +class FormatValue< + detail::DefaultValueWrapper, + typename detail::KeyableTraits::enabled> { + public: + explicit FormatValue(const detail::DefaultValueWrapper& 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); + } + + private: + const detail::DefaultValueWrapper& val_; +}; + // Partial specialization of FormatValue for pairs template class FormatValue> { @@ -1064,15 +1068,35 @@ class FormatValue> { const Tuple& val_; }; +// Partial specialization of FormatValue for nested Formatters +template class F> +class FormatValue, + typename std::enable_if>::value>::type> { + typedef typename F::BaseType FormatterValue; + + public: + 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_; +}; + /** * Formatter objects can be appended to strings, and therefore they're * compatible with folly::toAppend and folly::to. */ -template -typename std::enable_if< - detail::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 + +#pragma GCC diagnostic pop