]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Upgrade to fmt 6.1.2
authorJoel Rosdahl <joel@rosdahl.net>
Thu, 16 Jan 2020 20:19:38 +0000 (21:19 +0100)
committerJoel Rosdahl <joel@rosdahl.net>
Thu, 16 Jan 2020 20:19:38 +0000 (21:19 +0100)
LICENSE.adoc
src/third_party/fmt/core.h
src/third_party/fmt/format-inl.h
src/third_party/fmt/format.h
src/third_party/format.cpp

index 6abffb0ae78574c2991fb658a2dc4bb98270b874..dfd183b5ecdf47c0b7a739ddc576677f7f70ef01 100644 (file)
@@ -55,7 +55,7 @@ under less restrictive terms.
 src/third_party/fmt/*.h and src/third_party/format.cpp
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-This is a subset of https://fmt.dev[fmt] 6.0.0 with the following license:
+This is a subset of https://fmt.dev[fmt] 6.1.2 with the following license:
 
 -------------------------------------------------------------------------------
   Formatting library for C++
index bcce2f50e1ed34ed3490d9612b4456dc579df3ce..9fd8df287851e5b2a782d31356eeda5323892174 100644 (file)
@@ -8,7 +8,6 @@
 #ifndef FMT_CORE_H_
 #define FMT_CORE_H_
 
-#include <cassert>
 #include <cstdio>  // std::FILE
 #include <cstring>
 #include <iterator>
@@ -16,7 +15,7 @@
 #include <type_traits>
 
 // The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 60000
+#define FMT_VERSION 60102
 
 #ifdef __has_feature
 #  define FMT_HAS_FEATURE(x) __has_feature(x)
 #  define FMT_HAS_GXX_CXX11 0
 #endif
 
+#ifdef __NVCC__
+#  define FMT_NVCC __NVCC__
+#else
+#  define FMT_NVCC 0
+#endif
+
 #ifdef _MSC_VER
 #  define FMT_MSC_VER _MSC_VER
 #else
@@ -60,7 +65,8 @@
 #ifndef FMT_USE_CONSTEXPR
 #  define FMT_USE_CONSTEXPR                                           \
     (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
-     (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L))
+     (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) &&           \
+        !FMT_NVCC
 #endif
 #if FMT_USE_CONSTEXPR
 #  define FMT_CONSTEXPR constexpr
 #  endif
 #endif
 
+// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
+#if defined(__INTEL_COMPILER) || FMT_NVCC
+#  define FMT_DEPRECATED_ALIAS
+#else
+#  define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
+#endif
+
 #ifndef FMT_BEGIN_NAMESPACE
 #  if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
       FMT_MSC_VER >= 1900
 #  define FMT_EXTERN
 #endif
 
-#ifndef FMT_ASSERT
-#  define FMT_ASSERT(condition, message) assert((condition) && message)
-#endif
-
 // libc++ supports string_view in pre-c++17.
 #if (FMT_HAS_INCLUDE(<string_view>) &&                       \
      (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
@@ -200,6 +209,8 @@ template <typename T>
 using remove_reference_t = typename std::remove_reference<T>::type;
 template <typename T>
 using remove_const_t = typename std::remove_const<T>::type;
+template <typename T>
+using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
 
 struct monostate {};
 
@@ -213,6 +224,19 @@ namespace internal {
 // A workaround for gcc 4.8 to make void_t work in a SFINAE context.
 template <typename... Ts> struct void_t_impl { using type = void; };
 
+FMT_API void assert_fail(const char* file, int line, const char* message);
+
+#ifndef FMT_ASSERT
+#  ifdef NDEBUG
+#    define FMT_ASSERT(condition, message)
+#  else
+#    define FMT_ASSERT(condition, message) \
+      ((condition)                         \
+           ? void()                        \
+           : fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
+#  endif
+#endif
+
 #if defined(FMT_USE_STRING_VIEW)
 template <typename Char> using std_string_view = std::basic_string_view<Char>;
 #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
@@ -222,7 +246,21 @@ using std_string_view = std::experimental::basic_string_view<Char>;
 template <typename T> struct std_string_view {};
 #endif
 
-// Casts nonnegative integer to unsigned.
+#ifdef FMT_USE_INT128
+// Do nothing.
+#elif defined(__SIZEOF_INT128__)
+#  define FMT_USE_INT128 1
+using int128_t = __int128_t;
+using uint128_t = __uint128_t;
+#else
+#  define FMT_USE_INT128 0
+#endif
+#if !FMT_USE_INT128
+struct int128_t {};
+struct uint128_t {};
+#endif
+
+// Casts a nonnegative integer to unsigned.
 template <typename Int>
 FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
   FMT_ASSERT(value >= 0, "negative value");
@@ -266,10 +304,11 @@ template <typename Char> class basic_string_view {
       : data_(s), size_(std::char_traits<Char>::length(s)) {}
 
   /** Constructs a string reference from a ``std::basic_string`` object. */
