/*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#error This file may only be included from Format.h.
#endif
+#include <folly/Exception.h>
+#include <folly/Traits.h>
+
+// Ignore -Wformat-nonliteral warnings within this file
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
namespace folly {
namespace detail {
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 (; v >= 256; v >>= 7, v >>= 1) {
+ for (; !less_than<unsigned, 256>(v); v >>= 7, v >>= 1) {
auto b = v & 0xff;
bufLen -= 2;
buffer[bufLen] = repr[b][0];
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 (; v >= 512; v >>= 7, v >>= 2) {
+ for (; !less_than<unsigned, 512>(v); v >>= 7, v >>= 2) {
auto b = v & 0x1ff;
bufLen -= 3;
buffer[bufLen] = repr[b][0];
"Exactly one argument required in container mode");
}
+template <bool containerMode, class... Args>
+void Formatter<containerMode, Args...>::handleFormatStrError() const {
+ if (crashOnError_) {
+ LOG(FATAL) << "folly::format: bad format string \"" << str_ << "\": " <<
+ folly::exceptionStr(std::current_exception());
+ }
+ throw;
+}
+
template <bool containerMode, class... Args>
template <class Output>
void Formatter<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 <bool containerMode, class... Args>
+template <class Output>
+void Formatter<containerMode, Args...>::appendOutput(Output& out) const {
auto p = str_.begin();
auto end = str_.end();
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;
}
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");
}
// "{{" -> "{"
// Format string
q = static_cast<const char*>(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;
}
if (hasDefaultArgIndex && hasExplicitArgIndex) {
- throw std::invalid_argument(
+ throw BadFormatArg(
"folly::format: may not have both default and explicit arg indexes");
}
}
}
+template <bool containerMode, class... Args>
+void writeTo(FILE* fp, const Formatter<containerMode, Args...>& formatter) {
+ auto writer = [fp] (StringPiece sp) {
+ ssize_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>
UT uval;
char sign;
if (std::is_signed<T>::value) {
- if (val_ < 0) {
+ if (folly::is_negative(val_)) {
uval = static_cast<UT>(-val_);
sign = '-';
} else {
arg.precision = 6;
}
- bool done = false;
-
// 2+: for null terminator and optional sign shenanigans.
char buf[2 + std::max({
(2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint +
exponentSymbol,
-4, arg.precision,
0, 0);
- CHECK(conv.ToExponential(val, arg.precision, &builder));
+ arg.enforce(conv.ToExponential(val, arg.precision, &builder));
}
break;
case 'n': // should be locale-aware, but isn't
exponentSymbol,
-4, arg.precision,
0, 0);
- CHECK(conv.ToShortest(val, &builder));
+ arg.enforce(conv.ToShortest(val, &builder));
}
break;
default:
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)
static const value_type& at(const C& c, int idx) {
return c.at(static_cast<typename C::key_type>(idx));
}
+ static const value_type& at(const C& c, int idx,
+ const value_type& dflt) {
+ auto pos = c.find(static_cast<typename C::key_type>(idx));
+ return pos != c.end() ? pos->second : dflt;
+ }
};
// std::array
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
static const value_type& at(const T& map, StringPiece key) {
return map.at(KeyFromStringPiece<key_type>::convert(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
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>> {
*/
template <class Tgt, bool containerMode, class... Args>
typename std::enable_if<
- detail::IsSomeString<Tgt>::value>::type
+ IsSomeString<Tgt>::value>::type
toAppend(const Formatter<containerMode, Args...>& value, Tgt * result) {
value.appendTo(*result);
}
} // namespace folly
+
+#pragma GCC diagnostic pop