Move common/network/IPAddress.h and related to folly/
[folly.git] / folly / Format-inl.h
index 59f4550a50f7cb31aaaaf726d338fa7d1ecdb5d9..5575151c27c0cef40435c569f2d0ca0e436b640f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 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 {
@@ -43,7 +50,9 @@ const size_t kMaxBinaryLength = 8 * sizeof(uintmax_t);
 template <class Uint>
 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<unsigned, 256>(v); v >>= 7, v >>= 1) {
     auto b = v & 0xff;
     bufLen -= 2;
     buffer[bufLen] = repr[b][0];
@@ -85,7 +94,9 @@ inline size_t uintToHexUpper(char* buffer, size_t bufLen, Uint v) {
 template <class Uint>
 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<unsigned, 512>(v); v >>= 7, v >>= 2) {
     auto b = v & 0x1ff;
     bufLen -= 3;
     buffer[bufLen] = repr[b][0];
@@ -117,7 +128,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);
@@ -140,9 +151,51 @@ Formatter<containerMode, Args...>::Formatter(StringPiece str, Args&&... args)
                 "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();
 
@@ -163,8 +216,7 @@ void Formatter<containerMode, Args...>::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;
     }
@@ -183,8 +235,7 @@ void Formatter<containerMode, Args...>::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 +247,8 @@ void Formatter<containerMode, Args...>::operator()(Output& out) const {
 
     // 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;
@@ -228,7 +279,7 @@ void Formatter<containerMode, Args...>::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,6 +287,17 @@ void Formatter<containerMode, Args...>::operator()(Output& out) const {
   }
 }
 
+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>
@@ -373,7 +435,7 @@ class FormatValue<
     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 {
@@ -429,9 +491,8 @@ class FormatValue<
         useSprintf("%'ju");
       } else {
         // Use uintToBuffer, faster than sprintf
-        valBufEnd = valBuf + valBufSize - 1;
-        valBufBegin = valBuf + detail::uintToBuffer(valBuf, valBufSize - 1,
-                                                    uval);
+        valBufBegin = valBuf + 3;
+        valBufEnd = valBufBegin + uint64ToBufferUnsafe(uval, valBufBegin);
       }
       break;
     case 'c':
@@ -559,8 +620,6 @@ class FormatValue<double> {
       arg.precision = 6;
     }
 
-    bool done = false;
-
     // 2+: for null terminator and optional sign shenanigans.
     char buf[2 + std::max({
         (2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint +
@@ -618,7 +677,7 @@ class FormatValue<double> {
             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
@@ -638,7 +697,7 @@ class FormatValue<double> {
             exponentSymbol,
             -4, arg.precision,
             0, 0);
-        CHECK(conv.ToShortest(val, &builder));
+        arg.enforce(conv.ToShortest(val, &builder));
       }
       break;
     default:
@@ -788,6 +847,28 @@ class FormatValue<
   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<
@@ -804,8 +885,7 @@ class FormatValue<
     if (arg.keyEmpty()) {
       FormatValue<void*>((void*)val_).format(arg, cb);
     } else {
-      FormatValue<typename std::decay<T>::type>(
-          val_[arg.splitIntKey()]).format(arg, cb);
+      TryFormatValue<T>::formatOrFail(val_[arg.splitIntKey()], arg, cb);
     }
   }
  private:
@@ -1064,15 +1144,32 @@ class FormatValue<std::tuple<Args...>> {
   const Tuple& val_;
 };
 
+// Partial specialization of FormatValue for nested Formatters
+template <bool containerMode, class... Args>
+class FormatValue<Formatter<containerMode, Args...>, void> {
+  typedef Formatter<containerMode, Args...> 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, 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