-  template <typename Alloc>
-  FMT_CONSTEXPR basic_string_view(const std::basic_string<Char, Alloc>& s)
-      FMT_NOEXCEPT : data_(s.data()),
-                     size_(s.size()) {}
+  template <typename Traits, typename Alloc>
+  FMT_CONSTEXPR basic_string_view(
+      const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
+      : data_(s.data()),
+        size_(s.size()) {}
 
   template <
       typename S,
@@ -286,6 +325,8 @@ template <typename Char> class basic_string_view {
   FMT_CONSTEXPR iterator begin() const { return data_; }
   FMT_CONSTEXPR iterator end() const { return data_ + size_; }
 
+  FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
+
   FMT_CONSTEXPR void remove_prefix(size_t n) {
     data_ += n;
     size_ -= n;
@@ -357,10 +398,10 @@ inline basic_string_view<Char> to_string_view(const Char* s) {
   return s;
 }
 
-template <typename Char, typename Traits, typename Allocator>
+template <typename Char, typename Traits, typename Alloc>
 inline basic_string_view<Char> to_string_view(
-    const std::basic_string<Char, Traits, Allocator>& s) {
-  return {s.data(), s.size()};
+    const std::basic_string<Char, Traits, Alloc>& s) {
+  return s;
 }
 
 template <typename Char>
@@ -405,8 +446,8 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
 };
 
 struct error_handler {
-  FMT_CONSTEXPR error_handler() {}
-  FMT_CONSTEXPR error_handler(const error_handler&) {}
+  FMT_CONSTEXPR error_handler() = default;
+  FMT_CONSTEXPR error_handler(const error_handler&) = default;
 
   // This function is intentionally not constexpr to give a compile-time error.
   FMT_NORETURN FMT_API void on_error(const char* message);
@@ -416,10 +457,24 @@ struct error_handler {
 /** String's character type. */
 template <typename S> using char_t = typename internal::char_t_impl<S>::type;
 
-// Parsing context consisting of a format string range being parsed and an
-// argument counter for automatic indexing.
+/**
+  \rst
+  Parsing context consisting of a format string range being parsed and an
+  argument counter for automatic indexing.
+
+  You can use one of the following type aliases for common character types:
+
+  +-----------------------+-------------------------------------+
+  | Type                  | Definition                          |
+  +=======================+=====================================+
+  | format_parse_context  | basic_format_parse_context<char>    |
+  +-----------------------+-------------------------------------+
+  | wformat_parse_context | basic_format_parse_context<wchar_t> |
+  +-----------------------+-------------------------------------+
+  \endrst
+ */
 template <typename Char, typename ErrorHandler = internal::error_handler>
-class basic_parse_context : private ErrorHandler {
+class basic_format_parse_context : private ErrorHandler {
  private:
   basic_string_view<Char> format_str_;
   int next_arg_id_;
@@ -428,38 +483,47 @@ class basic_parse_context : private ErrorHandler {
   using char_type = Char;
   using iterator = typename basic_string_view<Char>::iterator;
 
-  explicit FMT_CONSTEXPR basic_parse_context(basic_string_view<Char> format_str,
-                                             ErrorHandler eh = ErrorHandler())
+  explicit FMT_CONSTEXPR basic_format_parse_context(
+      basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
       : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
 
-  // Returns an iterator to the beginning of the format string range being
-  // parsed.
+  /**
+    Returns an iterator to the beginning of the format string range being
+    parsed.
+   */
   FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
     return format_str_.begin();
   }
 
-  // Returns an iterator past the end of the format string range being parsed.
+  /**
+    Returns an iterator past the end of the format string range being parsed.
+   */
   FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
 
-  // Advances the begin iterator to ``it``.
+  /** Advances the begin iterator to ``it``. */
   FMT_CONSTEXPR void advance_to(iterator it) {
     format_str_.remove_prefix(internal::to_unsigned(it - begin()));
   }
 
-  // Returns the next argument index.
+  /**
+    Reports an error if using the manual argument indexing; otherwise returns
+    the next argument index and switches to the automatic indexing.
+   */
   FMT_CONSTEXPR int next_arg_id() {
     if (next_arg_id_ >= 0) return next_arg_id_++;
     on_error("cannot switch from manual to automatic argument indexing");
     return 0;
   }
 
-  FMT_CONSTEXPR bool check_arg_id(int) {
-    if (next_arg_id_ > 0) {
+  /**
+    Reports an error if using the automatic argument indexing; otherwise
+    switches to the manual indexing.
+   */
+  FMT_CONSTEXPR void check_arg_id(int) {
+    if (next_arg_id_ > 0)
       on_error("cannot switch from automatic to manual argument indexing");
-      return false;
-    }
-    next_arg_id_ = -1;
-    return true;
+    else
+      next_arg_id_ = -1;
   }
 
   FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
@@ -471,11 +535,14 @@ class basic_parse_context : private ErrorHandler {
   FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
 };
 
-using format_parse_context = basic_parse_context<char>;
-using wformat_parse_context = basic_parse_context<wchar_t>;
+using format_parse_context = basic_format_parse_context<char>;
+using wformat_parse_context = basic_format_parse_context<wchar_t>;
 
-using parse_context FMT_DEPRECATED = basic_parse_context<char>;
-using wparse_context FMT_DEPRECATED = basic_parse_context<wchar_t>;
+template <typename Char, typename ErrorHandler = internal::error_handler>
+using basic_parse_context FMT_DEPRECATED_ALIAS =
+    basic_format_parse_context<Char, ErrorHandler>;
+using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
+using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
 
 template <typename Context> class basic_format_arg;
 template <typename Context> class basic_format_args;
@@ -492,20 +559,17 @@ struct FMT_DEPRECATED convert_to_int
     : bool_constant<!std::is_arithmetic<T>::value &&
                     std::is_convertible<T, int>::value> {};
 
-namespace internal {
-
 // Specifies if T has an enabled formatter specialization. A type can be
 // formattable even if it doesn't have a formatter e.g. via a conversion.
 template <typename T, typename Context>
 using has_formatter =
     std::is_constructible<typename Context::template formatter_type<T>>;
 
+namespace internal {
+
 /** A contiguous memory buffer with an optional growing ability. */
 template <typename T> class buffer {
  private:
-  buffer(const buffer&) = delete;
-  void operator=(const buffer&) = delete;
-
   T* ptr_;
   std::size_t size_;
   std::size_t capacity_;
@@ -532,7 +596,9 @@ template <typename T> class buffer {
   using value_type = T;
   using const_reference = const T&;
 
-  virtual ~buffer() {}
+  buffer(const buffer&) = delete;
+  void operator=(const buffer&) = delete;
+  virtual ~buffer() = default;
 
   T* begin() FMT_NOEXCEPT { return ptr_; }
   T* end() FMT_NOEXCEPT { return ptr_ + size_; }
@@ -626,10 +692,13 @@ enum type {
   uint_type,
   long_long_type,
   ulong_long_type,
+  int128_type,
+  uint128_type,
   bool_type,
   char_type,
   last_integer_type = char_type,
   // followed by floating-point types.
+  float_type,
   double_type,
   long_double_type,
   last_numeric_type = long_double_type,
@@ -652,20 +721,23 @@ FMT_TYPE_CONSTANT(int, int_type);
 FMT_TYPE_CONSTANT(unsigned, uint_type);
 FMT_TYPE_CONSTANT(long long, long_long_type);
 FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
+FMT_TYPE_CONSTANT(int128_t, int128_type);
+FMT_TYPE_CONSTANT(uint128_t, uint128_type);
 FMT_TYPE_CONSTANT(bool, bool_type);
 FMT_TYPE_CONSTANT(Char, char_type);
+FMT_TYPE_CONSTANT(float, float_type);
 FMT_TYPE_CONSTANT(double, double_type);
 FMT_TYPE_CONSTANT(long double, long_double_type);
 FMT_TYPE_CONSTANT(const Char*, cstring_type);
 FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
 FMT_TYPE_CONSTANT(const void*, pointer_type);
 
-FMT_CONSTEXPR bool is_integral(type t) {
+FMT_CONSTEXPR bool is_integral_type(type t) {
   FMT_ASSERT(t != named_arg_type, "invalid argument type");
   return t > none_type && t <= last_integer_type;
 }
 
-FMT_CONSTEXPR bool is_arithmetic(type t) {
+FMT_CONSTEXPR bool is_arithmetic_type(type t) {
   FMT_ASSERT(t != named_arg_type, "invalid argument type");
   return t > none_type && t <= last_numeric_type;
 }
@@ -676,7 +748,7 @@ template <typename Char> struct string_value {
 };
 
 template <typename Context> struct custom_value {
-  using parse_context = basic_parse_context<typename Context::char_type>;
+  using parse_context = basic_format_parse_context<typename Context::char_type>;
   const void* value;
   void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
 };
@@ -691,8 +763,11 @@ template <typename Context> class value {
     unsigned uint_value;
     long long long_long_value;
     unsigned long long ulong_long_value;
+    int128_t int128_value;
+    uint128_t uint128_value;
     bool bool_value;
     char_type char_value;
+    float float_value;
     double double_value;
     long double long_double_value;
     const void* pointer;
@@ -705,6 +780,9 @@ template <typename Context> class value {
   FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
   value(long long val) : long_long_value(val) {}
   value(unsigned long long val) : ulong_long_value(val) {}
+  value(int128_t val) : int128_value(val) {}
+  value(uint128_t val) : uint128_value(val) {}
+  value(float val) : float_value(val) {}
   value(double val) : double_value(val) {}
   value(long double val) : long_double_value(val) {}
   value(bool val) : bool_value(val) {}
@@ -732,9 +810,9 @@ template <typename Context> class value {
  private:
   // Formats an argument of a custom type, such as a user-defined class.
   template <typename T, typename Formatter>
-  static void format_custom_arg(const void* arg,
-                                basic_parse_context<char_type>& parse_ctx,
-                                Context& ctx) {
+  static void format_custom_arg(
+      const void* arg, basic_format_parse_context<char_type>& parse_ctx,
+      Context& ctx) {
     Formatter f;
     parse_ctx.advance_to(f.parse(parse_ctx));
     ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
@@ -764,6 +842,8 @@ template <typename Context> struct arg_mapper {
   FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
   FMT_CONSTEXPR long long map(long long val) { return val; }
   FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
+  FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
+  FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
   FMT_CONSTEXPR bool map(bool val) { return val; }
 
   template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
@@ -774,7 +854,7 @@ template <typename Context> struct arg_mapper {
     return val;
   }
 
-  FMT_CONSTEXPR double map(float val) { return static_cast<double>(val); }
+  FMT_CONSTEXPR float map(float val) { return val; }
   FMT_CONSTEXPR double map(double val) { return val; }
   FMT_CONSTEXPR long double map(long double val) { return val; }
 
@@ -793,6 +873,15 @@ template <typename Context> struct arg_mapper {
   FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
     return basic_string_view<char_type>(val);
   }
+  template <
+      typename T,
+      FMT_ENABLE_IF(
+          std::is_constructible<std_string_view<char_type>, T>::value &&
+          !std::is_constructible<basic_string_view<char_type>, T>::value &&
+          !is_string<T>::value && !has_formatter<T, Context>::value)>
+  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
+    return std_string_view<char_type>(val);
+  }
   FMT_CONSTEXPR const char* map(const signed char* val) {
     static_assert(std::is_same<char_type, char>::value, "invalid string type");
     return reinterpret_cast<const char*>(val);
@@ -818,13 +907,18 @@ template <typename Context> struct arg_mapper {
             FMT_ENABLE_IF(std::is_enum<T>::value &&
                           !has_formatter<T, Context>::value &&
                           !has_fallback_formatter<T, Context>::value)>
-  FMT_CONSTEXPR int map(const T& val) {
-    return static_cast<int>(val);
+  FMT_CONSTEXPR auto map(const T& val) -> decltype(
+      map(static_cast<typename std::underlying_type<T>::type>(val))) {
+    return map(static_cast<typename std::underlying_type<T>::type>(val));
   }
-  template <typename T,
-            FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
-                          (has_formatter<T, Context>::value ||
-                           has_fallback_formatter<T, Context>::value))>
+  template <
+      typename T,
+      FMT_ENABLE_IF(
+          !is_string<T>::value && !is_char<T>::value &&
+          !std::is_constructible<basic_string_view<char_type>, T>::value &&
+          (has_formatter<T, Context>::value ||
+           (has_fallback_formatter<T, Context>::value &&
+            !std::is_constructible<std_string_view<char_type>, T>::value)))>
   FMT_CONSTEXPR const T& map(const T& val) {
     return val;
   }
@@ -841,12 +935,13 @@ template <typename Context> struct arg_mapper {
 // A type constant after applying arg_mapper<Context>.
 template <typename T, typename Context>
 using mapped_type_constant =
-    type_constant<decltype(arg_mapper<Context>().map(std::declval<T>())),
+    type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
                   typename Context::char_type>;
 
+enum { packed_arg_bits = 5 };
 // Maximum number of arguments with packed types.
-enum { max_packed_args = 15 };
-enum : unsigned long long { is_unpacked_bit = 1ull << 63 };
+enum { max_packed_args = 63 / packed_arg_bits };
+enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
 
 template <typename Context> class arg_map;
 }  // namespace internal
@@ -877,7 +972,8 @@ template <typename Context> class basic_format_arg {
    public:
     explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
 
-    void format(basic_parse_context<char_type>& parse_ctx, Context& ctx) const {
+    void format(basic_format_parse_context<char_type>& parse_ctx,
+                Context& ctx) const {
       custom_.format(custom_.value, parse_ctx, ctx);
     }
 
@@ -893,8 +989,8 @@ template <typename Context> class basic_format_arg {
 
   internal::type type() const { return type_; }
 
-  bool is_integral() const { return internal::is_integral(type_); }
-  bool is_arithmetic() const { return internal::is_arithmetic(type_); }
+  bool is_integral() const { return internal::is_integral_type(type_); }
+  bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
 };
 
 /**
@@ -923,10 +1019,22 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
     return vis(arg.value_.long_long_value);
   case internal::ulong_long_type:
     return vis(arg.value_.ulong_long_value);
+#if FMT_USE_INT128
+  case internal::int128_type:
+    return vis(arg.value_.int128_value);
+  case internal::uint128_type:
+    return vis(arg.value_.uint128_value);
+#else
+  case internal::int128_type:
+  case internal::uint128_type:
+    break;
+#endif
   case internal::bool_type:
     return vis(arg.value_.bool_value);
   case internal::char_type:
     return vis(arg.value_.char_value);
+  case internal::float_type:
+    return vis(arg.value_.float_value);
   case internal::double_type:
     return vis(arg.value_.double_value);
   case internal::long_double_type:
@@ -948,9 +1056,6 @@ namespace internal {
 // A map from argument names to their values for named arguments.
 template <typename Context> class arg_map {
  private:
-  arg_map(const arg_map&) = delete;
-  void operator=(const arg_map&) = delete;
-
   using char_type = typename Context::char_type;
 
   struct entry {
@@ -968,6 +1073,8 @@ template <typename Context> class arg_map {
   }
 
  public:
+  arg_map(const arg_map&) = delete;
+  void operator=(const arg_map&) = delete;
   arg_map() : map_(nullptr), size_(0) {}
   void init(const basic_format_args<Context>& args);
   ~arg_map() { delete[] map_; }
@@ -990,6 +1097,8 @@ class locale_ref {
   locale_ref() : locale_(nullptr) {}
   template <typename Locale> explicit locale_ref(const Locale& loc);
 
+  explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
+
   template <typename Locale> Locale get() const;
 };
 
@@ -998,7 +1107,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
 template <typename Context, typename Arg, typename... Args>
 constexpr unsigned long long encode_types() {
   return mapped_type_constant<Arg, Context>::value |
-         (encode_types<Context, Args...>() << 4);
+         (encode_types<Context, Args...>() << packed_arg_bits);
 }
 
 template <typename Context, typename T>
@@ -1034,14 +1143,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
   internal::arg_map<basic_format_context> map_;
   internal::locale_ref loc_;
 
-  basic_format_context(const basic_format_context&) = delete;
-  void operator=(const basic_format_context&) = delete;
-
  public:
   using iterator = OutputIt;
   using format_arg = basic_format_arg<basic_format_context>;
   template <typename T> using formatter_type = formatter<T, char_type>;
 
+  basic_format_context(const basic_format_context&) = delete;
+  void operator=(const basic_format_context&) = delete;
   /**
    Constructs a ``basic_format_context`` object. References to the arguments are
    stored in the object so make sure they have appropriate lifetimes.
@@ -1100,7 +1208,6 @@ template <typename Context, typename... Args> class format_arg_store {
   static constexpr unsigned long long types =
       is_packed ? internal::encode_types<Context, Args...>()
                 : internal::is_unpacked_bit | num_args;
-  FMT_DEPRECATED static constexpr unsigned long long TYPES = types;
 
   format_arg_store(const Args&... args)
       : data_{internal::make_arg<is_packed, Context>(args)...} {}
@@ -1143,8 +1250,9 @@ template <typename Context> class basic_format_args {
   bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
 
   internal::type type(int index) const {
-    int shift = index * 4;
-    return static_cast<internal::type>((types_ & (0xfull << shift)) >> shift);
+    int shift = index * internal::packed_arg_bits;
+    unsigned int mask = (1 << internal::packed_arg_bits) - 1;
+    return static_cast<internal::type>((types_ >> shift) & mask);
   }
 
   friend class internal::arg_map<Context>;
@@ -1177,7 +1285,7 @@ template <typename Context> class basic_format_args {
    */
   template <typename... Args>
   basic_format_args(const format_arg_store<Context, Args...>& store)
-      : types_(static_cast<unsigned long long>(store.types)) {
+      : types_(store.types) {
     set_data(store.data_);
   }
 
@@ -1371,7 +1479,7 @@ inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
 }
 
 FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
-FMT_API void vprint(std::FILE* f, wstring_view format_str, wformat_args args);
+FMT_API void vprint(string_view format_str, format_args args);
 
 /**
   \rst
@@ -1391,9 +1499,6 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
          internal::make_args_checked<Args...>(format_str, args...));
 }
 
-FMT_API void vprint(string_view format_str, format_args args);
-FMT_API void vprint(wstring_view format_str, wformat_args args);
-
 /**
   \rst
   Prints formatted data to ``stdout``.
index 147062fe5d385b733e4268f8790f9dafb8c18e0e..72b304665df9246cebe800dd633ee118c0208c04 100644 (file)
@@ -1,4 +1,4 @@
-// Formatting library for C++
+// Formatting library for C++ - implementation
 //
 // Copyright (c) 2012 - 2016, Victor Zverovich
 // All rights reserved.
 
 #include "format.h"
 
-#include <string.h>
-
+#include <cassert>
 #include <cctype>
-#include <cerrno>
 #include <climits>
 #include <cmath>
 #include <cstdarg>
-#include <cstddef>  // for std::ptrdiff_t
 #include <cstring>  // for std::memmove
 #include <cwchar>
 #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
 
 #ifdef _MSC_VER
 #  pragma warning(push)
-#  pragma warning(disable : 4127)  // conditional expression is constant
 #  pragma warning(disable : 4702)  // unreachable code
-// Disable deprecation warning for strerror. The latter is not called but
-// MSVC fails to detect it.
-#  pragma warning(disable : 4996)
 #endif
 
 // Dummy implementations of strerror_r and strerror_s called if corresponding
 // system functions are not available.
-inline fmt::internal::null<> strerror_r(int, char*, ...) {
-  return fmt::internal::null<>();
-}
-inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) {
-  return fmt::internal::null<>();
-}
+inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; }
+inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; }
 
 FMT_BEGIN_NAMESPACE
 namespace internal {
 
+FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
+  print(stderr, "{}:{}: assertion failed: {}", file, line, message);
+  std::abort();
+}
+
 #ifndef _MSC_VER
 #  define FMT_SNPRINTF snprintf
 #else  // _MSC_VER
@@ -81,7 +75,7 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
 
 using format_func = void (*)(internal::buffer<char>&, int, string_view);
 
-// Portable thread-safe version of strerror.
+// A portable thread-safe version of strerror.
 // Sets buffer to point to a string describing the error code.
 // This can be either a pointer to a string stored in buffer,
 // or a pointer to some static immutable string.
@@ -158,7 +152,7 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
   static const char ERROR_STR[] = "error ";
   // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
   std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
-  auto abs_value = static_cast<uint32_or_64_t<int>>(error_code);
+  auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
   if (internal::is_negative(error_code)) {
     abs_value = 0 - abs_value;
     ++error_code_size;
@@ -206,6 +200,9 @@ template <typename Locale> Locale locale_ref::get() const {
   return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
 }
 
+template <typename Char> FMT_FUNC std::string grouping_impl(locale_ref loc) {
+  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()).grouping();
+}
 template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
   return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
       .thousands_sep();
@@ -217,6 +214,10 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
 }  // namespace internal
 #else
 template <typename Char>
+FMT_FUNC std::string internal::grouping_impl(locale_ref) {
+  return "\03";
+}
+template <typename Char>
 FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
   return FMT_STATIC_THOUSANDS_SEPARATOR;
 }
@@ -226,8 +227,8 @@ FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
 }
 #endif
 
-FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT {}
-FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT {}
+FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default;
+FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default;
 
 FMT_FUNC void system_error::init(int err_code, string_view format_str,
                                  format_args args) {
@@ -241,27 +242,13 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
 namespace internal {
 
 template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
-  // Assume little endian; pointer formatting is implementation-defined anyway.
+  // fallback_uintptr is always stored in little endian.
   int i = static_cast<int>(sizeof(void*)) - 1;
   while (i > 0 && n.value[i] == 0) --i;
   auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
   return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
 }
 
-template <typename T>
-int format_float(char* buf, std::size_t size, const char* format, int precision,
-                 T value) {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-  if (precision > 100000)
-    throw std::runtime_error(
-        "fuzz mode - avoid large allocation inside snprintf");
-#endif
-  // Suppress the warning about nonliteral format string.
-  auto snprintf_ptr = FMT_SNPRINTF;
-  return precision < 0 ? snprintf_ptr(buf, size, format, value)
-                       : snprintf_ptr(buf, size, format, precision, value);
-}
-
 template <typename T>
 const char basic_data<T>::digits[] =
     "0001020304050607080910111213141516171819"
@@ -274,14 +261,14 @@ template <typename T>
 const char basic_data<T>::hex_digits[] = "0123456789abcdef";
 
 #define FMT_POWERS_OF_10(factor)                                             \
-  factor * 10, factor * 100, factor * 1000, factor * 10000, factor * 100000, \
-      factor * 1000000, factor * 10000000, factor * 100000000,               \
-      factor * 1000000000
+  factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
+      (factor)*1000000, (factor)*10000000, (factor)*100000000,               \
+      (factor)*1000000000
 
 template <typename T>
 const uint64_t basic_data<T>::powers_of_10_64[] = {
-    1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ull),
-    10000000000000000000ull};
+    1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
+    10000000000000000000ULL};
 
 template <typename T>
 const uint32_t basic_data<T>::zero_or_powers_of_10_32[] = {0,
@@ -289,8 +276,8 @@ const uint32_t basic_data<T>::zero_or_powers_of_10_32[] = {0,
 
 template <typename T>
 const uint64_t basic_data<T>::zero_or_powers_of_10_64[] = {
-    0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ull),
-    10000000000000000000ull};
+    0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
+    10000000000000000000ULL};
 
 // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
 // These are generated by support/compute-powers.py.
@@ -346,12 +333,24 @@ template <typename T>
 const char basic_data<T>::background_color[] = "\x1b[48;2;";
 template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
 template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
+template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
 
 template <typename T> struct bits {
   static FMT_CONSTEXPR_DECL const int value =
       static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
 };
 
+class fp;
+template <int SHIFT = 0> fp normalize(fp value);
+
+// Lower (upper) boundary is a value half way between a floating-point value
+// and its predecessor (successor). Boundaries have the same exponent as the
+// value so only significands are stored.
+struct boundaries {
+  uint64_t lower;
+  uint64_t upper;
+};
+
 // A handmade floating-point number f * pow(2, e).
 class fp {
  private:
@@ -363,7 +362,7 @@ class fp {
   static FMT_CONSTEXPR_DECL const int double_significand_size =
       std::numeric_limits<double>::digits - 1;
   static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
-      1ull << double_significand_size;
+      1ULL << double_significand_size;
 
  public:
   significand_type f;
@@ -377,95 +376,377 @@ class fp {
 
   // Constructs fp from an IEEE754 double. It is a template to prevent compile
   // errors on platforms where double is not IEEE754.
-  template <typename Double> explicit fp(Double d) {
+  template <typename Double> explicit fp(Double d) { assign(d); }
+
+  // Normalizes the value converted from double and multiplied by (1 << SHIFT).
+  template <int SHIFT> friend fp normalize(fp value) {
+    // Handle subnormals.
+    const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
+    while ((value.f & shifted_implicit_bit) == 0) {
+      value.f <<= 1;
+      --value.e;
+    }
+    // Subtract 1 to account for hidden bit.
+    const auto offset =
+        fp::significand_size - fp::double_significand_size - SHIFT - 1;
+    value.f <<= offset;
+    value.e -= offset;
+    return value;
+  }
+
+  // Assigns d to this and return true iff predecessor is closer than successor.
+  template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
+  bool assign(Double d) {
     // Assume double is in the format [sign][exponent][significand].
     using limits = std::numeric_limits<Double>;
     const int exponent_size =
         bits<Double>::value - double_significand_size - 1;  // -1 for sign
     const uint64_t significand_mask = implicit_bit - 1;
-    const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
+    const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
     const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
     auto u = bit_cast<uint64_t>(d);
-    auto biased_e = (u & exponent_mask) >> double_significand_size;
     f = u & significand_mask;
+    auto biased_e = (u & exponent_mask) >> double_significand_size;
+    // Predecessor is closer if d is a normalized power of 2 (f == 0) other than
+    // the smallest normalized number (biased_e > 1).
+    bool is_predecessor_closer = f == 0 && biased_e > 1;
     if (biased_e != 0)
       f += implicit_bit;
     else
       biased_e = 1;  // Subnormals use biased exponent 1 (min exponent).
     e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
+    return is_predecessor_closer;
   }
 
-  // Normalizes the value converted from double and multiplied by (1 << SHIFT).
-  template <int SHIFT = 0> void normalize() {
-    // Handle subnormals.
-    auto shifted_implicit_bit = implicit_bit << SHIFT;
-    while ((f & shifted_implicit_bit) == 0) {
-      f <<= 1;
-      --e;
-    }
-    // Subtract 1 to account for hidden bit.
-    auto offset = significand_size - double_significand_size - SHIFT - 1;
-    f <<= offset;
-    e -= offset;
+  template <typename Double, FMT_ENABLE_IF(sizeof(Double) != sizeof(uint64_t))>
+  bool assign(Double) {
+    *this = fp();
+    return false;
   }
 
-  // Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
-  // a boundary is a value half way between the number and its predecessor
+  // Assigns d to this together with computing lower and upper boundaries,
+  // where a boundary is a value half way between the number and its predecessor
   // (lower) or successor (upper). The upper boundary is normalized and lower
   // has the same exponent but may be not normalized.
-  void compute_boundaries(fp& lower, fp& upper) const {
-    lower =
-        f == implicit_bit ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
-    upper = fp((f << 1) + 1, e - 1);
-    upper.normalize<1>();  // 1 is to account for the exponent shift above.
+  template <typename Double> boundaries assign_with_boundaries(Double d) {
+    bool is_lower_closer = assign(d);
+    fp lower =
+        is_lower_closer ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
+    // 1 in normalize accounts for the exponent shift above.
+    fp upper = normalize<1>(fp((f << 1) + 1, e - 1));
+    lower.f <<= lower.e - upper.e;
+    return boundaries{lower.f, upper.f};
+  }
+
+  template <typename Double> boundaries assign_float_with_boundaries(Double d) {
+    assign(d);
+    constexpr int min_normal_e = std::numeric_limits<float>::min_exponent -
+                                 std::numeric_limits<double>::digits;
+    significand_type half_ulp = 1 << (std::numeric_limits<double>::digits -
+                                      std::numeric_limits<float>::digits - 1);
+    if (min_normal_e > e) half_ulp <<= min_normal_e - e;
+    fp upper = normalize<0>(fp(f + half_ulp, e));
+    fp lower = fp(
+        f - (half_ulp >> ((f == implicit_bit && e > min_normal_e) ? 1 : 0)), e);
     lower.f <<= lower.e - upper.e;
-    lower.e = upper.e;
+    return boundaries{lower.f, upper.f};
   }
 };
 
-// Returns an fp number representing x - y. Result may not be normalized.
-inline fp operator-(fp x, fp y) {
-  FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands");
-  return fp(x.f - y.f, x.e);
-}
+inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
 
-// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
-// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be
-// normalized.
-FMT_FUNC fp operator*(fp x, fp y) {
-  int exp = x.e + y.e + 64;
+// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
+inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
 #if FMT_USE_INT128
-  auto product = static_cast<__uint128_t>(x.f) * y.f;
+  auto product = static_cast<__uint128_t>(lhs) * rhs;
   auto f = static_cast<uint64_t>(product >> 64);
-  if ((static_cast<uint64_t>(product) & (1ULL << 63)) != 0) ++f;
-  return fp(f, exp);
+  return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
 #else
   // Multiply 32-bit parts of significands.
   uint64_t mask = (1ULL << 32) - 1;
-  uint64_t a = x.f >> 32, b = x.f & mask;
-  uint64_t c = y.f >> 32, d = y.f & mask;
+  uint64_t a = lhs >> 32, b = lhs & mask;
+  uint64_t c = rhs >> 32, d = rhs & mask;
   uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
   // Compute mid 64-bit of result and round.
   uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
-  return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), exp);
+  return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
 #endif
 }
 
-// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
-// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 28.
+inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
+
+// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
+// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
 FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) {
-  const double one_over_log2_10 = 0.30102999566398114;  // 1 / log2(10)
+  const uint64_t one_over_log2_10 = 0x4d104d42;  // round(pow(2, 32) / log2(10))
   int index = static_cast<int>(
-      std::ceil((min_exponent + fp::significand_size - 1) * one_over_log2_10));
+      static_cast<int64_t>(
+          (min_exponent + fp::significand_size - 1) * one_over_log2_10 +
+          ((uint64_t(1) << 32) - 1)  // ceil
+          ) >>
+      32  // arithmetic shift
+  );
   // Decimal exponent of the first (smallest) cached power of 10.
   const int first_dec_exp = -348;
   // Difference between 2 consecutive decimal exponents in cached powers of 10.
   const int dec_exp_step = 8;
   index = (index - first_dec_exp - 1) / dec_exp_step + 1;
   pow10_exponent = first_dec_exp + index * dec_exp_step;
-  return fp(data::pow10_significands[index], data::pow10_exponents[index]);
+  return {data::pow10_significands[index], data::pow10_exponents[index]};
 }
 
+// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
+// is not available.
+struct accumulator {
+  uint64_t lower;
+  uint64_t upper;
+
+  accumulator() : lower(0), upper(0) {}
+  explicit operator uint32_t() const { return static_cast<uint32_t>(lower); }
+
+  void operator+=(uint64_t n) {
+    lower += n;
+    if (lower < n) ++upper;
+  }
+  void operator>>=(int shift) {
+    assert(shift == 32);
+    (void)shift;
+    lower = (upper << 32) | (lower >> 32);
+    upper >>= 32;
+  }
+};
+
+class bigint {
+ private:
+  // A bigint is stored as an array of bigits (big digits), with bigit at index
+  // 0 being the least significant one.
+  using bigit = uint32_t;
+  using double_bigit = uint64_t;
+  enum { bigits_capacity = 32 };
+  basic_memory_buffer<bigit, bigits_capacity> bigits_;
+  int exp_;
+
+  static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
+
+  friend struct formatter<bigint>;
+
+  void subtract_bigits(int index, bigit other, bigit& borrow) {
+    auto result = static_cast<double_bigit>(bigits_[index]) - other - borrow;
+    bigits_[index] = static_cast<bigit>(result);
+    borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
+  }
+
+  void remove_leading_zeros() {
+    int num_bigits = static_cast<int>(bigits_.size()) - 1;
+    while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
+    bigits_.resize(num_bigits + 1);
+  }
+
+  // Computes *this -= other assuming aligned bigints and *this >= other.
+  void subtract_aligned(const bigint& other) {
+    FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
+    FMT_ASSERT(compare(*this, other) >= 0, "");
+    bigit borrow = 0;
+    int i = other.exp_ - exp_;
+    for (int j = 0, n = static_cast<int>(other.bigits_.size()); j != n;
+         ++i, ++j) {
+      subtract_bigits(i, other.bigits_[j], borrow);
+    }
+    while (borrow > 0) subtract_bigits(i, 0, borrow);
+    remove_leading_zeros();
+  }
+
+  void multiply(uint32_t value) {
+    const double_bigit wide_value = value;
+    bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      double_bigit result = bigits_[i] * wide_value + carry;
+      bigits_[i] = static_cast<bigit>(result);
+      carry = static_cast<bigit>(result >> bigit_bits);
+    }
+    if (carry != 0) bigits_.push_back(carry);
+  }
+
+  void multiply(uint64_t value) {
+    const bigit mask = ~bigit(0);
+    const double_bigit lower = value & mask;
+    const double_bigit upper = value >> bigit_bits;
+    double_bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      double_bigit result = bigits_[i] * lower + (carry & mask);
+      carry =
+          bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits);
+      bigits_[i] = static_cast<bigit>(result);
+    }
+    while (carry != 0) {
+      bigits_.push_back(carry & mask);
+      carry >>= bigit_bits;
+    }
+  }
+
+ public:
+  bigint() : exp_(0) {}
+  explicit bigint(uint64_t n) { assign(n); }
+  ~bigint() { assert(bigits_.capacity() <= bigits_capacity); }
+
+  bigint(const bigint&) = delete;
+  void operator=(const bigint&) = delete;
+
+  void assign(const bigint& other) {
+    bigits_.resize(other.bigits_.size());
+    auto data = other.bigits_.data();
+    std::copy(data, data + other.bigits_.size(), bigits_.data());
+    exp_ = other.exp_;
+  }
+
+  void assign(uint64_t n) {
+    int num_bigits = 0;
+    do {
+      bigits_[num_bigits++] = n & ~bigit(0);
+      n >>= bigit_bits;
+    } while (n != 0);
+    bigits_.resize(num_bigits);
+    exp_ = 0;
+  }
+
+  int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
+
+  bigint& operator<<=(int shift) {
+    assert(shift >= 0);
+    exp_ += shift / bigit_bits;
+    shift %= bigit_bits;
+    if (shift == 0) return *this;
+    bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      bigit c = bigits_[i] >> (bigit_bits - shift);
+      bigits_[i] = (bigits_[i] << shift) + carry;
+      carry = c;
+    }
+    if (carry != 0) bigits_.push_back(carry);
+    return *this;
+  }
+
+  template <typename Int> bigint& operator*=(Int value) {
+    FMT_ASSERT(value > 0, "");
+    multiply(uint32_or_64_or_128_t<Int>(value));
+    return *this;
+  }
+
+  friend int compare(const bigint& lhs, const bigint& rhs) {
+    int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
+    if (num_lhs_bigits != num_rhs_bigits)
+      return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
+    int i = static_cast<int>(lhs.bigits_.size()) - 1;
+    int j = static_cast<int>(rhs.bigits_.size()) - 1;
+    int end = i - j;
+    if (end < 0) end = 0;
+    for (; i >= end; --i, --j) {
+      bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j];
+      if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
+    }
+    if (i != j) return i > j ? 1 : -1;
+    return 0;
+  }
+
+  // Returns compare(lhs1 + lhs2, rhs).
+  friend int add_compare(const bigint& lhs1, const bigint& lhs2,
+                         const bigint& rhs) {
+    int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
+    int num_rhs_bigits = rhs.num_bigits();
+    if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
+    if (max_lhs_bigits > num_rhs_bigits) return 1;
+    auto get_bigit = [](const bigint& n, int i) -> bigit {
+      return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0;
+    };
+    double_bigit borrow = 0;
+    int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
+    for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
+      double_bigit sum =
+          static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
+      bigit rhs_bigit = get_bigit(rhs, i);
+      if (sum > rhs_bigit + borrow) return 1;
+      borrow = rhs_bigit + borrow - sum;
+      if (borrow > 1) return -1;
+      borrow <<= bigit_bits;
+    }
+    return borrow != 0 ? -1 : 0;
+  }
+
+  // Assigns pow(10, exp) to this bigint.
+  void assign_pow10(int exp) {
+    assert(exp >= 0);
+    if (exp == 0) return assign(1);
+    // Find the top bit.
+    int bitmask = 1;
+    while (exp >= bitmask) bitmask <<= 1;
+    bitmask >>= 1;
+    // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
+    // repeated squaring and multiplication.
+    assign(5);
+    bitmask >>= 1;
+    while (bitmask != 0) {
+      square();
+      if ((exp & bitmask) != 0) *this *= 5;
+      bitmask >>= 1;
+    }
+    *this <<= exp;  // Multiply by pow(2, exp) by shifting.
+  }
+
+  void square() {
+    basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
+    int num_bigits = static_cast<int>(bigits_.size());
+    int num_result_bigits = 2 * num_bigits;
+    bigits_.resize(num_result_bigits);
+    using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
+    auto sum = accumulator_t();
+    for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
+      // Compute bigit at position bigit_index of the result by adding
+      // cross-product terms n[i] * n[j] such that i + j == bigit_index.
+      for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
+        // Most terms are multiplied twice which can be optimized in the future.
+        sum += static_cast<double_bigit>(n[i]) * n[j];
+      }
+      bigits_[bigit_index] = static_cast<bigit>(sum);
+      sum >>= bits<bigit>::value;  // Compute the carry.
+    }
+    // Do the same for the top half.
+    for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
+         ++bigit_index) {
+      for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
+        sum += static_cast<double_bigit>(n[i++]) * n[j--];
+      bigits_[bigit_index] = static_cast<bigit>(sum);
+      sum >>= bits<bigit>::value;
+    }
+    --num_result_bigits;
+    remove_leading_zeros();
+    exp_ *= 2;
+  }
+
+  // Divides this bignum by divisor, assigning the remainder to this and
+  // returning the quotient.
+  int divmod_assign(const bigint& divisor) {
+    FMT_ASSERT(this != &divisor, "");
+    if (compare(*this, divisor) < 0) return 0;
+    int num_bigits = static_cast<int>(bigits_.size());
+    FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 0, "");
+    int exp_difference = exp_ - divisor.exp_;
+    if (exp_difference > 0) {
+      // Align bigints by adding trailing zeros to simplify subtraction.
+      bigits_.resize(num_bigits + exp_difference);
+      for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
+        bigits_[j] = bigits_[i];
+      std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
+      exp_ -= exp_difference;
+    }
+    int quotient = 0;
+    do {
+      subtract_aligned(divisor);
+      ++quotient;
+    } while (compare(*this, divisor) >= 0);
+    return quotient;
+  }
+};
+
 enum round_direction { unknown, up, down };
 
 // Given the divisor (normally a power of 10), the remainder = v % divisor for
@@ -500,13 +781,13 @@ enum result {
 // error: the size of the region (lower, upper) outside of which numbers
 // definitely do not round to value (Delta in Grisu3).
 template <typename Handler>
-digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
-                                Handler& handler) {
-  fp one(1ull << -value.e, value.e);
+FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error,
+                                                  int& exp, Handler& handler) {
+  const fp one(1ULL << -value.e, value.e);
   // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
   // zero because it contains a product of two 64-bit numbers with MSB set (due
   // to normalization) - 1, shifted right by at most 60 bits.
-  uint32_t integral = static_cast<uint32_t>(value.f >> -one.e);
+  auto integral = static_cast<uint32_t>(value.f >> -one.e);
   FMT_ASSERT(integral != 0, "");
   FMT_ASSERT(integral == value.f >> -one.e, "");
   // The fractional part of scaled value (p2 in Grisu) c = value % one.
@@ -519,44 +800,39 @@ digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
   // Generate digits for the integral part. This can produce up to 10 digits.
   do {
     uint32_t digit = 0;
-    // This optimization by miloyip reduces the number of integer divisions by
+    auto divmod_integral = [&](uint32_t divisor) {
+      digit = integral / divisor;
+      integral %= divisor;
+    };
+    // This optimization by Milo Yip reduces the number of integer divisions by
     // one per iteration.
     switch (exp) {
     case 10:
-      digit = integral / 1000000000;
-      integral %= 1000000000;
+      divmod_integral(1000000000);
       break;
     case 9:
-      digit = integral / 100000000;
-      integral %= 100000000;
+      divmod_integral(100000000);
       break;
     case 8:
-      digit = integral / 10000000;
-      integral %= 10000000;
+      divmod_integral(10000000);
       break;
     case 7:
-      digit = integral / 1000000;
-      integral %= 1000000;
+      divmod_integral(1000000);
       break;
     case 6:
-      digit = integral / 100000;
-      integral %= 100000;
+      divmod_integral(100000);
       break;
     case 5:
-      digit = integral / 10000;
-      integral %= 10000;
+      divmod_integral(10000);
       break;
     case 4:
-      digit = integral / 1000;
-      integral %= 1000;
+      divmod_integral(1000);
       break;
     case 3:
-      digit = integral / 100;
-      integral %= 100;
+      divmod_integral(100);
       break;
     case 2:
-      digit = integral / 10;
-      integral %= 10;
+      divmod_integral(10);
       break;
     case 1:
       digit = integral;
@@ -640,7 +916,7 @@ struct fixed_handler {
 };
 
 // The shortest representation digit handler.
-template <int GRISU_VERSION> struct grisu_shortest_handler {
+struct grisu_shortest_handler {
   char* buf;
   int size;
   // Distance between scaled value and upper bound (wp_W in Grisu3).
@@ -666,11 +942,6 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
                           uint64_t error, int exp, bool integral) {
     buf[size++] = digit;
     if (remainder >= error) return digits::more;
-    if (GRISU_VERSION != 3) {
-      uint64_t d = integral ? diff : diff * data::powers_of_10_64[-exp];
-      round(d, divisor, remainder, error);
-      return digits::done;
-    }
     uint64_t unit = integral ? 1 : data::powers_of_10_64[-exp];
     uint64_t up = (diff - 1) * unit;  // wp_Wup
     round(up, divisor, remainder, error);
@@ -686,151 +957,289 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
   }
 };
 
-template <typename Double,
-          enable_if_t<(sizeof(Double) == sizeof(uint64_t)), int>>
-FMT_API bool grisu_format(Double value, buffer<char>& buf, int precision,
-                          unsigned options, int& exp) {
+// Formats value using a variation of the Fixed-Precision Positive
+// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
+// https://fmt.dev/p372-steele.pdf.
+template <typename Double>
+void fallback_format(Double d, buffer<char>& buf, int& exp10) {
+  bigint numerator;    // 2 * R in (FPP)^2.
+  bigint denominator;  // 2 * S in (FPP)^2.
+  // lower and upper are differences between value and corresponding boundaries.
+  bigint lower;             // (M^- in (FPP)^2).
+  bigint upper_store;       // upper's value if different from lower.
+  bigint* upper = nullptr;  // (M^+ in (FPP)^2).
+  fp value;
+  // Shift numerator and denominator by an extra bit or two (if lower boundary
+  // is closer) to make lower and upper integers. This eliminates multiplication
+  // by 2 during later computations.
+  // TODO: handle float
+  int shift = value.assign(d) ? 2 : 1;
+  uint64_t significand = value.f << shift;
+  if (value.e >= 0) {
+    numerator.assign(significand);
+    numerator <<= value.e;
+    lower.assign(1);
+    lower <<= value.e;
+    if (shift != 1) {
+      upper_store.assign(1);
+      upper_store <<= value.e + 1;
+      upper = &upper_store;
+    }
+    denominator.assign_pow10(exp10);
+    denominator <<= 1;
+  } else if (exp10 < 0) {
+    numerator.assign_pow10(-exp10);
+    lower.assign(numerator);
+    if (shift != 1) {
+      upper_store.assign(numerator);
+      upper_store <<= 1;
+      upper = &upper_store;
+    }
+    numerator *= significand;
+    denominator.assign(1);
+    denominator <<= shift - value.e;
+  } else {
+    numerator.assign(significand);
+    denominator.assign_pow10(exp10);
+    denominator <<= shift - value.e;
+    lower.assign(1);
+    if (shift != 1) {
+      upper_store.assign(1ULL << 1);
+      upper = &upper_store;
+    }
+  }
+  if (!upper) upper = &lower;
+  // Invariant: value == (numerator / denominator) * pow(10, exp10).
+  bool even = (value.f & 1) == 0;
+  int num_digits = 0;
+  char* data = buf.data();
+  for (;;) {
+    int digit = numerator.divmod_assign(denominator);
+    bool low = compare(numerator, lower) - even < 0;  // numerator <[=] lower.
+    // numerator + upper >[=] pow10:
+    bool high = add_compare(numerator, *upper, denominator) + even > 0;
+    data[num_digits++] = static_cast<char>('0' + digit);
+    if (low || high) {
+      if (!low) {
+        ++data[num_digits - 1];
+      } else if (high) {
+        int result = add_compare(numerator, numerator, denominator);
+        // Round half to even.
+        if (result > 0 || (result == 0 && (digit % 2) != 0))
+          ++data[num_digits - 1];
+      }
+      buf.resize(num_digits);
+      exp10 -= num_digits - 1;
+      return;
+    }
+    numerator *= 10;
+    lower *= 10;
+    if (upper != &lower) *upper *= 10;
+  }
+}
+
+// Formats value using the Grisu algorithm
+// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf)
+// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
+template <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
+  static_assert(!std::is_same<T, float>(), "");
   FMT_ASSERT(value >= 0, "value is negative");
-  bool fixed = (options & grisu_options::fixed) != 0;
+
+  const bool fixed = specs.format == float_format::fixed;
   if (value <= 0) {  // <= instead of == to silence a warning.
     if (precision <= 0 || !fixed) {
-      exp = 0;
       buf.push_back('0');
-    } else {
-      exp = -precision;
-      buf.resize(precision);
-      std::uninitialized_fill_n(buf.data(), precision, '0');
+      return 0;
     }
-    return true;
+    buf.resize(to_unsigned(precision));
+    std::uninitialized_fill_n(buf.data(), precision, '0');
+    return -precision;
   }
 
-  fp fp_value(value);
+  if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf);
+
+  int exp = 0;
   const int min_exp = -60;  // alpha in Grisu.
   int cached_exp10 = 0;     // K in Grisu.
   if (precision != -1) {
-    if (precision > 17) return false;
-    fp_value.normalize();
-    auto cached_pow = get_cached_power(
-        min_exp - (fp_value.e + fp::significand_size), cached_exp10);
-    fp_value = fp_value * cached_pow;
+    if (precision > 17) return snprintf_float(value, precision, specs, buf);
+    fp normalized = normalize(fp(value));
+    const auto cached_pow = get_cached_power(
+        min_exp - (normalized.e + fp::significand_size), cached_exp10);
+    normalized = normalized * cached_pow;
     fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
-    if (grisu_gen_digits(fp_value, 1, exp, handler) == digits::error)
-      return false;
-    buf.resize(to_unsigned(handler.size));
+    if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
+      return snprintf_float(value, precision, specs, buf);
+    int num_digits = handler.size;
+    if (!fixed) {
+      // Remove trailing zeros.
+      while (num_digits > 0 && buf[num_digits - 1] == '0') {
+        --num_digits;
+        ++exp;
+      }
+    }
+    buf.resize(to_unsigned(num_digits));
   } else {
-    fp lower, upper;  // w^- and w^+ in the Grisu paper.
-    fp_value.compute_boundaries(lower, upper);
-    // Find a cached power of 10 such that multiplying upper by it will bring
+    fp fp_value;
+    auto boundaries = specs.binary32
+                          ? fp_value.assign_float_with_boundaries(value)
+                          : fp_value.assign_with_boundaries(value);
+    fp_value = normalize(fp_value);
+    // Find a cached power of 10 such that multiplying value by it will bring
     // the exponent in the range [min_exp, -32].
-    auto cached_pow = get_cached_power(  // \tilde{c}_{-k} in Grisu.
-        min_exp - (upper.e + fp::significand_size), cached_exp10);
-    fp_value.normalize();
+    const fp cached_pow = get_cached_power(
+        min_exp - (fp_value.e + fp::significand_size), cached_exp10);
+    // Multiply value and boundaries by the cached power of 10.
     fp_value = fp_value * cached_pow;
-    lower = lower * cached_pow;  // \tilde{M}^- in Grisu.
-    upper = upper * cached_pow;  // \tilde{M}^+ in Grisu.
-    assert(min_exp <= upper.e && upper.e <= -32);
-    auto result = digits::result();
-    int size = 0;
-    if ((options & grisu_options::grisu3) != 0) {
-      --lower.f;  // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
-      ++upper.f;  // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
-      // Numbers outside of (lower, upper) definitely do not round to value.
-      grisu_shortest_handler<3> handler{buf.data(), 0, (upper - fp_value).f};
-      result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
-      size = handler.size;
-    } else {
-      ++lower.f;  // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
-      --upper.f;  // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
-      grisu_shortest_handler<2> handler{buf.data(), 0, (upper - fp_value).f};
-      result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
-      size = handler.size;
+    boundaries.lower = multiply(boundaries.lower, cached_pow.f);
+    boundaries.upper = multiply(boundaries.upper, cached_pow.f);
+    assert(min_exp <= fp_value.e && fp_value.e <= -32);
+    --boundaries.lower;  // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
+    ++boundaries.upper;  // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
+    // Numbers outside of (lower, upper) definitely do not round to value.
+    grisu_shortest_handler handler{buf.data(), 0,
+                                   boundaries.upper - fp_value.f};
+    auto result =
+        grisu_gen_digits(fp(boundaries.upper, fp_value.e),
+                         boundaries.upper - boundaries.lower, exp, handler);
+    if (result == digits::error) {
+      exp += handler.size - cached_exp10 - 1;
+      fallback_format(value, buf, exp);
+      return exp;
     }
-    if (result == digits::error) return false;
-    buf.resize(to_unsigned(size));
+    buf.resize(to_unsigned(handler.size));
   }
-  exp -= cached_exp10;
-  return true;
+  return exp - cached_exp10;
 }
 
-template <typename Double>
-char* sprintf_format(Double value, internal::buffer<char>& buf,
-                     sprintf_specs specs) {
+template <typename T>
+int snprintf_float(T value, int precision, float_specs specs,
+                   buffer<char>& buf) {
   // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
-  FMT_ASSERT(buf.capacity() != 0, "empty buffer");
+  FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
+  static_assert(!std::is_same<T, float>(), "");
 
-  // Build format string.
-  enum { max_format_size = 10 };  // longest format: %#-*.*Lg
+  // Subtract 1 to account for the difference in precision since we use %e for
+  // both general and exponent format.
+  if (specs.format == float_format::general ||
+      specs.format == float_format::exp)
+    precision = (precision >= 0 ? precision : 6) - 1;
+
+  // Build the format string.
+  enum { max_format_size = 7 };  // Ths longest format is "%#.*Le".
   char format[max_format_size];
   char* format_ptr = format;
   *format_ptr++ = '%';
-  if (specs.alt || !specs.type) *format_ptr++ = '#';
-  if (specs.precision >= 0) {
+  if (specs.trailing_zeros) *format_ptr++ = '#';
+  if (precision >= 0) {
     *format_ptr++ = '.';
     *format_ptr++ = '*';
   }
-  if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
-
-  char type = specs.type;
-
-  if (type == '%')
-    type = 'f';
-  else if (type == 0 || type == 'n')
-    type = 'g';
-#if FMT_MSC_VER
-  if (type == 'F') {
-    // MSVC's printf doesn't support 'F'.
-    type = 'f';
-  }
-#endif
-  *format_ptr++ = type;
+  if (std::is_same<T, long double>()) *format_ptr++ = 'L';
+  *format_ptr++ = specs.format != float_format::hex
+                      ? (specs.format == float_format::fixed ? 'f' : 'e')
+                      : (specs.upper ? 'A' : 'a');
   *format_ptr = '\0';
 
   // Format using snprintf.
-  char* start = nullptr;
-  char* decimal_point_pos = nullptr;
+  auto offset = buf.size();
   for (;;) {
-    std::size_t buffer_size = buf.capacity();
-    start = &buf[0];
-    int result =
-        format_float(start, buffer_size, format, specs.precision, value);
-    if (result >= 0) {
-      unsigned n = internal::to_unsigned(result);
-      if (n < buf.capacity()) {
-        // Find the decimal point.
-        auto p = buf.data(), end = p + n;
-        if (*p == '+' || *p == '-') ++p;
-        if (specs.type != 'a' && specs.type != 'A') {
-          while (p < end && *p >= '0' && *p <= '9') ++p;
-          if (p < end && *p != 'e' && *p != 'E') {
-            decimal_point_pos = p;
-            if (!specs.type) {
-              // Keep only one trailing zero after the decimal point.
-              ++p;
-              if (*p == '0') ++p;
-              while (p != end && *p >= '1' && *p <= '9') ++p;
-              char* where = p;
-              while (p != end && *p == '0') ++p;
-              if (p == end || *p < '0' || *p > '9') {
-                if (p != end) std::memmove(where, p, to_unsigned(end - p));
-                n -= static_cast<unsigned>(p - where);
-              }
-            }
-          }
-        }
-        buf.resize(n);
-        break;  // The buffer is large enough - continue with formatting.
+    auto begin = buf.data() + offset;
+    auto capacity = buf.capacity() - offset;
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    if (precision > 100000)
+      throw std::runtime_error(
+          "fuzz mode - avoid large allocation inside snprintf");
+#endif
+    // Suppress the warning about a nonliteral format string.
+    auto snprintf_ptr = FMT_SNPRINTF;
+    int result = precision >= 0
+                     ? snprintf_ptr(begin, capacity, format, precision, value)
+                     : snprintf_ptr(begin, capacity, format, value);
+    if (result < 0) {
+      buf.reserve(buf.capacity() + 1);  // The buffer will grow exponentially.
+      continue;
+    }
+    unsigned size = to_unsigned(result);
+    // Size equal to capacity means that the last character was truncated.
+    if (size >= capacity) {
+      buf.reserve(size + offset + 1);  // Add 1 for the terminating '\0'.
+      continue;
+    }
+    auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
+    if (specs.format == float_format::fixed) {
+      if (precision == 0) {
+        buf.resize(size);
+        return 0;
       }
-      buf.reserve(n + 1);
-    } else {
-      // If result is negative we ask to increase the capacity by at least 1,
-      // but as std::vector, the buffer grows exponentially.
-      buf.reserve(buf.capacity() + 1);
+      // Find and remove the decimal point.
+      auto end = begin + size, p = end;
+      do {
+        --p;
+      } while (is_digit(*p));
+      int fraction_size = static_cast<int>(end - p - 1);
+      std::memmove(p, p + 1, fraction_size);
+      buf.resize(size - 1);
+      return -fraction_size;
     }
+    if (specs.format == float_format::hex) {
+      buf.resize(size + offset);
+      return 0;
+    }
+    // Find and parse the exponent.
+    auto end = begin + size, exp_pos = end;
+    do {
+      --exp_pos;
+    } while (*exp_pos != 'e');
+    char sign = exp_pos[1];
+    assert(sign == '+' || sign == '-');
+    int exp = 0;
+    auto p = exp_pos + 2;  // Skip 'e' and sign.
+    do {
+      assert(is_digit(*p));
+      exp = exp * 10 + (*p++ - '0');
+    } while (p != end);
+    if (sign == '-') exp = -exp;
+    int fraction_size = 0;
+    if (exp_pos != begin + 1) {
+      // Remove trailing zeros.
+      auto fraction_end = exp_pos - 1;
+      while (*fraction_end == '0') --fraction_end;
+      // Move the fractional part left to get rid of the decimal point.
+      fraction_size = static_cast<int>(fraction_end - begin - 1);
+      std::memmove(begin + 1, begin + 2, fraction_size);
+    }
+    buf.resize(fraction_size + offset + 1);
+    return exp - fraction_size;
   }
-  return decimal_point_pos;
 }
 }  // namespace internal
 
+template <> struct formatter<internal::bigint> {
+  format_parse_context::iterator parse(format_parse_context& ctx) {
+    return ctx.begin();
+  }
+
+  format_context::iterator format(const internal::bigint& n,
+                                  format_context& ctx) {
+    auto out = ctx.out();
+    bool first = true;
+    for (auto i = n.bigits_.size(); i > 0; --i) {
+      auto value = n.bigits_[i - 1];
+      if (first) {
+        out = format_to(out, "{:x}", value);
+        first = false;
+        continue;
+      }
+      out = format_to(out, "{:08x}", value);
+    }
+    if (n.exp_ > 0)
+      out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits);
+    return out;
+  }
+};
+
 #if FMT_USE_WINDOWS_H
 
 FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
@@ -974,23 +1383,10 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
   internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
 }
 
-FMT_FUNC void vprint(std::FILE* f, wstring_view format_str, wformat_args args) {
-  wmemory_buffer buffer;
-  internal::vformat_to(buffer, format_str, args);
-  buffer.push_back(L'\0');
-  if (std::fputws(buffer.data(), f) == -1) {
-    FMT_THROW(system_error(errno, "cannot write to file"));
-  }
-}
-
 FMT_FUNC void vprint(string_view format_str, format_args args) {
   vprint(stdout, format_str, args);
 }
 
-FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
-  vprint(stdout, format_str, args);
-}
-
 FMT_END_NAMESPACE
 
 #ifdef _MSC_VER
index efec5d6df59d25f16b3979622a56c26fdc0713ae..01f41f5cbdd339c4106caa7056a00fef38b5f477 100644 (file)
 #ifndef FMT_FORMAT_H_
 #define FMT_FORMAT_H_
 
+#include "core.h"
+
 #include <algorithm>
-#include <cassert>
+#include <cerrno>
 #include <cmath>
 #include <cstdint>
-#include <cstring>
-#include <iterator>
 #include <limits>
 #include <memory>
 #include <stdexcept>
 
-#include "core.h"
-
 #ifdef __clang__
 #  define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
 #else
 #  define FMT_HAS_BUILTIN(x) 0
 #endif
 
+#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) && \
+    (__cplusplus >= 201703 || FMT_GCC_VERSION != 0)
+#  define FMT_FALLTHROUGH [[fallthrough]]
+#else
+#  define FMT_FALLTHROUGH
+#endif
+
 #ifndef FMT_THROW
 #  if FMT_EXCEPTIONS
 #    if FMT_MSC_VER
@@ -84,7 +89,7 @@ template <typename Exception> inline void do_throw(const Exception& x) {
 }
 }  // namespace internal
 FMT_END_NAMESPACE
-#      define FMT_THROW(x) fmt::internal::do_throw(x)
+#      define FMT_THROW(x) internal::do_throw(x)
 #    else
 #      define FMT_THROW(x) throw x
 #    endif
@@ -92,7 +97,7 @@ FMT_END_NAMESPACE
 #    define FMT_THROW(x)              \
       do {                            \
         static_cast<void>(sizeof(x)); \
-        assert(false);                \
+        FMT_ASSERT(false, "");        \
       } while (false)
 #  endif
 #endif
@@ -123,14 +128,6 @@ FMT_END_NAMESPACE
 #  endif
 #endif
 
-#ifdef FMT_USE_INT128
-// Do nothing.
-#elif defined(__SIZEOF_INT128__)
-#  define FMT_USE_INT128 1
-#else
-#  define FMT_USE_INT128 0
-#endif
-
 // __builtin_clz is broken in clang with Microsoft CodeGen:
 // https://github.com/fmtlib/fmt/issues/519
 #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER
@@ -156,14 +153,14 @@ inline uint32_t clz(uint32_t x) {
   unsigned long r = 0;
   _BitScanReverse(&r, x);
 
-  assert(x != 0);
+  FMT_ASSERT(x != 0, "");
   // Static analysis complains about using uninitialized data
   // "r", but the only way that can happen is if "x" is 0,
   // which the callers guarantee to not happen.
 #  pragma warning(suppress : 6102)
   return 31 - r;
 }
-#  define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n)
+#  define FMT_BUILTIN_CLZ(n) internal::clz(n)
 
 #  if defined(_WIN64) && !defined(__clang__)
 #    pragma intrinsic(_BitScanReverse64)
@@ -181,40 +178,88 @@ inline uint32_t clzll(uint64_t x) {
   _BitScanReverse(&r, static_cast<uint32_t>(x));
 #  endif
 
-  assert(x != 0);
+  FMT_ASSERT(x != 0, "");
   // Static analysis complains about using uninitialized data
   // "r", but the only way that can happen is if "x" is 0,
   // which the callers guarantee to not happen.
 #  pragma warning(suppress : 6102)
   return 63 - r;
 }
-#  define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n)
+#  define FMT_BUILTIN_CLZLL(n) internal::clzll(n)
 }  // namespace internal
 FMT_END_NAMESPACE
 #endif
 
+// Enable the deprecated numeric alignment.
+#ifndef FMT_NUMERIC_ALIGN
+#  define FMT_NUMERIC_ALIGN 1
+#endif
+
+// Enable the deprecated percent specifier.
+#ifndef FMT_DEPRECATED_PERCENT
+#  define FMT_DEPRECATED_PERCENT 0
+#endif
+
 FMT_BEGIN_NAMESPACE
 namespace internal {
 
+// A helper function to suppress bogus "conditional expression is constant"
+// warnings.
+template <typename T> inline T const_check(T value) { return value; }
+
+// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
+// undefined behavior (e.g. due to type aliasing).
+// Example: uint64_t d = bit_cast<uint64_t>(2.718);
+template <typename Dest, typename Source>
+inline Dest bit_cast(const Source& source) {
+  static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
+  Dest dest;
+  std::memcpy(&dest, &source, sizeof(dest));
+  return dest;
+}
+
+inline bool is_big_endian() {
+  auto u = 1u;
+  struct bytes {
+    char data[sizeof(u)];
+  };
+  return bit_cast<bytes>(u).data[0] == 0;
+}
+
 // A fallback implementation of uintptr_t for systems that lack it.
 struct fallback_uintptr {
   unsigned char value[sizeof(void*)];
+
+  fallback_uintptr() = default;
+  explicit fallback_uintptr(const void* p) {
+    *this = bit_cast<fallback_uintptr>(p);
+    if (is_big_endian()) {
+      for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j)
+        std::swap(value[i], value[j]);
+    }
+  }
 };
 #ifdef UINTPTR_MAX
 using uintptr_t = ::uintptr_t;
+inline uintptr_t to_uintptr(const void* p) { return bit_cast<uintptr_t>(p); }
 #else
 using uintptr_t = fallback_uintptr;
+inline fallback_uintptr to_uintptr(const void* p) {
+  return fallback_uintptr(p);
+}
 #endif
 
-// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't produce
-// undefined behavior (e.g. due to type aliasing).
-// Example: uint64_t d = bit_cast<uint64_t>(2.718);
-template <typename Dest, typename Source>
-inline Dest bit_cast(const Source& source) {
-  static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
-  Dest dest;
-  std::memcpy(&dest, &source, sizeof(dest));
-  return dest;
+// Returns the largest possible value for type T. Same as
+// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
+template <typename T> constexpr T max_value() {
+  return (std::numeric_limits<T>::max)();
+}
+template <typename T> constexpr int num_bits() {
+  return std::numeric_limits<T>::digits;
+}
+template <> constexpr int num_bits<fallback_uintptr>() {
+  return static_cast<int>(sizeof(void*) *
+                          std::numeric_limits<unsigned char>::digits);
 }
 
 // An approximation of iterator_t for pre-C++20 systems.
@@ -290,19 +335,21 @@ inline Iterator& reserve(Iterator& it, std::size_t) {
 
 // An output iterator that counts the number of objects written to it and
 // discards them.
-template <typename T> class counting_iterator {
+class counting_iterator {
  private:
   std::size_t count_;
-  mutable T blackhole_;
 
  public:
   using iterator_category = std::output_iterator_tag;
-  using value_type = T;
   using difference_type = std::ptrdiff_t;
-  using pointer = T*;
-  using reference = T&;
+  using pointer = void;
+  using reference = void;
   using _Unchecked_type = counting_iterator;  // Mark iterator as checked.
 
+  struct value_type {
+    template <typename T> void operator=(const T&) {}
+  };
+
   counting_iterator() : count_(0) {}
 
   std::size_t count() const { return count_; }
@@ -318,7 +365,7 @@ template <typename T> class counting_iterator {
     return it;
   }
 
-  T& operator*() const { return blackhole_; }
+  value_type operator*() const { return {}; }
 };
 
 template <typename OutputIt> class truncating_iterator_base {
@@ -413,17 +460,6 @@ class output_range {
   sentinel end() const { return {}; }  // Sentinel is not used yet.
 };
 
-// A range with an iterator appending to a buffer.
-template <typename T>
-class buffer_range
-    : public output_range<std::back_insert_iterator<buffer<T>>, T> {
- public:
-  using iterator = std::back_insert_iterator<buffer<T>>;
-  using output_range<iterator, T>::output_range;
-  buffer_range(buffer<T>& buf)
-      : output_range<iterator, T>(std::back_inserter(buf)) {}
-};
-
 template <typename Char>
 inline size_t count_code_points(basic_string_view<Char> s) {
   return s.size();
@@ -439,6 +475,24 @@ inline size_t count_code_points(basic_string_view<char8_t> s) {
   return num_code_points;
 }
 
+template <typename Char>
+inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
+  size_t size = s.size();
+  return n < size ? n : size;
+}
+
+// Calculates the index of the nth code point in a UTF-8 string.
+inline size_t code_point_index(basic_string_view<char8_t> s, size_t n) {
+  const char8_t* data = s.data();
+  size_t num_code_points = 0;
+  for (size_t i = 0, size = s.size(); i != size; ++i) {
+    if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) {
+      return i;
+    }
+  }
+  return s.size();
+}
+
 inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
 
 template <typename InputIt, typename OutChar>
@@ -460,7 +514,7 @@ OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
 }
 
 #ifndef FMT_USE_GRISU
-#  define FMT_USE_GRISU 0
+#  define FMT_USE_GRISU 1
 #endif
 
 template <typename T> constexpr bool use_grisu() {
@@ -478,6 +532,17 @@ void buffer<T>::append(const U* begin, const U* end) {
 }
 }  // namespace internal
 
+// A range with an iterator appending to a buffer.
+template <typename T>
+class buffer_range : public internal::output_range<
+                         std::back_insert_iterator<internal::buffer<T>>, T> {
+ public:
+  using iterator = std::back_insert_iterator<internal::buffer<T>>;
+  using internal::output_range<iterator, T>::output_range;
+  buffer_range(internal::buffer<T>& buf)
+      : internal::output_range<iterator, T>(std::back_inserter(buf)) {}
+};
+
 // A UTF-8 string view.
 class u8string_view : public basic_string_view<char8_t> {
  public:
@@ -552,7 +617,7 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
       : Allocator(alloc) {
     this->set(store_, SIZE);
   }
-  ~basic_memory_buffer() { deallocate(); }
+  ~basic_memory_buffer() FMT_OVERRIDE { deallocate(); }
 
  private:
   // Move data from other to this buffer.
@@ -581,15 +646,15 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
     of the other object to it.
     \endrst
    */
-  basic_memory_buffer(basic_memory_buffer&& other) { move(other); }
+  basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); }
 
   /**
     \rst
     Moves the content of the other ``basic_memory_buffer`` object to this one.
     \endrst
    */
-  basic_memory_buffer& operator=(basic_memory_buffer&& other) {
-    assert(this != &other);
+  basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT {
+    FMT_ASSERT(this != &other, "");
     deallocate();
     move(other);
     return *this;
@@ -628,7 +693,11 @@ class FMT_API format_error : public std::runtime_error {
   explicit format_error(const char* message) : std::runtime_error(message) {}
   explicit format_error(const std::string& message)
       : std::runtime_error(message) {}
-  ~format_error() FMT_NOEXCEPT;
+  format_error(const format_error&) = default;
+  format_error& operator=(const format_error&) = default;
+  format_error(format_error&&) = default;
+  format_error& operator=(format_error&&) = default;
+  ~format_error() FMT_NOEXCEPT FMT_OVERRIDE;
 };
 
 namespace internal {
@@ -644,11 +713,12 @@ FMT_CONSTEXPR bool is_negative(T) {
   return false;
 }
 
-// Smallest of uint32_t and uint64_t that is large enough to represent all
-// values of T.
+// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
+// represent all values of T.
 template <typename T>
-using uint32_or_64_t =
-    conditional_t<std::numeric_limits<T>::digits <= 32, uint32_t, uint64_t>;
+using uint32_or_64_or_128_t = conditional_t<
+    std::numeric_limits<T>::digits <= 32, uint32_t,
+    conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>;
 
 // Static data is placed in this class template for the header-only config.
 template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
@@ -663,6 +733,7 @@ template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
   static const char background_color[];
   static const char reset_color[5];
   static const wchar_t wreset_color[5];
+  static const char signs[];
 };
 
 FMT_EXTERN template struct basic_data<void>;
@@ -697,6 +768,23 @@ inline int count_digits(uint64_t n) {
 }
 #endif
 
+#if FMT_USE_INT128
+inline int count_digits(uint128_t n) {
+  int count = 1;
+  for (;;) {
+    // Integer division is slow so do it for a group of four digits instead
+    // of for every digit. The idea comes from the talk by Alexandrescu
+    // "Three Optimization Tips for C++". See speed-test for a comparison.
+    if (n < 10) return count;
+    if (n < 100) return count + 1;
+    if (n < 1000) return count + 2;
+    if (n < 10000) return count + 3;
+    n /= 10000U;
+    count += 4;
+  }
+}
+#endif
+
 // Counts the number of digits in n. BITS = log2(radix).
 template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
   int num_digits = 0;
@@ -708,69 +796,12 @@ template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
 
 template <> int count_digits<4>(internal::fallback_uintptr n);
 
-#if FMT_HAS_CPP_ATTRIBUTE(always_inline)
-#  define FMT_ALWAYS_INLINE __attribute__((always_inline))
+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#  define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
 #else
 #  define FMT_ALWAYS_INLINE
 #endif
 
-template <typename Handler>
-inline char* lg(uint32_t n, Handler h) FMT_ALWAYS_INLINE;
-
-// Computes g = floor(log10(n)) and calls h.on<g>(n);
-template <typename Handler> inline char* lg(uint32_t n, Handler h) {
-  return n < 100 ? n < 10 ? h.template on<0>(n) : h.template on<1>(n)
-                 : n < 1000000
-                       ? n < 10000 ? n < 1000 ? h.template on<2>(n)
-                                              : h.template on<3>(n)
-                                   : n < 100000 ? h.template on<4>(n)
-                                                : h.template on<5>(n)
-                       : n < 100000000 ? n < 10000000 ? h.template on<6>(n)
-                                                      : h.template on<7>(n)
-                                       : n < 1000000000 ? h.template on<8>(n)
-                                                        : h.template on<9>(n);
-}
-
-// An lg handler that formats a decimal number.
-// Usage: lg(n, decimal_formatter(buffer));
-class decimal_formatter {
- private:
-  char* buffer_;
-
-  void write_pair(unsigned N, uint32_t index) {
-    std::memcpy(buffer_ + N, data::digits + index * 2, 2);
-  }
-
- public:
-  explicit decimal_formatter(char* buf) : buffer_(buf) {}
-
-  template <unsigned N> char* on(uint32_t u) {
-    if (N == 0) {
-      *buffer_ = static_cast<char>(u) + '0';
-    } else if (N == 1) {
-      write_pair(0, u);
-    } else {
-      // The idea of using 4.32 fixed-point numbers is based on
-      // https://github.com/jeaiii/itoa
-      unsigned n = N - 1;
-      unsigned a = n / 5 * n * 53 / 16;
-      uint64_t t =
-          ((1ULL << (32 + a)) / data::zero_or_powers_of_10_32[n] + 1 - n / 9);
-      t = ((t * u) >> a) + n / 5 * 4;
-      write_pair(0, t >> 32);
-      for (unsigned i = 2; i < N; i += 2) {
-        t = 100ULL * static_cast<uint32_t>(t);
-        write_pair(i, t >> 32);
-      }
-      if (N % 2 == 0) {
-        buffer_[N] =
-            static_cast<char>((10ULL * static_cast<uint32_t>(t)) >> 32) + '0';
-      }
-    }
-    return buffer_ += N + 1;
-  }
-};
-
 #ifdef FMT_BUILTIN_CLZ
 // Optional version of count_digits for better performance on 32-bit platforms.
 inline int count_digits(uint32_t n) {
@@ -779,6 +810,14 @@ inline int count_digits(uint32_t n) {
 }
 #endif
 
+template <typename Char> FMT_API std::string grouping_impl(locale_ref loc);
+template <typename Char> inline std::string grouping(locale_ref loc) {
+  return grouping_impl<char>(loc);
+}
+template <> inline std::string grouping<wchar_t>(locale_ref loc) {
+  return grouping_impl<wchar_t>(loc);
+}
+
 template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc);
 template <typename Char> inline Char thousands_sep(locale_ref loc) {
   return Char(thousands_sep_impl<char>(loc));
@@ -808,7 +847,7 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
     // Integer division is slow so do it for a group of two digits instead
     // of for every digit. The idea comes from the talk by Alexandrescu
     // "Three Optimization Tips for C++". See speed-test for a comparison.
-    unsigned index = static_cast<unsigned>((value % 100) * 2);
+    auto index = static_cast<unsigned>((value % 100) * 2);
     value /= 100;
     *--buffer = static_cast<Char>(data::digits[index + 1]);
     add_thousands_sep(buffer);
@@ -819,20 +858,26 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
     *--buffer = static_cast<Char>('0' + value);
     return end;
   }
-  unsigned index = static_cast<unsigned>(value * 2);
+  auto index = static_cast<unsigned>(value * 2);
   *--buffer = static_cast<Char>(data::digits[index + 1]);
   add_thousands_sep(buffer);
   *--buffer = static_cast<Char>(data::digits[index]);
   return end;
 }
 
+template <typename Int> constexpr int digits10() noexcept {
+  return std::numeric_limits<Int>::digits10;
+}
+template <> constexpr int digits10<int128_t>() noexcept { return 38; }
+template <> constexpr int digits10<uint128_t>() noexcept { return 38; }
+
 template <typename Char, typename UInt, typename Iterator, typename F>
 inline Iterator format_decimal(Iterator out, UInt value, int num_digits,
                                F add_thousands_sep) {
   FMT_ASSERT(num_digits >= 0, "invalid digit count");
   // Buffer should be large enough to hold all digits (<= digits10 + 1).
-  enum { max_size = std::numeric_limits<UInt>::digits10 + 1 };
-  Char buffer[max_size + max_size / 3];
+  enum { max_size = digits10<UInt>() + 1 };
+  Char buffer[2 * max_size];
   auto end = format_decimal(buffer, value, num_digits, add_thousands_sep);
   return internal::copy_str<Char>(buffer, end, out);
 }
@@ -881,7 +926,7 @@ Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits,
 template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
 inline It format_uint(It out, UInt value, int num_digits, bool upper = false) {
   // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
-  char buffer[std::numeric_limits<UInt>::digits / BASE_BITS + 1];
+  char buffer[num_bits<UInt>() / BASE_BITS + 1];
   format_uint<BASE_BITS>(buffer, value, num_digits, upper);
   return internal::copy_str<Char>(buffer, buffer + num_digits, out);
 }
@@ -929,9 +974,8 @@ class utf16_to_utf8 {
   FMT_API int convert(wstring_view s);
 };
 
-FMT_API void format_windows_error(fmt::internal::buffer<char>& out,
-                                  int error_code,
-                                  fmt::string_view message) FMT_NOEXCEPT;
+FMT_API void format_windows_error(internal::buffer<char>& out, int error_code,
+                                  string_view message) FMT_NOEXCEPT;
 #endif
 
 template <typename T = void> struct null {};
@@ -991,9 +1035,29 @@ using format_specs = basic_format_specs<char>;
 
 namespace internal {
 
+// A floating-point presentation format.
+enum class float_format : unsigned char {
+  general,  // General: exponent notation or fixed point based on magnitude.
+  exp,      // Exponent notation with the default precision of 6, e.g. 1.2e-3.
+  fixed,    // Fixed point with the default precision of 6, e.g. 0.0012.
+  hex
+};
+
+struct float_specs {
+  int precision;
+  float_format format : 8;
+  sign_t sign : 8;
+  bool upper : 1;
+  bool locale : 1;
+  bool percent : 1;
+  bool binary32 : 1;
+  bool use_grisu : 1;
+  bool trailing_zeros : 1;
+};
+
 // Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
 template <typename Char, typename It> It write_exponent(int exp, It it) {
-  FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
+  FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
   if (exp < 0) {
     *it++ = static_cast<Char>('-');
     exp = -exp;
@@ -1001,7 +1065,9 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
     *it++ = static_cast<Char>('+');
   }
   if (exp >= 100) {
-    *it++ = static_cast<Char>(static_cast<char>('0' + exp / 100));
+    const char* top = data::digits + (exp / 100) * 2;
+    if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
+    *it++ = static_cast<Char>(top[1]);
     exp %= 100;
   }
   const char* d = data::digits + exp * 2;
@@ -1010,103 +1076,122 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
   return it;
 }
 
-struct gen_digits_params {
-  int num_digits;
-  bool fixed;
-  bool upper;
-  bool trailing_zeros;
-};
-
-// The number is given as v = digits * pow(10, exp).
-template <typename Char, typename It>
-It grisu_prettify(const char* digits, int size, int exp, It it,
-                  gen_digits_params params, Char decimal_point) {
-  // pow(10, full_exp - 1) <= v <= pow(10, full_exp).
-  int full_exp = size + exp;
-  if (!params.fixed) {
-    // Insert a decimal point after the first digit and add an exponent.
-    *it++ = static_cast<Char>(*digits);
-    if (size > 1) *it++ = decimal_point;
-    exp += size - 1;
-    it = copy_str<Char>(digits + 1, digits + size, it);
-    if (size < params.num_digits)
-      it = std::fill_n(it, params.num_digits - size, static_cast<Char>('0'));
-    *it++ = static_cast<Char>(params.upper ? 'E' : 'e');
-    return write_exponent<Char>(exp, it);
-  }
-  if (size <= full_exp) {
-    // 1234e7 -> 12340000000[.0+]
-    it = copy_str<Char>(digits, digits + size, it);
-    it = std::fill_n(it, full_exp - size, static_cast<Char>('0'));
-    int num_zeros = (std::max)(params.num_digits - full_exp, 1);
-    if (params.trailing_zeros) {
-      *it++ = decimal_point;
+template <typename Char> class float_writer {
+ private:
+  // The number is given as v = digits_ * pow(10, exp_).
+  const char* digits_;
+  int num_digits_;
+  int exp_;
+  size_t size_;
+  float_specs specs_;
+  Char decimal_point_;
+
+  template <typename It> It prettify(It it) const {
+    // pow(10, full_exp - 1) <= v <= pow(10, full_exp).
+    int full_exp = num_digits_ + exp_;
+    if (specs_.format == float_format::exp) {
+      // Insert a decimal point after the first digit and add an exponent.
+      *it++ = static_cast<Char>(*digits_);
+      int num_zeros = specs_.precision - num_digits_;
+      bool trailing_zeros = num_zeros > 0 && specs_.trailing_zeros;
+      if (num_digits_ > 1 || trailing_zeros) *it++ = decimal_point_;
+      it = copy_str<Char>(digits_ + 1, digits_ + num_digits_, it);
+      if (trailing_zeros)
+        it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+      *it++ = static_cast<Char>(specs_.upper ? 'E' : 'e');
+      return write_exponent<Char>(full_exp - 1, it);
+    }
+    if (num_digits_ <= full_exp) {
+      // 1234e7 -> 12340000000[.0+]
+      it = copy_str<Char>(digits_, digits_ + num_digits_, it);
+      it = std::fill_n(it, full_exp - num_digits_, static_cast<Char>('0'));
+      if (specs_.trailing_zeros) {
+        *it++ = decimal_point_;
+        int num_zeros = specs_.precision - full_exp;
+        if (num_zeros <= 0) {
+          if (specs_.format != float_format::fixed)
+            *it++ = static_cast<Char>('0');
+          return it;
+        }
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-      if (num_zeros > 1000)
-        throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
+        if (num_zeros > 1000)
+          throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
 #endif
-      it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
-    }
-  } else if (full_exp > 0) {
-    // 1234e-2 -> 12.34[0+]
-    it = copy_str<Char>(digits, digits + full_exp, it);
-    if (!params.trailing_zeros) {
-      // Remove trailing zeros.
-      while (size > full_exp && digits[size - 1] == '0') --size;
-      if (size != full_exp) *it++ = decimal_point;
-      return copy_str<Char>(digits + full_exp, digits + size, it);
-    }
-    *it++ = decimal_point;
-    it = copy_str<Char>(digits + full_exp, digits + size, it);
-    if (params.num_digits > size) {
-      // Add trailing zeros.
-      int num_zeros = params.num_digits - size;
-      it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
-    }
-  } else {
-    // 1234e-6 -> 0.001234
-    *it++ = static_cast<Char>('0');
-    int num_zeros = -full_exp;
-    if (params.num_digits >= 0 && params.num_digits < num_zeros)
-      num_zeros = params.num_digits;
-    if (!params.trailing_zeros)
-      while (size > 0 && digits[size - 1] == '0') --size;
-    if (num_zeros != 0 || size != 0) {
-      *it++ = decimal_point;
-      it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
-      it = copy_str<Char>(digits, digits + size, it);
+        it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+      }
+    } else if (full_exp > 0) {
+      // 1234e-2 -> 12.34[0+]
+      it = copy_str<Char>(digits_, digits_ + full_exp, it);
+      if (!specs_.trailing_zeros) {
+        // Remove trailing zeros.
+        int num_digits = num_digits_;
+        while (num_digits > full_exp && digits_[num_digits - 1] == '0')
+          --num_digits;
+        if (num_digits != full_exp) *it++ = decimal_point_;
+        return copy_str<Char>(digits_ + full_exp, digits_ + num_digits, it);
+      }
+      *it++ = decimal_point_;
+      it = copy_str<Char>(digits_ + full_exp, digits_ + num_digits_, it);
+      if (specs_.precision > num_digits_) {
+        // Add trailing zeros.
+        int num_zeros = specs_.precision - num_digits_;
+        it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+      }
+    } else {
+      // 1234e-6 -> 0.001234
+      *it++ = static_cast<Char>('0');
+      int num_zeros = -full_exp;
+      if (specs_.precision >= 0 && specs_.precision < num_zeros)
+        num_zeros = specs_.precision;
+      int num_digits = num_digits_;
+      if (!specs_.trailing_zeros)
+        while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits;
+      if (num_zeros != 0 || num_digits != 0) {
+        *it++ = decimal_point_;
+        it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+        it = copy_str<Char>(digits_, digits_ + num_digits, it);
+      }
     }
+    return it;
   }
-  return it;
-}
 
-namespace grisu_options {
-enum { fixed = 1, grisu3 = 2 };
-}
+ public:
+  float_writer(const char* digits, int num_digits, int exp, float_specs specs,
+               Char decimal_point)
+      : digits_(digits),
+        num_digits_(num_digits),
+        exp_(exp),
+        specs_(specs),
+        decimal_point_(decimal_point) {
+    int full_exp = num_digits + exp - 1;
+    int precision = specs.precision > 0 ? specs.precision : 16;
+    if (specs_.format == float_format::general &&
+        !(full_exp >= -4 && full_exp < precision)) {
+      specs_.format = float_format::exp;
+    }
+    size_ = prettify(counting_iterator()).count();
+    size_ += specs.sign ? 1 : 0;
+  }
 
-// Formats value using the Grisu algorithm:
-// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
-template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
-FMT_API bool grisu_format(Double, buffer<char>&, int, unsigned, int&);
-template <typename Double, FMT_ENABLE_IF(sizeof(Double) != sizeof(uint64_t))>
-inline bool grisu_format(Double, buffer<char>&, int, unsigned, int&) {
-  return false;
-}
+  size_t size() const { return size_; }
+  size_t width() const { return size(); }
 
-struct sprintf_specs {
-  int precision;
-  char type;
-  bool alt : 1;
+  template <typename It> void operator()(It&& it) {
+    if (specs_.sign) *it++ = static_cast<Char>(data::signs[specs_.sign]);
+    it = prettify(it);
+  }
+};
 
-  template <typename Char>
-  constexpr sprintf_specs(basic_format_specs<Char> specs)
-      : precision(specs.precision), type(specs.type), alt(specs.alt) {}
+template <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf);
 
-  constexpr bool has_precision() const { return precision >= 0; }
-};
+// Formats a floating-point number with snprintf.
+template <typename T>
+int snprintf_float(T value, int precision, float_specs specs,
+                   buffer<char>& buf);
 
-template <typename Double>
-char* sprintf_format(Double, internal::buffer<char>&, sprintf_specs);
+template <typename T> T promote_float(T value) { return value; }
+inline double promote_float(float value) { return value; }
 
 template <typename Handler>
 FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
@@ -1134,36 +1219,56 @@ FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
   }
 }
 
-template <typename Handler>
-FMT_CONSTEXPR void handle_float_type_spec(char spec, Handler&& handler) {
-  switch (spec) {
+template <typename ErrorHandler = error_handler, typename Char>
+FMT_CONSTEXPR float_specs parse_float_type_spec(
+    const basic_format_specs<Char>& specs, ErrorHandler&& eh = {}) {
+  auto result = float_specs();
+  result.trailing_zeros = specs.alt;
+  switch (specs.type) {
   case 0:
-  case 'g':
+    result.format = float_format::general;
+    result.trailing_zeros |= specs.precision != 0;
+    break;
   case 'G':
-    handler.on_general();
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case 'g':
+    result.format = float_format::general;
     break;
-  case 'e':
   case 'E':
-    handler.on_exp();
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case 'e':
+    result.format = float_format::exp;
+    result.trailing_zeros |= specs.precision != 0;
     break;
-  case 'f':
   case 'F':
-    handler.on_fixed();
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case 'f':
+    result.format = float_format::fixed;
+    result.trailing_zeros |= specs.precision != 0;
     break;
+#if FMT_DEPRECATED_PERCENT
   case '%':
-    handler.on_percent();
+    result.format = float_format::fixed;
+    result.percent = true;
     break;
-  case 'a':
+#endif
   case 'A':
-    handler.on_hex();
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case 'a':
+    result.format = float_format::hex;
     break;
   case 'n':
-    handler.on_num();
+    result.locale = true;
     break;
   default:
-    handler.on_error();
+    eh.on_error("invalid type specifier");
     break;
   }
+  return result;
 }
 
 template <typename Char, typename Handler>
@@ -1211,24 +1316,6 @@ template <typename ErrorHandler> class int_type_checker : private ErrorHandler {
   }
 };
 
-template <typename ErrorHandler>
-class float_type_checker : private ErrorHandler {
- public:
-  FMT_CONSTEXPR explicit float_type_checker(ErrorHandler eh)
-      : ErrorHandler(eh) {}
-
-  FMT_CONSTEXPR void on_general() {}
-  FMT_CONSTEXPR void on_exp() {}
-  FMT_CONSTEXPR void on_fixed() {}
-  FMT_CONSTEXPR void on_percent() {}
-  FMT_CONSTEXPR void on_hex() {}
-  FMT_CONSTEXPR void on_num() {}
-
-  FMT_CONSTEXPR void on_error() {
-    ErrorHandler::on_error("invalid type specifier");
-  }
-};
-
 template <typename ErrorHandler>
 class char_specs_checker : public ErrorHandler {
  private:
@@ -1271,6 +1358,20 @@ void arg_map<Context>::init(const basic_format_args<Context>& args) {
   }
 }
 
+template <typename Char> struct nonfinite_writer {
+  sign_t sign;
+  const char* str;
+  static constexpr size_t str_size = 3;
+
+  size_t size() const { return str_size + (sign ? 1 : 0); }
+  size_t width() const { return size(); }
+
+  template <typename It> void operator()(It&& it) const {
+    if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+    it = copy_str<Char>(str, str + str_size, it);
+  }
+};
+
 // This template provides operations for formatting and writing data into a
 // character range.
 template <typename Range> class basic_writer {
@@ -1281,7 +1382,7 @@ template <typename Range> class basic_writer {
 
  private:
   iterator out_;  // Output iterator.
-  internal::locale_ref locale_;
+  locale_ref locale_;
 
   // Attempts to reserve space for n extra characters in the output range.
   // Returns a pointer to the reserved range or a reference to out_.
@@ -1301,7 +1402,7 @@ template <typename Range> class basic_writer {
 
     template <typename It> void operator()(It&& it) const {
       if (prefix.size() != 0)
-        it = internal::copy_str<char_type>(prefix.begin(), prefix.end(), it);
+        it = copy_str<char_type>(prefix.begin(), prefix.end(), it);
       it = std::fill_n(it, padding, fill);
       f(it);
     }
@@ -1312,18 +1413,18 @@ template <typename Range> class basic_writer {
   // where <digits> are written by f(it).
   template <typename F>
   void write_int(int num_digits, string_view prefix, format_specs specs, F f) {
-    std::size_t size = prefix.size() + internal::to_unsigned(num_digits);
+    std::size_t size = prefix.size() + to_unsigned(num_digits);
     char_type fill = specs.fill[0];
     std::size_t padding = 0;
     if (specs.align == align::numeric) {
-      auto unsiged_width = internal::to_unsigned(specs.width);
+      auto unsiged_width = to_unsigned(specs.width);
       if (unsiged_width > size) {
         padding = unsiged_width - size;
         size = unsiged_width;
       }
     } else if (specs.precision > num_digits) {
-      size = prefix.size() + internal::to_unsigned(specs.precision);
-      padding = internal::to_unsigned(specs.precision - num_digits);
+      size = prefix.size() + to_unsigned(specs.precision);
+      padding = to_unsigned(specs.precision - num_digits);
       fill = static_cast<char_type>('0');
     }
     if (specs.align == align::none) specs.align = align::right;
@@ -1332,19 +1433,19 @@ template <typename Range> class basic_writer {
 
   // Writes a decimal integer.
   template <typename Int> void write_decimal(Int value) {
-    auto abs_value = static_cast<uint32_or_64_t<Int>>(value);
-    bool is_negative = internal::is_negative(value);
-    if (is_negative) abs_value = 0 - abs_value;
-    int num_digits = internal::count_digits(abs_value);
-    auto&& it =
-        reserve((is_negative ? 1 : 0) + static_cast<size_t>(num_digits));
-    if (is_negative) *it++ = static_cast<char_type>('-');
-    it = internal::format_decimal<char_type>(it, abs_value, num_digits);
+    auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value);
+    bool negative = is_negative(value);
+    // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
+    if (negative) abs_value = ~abs_value + 1;
+    int num_digits = count_digits(abs_value);
+    auto&& it = reserve((negative ? 1 : 0) + static_cast<size_t>(num_digits));
+    if (negative) *it++ = static_cast<char_type>('-');
+    it = format_decimal<char_type>(it, abs_value, num_digits);
   }
 
   // The handle_int_type_spec handler that writes an integer.
   template <typename Int, typename Specs> struct int_writer {
-    using unsigned_type = uint32_or_64_t<Int>;
+    using unsigned_type = uint32_or_64_or_128_t<Int>;
 
     basic_writer<Range>& writer;
     const Specs& specs;
@@ -1359,7 +1460,7 @@ template <typename Range> class basic_writer {
           specs(s),
           abs_value(static_cast<unsigned_type>(value)),
           prefix_size(0) {
-      if (internal::is_negative(value)) {
+      if (is_negative(value)) {
         prefix[0] = '-';
         ++prefix_size;
         abs_value = 0 - abs_value;
@@ -1379,7 +1480,7 @@ template <typename Range> class basic_writer {
     };
 
     void on_dec() {
-      int num_digits = internal::count_digits(abs_value);
+      int num_digits = count_digits(abs_value);
       writer.write_int(num_digits, get_prefix(), specs,
                        dec_writer{abs_value, num_digits});
     }
@@ -1389,8 +1490,8 @@ template <typename Range> class basic_writer {
       int num_digits;
 
       template <typename It> void operator()(It&& it) const {
-        it = internal::format_uint<4, char_type>(it, self.abs_value, num_digits,
-                                                 self.specs.type != 'x');
+        it = format_uint<4, char_type>(it, self.abs_value, num_digits,
+                                       self.specs.type != 'x');
       }
     };
 
@@ -1399,7 +1500,7 @@ template <typename Range> class basic_writer {
         prefix[prefix_size++] = '0';
         prefix[prefix_size++] = specs.type;
       }
-      int num_digits = internal::count_digits<4>(abs_value);
+      int num_digits = count_digits<4>(abs_value);
       writer.write_int(num_digits, get_prefix(), specs,
                        hex_writer{*this, num_digits});
     }
@@ -1409,7 +1510,7 @@ template <typename Range> class basic_writer {
       int num_digits;
 
       template <typename It> void operator()(It&& it) const {
-        it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
+        it = format_uint<BITS, char_type>(it, abs_value, num_digits);
       }
     };
 
@@ -1418,14 +1519,14 @@ template <typename Range> class basic_writer {
         prefix[prefix_size++] = '0';
         prefix[prefix_size++] = static_cast<char>(specs.type);
       }
-      int num_digits = internal::count_digits<1>(abs_value);
+      int num_digits = count_digits<1>(abs_value);
       writer.write_int(num_digits, get_prefix(), specs,
                        bin_writer<1>{abs_value, num_digits});
     }
 
     void on_oct() {
-      int num_digits = internal::count_digits<3>(abs_value);
-      if (specs.alt && specs.precision <= num_digits) {
+      int num_digits = count_digits<3>(abs_value);
+      if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
         // Octal prefix '0' is counted as a digit, so only add it if precision
         // is not greater than the number of digits.
         prefix[prefix_size++] = '0';
@@ -1439,30 +1540,50 @@ template <typename Range> class basic_writer {
     struct num_writer {
       unsigned_type abs_value;
       int size;
+      const std::string& groups;
       char_type sep;
 
       template <typename It> void operator()(It&& it) const {
         basic_string_view<char_type> s(&sep, sep_size);
         // Index of a decimal digit with the least significant digit having
         // index 0.
-        unsigned digit_index = 0;
-        it = internal::format_decimal<char_type>(
-            it, abs_value, size, [s, &digit_index](char_type*& buffer) {
-              if (++digit_index % 3 != 0) return;
+        int digit_index = 0;
+        std::string::const_iterator group = groups.cbegin();
+        it = format_decimal<char_type>(
+            it, abs_value, size,
+            [this, s, &group, &digit_index](char_type*& buffer) {
+              if (*group <= 0 || ++digit_index % *group != 0 ||
+                  *group == max_value<char>())
+                return;
+              if (group + 1 != groups.cend()) {
+                digit_index = 0;
+                ++group;
+              }
               buffer -= s.size();
               std::uninitialized_copy(s.data(), s.data() + s.size(),
-                                      internal::make_checked(buffer, s.size()));
+                                      make_checked(buffer, s.size()));
             });
       }
     };
 
     void on_num() {
-      char_type sep = internal::thousands_sep<char_type>(writer.locale_);
+      std::string groups = grouping<char_type>(writer.locale_);
+      if (groups.empty()) return on_dec();
+      auto sep = thousands_sep<char_type>(writer.locale_);
       if (!sep) return on_dec();
-      int num_digits = internal::count_digits(abs_value);
-      int size = num_digits + sep_size * ((num_digits - 1) / 3);
+      int num_digits = count_digits(abs_value);
+      int size = num_digits;
+      std::string::const_iterator group = groups.cbegin();
+      while (group != groups.cend() && num_digits > *group && *group > 0 &&
+             *group != max_value<char>()) {
+        size += sep_size;
+        num_digits -= *group;
+        ++group;
+      }
+      if (group == groups.cend())
+        size += sep_size * ((num_digits - 1) / groups.back());
       writer.write_int(size, get_prefix(), specs,
-                       num_writer{abs_value, size, sep});
+                       num_writer{abs_value, size, groups, sep});
     }
 
     FMT_NORETURN void on_error() {
@@ -1470,98 +1591,17 @@ template <typename Range> class basic_writer {
     }
   };
 
-  enum { inf_size = 3 };  // This is an enum to workaround a bug in MSVC.
-
-  struct inf_or_nan_writer {
-    char sign;
-    bool as_percentage;
-    const char* str;
-
-    size_t size() const {
-      return static_cast<std::size_t>(inf_size + (sign ? 1 : 0) +
-                                      (as_percentage ? 1 : 0));
-    }
-    size_t width() const { return size(); }
-
-    template <typename It> void operator()(It&& it) const {
-      if (sign) *it++ = static_cast<char_type>(sign);
-      it = internal::copy_str<char_type>(
-          str, str + static_cast<std::size_t>(inf_size), it);
-      if (as_percentage) *it++ = static_cast<char_type>('%');
-    }
-  };
-
-  struct double_writer {
-    char sign;
-    internal::buffer<char>& buffer;
-    char* decimal_point_pos;
-    char_type decimal_point;
-
-    size_t size() const { return buffer.size() + (sign ? 1 : 0); }
-    size_t width() const { return size(); }
-
-    template <typename It> void operator()(It&& it) {
-      if (sign) *it++ = static_cast<char_type>(sign);
-      auto begin = buffer.begin();
-      if (decimal_point_pos) {
-        it = internal::copy_str<char_type>(begin, decimal_point_pos, it);
-        *it++ = decimal_point;
-        begin = decimal_point_pos + 1;
-      }
-      it = internal::copy_str<char_type>(begin, buffer.end(), it);
-    }
-  };
-
-  class grisu_writer {
-   private:
-    internal::buffer<char>& digits_;
-    size_t size_;
-    char sign_;
-    int exp_;
-    internal::gen_digits_params params_;
-    char_type decimal_point_;
-
-   public:
-    grisu_writer(char sign, internal::buffer<char>& digits, int exp,
-                 const internal::gen_digits_params& params,
-                 char_type decimal_point)
-        : digits_(digits),
-          sign_(sign),
-          exp_(exp),
-          params_(params),
-          decimal_point_(decimal_point) {
-      int num_digits = static_cast<int>(digits.size());
-      int full_exp = num_digits + exp - 1;
-      int precision = params.num_digits > 0 ? params.num_digits : 11;
-      params_.fixed |= full_exp >= -4 && full_exp < precision;
-      auto it = internal::grisu_prettify<char>(
-          digits.data(), num_digits, exp, internal::counting_iterator<char>(),
-          params_, '.');
-      size_ = it.count();
-    }
-
-    size_t size() const { return size_ + (sign_ ? 1 : 0); }
-    size_t width() const { return size(); }
-
-    template <typename It> void operator()(It&& it) {
-      if (sign_) *it++ = static_cast<char_type>(sign_);
-      int num_digits = static_cast<int>(digits_.size());
-      it = internal::grisu_prettify<char_type>(digits_.data(), num_digits, exp_,
-                                               it, params_, decimal_point_);
-    }
-  };
-
   template <typename Char> struct str_writer {
     const Char* s;
     size_t size_;
 
     size_t size() const { return size_; }
     size_t width() const {
-      return internal::count_code_points(basic_string_view<Char>(s, size_));
+      return count_code_points(basic_string_view<Char>(s, size_));
     }
 
     template <typename It> void operator()(It&& it) const {
-      it = internal::copy_str<char_type>(s, s + size_, it);
+      it = copy_str<char_type>(s, s + size_, it);
     }
   };
 
@@ -1575,14 +1615,12 @@ template <typename Range> class basic_writer {
     template <typename It> void operator()(It&& it) const {
       *it++ = static_cast<char_type>('0');
       *it++ = static_cast<char_type>('x');
-      it = internal::format_uint<4, char_type>(it, value, num_digits);
+      it = format_uint<4, char_type>(it, value, num_digits);
     }
   };
 
  public:
-  /** Constructs a ``basic_writer`` object. */
-  explicit basic_writer(Range out,
-                        internal::locale_ref loc = internal::locale_ref())
+  explicit basic_writer(Range out, locale_ref loc = locale_ref())
       : out_(out.begin()), locale_(loc) {}
 
   iterator out() const { return out_; }
@@ -1621,32 +1659,70 @@ template <typename Range> class basic_writer {
   void write(unsigned long value) { write_decimal(value); }
   void write(unsigned long long value) { write_decimal(value); }
 
-  // Writes a formatted integer.
+#if FMT_USE_INT128
+  void write(int128_t value) { write_decimal(value); }
+  void write(uint128_t value) { write_decimal(value); }
+#endif
+
   template <typename T, typename Spec>
   void write_int(T value, const Spec& spec) {
-    internal::handle_int_type_spec(spec.type,
-                                   int_writer<T, Spec>(*this, value, spec));
+    handle_int_type_spec(spec.type, int_writer<T, Spec>(*this, value, spec));
   }
 
-  void write(double value, const format_specs& specs = format_specs()) {
-    write_double(value, specs);
-  }
+  template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+  void write(T value, format_specs specs = {}) {
+    float_specs fspecs = parse_float_type_spec(specs);
+    fspecs.sign = specs.sign;
+    if (std::signbit(value)) {  // value < 0 is false for NaN so use signbit.
+      fspecs.sign = sign::minus;
+      value = -value;
+    } else if (fspecs.sign == sign::minus) {
+      fspecs.sign = sign::none;
+    }
 
-  /**
-    \rst
-    Formats *value* using the general format for floating-point numbers
-    (``'g'``) and writes it to the buffer.
-    \endrst
-   */
-  void write(long double value, const format_specs& specs = format_specs()) {
-    write_double(value, specs);
-  }
+    if (!std::isfinite(value)) {
+      auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf")
+                                   : (fspecs.upper ? "NAN" : "nan");
+      return write_padded(specs, nonfinite_writer<char_type>{fspecs.sign, str});
+    }
 
-  // Formats a floating-point number (double or long double).
-  template <typename T, bool USE_GRISU = fmt::internal::use_grisu<T>()>
-  void write_double(T value, const format_specs& specs);
+    if (specs.align == align::none) {
+      specs.align = align::right;
+    } else if (specs.align == align::numeric) {
+      if (fspecs.sign) {
+        auto&& it = reserve(1);
+        *it++ = static_cast<char_type>(data::signs[fspecs.sign]);
+        fspecs.sign = sign::none;
+        if (specs.width != 0) --specs.width;
+      }
+      specs.align = align::right;
+    }
+
+    memory_buffer buffer;
+    if (fspecs.format == float_format::hex) {
+      if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
+      snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
+      write_padded(specs, str_writer<char>{buffer.data(), buffer.size()});
+      return;
+    }
+    int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
+    if (fspecs.format == float_format::exp) ++precision;
+    if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+    fspecs.use_grisu = use_grisu<T>();
+    if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100;
+    int exp = format_float(promote_float(value), precision, fspecs, buffer);
+    if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) {
+      buffer.push_back('%');
+      --exp;  // Adjust decimal place position.
+    }
+    fspecs.precision = precision;
+    char_type point = fspecs.locale ? decimal_point<char_type>(locale_)
+                                    : static_cast<char_type>('.');
+    write_padded(specs, float_writer<char_type>(buffer.data(),
+                                                static_cast<int>(buffer.size()),
+                                                exp, fspecs, point));
+  }
 
-  /** Writes a character to the buffer. */
   void write(char value) {
     auto&& it = reserve(1);
     *it++ = value;
@@ -1658,14 +1734,9 @@ template <typename Range> class basic_writer {
     *it++ = value;
   }
 
-  /**
-    \rst
-    Writes *value* to the buffer.
-    \endrst
-   */
   void write(string_view value) {
     auto&& it = reserve(value.size());
-    it = internal::copy_str<char_type>(value.begin(), value.end(), it);
+    it = copy_str<char_type>(value.begin(), value.end(), it);
   }
   void write(wstring_view value) {
     static_assert(std::is_same<char_type, wchar_t>::value, "");
@@ -1673,25 +1744,23 @@ template <typename Range> class basic_writer {
     it = std::copy(value.begin(), value.end(), it);
   }
 
-  // Writes a formatted string.
   template <typename Char>
   void write(const Char* s, std::size_t size, const format_specs& specs) {
     write_padded(specs, str_writer<Char>{s, size});
   }
 
   template <typename Char>
-  void write(basic_string_view<Char> s,
-             const format_specs& specs = format_specs()) {
+  void write(basic_string_view<Char> s, const format_specs& specs = {}) {
     const Char* data = s.data();
     std::size_t size = s.size();
-    if (specs.precision >= 0 && internal::to_unsigned(specs.precision) < size)
-      size = internal::to_unsigned(specs.precision);
+    if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
+      size = code_point_index(s, to_unsigned(specs.precision));
     write(data, size, specs);
   }
 
   template <typename UIntPtr>
   void write_pointer(UIntPtr value, const format_specs* specs) {
-    int num_digits = internal::count_digits<4>(value);
+    int num_digits = count_digits<4>(value);
     auto pw = pointer_writer<UIntPtr>{value, num_digits};
     if (!specs) return pw(reserve(to_unsigned(num_digits) + 2));
     format_specs specs_copy = *specs;
@@ -1702,6 +1771,10 @@ template <typename Range> class basic_writer {
 
 using writer = basic_writer<buffer_range<char>>;
 
+template <typename T> struct is_integral : std::is_integral<T> {};
+template <> struct is_integral<int128_t> : std::true_type {};
+template <> struct is_integral<uint128_t> : std::true_type {};
+
 template <typename Range, typename ErrorHandler = internal::error_handler>
 class arg_formatter_base {
  public:
@@ -1731,7 +1804,7 @@ class arg_formatter_base {
   }
 
   void write_pointer(const void* p) {
-    writer_.write_pointer(internal::bit_cast<internal::uintptr_t>(p), specs_);
+    writer_.write_pointer(internal::to_uintptr(p), specs_);
   }
 
  protected:
@@ -1764,7 +1837,7 @@ class arg_formatter_base {
     return out();
   }
 
-  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
   iterator operator()(T value) {
     if (specs_)
       writer_.write_int(value, *specs_);
@@ -1787,7 +1860,7 @@ class arg_formatter_base {
 
   template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
   iterator operator()(T value) {
-    writer_.write_double(value, specs_ ? *specs_ : format_specs());
+    writer_.write(value, specs_ ? *specs_ : format_specs());
     return out();
   }
 
@@ -1852,14 +1925,14 @@ template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
 template <typename Char, typename ErrorHandler>
 FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
                                         ErrorHandler&& eh) {
-  assert(begin != end && '0' <= *begin && *begin <= '9');
+  FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
   if (*begin == '0') {
     ++begin;
     return 0;
   }
   unsigned value = 0;
   // Convert to unsigned to prevent a warning.
-  constexpr unsigned max_int = (std::numeric_limits<int>::max)();
+  constexpr unsigned max_int = max_value<int>();
   unsigned big = max_int / 10;
   do {
     // Check for overflow.
@@ -1878,11 +1951,11 @@ template <typename Context> class custom_formatter {
  private:
   using char_type = typename Context::char_type;
 
-  basic_parse_context<char_type>& parse_ctx_;
+  basic_format_parse_context<char_type>& parse_ctx_;
   Context& ctx_;
 
  public:
-  explicit custom_formatter(basic_parse_context<char_type>& parse_ctx,
+  explicit custom_formatter(basic_format_parse_context<char_type>& parse_ctx,
                             Context& ctx)
       : parse_ctx_(parse_ctx), ctx_(ctx) {}
 
@@ -1896,7 +1969,7 @@ template <typename Context> class custom_formatter {
 
 template <typename T>
 using is_integer =
-    bool_constant<std::is_integral<T>::value && !std::is_same<T, bool>::value &&
+    bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
                   !std::is_same<T, char>::value &&
                   !std::is_same<T, wchar_t>::value>;
 
@@ -1981,20 +2054,20 @@ template <typename ErrorHandler> class numeric_specs_checker {
       : error_handler_(eh), arg_type_(arg_type) {}
 
   FMT_CONSTEXPR void require_numeric_argument() {
-    if (!is_arithmetic(arg_type_))
+    if (!is_arithmetic_type(arg_type_))
       error_handler_.on_error("format specifier requires numeric argument");
   }
 
   FMT_CONSTEXPR void check_sign() {
     require_numeric_argument();
-    if (is_integral(arg_type_) && arg_type_ != int_type &&
+    if (is_integral_type(arg_type_) && arg_type_ != int_type &&
         arg_type_ != long_long_type && arg_type_ != internal::char_type) {
       error_handler_.on_error("format specifier requires signed argument");
     }
   }
 
   FMT_CONSTEXPR void check_precision() {
-    if (is_integral(arg_type_) || arg_type_ == internal::pointer_type)
+    if (is_integral_type(arg_type_) || arg_type_ == internal::pointer_type)
       error_handler_.on_error("precision not allowed for this argument type");
   }
 
@@ -2049,14 +2122,12 @@ template <typename Handler> class specs_checker : public Handler {
   numeric_specs_checker<Handler> checker_;
 };
 
-template <template <typename> class Handler, typename T, typename FormatArg,
+template <template <typename> class Handler, typename FormatArg,
           typename ErrorHandler>
-FMT_CONSTEXPR void set_dynamic_spec(T& value, FormatArg arg, ErrorHandler eh) {
-  unsigned long long big_value =
-      visit_format_arg(Handler<ErrorHandler>(eh), arg);
-  if (big_value > to_unsigned((std::numeric_limits<int>::max)()))
-    eh.on_error("number is too big");
-  value = static_cast<T>(big_value);
+FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
+  unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
+  if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
+  return static_cast<int>(value);
 }
 
 struct auto_id {};
@@ -2081,13 +2152,13 @@ class specs_handler : public specs_setter<typename Context::char_type> {
         context_(ctx) {}
 
   template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
-    set_dynamic_spec<width_checker>(this->specs_.width, get_arg(arg_id),
-                                    context_.error_handler());
+    this->specs_.width = get_dynamic_spec<width_checker>(
+        get_arg(arg_id), context_.error_handler());
   }
 
   template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
-    set_dynamic_spec<precision_checker>(this->specs_.precision, get_arg(arg_id),
-                                        context_.error_handler());
+    this->specs_.precision = get_dynamic_spec<precision_checker>(
+        get_arg(arg_id), context_.error_handler());
   }
 
   void on_error(const char* message) { context_.on_error(message); }
@@ -2114,24 +2185,6 @@ class specs_handler : public specs_setter<typename Context::char_type> {
   Context& context_;
 };
 
-struct string_view_metadata {
-  FMT_CONSTEXPR string_view_metadata() : offset_(0u), size_(0u) {}
-  template <typename Char>
-  FMT_CONSTEXPR string_view_metadata(basic_string_view<Char> primary_string,
-                                     basic_string_view<Char> view)
-      : offset_(to_unsigned(view.data() - primary_string.data())),
-        size_(view.size()) {}
-  FMT_CONSTEXPR string_view_metadata(std::size_t offset, std::size_t size)
-      : offset_(offset), size_(size) {}
-  template <typename Char>
-  FMT_CONSTEXPR basic_string_view<Char> to_view(const Char* str) const {
-    return {str + offset_, size_};
-  }
-
-  std::size_t offset_;
-  std::size_t size_;
-};
-
 enum class arg_id_kind { none, index, name };
 
 // An argument reference.
@@ -2139,7 +2192,7 @@ template <typename Char> struct arg_ref {
   FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
   FMT_CONSTEXPR explicit arg_ref(int index)
       : kind(arg_id_kind::index), val(index) {}
-  FMT_CONSTEXPR explicit arg_ref(string_view_metadata name)
+  FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
       : kind(arg_id_kind::name), val(name) {}
 
   FMT_CONSTEXPR arg_ref& operator=(int idx) {
@@ -2150,12 +2203,11 @@ template <typename Char> struct arg_ref {
 
   arg_id_kind kind;
   union value {
-    FMT_CONSTEXPR value() : index(0u) {}
-    FMT_CONSTEXPR value(int id) : index(id) {}
-    FMT_CONSTEXPR value(string_view_metadata n) : name(n) {}
+    FMT_CONSTEXPR value(int id = 0) : index{id} {}
+    FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
 
     int index;
-    string_view_metadata name;
+    basic_string_view<Char> name;
   } val;
 };
 
@@ -2213,8 +2265,7 @@ class dynamic_specs_handler
     context_.check_arg_id(arg_id);
     basic_string_view<char_type> format_str(
         context_.begin(), to_unsigned(context_.end() - context_.begin()));
-    const auto id_metadata = string_view_metadata(format_str, arg_id);
-    return arg_ref_type(id_metadata);
+    return arg_ref_type(arg_id);
   }
 
   dynamic_format_specs<char_type>& specs_;
@@ -2224,18 +2275,24 @@ class dynamic_specs_handler
 template <typename Char, typename IDHandler>
 FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
                                        IDHandler&& handler) {
-  assert(begin != end);
+  FMT_ASSERT(begin != end, "");
   Char c = *begin;
-  if (c == '}' || c == ':') return handler(), begin;
+  if (c == '}' || c == ':') {
+    handler();
+    return begin;
+  }
   if (c >= '0' && c <= '9') {
     int index = parse_nonnegative_int(begin, end, handler);
     if (begin == end || (*begin != '}' && *begin != ':'))
-      return handler.on_error("invalid format string"), begin;
-    handler(index);
+      handler.on_error("invalid format string");
+    else
+      handler(index);
+    return begin;
+  }
+  if (!is_name_start(c)) {
+    handler.on_error("invalid format string");
     return begin;
   }
-  if (!is_name_start(c))
-    return handler.on_error("invalid format string"), begin;
   auto it = begin;
   do {
     ++it;
@@ -2294,9 +2351,11 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
     case '>':
       align = align::right;
       break;
+#if FMT_NUMERIC_ALIGN
     case '=':
       align = align::numeric;
       break;
+#endif
     case '^':
       align = align::center;
       break;
@@ -2439,7 +2498,7 @@ template <typename Handler, typename Char> struct id_adapter {
 template <bool IS_CONSTEXPR, typename Char, typename Handler>
 FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
                                        Handler&& handler) {
-  struct writer {
+  struct pfs_writer {
     FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
       if (begin == end) return;
       for (;;) {
@@ -2497,10 +2556,9 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
       conditional_t<internal::mapped_type_constant<T, context>::value !=
                         internal::custom_type,
                     decltype(arg_mapper<context>().map(std::declval<T>())), T>;
-  conditional_t<has_formatter<mapped_type, context>::value,
-                formatter<mapped_type, char_type>,
-                internal::fallback_formatter<T, char_type>>
-      f;
+  auto f = conditional_t<has_formatter<mapped_type, context>::value,
+                         formatter<mapped_type, char_type>,
+                         internal::fallback_formatter<T, char_type>>();
   return f.parse(ctx);
 }
 
@@ -2509,7 +2567,7 @@ class format_string_checker {
  public:
   explicit FMT_CONSTEXPR format_string_checker(
       basic_string_view<Char> format_str, ErrorHandler eh)
-      : arg_id_((std::numeric_limits<unsigned>::max)()),
+      : arg_id_(-1),
         context_(format_str, eh),
         parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
 
@@ -2540,7 +2598,7 @@ class format_string_checker {
   }
 
  private:
-  using parse_context_type = basic_parse_context<Char, ErrorHandler>;
+  using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
   enum { num_args = sizeof...(Args) };
 
   FMT_CONSTEXPR void check_arg_id() {
@@ -2550,7 +2608,7 @@ class format_string_checker {
   // Format specifier parsing function.
   using parse_func = const Char* (*)(parse_context_type&);
 
-  unsigned arg_id_;
+  int arg_id_;
   parse_context_type context_;
   parse_func parse_funcs_[num_args > 0 ? num_args : 1];
 };
@@ -2573,32 +2631,29 @@ void check_format_string(S format_str) {
   (void)invalid_format;
 }
 
-template <template <typename> class Handler, typename Spec, typename Context>
-void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref,
-                         Context& ctx,
-                         const typename Context::char_type* format_str) {
+template <template <typename> class Handler, typename Context>
+void handle_dynamic_spec(int& value, arg_ref<typename Context::char_type> ref,
+                         Context& ctx) {
   switch (ref.kind) {
   case arg_id_kind::none:
     break;
   case arg_id_kind::index:
-    internal::set_dynamic_spec<Handler>(value, ctx.arg(ref.val.index),
-                                        ctx.error_handler());
+    value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+                                                ctx.error_handler());
     break;
-  case arg_id_kind::name: {
-    const auto arg_id = ref.val.name.to_view(format_str);
-    internal::set_dynamic_spec<Handler>(value, ctx.arg(arg_id),
-                                        ctx.error_handler());
+  case arg_id_kind::name:
+    value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+                                                ctx.error_handler());
     break;
   }
-  }
 }
 }  // namespace internal
 
 template <typename Range>
-using basic_writer FMT_DEPRECATED = internal::basic_writer<Range>;
-using writer FMT_DEPRECATED = internal::writer;
-using wwriter FMT_DEPRECATED =
-    internal::basic_writer<internal::buffer_range<wchar_t>>;
+using basic_writer FMT_DEPRECATED_ALIAS = internal::basic_writer<Range>;
+using writer FMT_DEPRECATED_ALIAS = internal::writer;
+using wwriter FMT_DEPRECATED_ALIAS =
+    internal::basic_writer<buffer_range<wchar_t>>;
 
 /** The default argument formatter. */
 template <typename Range>
@@ -2609,7 +2664,7 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
   using context_type = basic_format_context<typename base::iterator, char_type>;
 
   context_type& ctx_;
-  basic_parse_context<char_type>* parse_ctx_;
+  basic_format_parse_context<char_type>* parse_ctx_;
 
  public:
   using range = Range;
@@ -2623,9 +2678,10 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
     *specs* contains format specifier information for standard argument types.
     \endrst
    */
-  explicit arg_formatter(context_type& ctx,
-                         basic_parse_context<char_type>* parse_ctx = nullptr,
-                         format_specs* specs = nullptr)
+  explicit arg_formatter(
+      context_type& ctx,
+      basic_format_parse_context<char_type>* parse_ctx = nullptr,
+      format_specs* specs = nullptr)
       : base(Range(ctx.out()), specs, ctx.locale()),
         ctx_(ctx),
         parse_ctx_(parse_ctx) {}
@@ -2635,7 +2691,7 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
   /** Formats an argument of a user-defined type. */
   iterator operator()(typename basic_format_arg<context_type>::handle handle) {
     handle.format(*parse_ctx_, ctx_);
-    return this->out();
+    return ctx_.out();
   }
 };
 
@@ -2676,7 +2732,11 @@ class FMT_API system_error : public std::runtime_error {
       : std::runtime_error("") {
     init(error_code, message, make_format_args(args...));
   }
-  ~system_error() FMT_NOEXCEPT;
+  system_error(const system_error&) = default;
+  system_error& operator=(const system_error&) = default;
+  system_error(system_error&&) = default;
+  system_error& operator=(system_error&&) = default;
+  ~system_error() FMT_NOEXCEPT FMT_OVERRIDE;
 
   int error_code() const { return error_code_; }
 };
@@ -2698,126 +2758,7 @@ class FMT_API system_error : public std::runtime_error {
   \endrst
  */
 FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
-                                 fmt::string_view message) FMT_NOEXCEPT;
-
-struct float_spec_handler {
-  char type;
-  bool upper;
-  bool fixed;
-  bool as_percentage;
-  bool use_locale;
-
-  explicit float_spec_handler(char t)
-      : type(t),
-        upper(false),
-        fixed(false),
-        as_percentage(false),
-        use_locale(false) {}
-
-  void on_general() {
-    if (type == 'G') upper = true;
-  }
-
-  void on_exp() {
-    if (type == 'E') upper = true;
-  }
-
-  void on_fixed() {
-    fixed = true;
-    if (type == 'F') upper = true;
-  }
-
-  void on_percent() {
-    fixed = true;
-    as_percentage = true;
-  }
-
-  void on_hex() {
-    if (type == 'A') upper = true;
-  }
-
-  void on_num() { use_locale = true; }
-
-  FMT_NORETURN void on_error() {
-    FMT_THROW(format_error("invalid type specifier"));
-  }
-};
-
-template <typename Range>
-template <typename T, bool USE_GRISU>
-void internal::basic_writer<Range>::write_double(T value,
-                                                 const format_specs& specs) {
-  // Check type.
-  float_spec_handler handler(static_cast<char>(specs.type));
-  internal::handle_float_type_spec(handler.type, handler);
-
-  char sign = 0;
-  // Use signbit instead of value < 0 since the latter is always false for NaN.
-  if (std::signbit(value)) {
-    sign = '-';
-    value = -value;
-  } else if (specs.sign != sign::none) {
-    if (specs.sign == sign::plus)
-      sign = '+';
-    else if (specs.sign == sign::space)
-      sign = ' ';
-  }
-
-  if (!std::isfinite(value)) {
-    // Format infinity and NaN ourselves because sprintf's output is not
-    // consistent across platforms.
-    const char* str = std::isinf(value) ? (handler.upper ? "INF" : "inf")
-                                        : (handler.upper ? "NAN" : "nan");
-    return write_padded(specs,
-                        inf_or_nan_writer{sign, handler.as_percentage, str});
-  }
-
-  if (handler.as_percentage) value *= 100;
-
-  memory_buffer buffer;
-  int exp = 0;
-  int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
-  unsigned options = handler.fixed ? internal::grisu_options::fixed : 0;
-  bool use_grisu = USE_GRISU &&
-                   (specs.type != 'a' && specs.type != 'A' &&
-                    specs.type != 'e' && specs.type != 'E') &&
-                   internal::grisu_format(static_cast<double>(value), buffer,
-                                          precision, options, exp);
-  char* decimal_point_pos = nullptr;
-  if (!use_grisu)
-    decimal_point_pos = internal::sprintf_format(value, buffer, specs);
-
-  if (handler.as_percentage) {
-    buffer.push_back('%');
-    --exp;  // Adjust decimal place position.
-  }
-  format_specs as = specs;
-  if (specs.align == align::numeric) {
-    if (sign) {
-      auto&& it = reserve(1);
-      *it++ = static_cast<char_type>(sign);
-      sign = 0;
-      if (as.width) --as.width;
-    }
-    as.align = align::right;
-  } else if (specs.align == align::none) {
-    as.align = align::right;
-  }
-  char_type decimal_point = handler.use_locale
-                                ? internal::decimal_point<char_type>(locale_)
-                                : static_cast<char_type>('.');
-  if (use_grisu) {
-    auto params = internal::gen_digits_params();
-    params.fixed = handler.fixed;
-    params.num_digits = precision;
-    params.trailing_zeros =
-        (precision != 0 && (handler.fixed || !specs.type)) || specs.alt;
-    write_padded(as, grisu_writer(sign, buffer, exp, params, decimal_point));
-  } else {
-    write_padded(as,
-                 double_writer{sign, buffer, decimal_point_pos, decimal_point});
-  }
-}
+                                 string_view message) FMT_NOEXCEPT;
 
 // Reports a system error without throwing an exception.
 // Can be used to report errors from destructors.
@@ -2889,7 +2830,7 @@ class format_int {
       // Integer division is slow so do it for a group of two digits instead
       // of for every digit. The idea comes from the talk by Alexandrescu
       // "Three Optimization Tips for C++". See speed-test for a comparison.
-      unsigned index = static_cast<unsigned>((value % 100) * 2);
+      auto index = static_cast<unsigned>((value % 100) * 2);
       value /= 100;
       *--ptr = internal::data::digits[index + 1];
       *--ptr = internal::data::digits[index];
@@ -2898,14 +2839,14 @@ class format_int {
       *--ptr = static_cast<char>('0' + value);
       return ptr;
     }
-    unsigned index = static_cast<unsigned>(value * 2);
+    auto index = static_cast<unsigned>(value * 2);
     *--ptr = internal::data::digits[index + 1];
     *--ptr = internal::data::digits[index];
     return ptr;
   }
 
   void format_signed(long long value) {
-    unsigned long long abs_value = static_cast<unsigned long long>(value);
+    auto abs_value = static_cast<unsigned long long>(value);
     bool negative = value < 0;
     if (negative) abs_value = 0 - abs_value;
     str_ = format_decimal(abs_value);
@@ -2954,13 +2895,12 @@ template <typename T, typename Char>
 struct formatter<T, Char,
                  enable_if_t<internal::type_constant<T, Char>::value !=
                              internal::custom_type>> {
-  FMT_CONSTEXPR formatter() : format_str_(nullptr) {}
+  FMT_CONSTEXPR formatter() = default;
 
   // Parses format specifiers stopping either at the end of the range or at the
   // terminating '}'.
   template <typename ParseContext>
   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    format_str_ = ctx.begin();
     using handler_type = internal::dynamic_specs_handler<ParseContext>;
     auto type = internal::type_constant<T, Char>::value;
     internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
@@ -2976,6 +2916,8 @@ struct formatter<T, Char,
     case internal::uint_type:
     case internal::long_long_type:
     case internal::ulong_long_type:
+    case internal::int128_type:
+    case internal::uint128_type:
     case internal::bool_type:
       handle_int_type_spec(specs_.type,
                            internal::int_type_checker<decltype(eh)>(eh));
@@ -2984,10 +2926,10 @@ struct formatter<T, Char,
       handle_char_specs(
           &specs_, internal::char_specs_checker<decltype(eh)>(specs_.type, eh));
       break;
+    case internal::float_type:
     case internal::double_type:
     case internal::long_double_type:
-      handle_float_type_spec(specs_.type,
-                             internal::float_type_checker<decltype(eh)>(eh));
+      internal::parse_float_type_spec(specs_, eh);
       break;
     case internal::cstring_type:
       internal::handle_cstring_type_spec(
@@ -3010,9 +2952,9 @@ struct formatter<T, Char,
   template <typename FormatContext>
   auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
     internal::handle_dynamic_spec<internal::width_checker>(
-        specs_.width, specs_.width_ref, ctx, format_str_);
+        specs_.width, specs_.width_ref, ctx);
     internal::handle_dynamic_spec<internal::precision_checker>(
-        specs_.precision, specs_.precision_ref, ctx, format_str_);
+        specs_.precision, specs_.precision_ref, ctx);
     using range_type =
         internal::output_range<typename FormatContext::iterator,
                                typename FormatContext::char_type>;
@@ -3022,7 +2964,6 @@ struct formatter<T, Char,
 
  private:
   internal::dynamic_format_specs<Char> specs_;
-  const Char* format_str_;
 };
 
 #define FMT_FORMAT_AS(Type, Base)                                             \
@@ -3040,7 +2981,6 @@ FMT_FORMAT_AS(short, int);
 FMT_FORMAT_AS(unsigned short, unsigned);
 FMT_FORMAT_AS(long, long long);
 FMT_FORMAT_AS(unsigned long, unsigned long long);
-FMT_FORMAT_AS(float, double);
 FMT_FORMAT_AS(Char*, const Char*);
 FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
 FMT_FORMAT_AS(std::nullptr_t, const void*);
@@ -3123,9 +3063,9 @@ template <typename Char = char> class dynamic_formatter {
  private:
   template <typename Context> void handle_specs(Context& ctx) {
     internal::handle_dynamic_spec<internal::width_checker>(
-        specs_.width, specs_.width_ref, ctx, format_str_);
+        specs_.width, specs_.width_ref, ctx);
     internal::handle_dynamic_spec<internal::precision_checker>(
-        specs_.precision, specs_.precision_ref, ctx, format_str_);
+        specs_.precision, specs_.precision_ref, ctx);
   }
 
   internal::dynamic_format_specs<Char> specs_;
@@ -3142,8 +3082,8 @@ basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) {
 }
 
 template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void advance_to(basic_parse_context<Char, ErrorHandler>& ctx,
-                              const Char* p) {
+FMT_CONSTEXPR void advance_to(
+    basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
   ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
 }
 
@@ -3175,10 +3115,8 @@ struct format_handler : internal::error_handler {
 
   void on_replacement_field(const Char* p) {
     advance_to(parse_context, p);
-    internal::custom_formatter<Context> f(parse_context, context);
-    if (!visit_format_arg(f, arg))
-      context.advance_to(
-          visit_format_arg(ArgFormatter(context, &parse_context), arg));
+    context.advance_to(
+        visit_format_arg(ArgFormatter(context, &parse_context), arg));
   }
 
   const Char* on_format_specs(const Char* begin, const Char* end) {
@@ -3187,7 +3125,7 @@ struct format_handler : internal::error_handler {
     if (visit_format_arg(f, arg)) return parse_context.begin();
     basic_format_specs<Char> specs;
     using internal::specs_handler;
-    using parse_context_t = basic_parse_context<Char>;
+    using parse_context_t = basic_format_parse_context<Char>;
     internal::specs_checker<specs_handler<parse_context_t, Context>> handler(
         specs_handler<parse_context_t, Context>(specs, parse_context, context),
         arg.type());
@@ -3199,7 +3137,7 @@ struct format_handler : internal::error_handler {
     return begin;
   }
 
-  basic_parse_context<Char> parse_context;
+  basic_format_parse_context<Char> parse_context;
   Context context;
   basic_format_arg<Context> arg;
 };
@@ -3396,7 +3334,7 @@ template <typename OutputIt> struct format_to_n_result {
 
 template <typename OutputIt, typename Char = typename OutputIt::value_type>
 using format_to_n_context =
-    format_context_t<fmt::internal::truncating_iterator<OutputIt>, Char>;
+    format_context_t<internal::truncating_iterator<OutputIt>, Char>;
 
 template <typename OutputIt, typename Char = typename OutputIt::value_type>
 using format_to_n_args = basic_format_args<format_to_n_context<OutputIt, Char>>;
@@ -3443,7 +3381,7 @@ inline std::basic_string<Char> internal::vformat(
     basic_format_args<buffer_context<Char>> args) {
   basic_memory_buffer<Char> buffer;
   internal::vformat_to(buffer, format_str, args);
-  return fmt::to_string(buffer);
+  return to_string(buffer);
 }
 
 /**
@@ -3452,8 +3390,22 @@ inline std::basic_string<Char> internal::vformat(
  */
 template <typename... Args>
 inline std::size_t formatted_size(string_view format_str, const Args&... args) {
-  auto it = format_to(internal::counting_iterator<char>(), format_str, args...);
-  return it.count();
+  return format_to(internal::counting_iterator(), format_str, args...).count();
+}
+
+template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
+void vprint(std::FILE* f, basic_string_view<Char> format_str,
+            wformat_args args) {
+  wmemory_buffer buffer;
+  internal::vformat_to(buffer, format_str, args);
+  buffer.push_back(L'\0');
+  if (std::fputws(buffer.data(), f) == -1)
+    FMT_THROW(system_error(errno, "cannot write to file"));
+}
+
+template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
+void vprint(basic_string_view<Char> format_str, wformat_args args) {
+  vprint(stdout, format_str, args);
 }
 
 #if FMT_USE_USER_DEFINED_LITERALS
@@ -3466,7 +3418,7 @@ template <typename Char, Char... CHARS> class udl_formatter {
   std::basic_string<Char> operator()(Args&&... args) const {
     FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
     FMT_CONSTEXPR_DECL bool invalid_format =
-        do_check_format_string<Char, error_handler, Args...>(
+        do_check_format_string<Char, error_handler, remove_cvref_t<Args>...>(
             basic_string_view<Char>(s, sizeof...(CHARS)));
     (void)invalid_format;
     return format(s, std::forward<Args>(args)...);
@@ -3547,6 +3499,22 @@ FMT_CONSTEXPR internal::udl_arg<wchar_t> operator"" _a(const wchar_t* s,
 #endif  // FMT_USE_USER_DEFINED_LITERALS
 FMT_END_NAMESPACE
 
+#define FMT_STRING_IMPL(s, ...)                                         \
+  [] {                                                                  \
+    struct str : fmt::compile_string {                                  \
+      using char_type = typename std::remove_cv<std::remove_pointer<    \
+          typename std::decay<decltype(s)>::type>::type>::type;         \
+      __VA_ARGS__ FMT_CONSTEXPR                                         \
+      operator fmt::basic_string_view<char_type>() const {              \
+        return {s, sizeof(s) / sizeof(char_type) - 1};                  \
+      }                                                                 \
+    } result;                                                           \
+    /* Suppress Qt Creator warning about unused operator. */            \
+    (void)static_cast<fmt::basic_string_view<typename str::char_type>>( \
+        result);                                                        \
+    return result;                                                      \
+  }()
+
 /**
   \rst
   Constructs a compile-time format string.
@@ -3557,37 +3525,10 @@ FMT_END_NAMESPACE
     std::string s = format(FMT_STRING("{:d}"), "foo");
   \endrst
  */
-#define FMT_STRING(s)                                                    \
-  [] {                                                                   \
-    struct str : fmt::compile_string {                                   \
-      using char_type = typename std::remove_cv<std::remove_pointer<     \
-          typename std::decay<decltype(s)>::type>::type>::type;          \
-      FMT_CONSTEXPR operator fmt::basic_string_view<char_type>() const { \
-        return {s, sizeof(s) / sizeof(char_type) - 1};                   \
-      }                                                                  \
-    } result;                                                            \
-    /* Suppress Qt Creator warning about unused operator. */             \
-    (void)static_cast<fmt::basic_string_view<typename str::char_type>>(  \
-        result);                                                         \
-    return result;                                                       \
-  }()
+#define FMT_STRING(s) FMT_STRING_IMPL(s, )
 
 #if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS
-/**
-  \rst
-  Constructs a compile-time format string. This macro is disabled by default to
-  prevent potential name collisions. To enable it define ``FMT_STRING_ALIAS`` to
-  1 before including ``fmt/format.h``.
-
-  **Example**::
-
-    #define FMT_STRING_ALIAS 1
-    #include <fmt/format.h>
-    // A compile-time error because 'd' is an invalid specifier for strings.
-    std::string s = format(fmt("{:d}"), "foo");
-  \endrst
- */
-#  define fmt(s) FMT_STRING(s)
+#  define fmt(s) FMT_STRING_IMPL(s, [[deprecated]])
 #endif
 
 #ifdef FMT_HEADER_ONLY
index 41076f1633ceaf2e661b19e987a69991a5a9ef4f..44ba77f02869482fc40c07f4adcc454fef5bf016 100644 (file)
 #include "fmt/format-inl.h"
 
 FMT_BEGIN_NAMESPACE
+namespace internal {
+
+template <typename T>
+int format_float(char* buf, std::size_t size, const char* format, int precision,
+                 T value) {
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+  if (precision > 100000)
+    throw std::runtime_error(
+        "fuzz mode - avoid large allocation inside snprintf");
+#endif
+  // Suppress the warning about nonliteral format string.
+  auto snprintf_ptr = FMT_SNPRINTF;
+  return precision < 0 ? snprintf_ptr(buf, size, format, value)
+                       : snprintf_ptr(buf, size, format, precision, value);
+}
+struct sprintf_specs {
+  int precision;
+  char type;
+  bool alt : 1;
+
+  template <typename Char>
+  constexpr sprintf_specs(basic_format_specs<Char> specs)
+      : precision(specs.precision), type(specs.type), alt(specs.alt) {}
+
+  constexpr bool has_precision() const { return precision >= 0; }
+};
+
+// This is deprecated and is kept only to preserve ABI compatibility.
+template <typename Double>
+char* sprintf_format(Double value, internal::buffer<char>& buf,
+                     sprintf_specs specs) {
+  // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
+  FMT_ASSERT(buf.capacity() != 0, "empty buffer");
+
+  // Build format string.
+  enum { max_format_size = 10 };  // longest format: %#-*.*Lg
+  char format[max_format_size];
+  char* format_ptr = format;
+  *format_ptr++ = '%';
+  if (specs.alt || !specs.type) *format_ptr++ = '#';
+  if (specs.precision >= 0) {
+    *format_ptr++ = '.';
+    *format_ptr++ = '*';
+  }
+  if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
+
+  char type = specs.type;
+
+  if (type == '%')
+    type = 'f';
+  else if (type == 0 || type == 'n')
+    type = 'g';
+#if FMT_MSC_VER
+  if (type == 'F') {
+    // MSVC's printf doesn't support 'F'.
+    type = 'f';
+  }
+#endif
+  *format_ptr++ = type;
+  *format_ptr = '\0';
+
+  // Format using snprintf.
+  char* start = nullptr;
+  char* decimal_point_pos = nullptr;
+  for (;;) {
+    std::size_t buffer_size = buf.capacity();
+    start = &buf[0];
+    int result =
+        format_float(start, buffer_size, format, specs.precision, value);
+    if (result >= 0) {
+      unsigned n = internal::to_unsigned(result);
+      if (n < buf.capacity()) {
+        // Find the decimal point.
+        auto p = buf.data(), end = p + n;
+        if (*p == '+' || *p == '-') ++p;
+        if (specs.type != 'a' && specs.type != 'A') {
+          while (p < end && *p >= '0' && *p <= '9') ++p;
+          if (p < end && *p != 'e' && *p != 'E') {
+            decimal_point_pos = p;
+            if (!specs.type) {
+              // Keep only one trailing zero after the decimal point.
+              ++p;
+              if (*p == '0') ++p;
+              while (p != end && *p >= '1' && *p <= '9') ++p;
+              char* where = p;
+              while (p != end && *p == '0') ++p;
+              if (p == end || *p < '0' || *p > '9') {
+                if (p != end) std::memmove(where, p, to_unsigned(end - p));
+                n -= static_cast<unsigned>(p - where);
+              }
+            }
+          }
+        }
+        buf.resize(n);
+        break;  // The buffer is large enough - continue with formatting.
+      }
+      buf.reserve(n + 1);
+    } else {
+      // If result is negative we ask to increase the capacity by at least 1,
+      // but as std::vector, the buffer grows exponentially.
+      buf.reserve(buf.capacity() + 1);
+    }
+  }
+  return decimal_point_pos;
+}
+}  // namespace internal
+
+template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
+                                                sprintf_specs);
+template FMT_API char* internal::sprintf_format(long double,
+                                                internal::buffer<char>&,
+                                                sprintf_specs);
+
 template struct FMT_API internal::basic_data<void>;
 
-// Workaround a bug in MSVC2013 that prevents instantiation of grisu_format.
-bool (*instantiate_grisu_format)(double, internal::buffer<char>&, int, unsigned,
-                                 int&) = internal::grisu_format;
+// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
+int (*instantiate_format_float)(double, int, internal::float_specs,
+                                internal::buffer<char>&) =
+    internal::format_float;
 
 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
 template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
@@ -21,6 +135,7 @@ template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
 
 // Explicit instantiations for char.
 
+template FMT_API std::string internal::grouping_impl<char>(locale_ref);
 template FMT_API char internal::thousands_sep_impl(locale_ref);
 template FMT_API char internal::decimal_point_impl(locale_ref);
 
@@ -35,23 +150,27 @@ template FMT_API std::string internal::vformat<char>(
 template FMT_API format_context::iterator internal::vformat_to(
     internal::buffer<char>&, string_view, basic_format_args<format_context>);
 
-template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
-                                                sprintf_specs);
-template FMT_API char* internal::sprintf_format(long double,
-                                                internal::buffer<char>&,
-                                                sprintf_specs);
+template FMT_API int internal::snprintf_float(double, int,
+                                              internal::float_specs,
+                                              internal::buffer<char>&);
+template FMT_API int internal::snprintf_float(long double, int,
+                                              internal::float_specs,
+                                              internal::buffer<char>&);
+template FMT_API int internal::format_float(double, int, internal::float_specs,
+                                            internal::buffer<char>&);
+template FMT_API int internal::format_float(long double, int,
+                                            internal::float_specs,
+                                            internal::buffer<char>&);
 
 // Explicit instantiations for wchar_t.
 
+template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
 template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
 template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
 
 template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
                                                         const wchar_t*);
 
-template FMT_API void internal::arg_map<wformat_context>::init(
-    const basic_format_args<wformat_context>&);
-
 template FMT_API std::wstring internal::vformat<wchar_t>(
     wstring_view, basic_format_args<wformat_context>);
 FMT_END_NAMESPACE