/*
- * Copyright 2014 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.
"Exactly one argument required in container mode");
}
-template <class Derived, bool containerMode, class... Args>
-void BaseFormatter<Derived, containerMode, Args...>::handleFormatStrError()
- const {
- if (crashOnError_) {
- LOG(FATAL) << "folly::format: bad format string \"" << str_ << "\": " <<
- folly::exceptionStr(std::current_exception());
- }
- throw;
-}
-
template <class Derived, bool containerMode, class... Args>
template <class Output>
void BaseFormatter<Derived, containerMode, Args...>::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 <class Derived, bool containerMode, class... Args>
-template <class Output>
-void BaseFormatter<Derived, containerMode, Args...>::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 "}"
int prefixLen = 0;
switch (presentation) {
- case 'n':
+ case 'n': {
arg.enforce(!arg.basePrefix,
"base prefix not allowed with '", presentation,
"' specifier");
"' specifier");
valBufBegin = valBuf + 3; // room for sign and base prefix
- valBufEnd = valBufBegin + sprintf(valBufBegin, "%'ju",
- static_cast<uintmax_t>(uval));
+ int len = snprintf(valBufBegin, (valBuf + valBufSize) - valBufBegin,
+ "%'ju", static_cast<uintmax_t>(uval));
+ // 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,
template <class FormatCallback>
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<int> (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_;
};