123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134 |
- /*
- * Copyright 2012-present Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #ifndef FOLLY_FORMAT_H_
- #error This file may only be included from Format.h.
- #endif
- #include <array>
- #include <cinttypes>
- #include <deque>
- #include <map>
- #include <unordered_map>
- #include <vector>
- #include <folly/Exception.h>
- #include <folly/FormatTraits.h>
- #include <folly/MapUtil.h>
- #include <folly/Traits.h>
- #include <folly/lang/Exception.h>
- #include <folly/portability/Windows.h>
- // Ignore -Wformat-nonliteral warnings within this file
- FOLLY_PUSH_WARNING
- FOLLY_GNU_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 std::array<std::array<char, 2>, 256> formatHexUpper;
- extern const std::array<std::array<char, 2>, 256> formatHexLower;
- extern const std::array<std::array<char, 3>, 512> formatOctal;
- extern const std::array<std::array<char, 8>, 256> formatBinary;
- const size_t kMaxHexLength = 2 * sizeof(uintmax_t);
- const size_t kMaxOctalLength = 3 * sizeof(uintmax_t);
- const size_t kMaxBinaryLength = 8 * sizeof(uintmax_t);
- /**
- * Convert an unsigned to hex, using repr (which maps from each possible
- * 2-hex-bytes value to the 2-character representation).
- *
- * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of
- * the supplied buffer and returns the offset of the beginning of the string
- * from the start of the buffer. The formatted string will be in range
- * [buf+begin, buf+bufLen).
- */
- template <class Uint>
- size_t uintToHex(
- char* buffer,
- size_t bufLen,
- Uint v,
- std::array<std::array<char, 2>, 256> const& repr) {
- // '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<unsigned, 256>(v); v >>= 7, v >>= 1) {
- auto b = v & 0xff;
- bufLen -= 2;
- buffer[bufLen] = repr[b][0];
- buffer[bufLen + 1] = repr[b][1];
- }
- buffer[--bufLen] = repr[v][1];
- if (v >= 16) {
- buffer[--bufLen] = repr[v][0];
- }
- return bufLen;
- }
- /**
- * Convert an unsigned to hex, using lower-case letters for the digits
- * above 9. See the comments for uintToHex.
- */
- template <class Uint>
- inline size_t uintToHexLower(char* buffer, size_t bufLen, Uint v) {
- return uintToHex(buffer, bufLen, v, formatHexLower);
- }
- /**
- * Convert an unsigned to hex, using upper-case letters for the digits
- * above 9. See the comments for uintToHex.
- */
- template <class Uint>
- inline size_t uintToHexUpper(char* buffer, size_t bufLen, Uint v) {
- return uintToHex(buffer, bufLen, v, formatHexUpper);
- }
- /**
- * Convert an unsigned to octal.
- *
- * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of
- * the supplied buffer and returns the offset of the beginning of the string
- * from the start of the buffer. The formatted string will be in range
- * [buf+begin, buf+bufLen).
- */
- template <class Uint>
- size_t uintToOctal(char* buffer, size_t bufLen, Uint v) {
- auto& repr = formatOctal;
- // '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<unsigned, 512>(v); v >>= 7, v >>= 2) {
- auto b = v & 0x1ff;
- bufLen -= 3;
- buffer[bufLen] = repr[b][0];
- buffer[bufLen + 1] = repr[b][1];
- buffer[bufLen + 2] = repr[b][2];
- }
- buffer[--bufLen] = repr[v][2];
- if (v >= 8) {
- buffer[--bufLen] = repr[v][1];
- }
- if (v >= 64) {
- buffer[--bufLen] = repr[v][0];
- }
- return bufLen;
- }
- /**
- * Convert an unsigned to binary.
- *
- * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of
- * the supplied buffer and returns the offset of the beginning of the string
- * from the start of the buffer. The formatted string will be in range
- * [buf+begin, buf+bufLen).
- */
- template <class Uint>
- size_t uintToBinary(char* buffer, size_t bufLen, Uint v) {
- auto& repr = formatBinary;
- if (v == 0) {
- buffer[--bufLen] = '0';
- return bufLen;
- }
- for (; v; v >>= 7, v >>= 1) {
- auto b = v & 0xff;
- bufLen -= 8;
- memcpy(buffer + bufLen, &(repr[b][0]), 8);
- }
- while (buffer[bufLen] == '0') {
- ++bufLen;
- }
- return bufLen;
- }
- } // namespace detail
- template <class Derived, bool containerMode, class... Args>
- BaseFormatter<Derived, containerMode, Args...>::BaseFormatter(
- StringPiece str,
- Args&&... args)
- : str_(str), values_(std::forward<Args>(args)...) {}
- template <class Derived, bool containerMode, class... Args>
- template <class Output>
- void BaseFormatter<Derived, containerMode, Args...>::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 p = s.begin();
- auto end = s.end();
- while (p != end) {
- auto q = static_cast<const char*>(memchr(p, '}', size_t(end - p)));
- if (!q) {
- out(StringPiece(p, end));
- break;
- }
- ++q;
- out(StringPiece(p, q));
- p = q;
- if (p == end || *p != '}') {
- throw_exception<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;
- while (p != end) {
- auto q = static_cast<const char*>(memchr(p, '{', size_t(end - p)));
- if (!q) {
- outputString(StringPiece(p, end));
- break;
- }
- outputString(StringPiece(p, q));
- p = q + 1;
- if (p == end) {
- throw_exception<BadFormatArg>(
- "folly::format: '}' at end of format string");
- }
- // "{{" -> "{"
- if (*p == '{') {
- out(StringPiece(p, 1));
- ++p;
- continue;
- }
- // Format string
- q = static_cast<const char*>(memchr(p, '}', size_t(end - p)));
- if (q == nullptr) {
- throw_exception<BadFormatArg>("folly::format: missing ending '}'");
- }
- FormatArg arg(StringPiece(p, q));
- p = q + 1;
- int argIndex = 0;
- auto piece = arg.splitKey<true>(); // 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;
- } else {
- arg.setNextKey(piece);
- hasExplicitArgIndex = true;
- }
- } 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<int>(piece);
- } catch (const std::out_of_range&) {
- arg.error("argument index must be integer");
- }
- arg.enforce(argIndex >= 0, "argument index must be non-negative");
- hasExplicitArgIndex = true;
- }
- }
- if (hasDefaultArgIndex && hasExplicitArgIndex) {
- throw_exception<BadFormatArg>(
- "folly::format: may not have both default and explicit arg indexes");
- }
- asDerived().doFormat(size_t(argIndex), arg, out);
- }
- }
- template <class Derived, bool containerMode, class... Args>
- void writeTo(
- FILE* fp,
- const BaseFormatter<Derived, containerMode, Args...>& 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 <class FormatCallback>
- void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) {
- if (arg.width != FormatArg::kDefaultWidth && arg.width < 0) {
- throw_exception<BadFormatArg>("folly::format: invalid width");
- }
- if (arg.precision != FormatArg::kDefaultPrecision && arg.precision < 0) {
- throw_exception<BadFormatArg>("folly::format: invalid precision");
- }
- if (arg.precision != FormatArg::kDefaultPrecision &&
- val.size() > static_cast<size_t>(arg.precision)) {
- val.reset(val.data(), static_cast<size_t>(arg.precision));
- }
- constexpr int padBufSize = 128;
- char padBuf[padBufSize];
- // Output padding, no more than padBufSize at once
- auto pad = [&padBuf, &cb, padBufSize](int chars) {
- while (chars) {
- int n = std::min(chars, padBufSize);
- cb(StringPiece(padBuf, size_t(n)));
- chars -= n;
- }
- };
- int padRemaining = 0;
- if (arg.width != FormatArg::kDefaultWidth &&
- val.size() < static_cast<size_t>(arg.width)) {
- char fill = arg.fill == FormatArg::kDefaultFill ? ' ' : arg.fill;
- int padChars = static_cast<int>(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;
- }
- }
- cb(val);
- if (padRemaining) {
- pad(padRemaining);
- }
- }
- template <class FormatCallback>
- 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, size_t(prefixLen)));
- val.advance(size_t(prefixLen));
- arg.width = std::max(arg.width - prefixLen, 0);
- }
- format_value::formatString(val, arg, cb);
- }
- template <
- class FormatCallback,
- class Derived,
- bool containerMode,
- class... Args>
- void formatFormatter(
- const BaseFormatter<Derived, containerMode, Args...>& 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) {
- // 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 {
- int sz = static_cast<int>(sp.size());
- if (arg.precision != FormatArg::kDefaultPrecision) {
- sz = std::min(arg.precision, sz);
- sp.reset(sp.data(), size_t(sz));
- arg.precision -= sz;
- }
- if (!sp.empty()) {
- cb(sp);
- if (arg.width != FormatArg::kDefaultWidth) {
- arg.width = std::max(arg.width - sz, 0);
- }
- }
- };
- formatter(fn);
- if (arg.width != FormatArg::kDefaultWidth && arg.width != 0) {
- // Rely on formatString to do appropriate padding
- format_value::formatString(StringPiece(), arg, cb);
- }
- }
- }
- } // namespace format_value
- // Definitions for default FormatValue classes
- // Integral types (except bool)
- template <class T>
- class FormatValue<
- T,
- typename std::enable_if<
- std::is_integral<T>::value && !std::is_same<T, bool>::value>::type> {
- public:
- explicit FormatValue(T val) : val_(val) {}
- T getValue() const {
- return val_;
- }
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- arg.validate(FormatArg::Type::INTEGER);
- doFormat(arg, cb);
- }
- template <class FormatCallback>
- void doFormat(FormatArg& arg, FormatCallback& cb) const {
- char presentation = arg.presentation;
- if (presentation == FormatArg::kDefaultPresentation) {
- presentation = std::is_same<T, char>::value ? 'c' : 'd';
- }
- // Do all work as unsigned, we'll add the prefix ('0' or '0x' if necessary)
- // and sign ourselves.
- typedef typename std::make_unsigned<T>::type UT;
- UT uval;
- char sign;
- if (std::is_signed<T>::value) {
- if (folly::is_negative(val_)) {
- uval = UT(-static_cast<UT>(val_));
- sign = '-';
- } else {
- uval = static_cast<UT>(val_);
- switch (arg.sign) {
- case FormatArg::Sign::PLUS_OR_MINUS:
- sign = '+';
- break;
- case FormatArg::Sign::SPACE_OR_MINUS:
- sign = ' ';
- break;
- default:
- sign = '\0';
- break;
- }
- }
- } else {
- uval = static_cast<UT>(val_);
- sign = '\0';
- arg.enforce(
- arg.sign == FormatArg::Sign::DEFAULT,
- "sign specifications not allowed for unsigned values");
- }
- // max of:
- // #x: 0x prefix + 16 bytes = 18 bytes
- // #o: 0 prefix + 22 bytes = 23 bytes
- // #b: 0b prefix + 64 bytes = 65 bytes
- // ,d: 26 bytes (including thousands separators!)
- // + nul terminator
- // + 3 for sign and prefix shenanigans (see below)
- constexpr size_t valBufSize = 69;
- char valBuf[valBufSize];
- char* valBufBegin = nullptr;
- char* valBufEnd = nullptr;
- int prefixLen = 0;
- switch (presentation) {
- 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<uintmax_t>(uval));
- #else
- int len = snprintf(
- valBufBegin,
- size_t((valBuf + valBufSize) - valBufBegin),
- "%ju",
- static_cast<uintmax_t>(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) {
- 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<char>(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) {
- *--valBufBegin = sign;
- ++prefixLen;
- }
- format_value::formatNumber(
- StringPiece(valBufBegin, valBufEnd), prefixLen, arg, cb);
- }
- private:
- T val_;
- };
- // Bool
- template <>
- class FormatValue<bool> {
- public:
- explicit FormatValue(bool val) : val_(val) {}
- template <class FormatCallback>
- 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
- FormatValue<int>(val_).format(arg, cb);
- }
- }
- private:
- bool val_;
- };
- // double
- template <>
- class FormatValue<double> {
- public:
- explicit FormatValue(double val) : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- 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_;
- };
- // float (defer to double)
- template <>
- class FormatValue<float> {
- public:
- explicit FormatValue(float val) : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- FormatValue<double>(val_).format(arg, cb);
- }
- private:
- float val_;
- };
- // String-y types (implicitly convertible to StringPiece, except char*)
- template <class T>
- class FormatValue<
- T,
- typename std::enable_if<
- (!std::is_pointer<T>::value ||
- !std::is_same<
- char,
- typename std::decay<typename std::remove_pointer<T>::type>::type>::
- value) &&
- std::is_convertible<T, StringPiece>::value>::type> {
- public:
- explicit FormatValue(StringPiece val) : val_(val) {}
- template <class FormatCallback>
- 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,
- "'");
- format_value::formatString(val_, arg, cb);
- } else {
- FormatValue<char>(val_.at(size_t(arg.splitIntKey()))).format(arg, cb);
- }
- }
- private:
- StringPiece val_;
- };
- // Null
- template <>
- class FormatValue<std::nullptr_t> {
- public:
- explicit FormatValue(std::nullptr_t) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- arg.validate(FormatArg::Type::OTHER);
- arg.enforce(
- arg.presentation == FormatArg::kDefaultPresentation,
- "invalid specifier '",
- arg.presentation,
- "'");
- format_value::formatString("(null)", arg, cb);
- }
- };
- // Partial specialization of FormatValue for char*
- template <class T>
- class FormatValue<
- T*,
- typename std::enable_if<
- std::is_same<char, typename std::decay<T>::type>::value>::type> {
- public:
- explicit FormatValue(T* val) : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- if (arg.keyEmpty()) {
- if (!val_) {
- FormatValue<std::nullptr_t>(nullptr).format(arg, cb);
- } else {
- FormatValue<StringPiece>(val_).format(arg, cb);
- }
- } else {
- FormatValue<typename std::decay<T>::type>(val_[arg.splitIntKey()])
- .format(arg, cb);
- }
- }
- private:
- T* val_;
- };
- // Partial specialization of FormatValue for void*
- template <class T>
- class FormatValue<
- T*,
- typename std::enable_if<
- std::is_same<void, typename std::decay<T>::type>::value>::type> {
- public:
- explicit FormatValue(T* val) : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- if (!val_) {
- FormatValue<std::nullptr_t>(nullptr).format(arg, cb);
- } else {
- // Print as a pointer, in hex.
- arg.validate(FormatArg::Type::OTHER);
- 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<uintptr_t>(reinterpret_cast<uintptr_t>(val_))
- .doFormat(arg, cb);
- }
- }
- private:
- T* val_;
- };
- template <class T, class = void>
- class TryFormatValue {
- public:
- template <class FormatCallback>
- static void
- formatOrFail(T& /* value */, FormatArg& arg, FormatCallback& /* cb */) {
- arg.error("No formatter available for this type");
- }
- };
- template <class T>
- class TryFormatValue<
- T,
- typename std::enable_if<
- 0 < sizeof(FormatValue<typename std::decay<T>::type>)>::type> {
- public:
- template <class FormatCallback>
- static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) {
- FormatValue<typename std::decay<T>::type>(value).format(arg, cb);
- }
- };
- // Partial specialization of FormatValue for other pointers
- template <class T>
- class FormatValue<
- T*,
- typename std::enable_if<
- !std::is_same<char, typename std::decay<T>::type>::value &&
- !std::is_same<void, typename std::decay<T>::type>::value>::type> {
- public:
- explicit FormatValue(T* val) : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- if (arg.keyEmpty()) {
- FormatValue<void*>((void*)val_).format(arg, cb);
- } else {
- TryFormatValue<T>::formatOrFail(val_[arg.splitIntKey()], arg, cb);
- }
- }
- private:
- T* val_;
- };
- namespace detail {
- // std::array
- template <class T, size_t N>
- struct IndexableTraits<std::array<T, N>>
- : public IndexableTraitsSeq<std::array<T, N>> {};
- // std::vector
- template <class T, class A>
- struct IndexableTraits<std::vector<T, A>>
- : public IndexableTraitsSeq<std::vector<T, A>> {};
- // std::deque
- template <class T, class A>
- struct IndexableTraits<std::deque<T, A>>
- : public IndexableTraitsSeq<std::deque<T, A>> {};
- // std::map with integral keys
- template <class K, class T, class C, class A>
- struct IndexableTraits<
- std::map<K, T, C, A>,
- typename std::enable_if<std::is_integral<K>::value>::type>
- : public IndexableTraitsAssoc<std::map<K, T, C, A>> {};
- // std::unordered_map with integral keys
- template <class K, class T, class H, class E, class A>
- struct IndexableTraits<
- std::unordered_map<K, T, H, E, A>,
- typename std::enable_if<std::is_integral<K>::value>::type>
- : public IndexableTraitsAssoc<std::unordered_map<K, T, H, E, A>> {};
- } // namespace detail
- // Partial specialization of FormatValue for integer-indexable containers
- template <class T>
- class FormatValue<T, typename detail::IndexableTraits<T>::enabled> {
- public:
- explicit FormatValue(const T& val) : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- FormatValue<typename std::decay<
- typename detail::IndexableTraits<T>::value_type>::type>(
- detail::IndexableTraits<T>::at(val_, arg.splitIntKey()))
- .format(arg, cb);
- }
- private:
- const T& val_;
- };
- template <class Container, class Value>
- class FormatValue<
- detail::DefaultValueWrapper<Container, Value>,
- typename detail::IndexableTraits<Container>::enabled> {
- public:
- explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val)
- : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- FormatValue<typename std::decay<
- typename detail::IndexableTraits<Container>::value_type>::type>(
- detail::IndexableTraits<Container>::at(
- val_.container, arg.splitIntKey(), val_.defaultValue))
- .format(arg, cb);
- }
- private:
- const detail::DefaultValueWrapper<Container, Value>& val_;
- };
- namespace detail {
- // Define enabled, key_type, convert from StringPiece to the key types
- // that we support
- template <class T>
- struct KeyFromStringPiece;
- // std::string
- template <>
- struct KeyFromStringPiece<std::string> : public FormatTraitsBase {
- typedef std::string key_type;
- static std::string convert(StringPiece s) {
- return s.toString();
- }
- typedef void enabled;
- };
- // fbstring
- template <>
- struct KeyFromStringPiece<fbstring> : public FormatTraitsBase {
- typedef fbstring key_type;
- static fbstring convert(StringPiece s) {
- return s.to<fbstring>();
- }
- };
- // StringPiece
- template <>
- struct KeyFromStringPiece<StringPiece> : public FormatTraitsBase {
- typedef StringPiece key_type;
- static StringPiece convert(StringPiece s) {
- return s;
- }
- };
- // Base class for associative types keyed by strings
- template <class T>
- 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) {
- if (auto ptr = get_ptr(map, KeyFromStringPiece<key_type>::convert(key))) {
- return *ptr;
- }
- throw_exception<FormatKeyNotFoundException>(key);
- }
- static const value_type&
- at(const T& map, StringPiece key, const value_type& dflt) {
- auto pos = map.find(KeyFromStringPiece<key_type>::convert(key));
- return pos != map.end() ? pos->second : dflt;
- }
- };
- // Define enabled, key_type, value_type, at() for supported string-keyed
- // types
- template <class T, class Enabled = void>
- struct KeyableTraits;
- // std::map with string key
- template <class K, class T, class C, class A>
- struct KeyableTraits<
- std::map<K, T, C, A>,
- typename KeyFromStringPiece<K>::enabled>
- : public KeyableTraitsAssoc<std::map<K, T, C, A>> {};
- // std::unordered_map with string key
- template <class K, class T, class H, class E, class A>
- struct KeyableTraits<
- std::unordered_map<K, T, H, E, A>,
- typename KeyFromStringPiece<K>::enabled>
- : public KeyableTraitsAssoc<std::unordered_map<K, T, H, E, A>> {};
- } // namespace detail
- // Partial specialization of FormatValue for string-keyed containers
- template <class T>
- class FormatValue<T, typename detail::KeyableTraits<T>::enabled> {
- public:
- explicit FormatValue(const T& val) : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- FormatValue<typename std::decay<
- typename detail::KeyableTraits<T>::value_type>::type>(
- detail::KeyableTraits<T>::at(val_, arg.splitKey()))
- .format(arg, cb);
- }
- private:
- const T& val_;
- };
- template <class Container, class Value>
- class FormatValue<
- detail::DefaultValueWrapper<Container, Value>,
- typename detail::KeyableTraits<Container>::enabled> {
- public:
- explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val)
- : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- FormatValue<typename std::decay<
- typename detail::KeyableTraits<Container>::value_type>::type>(
- detail::KeyableTraits<Container>::at(
- val_.container, arg.splitKey(), val_.defaultValue))
- .format(arg, cb);
- }
- private:
- const detail::DefaultValueWrapper<Container, Value>& val_;
- };
- // Partial specialization of FormatValue for pairs
- template <class A, class B>
- class FormatValue<std::pair<A, B>> {
- public:
- explicit FormatValue(const std::pair<A, B>& val) : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- int key = arg.splitIntKey();
- switch (key) {
- case 0:
- FormatValue<typename std::decay<A>::type>(val_.first).format(arg, cb);
- break;
- case 1:
- FormatValue<typename std::decay<B>::type>(val_.second).format(arg, cb);
- break;
- default:
- arg.error("invalid index for pair");
- }
- }
- private:
- const std::pair<A, B>& val_;
- };
- // Partial specialization of FormatValue for tuples
- template <class... Args>
- class FormatValue<std::tuple<Args...>> {
- typedef std::tuple<Args...> Tuple;
- public:
- explicit FormatValue(const Tuple& val) : val_(val) {}
- template <class FormatCallback>
- void format(FormatArg& arg, FormatCallback& cb) const {
- int key = arg.splitIntKey();
- arg.enforce(key >= 0, "tuple index must be non-negative");
- doFormat(size_t(key), arg, cb);
- }
- private:
- static constexpr size_t valueCount = std::tuple_size<Tuple>::value;
- template <size_t K, class Callback>
- typename std::enable_if<K == valueCount>::type
- doFormatFrom(size_t i, FormatArg& arg, Callback& /* cb */) const {
- arg.error("tuple index out of range, max=", i);
- }
- template <size_t K, class Callback>
- typename std::enable_if<(K < valueCount)>::type
- doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const {
- if (i == K) {
- FormatValue<typename std::decay<
- typename std::tuple_element<K, Tuple>::type>::type>(std::get<K>(val_))
- .format(arg, cb);
- } else {
- doFormatFrom<K + 1>(i, arg, cb);
- }
- }
- template <class Callback>
- void doFormat(size_t i, FormatArg& arg, Callback& cb) const {
- return doFormatFrom<0>(i, arg, cb);
- }
- const Tuple& val_;
- };
- // Partial specialization of FormatValue for nested Formatters
- template <bool containerMode, class... Args, template <bool, class...> class F>
- class FormatValue<
- F<containerMode, Args...>,
- typename std::enable_if<
- detail::IsFormatter<F<containerMode, Args...>>::value>::type> {
- typedef typename F<containerMode, Args...>::BaseType FormatterValue;
- public:
- explicit FormatValue(const FormatterValue& f) : f_(f) {}
- template <class FormatCallback>
- 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 <class Tgt, class Derived, bool containerMode, class... Args>
- typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(
- const BaseFormatter<Derived, containerMode, Args...>& value,
- Tgt* result) {
- value.appendTo(*result);
- }
- } // namespace folly
- FOLLY_POP_WARNING
|