]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Upgrade to fmt 7.0.2
authorJoel Rosdahl <joel@rosdahl.net>
Thu, 6 Aug 2020 13:17:12 +0000 (15:17 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Thu, 6 Aug 2020 14:23:15 +0000 (16:23 +0200)
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 ab40fb19cc66d4d1c26ce34d5cddd42feb8abe56..405d7e346e6e5c7bafa074b4765a1e26ebff76b5 100644 (file)
@@ -86,7 +86,7 @@ SOFTWARE.
 src/third_party/fmt/*.h and src/third_party/format.cpp
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-This is a subset of https://fmt.dev[fmt] 6.2.0 with the following license:
+This is a subset of https://fmt.dev[fmt] 7.0.2 with the following license:
 
 -------------------------------------------------------------------------------
   Formatting library for C++
index 6df2875acfc2ab4000e89e44a9badbecdcdc7e1b..40c4ddb077c792e17cfd70fa7326323bfa98012c 100644 (file)
 #include <vector>
 
 // The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 60200
-
-#ifdef __has_feature
-#  define FMT_HAS_FEATURE(x) __has_feature(x)
-#else
-#  define FMT_HAS_FEATURE(x) 0
-#endif
-
-#if defined(__has_include) && !defined(__INTELLISENSE__) && \
-    !(defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1600)
-#  define FMT_HAS_INCLUDE(x) __has_include(x)
-#else
-#  define FMT_HAS_INCLUDE(x) 0
-#endif
-
-#ifdef __has_cpp_attribute
-#  define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
-#else
-#  define FMT_HAS_CPP_ATTRIBUTE(x) 0
-#endif
-
-#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
-  (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
-
-#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
-  (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+#define FMT_VERSION 70002
 
 #ifdef __clang__
 #  define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
 #  define FMT_GCC_VERSION 0
 #endif
 
+#if defined(__INTEL_COMPILER)
+#  define FMT_ICC_VERSION __INTEL_COMPILER
+#else
+#  define FMT_ICC_VERSION 0
+#endif
+
 #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
 #  define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION
 #else
 
 #ifdef _MSC_VER
 #  define FMT_MSC_VER _MSC_VER
+#  define FMT_SUPPRESS_MSC_WARNING(n) __pragma(warning(suppress : n))
 #else
 #  define FMT_MSC_VER 0
+#  define FMT_SUPPRESS_MSC_WARNING(n)
+#endif
+#ifdef __has_feature
+#  define FMT_HAS_FEATURE(x) __has_feature(x)
+#else
+#  define FMT_HAS_FEATURE(x) 0
 #endif
 
+#if defined(__has_include) && !defined(__INTELLISENSE__) && \
+    !(FMT_ICC_VERSION && FMT_ICC_VERSION < 1600)
+#  define FMT_HAS_INCLUDE(x) __has_include(x)
+#else
+#  define FMT_HAS_INCLUDE(x) 0
+#endif
+
+#ifdef __has_cpp_attribute
+#  define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+#  define FMT_HAS_CPP_ATTRIBUTE(x) 0
+#endif
+
+#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
+  (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
+  (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
 // Check if relaxed C++14 constexpr is supported.
 // GCC doesn't allow throw in constexpr until version 6 (bug 67371).
 #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_NVCC
+        !FMT_NVCC && !FMT_ICC_VERSION
 #endif
 #if FMT_USE_CONSTEXPR
 #  define FMT_CONSTEXPR constexpr
 #  define FMT_NORETURN
 #endif
 
-#ifndef FMT_MAYBE_UNUSED
-#  if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
-#    define FMT_MAYBE_UNUSED [[maybe_unused]]
-#  else
-#    define FMT_MAYBE_UNUSED
-#  endif
-#endif
-
 #ifndef FMT_DEPRECATED
 #  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
 #    define FMT_DEPRECATED [[deprecated]]
 #endif
 
 // Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
-#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC
+#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC
 #  define FMT_DEPRECATED_ALIAS
 #else
 #  define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
 #endif
 
+#ifndef FMT_INLINE
+#  if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#    define FMT_INLINE inline __attribute__((always_inline))
+#  else
+#    define FMT_INLINE inline
+#  endif
+#endif
+
 #ifndef FMT_BEGIN_NAMESPACE
 #  if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
       FMT_MSC_VER >= 1900
 #    define FMT_INLINE_NAMESPACE namespace
 #    define FMT_END_NAMESPACE \
       }                       \
-      using namespace v6;     \
+      using namespace v7;     \
       }
 #  endif
 #  define FMT_BEGIN_NAMESPACE \
     namespace fmt {           \
-    FMT_INLINE_NAMESPACE v6 {
+    FMT_INLINE_NAMESPACE v7 {
 #endif
 
 #if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
-#  if FMT_MSC_VER
-#    define FMT_NO_W4275 __pragma(warning(suppress : 4275))
-#  else
-#    define FMT_NO_W4275
-#  endif
-#  define FMT_CLASS_API FMT_NO_W4275
+#  define FMT_CLASS_API FMT_SUPPRESS_MSC_WARNING(4275)
 #  ifdef FMT_EXPORT
 #    define FMT_API __declspec(dllexport)
+#    define FMT_EXTERN_TEMPLATE_API FMT_API
+#    define FMT_EXPORTED
 #  elif defined(FMT_SHARED)
 #    define FMT_API __declspec(dllimport)
 #    define FMT_EXTERN_TEMPLATE_API FMT_API
 #  endif
-#endif
-#ifndef FMT_CLASS_API
+#else
 #  define FMT_CLASS_API
 #endif
 #ifndef FMT_API
-#  if FMT_GCC_VERSION || FMT_CLANG_VERSION
-#    define FMT_API __attribute__((visibility("default")))
-#    define FMT_EXTERN_TEMPLATE_API FMT_API
-#    define FMT_INSTANTIATION_DEF_API
-#  else
-#    define FMT_API
-#  endif
+#  define FMT_API
 #endif
 #ifndef FMT_EXTERN_TEMPLATE_API
 #  define FMT_EXTERN_TEMPLATE_API
@@ -270,14 +267,11 @@ struct monostate {};
 // to workaround a bug in MSVC 2019 (see #1140 and #1186).
 #define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
 
-namespace internal {
+namespace detail {
 
 // A helper function to suppress bogus "conditional expression is constant"
 // warnings.
-template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
-
-// 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; };
+template <typename T> constexpr T const_check(T value) { return value; }
 
 FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
                                       const char* message);
@@ -290,7 +284,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
 #    define FMT_ASSERT(condition, message)                                    \
       ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
            ? (void)0                                                          \
-           : ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
+           : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
 #  endif
 #endif
 
@@ -324,7 +318,7 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
   return static_cast<typename std::make_unsigned<Int>::type>(value);
 }
 
-constexpr unsigned char micro[] = "\u00B5";
+FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5";
 
 template <typename Char> constexpr bool is_unicode() {
   return FMT_UNICODE || sizeof(Char) != 1 ||
@@ -336,10 +330,11 @@ using char8_type = char8_t;
 #else
 enum char8_type : unsigned char {};
 #endif
-}  // namespace internal
+}  // namespace detail
 
-template <typename... Ts>
-using void_t = typename internal::void_t_impl<Ts...>::type;
+#ifdef FMT_USE_INTERNAL
+namespace internal = detail;  // DEPRECATED
+#endif
 
 /**
   An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
@@ -354,14 +349,13 @@ template <typename Char> class basic_string_view {
   size_t size_;
 
  public:
-  using char_type FMT_DEPRECATED_ALIAS = Char;
   using value_type = Char;
   using iterator = const Char*;
 
-  FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
+  constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
 
   /** Constructs a string reference object from a C string and a size. */
-  FMT_CONSTEXPR basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
+  constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
       : data_(s),
         size_(count) {}
 
@@ -384,22 +378,21 @@ template <typename Char> class basic_string_view {
       : data_(s.data()),
         size_(s.size()) {}
 
-  template <
-      typename S,
-      FMT_ENABLE_IF(std::is_same<S, internal::std_string_view<Char>>::value)>
+  template <typename S, FMT_ENABLE_IF(std::is_same<
+                                      S, detail::std_string_view<Char>>::value)>
   FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()),
                                                       size_(s.size()) {}
 
   /** Returns a pointer to the string data. */
-  FMT_CONSTEXPR const Char* data() const { return data_; }
+  constexpr const Char* data() const { return data_; }
 
   /** Returns the string size. */
-  FMT_CONSTEXPR size_t size() const { return size_; }
+  constexpr size_t size() const { return size_; }
 
-  FMT_CONSTEXPR iterator begin() const { return data_; }
-  FMT_CONSTEXPR iterator end() const { return data_ + size_; }
+  constexpr iterator begin() const { return data_; }
+  constexpr iterator end() const { return data_ + size_; }
 
-  FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
+  constexpr const Char& operator[](size_t pos) const { return data_[pos]; }
 
   FMT_CONSTEXPR void remove_prefix(size_t n) {
     data_ += n;
@@ -438,16 +431,11 @@ template <typename Char> class basic_string_view {
 using string_view = basic_string_view<char>;
 using wstring_view = basic_string_view<wchar_t>;
 
-#ifndef __cpp_char8_t
-// char8_t is deprecated; use char instead.
-using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type;
-#endif
-
 /** Specifies if ``T`` is a character type. Can be specialized by users. */
 template <typename T> struct is_char : std::false_type {};
 template <> struct is_char<char> : std::true_type {};
 template <> struct is_char<wchar_t> : std::true_type {};
-template <> struct is_char<internal::char8_type> : std::true_type {};
+template <> struct is_char<detail::char8_type> : std::true_type {};
 template <> struct is_char<char16_t> : std::true_type {};
 template <> struct is_char<char32_t> : std::true_type {};
 
@@ -484,14 +472,13 @@ inline basic_string_view<Char> to_string_view(basic_string_view<Char> s) {
 }
 
 template <typename Char,
-          FMT_ENABLE_IF(!std::is_empty<internal::std_string_view<Char>>::value)>
-inline basic_string_view<Char> to_string_view(
-    internal::std_string_view<Char> s) {
+          FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)>
+inline basic_string_view<Char> to_string_view(detail::std_string_view<Char> s) {
   return s;
 }
 
 // A base class for compile-time strings. It is defined in the fmt namespace to
-// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42).
+// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42).
 struct compile_string {};
 
 template <typename S>
@@ -502,9 +489,9 @@ constexpr basic_string_view<typename S::char_type> to_string_view(const S& s) {
   return s;
 }
 
-namespace internal {
+namespace detail {
 void to_string_view(...);
-using fmt::v6::to_string_view;
+using fmt::v7::to_string_view;
 
 // Specifies whether S is a string type convertible to fmt::basic_string_view.
 // It should be a constexpr function but MSVC 2017 fails to compile it in
@@ -520,16 +507,16 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
 };
 
 struct error_handler {
-  FMT_CONSTEXPR error_handler() = default;
-  FMT_CONSTEXPR error_handler(const error_handler&) = default;
+  constexpr error_handler() = default;
+  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);
 };
-}  // namespace internal
+}  // namespace detail
 
 /** String's character type. */
-template <typename S> using char_t = typename internal::char_t_impl<S>::type;
+template <typename S> using char_t = typename detail::char_t_impl<S>::type;
 
 /**
   \rst
@@ -547,7 +534,7 @@ template <typename S> using char_t = typename internal::char_t_impl<S>::type;
   +-----------------------+-------------------------------------+
   \endrst
  */
-template <typename Char, typename ErrorHandler = internal::error_handler>
+template <typename Char, typename ErrorHandler = detail::error_handler>
 class basic_format_parse_context : private ErrorHandler {
  private:
   basic_string_view<Char> format_str_;
@@ -557,26 +544,24 @@ class basic_format_parse_context : private ErrorHandler {
   using char_type = Char;
   using iterator = typename basic_string_view<Char>::iterator;
 
-  explicit FMT_CONSTEXPR basic_format_parse_context(
-      basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
+  explicit constexpr basic_format_parse_context(
+      basic_string_view<Char> format_str, ErrorHandler eh = {})
       : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
 
   /**
     Returns an iterator to the beginning of the format string range being
     parsed.
    */
-  FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
-    return format_str_.begin();
-  }
+  constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); }
 
   /**
     Returns an iterator past the end of the format string range being parsed.
    */
-  FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
+  constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
 
   /** Advances the begin iterator to ``it``. */
   FMT_CONSTEXPR void advance_to(iterator it) {
-    format_str_.remove_prefix(internal::to_unsigned(it - begin()));
+    format_str_.remove_prefix(detail::to_unsigned(it - begin()));
   }
 
   /**
@@ -584,6 +569,8 @@ class basic_format_parse_context : private ErrorHandler {
     the next argument index and switches to the automatic indexing.
    */
   FMT_CONSTEXPR int next_arg_id() {
+    // Don't check if the argument id is valid to avoid overhead and because it
+    // will be checked during formatting anyway.
     if (next_arg_id_ >= 0) return next_arg_id_++;
     on_error("cannot switch from manual to automatic argument indexing");
     return 0;
@@ -606,20 +593,15 @@ class basic_format_parse_context : private ErrorHandler {
     ErrorHandler::on_error(message);
   }
 
-  FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
+  constexpr ErrorHandler error_handler() const { return *this; }
 };
 
 using format_parse_context = basic_format_parse_context<char>;
 using wformat_parse_context = basic_format_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;
+template <typename Context> class dynamic_format_arg_store;
 
 // A formatter for objects of type T.
 template <typename T, typename Char = char, typename Enable = void>
@@ -628,43 +610,44 @@ struct formatter {
   formatter() = delete;
 };
 
-template <typename T, typename Char, typename Enable = void>
-struct FMT_DEPRECATED convert_to_int
-    : bool_constant<!std::is_arithmetic<T>::value &&
-                    std::is_convertible<T, int>::value> {};
-
 // 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 {
+namespace detail {
 
-/** A contiguous memory buffer with an optional growing ability. */
+/**
+  \rst
+  A contiguous memory buffer with an optional growing ability. It is an internal
+  class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
+  \endrst
+ */
 template <typename T> class buffer {
  private:
   T* ptr_;
-  std::size_t size_;
-  std::size_t capacity_;
+  size_t size_;
+  size_t capacity_;
 
  protected:
   // Don't initialize ptr_ since it is not accessed to save a few cycles.
-  buffer(std::size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
+  FMT_SUPPRESS_MSC_WARNING(26495)
+  buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
 
-  buffer(T* p = nullptr, std::size_t sz = 0, std::size_t cap = 0) FMT_NOEXCEPT
+  buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT
       : ptr_(p),
         size_(sz),
         capacity_(cap) {}
 
   /** Sets the buffer data and capacity. */
-  void set(T* buf_data, std::size_t buf_capacity) FMT_NOEXCEPT {
+  void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
     ptr_ = buf_data;
     capacity_ = buf_capacity;
   }
 
   /** Increases the buffer capacity to hold at least *capacity* elements. */
-  virtual void grow(std::size_t capacity) = 0;
+  virtual void grow(size_t capacity) = 0;
 
  public:
   using value_type = T;
@@ -681,10 +664,10 @@ template <typename T> class buffer {
   const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
 
   /** Returns the size of this buffer. */
-  std::size_t size() const FMT_NOEXCEPT { return size_; }
+  size_t size() const FMT_NOEXCEPT { return size_; }
 
   /** Returns the capacity of this buffer. */
-  std::size_t capacity() const FMT_NOEXCEPT { return capacity_; }
+  size_t capacity() const FMT_NOEXCEPT { return capacity_; }
 
   /** Returns a pointer to the buffer data. */
   T* data() FMT_NOEXCEPT { return ptr_; }
@@ -695,7 +678,7 @@ template <typename T> class buffer {
   /**
     Resizes the buffer. If T is a POD type new elements may not be initialized.
    */
-  void resize(std::size_t new_size) {
+  void resize(size_t new_size) {
     reserve(new_size);
     size_ = new_size;
   }
@@ -704,7 +687,7 @@ template <typename T> class buffer {
   void clear() { size_ = 0; }
 
   /** Reserves space to store at least *capacity* elements. */
-  void reserve(std::size_t new_capacity) {
+  void reserve(size_t new_capacity) {
     if (new_capacity > capacity_) grow(new_capacity);
   }
 
@@ -729,7 +712,7 @@ class container_buffer : public buffer<typename Container::value_type> {
   Container& container_;
 
  protected:
-  void grow(std::size_t capacity) FMT_OVERRIDE {
+  void grow(size_t capacity) FMT_OVERRIDE {
     container_.resize(capacity);
     this->set(&container_[0], capacity);
   }
@@ -760,12 +743,78 @@ template <typename T, typename Context>
 using has_fallback_formatter =
     std::is_constructible<fallback_formatter<T, typename Context::char_type>>;
 
-template <typename Char> struct named_arg_base;
-template <typename T, typename Char> struct named_arg;
+struct view {};
+
+template <typename Char, typename T> struct named_arg : view {
+  const Char* name;
+  const T& value;
+  named_arg(const Char* n, const T& v) : name(n), value(v) {}
+};
+
+template <typename Char> struct named_arg_info {
+  const Char* name;
+  int id;
+};
+
+template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+struct arg_data {
+  // args_[0].named_args points to named_args_ to avoid bloating format_args.
+  T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : 1)];
+  named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
+
+  template <typename... U>
+  arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
+  arg_data(const arg_data& other) = delete;
+  const T* args() const { return args_ + 1; }
+  named_arg_info<Char>* named_args() { return named_args_; }
+};
+
+template <typename T, typename Char, size_t NUM_ARGS>
+struct arg_data<T, Char, NUM_ARGS, 0> {
+  T args_[NUM_ARGS != 0 ? NUM_ARGS : 1];
+
+  template <typename... U>
+  FMT_INLINE arg_data(const U&... init) : args_{init...} {}
+  FMT_INLINE const T* args() const { return args_; }
+  FMT_INLINE std::nullptr_t named_args() { return nullptr; }
+};
+
+template <typename Char>
+inline void init_named_args(named_arg_info<Char>*, int, int) {}
+
+template <typename Char, typename T, typename... Tail>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+                     int named_arg_count, const T&, const Tail&... args) {
+  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename Char, typename T, typename... Tail>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+                     int named_arg_count, const named_arg<Char, T>& arg,
+                     const Tail&... args) {
+  named_args[named_arg_count++] = {arg.name, arg_count};
+  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename... Args>
+FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {}
+
+template <typename T> struct is_named_arg : std::false_type {};
+
+template <typename T, typename Char>
+struct is_named_arg<named_arg<Char, T>> : std::true_type {};
+
+template <bool B = false> constexpr size_t count() { return B ? 1 : 0; }
+template <bool B1, bool B2, bool... Tail> constexpr size_t count() {
+  return (B1 ? 1 : 0) + count<B2, Tail...>();
+}
+
+template <typename... Args> constexpr size_t count_named_args() {
+  return count<is_named_arg<Args>::value...>();
+}
 
 enum class type {
   none_type,
-  named_arg_type,
   // Integer types should go first,
   int_type,
   uint_type,
@@ -796,7 +845,6 @@ struct type_constant : std::integral_constant<type, type::custom_type> {};
   struct type_constant<Type, Char>        \
       : std::integral_constant<type, type::constant> {}
 
-FMT_TYPE_CONSTANT(const named_arg_base<Char>&, named_arg_type);
 FMT_TYPE_CONSTANT(int, int_type);
 FMT_TYPE_CONSTANT(unsigned, uint_type);
 FMT_TYPE_CONSTANT(long long, long_long_type);
@@ -812,23 +860,26 @@ 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(type t) {
-  FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
+constexpr bool is_integral_type(type t) {
   return t > type::none_type && t <= type::last_integer_type;
 }
 
-FMT_CONSTEXPR bool is_arithmetic_type(type t) {
-  FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
+constexpr bool is_arithmetic_type(type t) {
   return t > type::none_type && t <= type::last_numeric_type;
 }
 
 template <typename Char> struct string_value {
   const Char* data;
-  std::size_t size;
+  size_t size;
+};
+
+template <typename Char> struct named_arg_value {
+  const named_arg_info<Char>* data;
+  size_t size;
 };
 
 template <typename Context> struct custom_value {
-  using parse_context = basic_format_parse_context<typename Context::char_type>;
+  using parse_context = typename Context::parse_context_type;
   const void* value;
   void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
 };
@@ -853,28 +904,30 @@ template <typename Context> class value {
     const void* pointer;
     string_value<char_type> string;
     custom_value<Context> custom;
-    const named_arg_base<char_type>* named_arg;
+    named_arg_value<char_type> named_args;
   };
 
-  FMT_CONSTEXPR value(int val = 0) : int_value(val) {}
-  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) {}
-  value(char_type val) : char_value(val) {}
-  value(const char_type* val) { string.data = val; }
-  value(basic_string_view<char_type> val) {
+  constexpr FMT_INLINE value(int val = 0) : int_value(val) {}
+  constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
+  FMT_INLINE value(long long val) : long_long_value(val) {}
+  FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
+  FMT_INLINE value(int128_t val) : int128_value(val) {}
+  FMT_INLINE value(uint128_t val) : uint128_value(val) {}
+  FMT_INLINE value(float val) : float_value(val) {}
+  FMT_INLINE value(double val) : double_value(val) {}
+  FMT_INLINE value(long double val) : long_double_value(val) {}
+  FMT_INLINE value(bool val) : bool_value(val) {}
+  FMT_INLINE value(char_type val) : char_value(val) {}
+  FMT_INLINE value(const char_type* val) { string.data = val; }
+  FMT_INLINE value(basic_string_view<char_type> val) {
     string.data = val.data();
     string.size = val.size();
   }
-  value(const void* val) : pointer(val) {}
+  FMT_INLINE value(const void* val) : pointer(val) {}
+  FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
+      : named_args{args, size} {}
 
-  template <typename T> value(const T& val) {
+  template <typename T> FMT_INLINE value(const T& val) {
     custom.value = &val;
     // Get the formatter type through the context to allow different contexts
     // have different extension points, e.g. `formatter<T>` for `format` and
@@ -885,14 +938,12 @@ template <typename Context> class value {
                          fallback_formatter<T, char_type>>>;
   }
 
-  value(const named_arg_base<char_type>& val) { named_arg = &val; }
-
  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_format_parse_context<char_type>& parse_ctx,
-      Context& ctx) {
+  static void format_custom_arg(const void* arg,
+                                typename Context::parse_context_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));
@@ -972,6 +1023,14 @@ template <typename Context> struct arg_mapper {
     static_assert(std::is_same<char_type, char>::value, "invalid string type");
     return reinterpret_cast<const char*>(val);
   }
+  FMT_CONSTEXPR const char* map(signed char* val) {
+    const auto* const_val = val;
+    return map(const_val);
+  }
+  FMT_CONSTEXPR const char* map(unsigned char* val) {
+    const auto* const_val = val;
+    return map(const_val);
+  }
 
   FMT_CONSTEXPR const void* map(void* val) { return val; }
   FMT_CONSTEXPR const void* map(const void* val) { return val; }
@@ -1003,11 +1062,9 @@ template <typename Context> struct arg_mapper {
   }
 
   template <typename T>
-  FMT_CONSTEXPR const named_arg_base<char_type>& map(
-      const named_arg<T, char_type>& val) {
-    auto arg = make_arg<Context>(val.value);
-    std::memcpy(val.data, &arg, sizeof(arg));
-    return val;
+  FMT_CONSTEXPR auto map(const named_arg<char_type, T>& val)
+      -> decltype(std::declval<arg_mapper>().map(val.value)) {
+    return map(val.value);
   }
 
   int map(...) {
@@ -1027,23 +1084,22 @@ using mapped_type_constant =
     type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
                   typename Context::char_type>;
 
-enum { packed_arg_bits = 5 };
+enum { packed_arg_bits = 4 };
 // Maximum number of arguments with packed types.
-enum { max_packed_args = 63 / packed_arg_bits };
+enum { max_packed_args = 62 / packed_arg_bits };
 enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
-
-template <typename Context> class arg_map;
-}  // namespace internal
+enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
+}  // namespace detail
 
 // A formatting argument. It is a trivially copyable/constructible type to
 // allow storage in basic_memory_buffer.
 template <typename Context> class basic_format_arg {
  private:
-  internal::value<Context> value_;
-  internal::type type_;
+  detail::value<Context> value_;
+  detail::type type_;
 
   template <typename ContextType, typename T>
-  friend FMT_CONSTEXPR basic_format_arg<ContextType> internal::make_arg(
+  friend FMT_CONSTEXPR basic_format_arg<ContextType> detail::make_arg(
       const T& value);
 
   template <typename Visitor, typename Ctx>
@@ -1052,34 +1108,40 @@ template <typename Context> class basic_format_arg {
       -> decltype(vis(0));
 
   friend class basic_format_args<Context>;
-  friend class internal::arg_map<Context>;
+  friend class dynamic_format_arg_store<Context>;
 
   using char_type = typename Context::char_type;
 
+  template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+  friend struct detail::arg_data;
+
+  basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
+      : value_(args, size) {}
+
  public:
   class handle {
    public:
-    explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
+    explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
 
-    void format(basic_format_parse_context<char_type>& parse_ctx,
+    void format(typename Context::parse_context_type& parse_ctx,
                 Context& ctx) const {
       custom_.format(custom_.value, parse_ctx, ctx);
     }
 
    private:
-    internal::custom_value<Context> custom_;
+    detail::custom_value<Context> custom_;
   };
 
-  FMT_CONSTEXPR basic_format_arg() : type_(internal::type::none_type) {}
+  constexpr basic_format_arg() : type_(detail::type::none_type) {}
 
-  FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
-    return type_ != internal::type::none_type;
+  constexpr explicit operator bool() const FMT_NOEXCEPT {
+    return type_ != detail::type::none_type;
   }
 
-  internal::type type() const { return type_; }
+  detail::type type() const { return type_; }
 
-  bool is_integral() const { return internal::is_integral_type(type_); }
-  bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
+  bool is_integral() const { return detail::is_integral_type(type_); }
+  bool is_arithmetic() const { return detail::is_arithmetic_type(type_); }
 };
 
 /**
@@ -1090,92 +1152,73 @@ template <typename Context> class basic_format_arg {
   \endrst
  */
 template <typename Visitor, typename Context>
-FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
-                                    const basic_format_arg<Context>& arg)
-    -> decltype(vis(0)) {
+FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
+    Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
   using char_type = typename Context::char_type;
   switch (arg.type_) {
-  case internal::type::none_type:
+  case detail::type::none_type:
     break;
-  case internal::type::named_arg_type:
-    FMT_ASSERT(false, "invalid argument type");
-    break;
-  case internal::type::int_type:
+  case detail::type::int_type:
     return vis(arg.value_.int_value);
-  case internal::type::uint_type:
+  case detail::type::uint_type:
     return vis(arg.value_.uint_value);
-  case internal::type::long_long_type:
+  case detail::type::long_long_type:
     return vis(arg.value_.long_long_value);
-  case internal::type::ulong_long_type:
+  case detail::type::ulong_long_type:
     return vis(arg.value_.ulong_long_value);
 #if FMT_USE_INT128
-  case internal::type::int128_type:
+  case detail::type::int128_type:
     return vis(arg.value_.int128_value);
-  case internal::type::uint128_type:
+  case detail::type::uint128_type:
     return vis(arg.value_.uint128_value);
 #else
-  case internal::type::int128_type:
-  case internal::type::uint128_type:
+  case detail::type::int128_type:
+  case detail::type::uint128_type:
     break;
 #endif
-  case internal::type::bool_type:
+  case detail::type::bool_type:
     return vis(arg.value_.bool_value);
-  case internal::type::char_type:
+  case detail::type::char_type:
     return vis(arg.value_.char_value);
-  case internal::type::float_type:
+  case detail::type::float_type:
     return vis(arg.value_.float_value);
-  case internal::type::double_type:
+  case detail::type::double_type:
     return vis(arg.value_.double_value);
-  case internal::type::long_double_type:
+  case detail::type::long_double_type:
     return vis(arg.value_.long_double_value);
-  case internal::type::cstring_type:
+  case detail::type::cstring_type:
     return vis(arg.value_.string.data);
-  case internal::type::string_type:
+  case detail::type::string_type:
     return vis(basic_string_view<char_type>(arg.value_.string.data,
                                             arg.value_.string.size));
-  case internal::type::pointer_type:
+  case detail::type::pointer_type:
     return vis(arg.value_.pointer);
-  case internal::type::custom_type:
+  case detail::type::custom_type:
     return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
   }
   return vis(monostate());
 }
 
-namespace internal {
-// A map from argument names to their values for named arguments.
-template <typename Context> class arg_map {
- private:
-  using char_type = typename Context::char_type;
-
-  struct entry {
-    basic_string_view<char_type> name;
-    basic_format_arg<Context> arg;
-  };
+// Checks whether T is a container with contiguous storage.
+template <typename T> struct is_contiguous : std::false_type {};
+template <typename Char>
+struct is_contiguous<std::basic_string<Char>> : std::true_type {};
+template <typename Char>
+struct is_contiguous<detail::buffer<Char>> : std::true_type {};
 
-  entry* map_;
-  unsigned size_;
+namespace detail {
 
-  void push_back(value<Context> val) {
-    const auto& named = *val.named_arg;
-    map_[size_] = {named.name, named.template deserialize<Context>()};
-    ++size_;
-  }
+template <typename OutputIt>
+struct is_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_back_insert_iterator<std::back_insert_iterator<Container>>
+    : std::true_type {};
 
- 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_; }
-
-  basic_format_arg<Context> find(basic_string_view<char_type> name) const {
-    // The list is unsorted, so just return the first matching name.
-    for (entry *it = map_, *end = map_ + size_; it != end; ++it) {
-      if (it->name == name) return it->arg;
-    }
-    return {};
-  }
-};
+template <typename OutputIt>
+struct is_contiguous_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
+    : is_contiguous<Container> {};
 
 // A type-erased reference to an std::locale to avoid heavy <locale> include.
 class locale_ref {
@@ -1207,23 +1250,30 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
   return arg;
 }
 
-template <bool IS_PACKED, typename Context, typename T,
+// The type template parameter is there to avoid an ODR violation when using
+// a fallback formatter in one translation unit and an implicit conversion in
+// another (not recommended).
+template <bool IS_PACKED, typename Context, type, typename T,
           FMT_ENABLE_IF(IS_PACKED)>
 inline value<Context> make_arg(const T& val) {
   return arg_mapper<Context>().map(val);
 }
 
-template <bool IS_PACKED, typename Context, typename T,
+template <bool IS_PACKED, typename Context, type, typename T,
           FMT_ENABLE_IF(!IS_PACKED)>
 inline basic_format_arg<Context> make_arg(const T& value) {
   return make_arg<Context>(value);
 }
 
 template <typename T> struct is_reference_wrapper : std::false_type {};
-
 template <typename T>
 struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
 
+template <typename T> const T& unwrap(const T& v) { return v; }
+template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
+  return static_cast<const T&>(v);
+}
+
 class dynamic_arg_list {
   // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
   // templates it doesn't complain about inability to deduce single translation
@@ -1248,14 +1298,14 @@ class dynamic_arg_list {
 
  public:
   template <typename T, typename Arg> const T& push(const Arg& arg) {
-    auto node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
-    auto& value = node->value;
-    node->next = std::move(head_);
-    head_ = std::move(node);
+    auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
+    auto& value = new_node->value;
+    new_node->next = std::move(head_);
+    head_ = std::move(new_node);
     return value;
   }
 };
-}  // namespace internal
+}  // namespace detail
 
 // Formatting context.
 template <typename OutputIt, typename Char> class basic_format_context {
@@ -1266,12 +1316,12 @@ template <typename OutputIt, typename Char> class basic_format_context {
  private:
   OutputIt out_;
   basic_format_args<basic_format_context> args_;
-  internal::arg_map<basic_format_context> map_;
-  internal::locale_ref loc_;
+  detail::locale_ref loc_;
 
  public:
   using iterator = OutputIt;
   using format_arg = basic_format_arg<basic_format_context>;
+  using parse_context_type = basic_format_parse_context<Char>;
   template <typename T> using formatter_type = formatter<T, char_type>;
 
   basic_format_context(const basic_format_context&) = delete;
@@ -1282,34 +1332,38 @@ template <typename OutputIt, typename Char> class basic_format_context {
    */
   basic_format_context(OutputIt out,
                        basic_format_args<basic_format_context> ctx_args,
-                       internal::locale_ref loc = internal::locale_ref())
+                       detail::locale_ref loc = detail::locale_ref())
       : out_(out), args_(ctx_args), loc_(loc) {}
 
   format_arg arg(int id) const { return args_.get(id); }
+  format_arg arg(basic_string_view<char_type> name) { return args_.get(name); }
+  int arg_id(basic_string_view<char_type> name) { return args_.get_id(name); }
+  const basic_format_args<basic_format_context>& args() const { return args_; }
 
-  // Checks if manual indexing is used and returns the argument with the
-  // specified name.
-  format_arg arg(basic_string_view<char_type> name);
-
-  internal::error_handler error_handler() { return {}; }
+  detail::error_handler error_handler() { return {}; }
   void on_error(const char* message) { error_handler().on_error(message); }
 
   // Returns an iterator to the beginning of the output range.
   iterator out() { return out_; }
 
   // Advances the begin iterator to ``it``.
-  void advance_to(iterator it) { out_ = it; }
+  void advance_to(iterator it) {
+    if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
+  }
 
-  internal::locale_ref locale() { return loc_; }
+  detail::locale_ref locale() { return loc_; }
 };
 
 template <typename Char>
 using buffer_context =
-    basic_format_context<std::back_insert_iterator<internal::buffer<Char>>,
-                         Char>;
+    basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
 using format_context = buffer_context<char>;
 using wformat_context = buffer_context<wchar_t>;
 
+// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164.
+#define FMT_BUFFER_CONTEXT(Char) \
+  basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>
+
 /**
   \rst
   An array of references to arguments. It can be implicitly converted into
@@ -1326,27 +1380,35 @@ class format_arg_store
 {
  private:
   static const size_t num_args = sizeof...(Args);
-  static const bool is_packed = num_args < internal::max_packed_args;
+  static const size_t num_named_args = detail::count_named_args<Args...>();
+  static const bool is_packed = num_args <= detail::max_packed_args;
 
-  using value_type = conditional_t<is_packed, internal::value<Context>,
+  using value_type = conditional_t<is_packed, detail::value<Context>,
                                    basic_format_arg<Context>>;
 
-  // If the arguments are not packed, add one more element to mark the end.
-  value_type data_[num_args + (num_args == 0 ? 1 : 0)];
+  detail::arg_data<value_type, typename Context::char_type, num_args,
+                   num_named_args>
+      data_;
 
   friend class basic_format_args<Context>;
 
- public:
-  static constexpr unsigned long long types =
-      is_packed ? internal::encode_types<Context, Args...>()
-                : internal::is_unpacked_bit | num_args;
+  static constexpr unsigned long long desc =
+      (is_packed ? detail::encode_types<Context, Args...>()
+                 : detail::is_unpacked_bit | num_args) |
+      (num_named_args != 0
+           ? static_cast<unsigned long long>(detail::has_named_args_bit)
+           : 0);
 
+ public:
   format_arg_store(const Args&... args)
       :
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
         basic_format_args<Context>(*this),
 #endif
-        data_{internal::make_arg<is_packed, Context>(args)...} {
+        data_{detail::make_arg<
+            is_packed, Context,
+            detail::mapped_type_constant<Args, Context>::value>(args)...} {
+    detail::init_named_args(data_.named_args(), 0, 0, args...);
   }
 };
 
@@ -1366,8 +1428,24 @@ inline format_arg_store<Context, Args...> make_format_args(
 
 /**
   \rst
-  A dynamic version of `fmt::format_arg_store<>`.
-  It's equipped with a storage to potentially temporary objects which lifetime
+  Returns a named argument to be used in a formatting function. It should only
+  be used in a call to a formatting function.
+
+  **Example**::
+
+    fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
+  \endrst
+ */
+template <typename Char, typename T>
+inline detail::named_arg<Char, T> arg(const Char* name, const T& arg) {
+  static_assert(!detail::is_named_arg<T>(), "nested named arguments");
+  return {name, arg};
+}
+
+/**
+  \rst
+  A dynamic version of `fmt::format_arg_store`.
+  It's equipped with a storage to potentially temporary objects which lifetimes
   could be shorter than the format arguments object.
 
   It can be implicitly converted into `~fmt::basic_format_args` for passing
@@ -1385,49 +1463,73 @@ class dynamic_format_arg_store
   using char_type = typename Context::char_type;
 
   template <typename T> struct need_copy {
-    static constexpr internal::type mapped_type =
-        internal::mapped_type_constant<T, Context>::value;
+    static constexpr detail::type mapped_type =
+        detail::mapped_type_constant<T, Context>::value;
 
     enum {
-      value = !(internal::is_reference_wrapper<T>::value ||
+      value = !(detail::is_reference_wrapper<T>::value ||
                 std::is_same<T, basic_string_view<char_type>>::value ||
-                std::is_same<T, internal::std_string_view<char_type>>::value ||
-                (mapped_type != internal::type::cstring_type &&
-                 mapped_type != internal::type::string_type &&
-                 mapped_type != internal::type::custom_type &&
-                 mapped_type != internal::type::named_arg_type))
+                std::is_same<T, detail::std_string_view<char_type>>::value ||
+                (mapped_type != detail::type::cstring_type &&
+                 mapped_type != detail::type::string_type &&
+                 mapped_type != detail::type::custom_type))
     };
   };
 
   template <typename T>
-  using stored_type = conditional_t<internal::is_string<T>::value,
+  using stored_type = conditional_t<detail::is_string<T>::value,
                                     std::basic_string<char_type>, T>;
 
   // Storage of basic_format_arg must be contiguous.
   std::vector<basic_format_arg<Context>> data_;
+  std::vector<detail::named_arg_info<char_type>> named_info_;
 
   // Storage of arguments not fitting into basic_format_arg must grow
   // without relocation because items in data_ refer to it.
-  internal::dynamic_arg_list dynamic_args_;
+  detail::dynamic_arg_list dynamic_args_;
 
   friend class basic_format_args<Context>;
 
   unsigned long long get_types() const {
-    return internal::is_unpacked_bit | data_.size();
+    return detail::is_unpacked_bit | data_.size() |
+           (named_info_.empty()
+                ? 0ULL
+                : static_cast<unsigned long long>(detail::has_named_args_bit));
+  }
+
+  const basic_format_arg<Context>* data() const {
+    return named_info_.empty() ? data_.data() : data_.data() + 1;
   }
 
   template <typename T> void emplace_arg(const T& arg) {
-    data_.emplace_back(internal::make_arg<Context>(arg));
+    data_.emplace_back(detail::make_arg<Context>(arg));
+  }
+
+  template <typename T>
+  void emplace_arg(const detail::named_arg<char_type, T>& arg) {
+    if (named_info_.empty()) {
+      constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
+      data_.insert(data_.begin(), {zero_ptr, 0});
+    }
+    data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
+    auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
+      data->pop_back();
+    };
+    std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
+        guard{&data_, pop_one};
+    named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
+    data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
+    guard.release();
   }
 
  public:
   /**
     \rst
-    Adds an argument into the dynamic store for later passing to a formating
+    Adds an argument into the dynamic store for later passing to a formatting
     function.
 
-    Note that custom types and string types (but not string views!) are copied
-    into the store with dynamic memory (in addition to resizing vector).
+    Note that custom types and string types (but not string views) are copied
+    into the store dynamically allocating memory if necessary.
 
     **Example**::
 
@@ -1439,25 +1541,78 @@ class dynamic_format_arg_store
     \endrst
   */
   template <typename T> void push_back(const T& arg) {
-    static_assert(
-        !std::is_base_of<internal::named_arg_base<char_type>, T>::value,
-        "named arguments are not supported yet");
-    if (internal::const_check(need_copy<T>::value))
+    if (detail::const_check(need_copy<T>::value))
       emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
     else
-      emplace_arg(arg);
+      emplace_arg(detail::unwrap(arg));
   }
 
   /**
+    \rst
     Adds a reference to the argument into the dynamic store for later passing to
-    a formating function.
+    a formatting function. Supports named arguments wrapped in
+    ``std::reference_wrapper`` via ``std::ref()``/``std::cref()``.
+
+    **Example**::
+
+      fmt::dynamic_format_arg_store<fmt::format_context> store;
+      char str[] = "1234567890";
+      store.push_back(std::cref(str));
+      int a1_val{42};
+      auto a1 = fmt::arg("a1_", a1_val);
+      store.push_back(std::cref(a1));
+
+      // Changing str affects the output but only for string and custom types.
+      str[0] = 'X';
+
+      std::string result = fmt::vformat("{} and {a1_}");
+      assert(result == "X234567890 and 42");
+    \endrst
   */
   template <typename T> void push_back(std::reference_wrapper<T> arg) {
     static_assert(
-        need_copy<T>::value,
+        detail::is_named_arg<typename std::remove_cv<T>::type>::value ||
+            need_copy<T>::value,
         "objects of built-in types and string views are always copied");
     emplace_arg(arg.get());
   }
+
+  /**
+    Adds named argument into the dynamic store for later passing to a formatting
+    function. ``std::reference_wrapper`` is supported to avoid copying of the
+    argument.
+  */
+  template <typename T>
+  void push_back(const detail::named_arg<char_type, T>& arg) {
+    const char_type* arg_name =
+        dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
+    if (detail::const_check(need_copy<T>::value)) {
+      emplace_arg(
+          fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
+    } else {
+      emplace_arg(fmt::arg(arg_name, arg.value));
+    }
+  }
+
+  /** Erase all elements from the store */
+  void clear() {
+    data_.clear();
+    named_info_.clear();
+    dynamic_args_ = detail::dynamic_arg_list();
+  }
+
+  /**
+    \rst
+    Reserves space to store at least *new_cap* arguments including
+    *new_cap_named* named arguments.
+    \endrst
+  */
+  void reserve(size_t new_cap, size_t new_cap_named) {
+    FMT_ASSERT(new_cap >= new_cap_named,
+               "Set of arguments includes set of named arguments");
+    data_.reserve(new_cap);
+    named_info_.reserve(new_cap_named);
+  }
 };
 
 /**
@@ -1476,49 +1631,40 @@ template <typename Context> class basic_format_args {
   using format_arg = basic_format_arg<Context>;
 
  private:
-  // To reduce compiled code size per formatting function call, types of first
-  // max_packed_args arguments are passed in the types_ field.
-  unsigned long long types_;
+  // A descriptor that contains information about formatting arguments.
+  // If the number of arguments is less or equal to max_packed_args then
+  // argument types are passed in the descriptor. This reduces binary code size
+  // per formatting function call.
+  unsigned long long desc_;
   union {
-    // If the number of arguments is less than max_packed_args, the argument
-    // values are stored in values_, otherwise they are stored in args_.
-    // This is done to reduce compiled code size as storing larger objects
+    // If is_packed() returns true then argument values are stored in values_;
+    // otherwise they are stored in args_. This is done to improve cache
+    // locality and reduce compiled code size since storing larger objects
     // may require more code (at least on x86-64) even if the same amount of
     // data is actually copied to stack. It saves ~10% on the bloat test.
-    const internal::value<Context>* values_;
+    const detail::value<Context>* values_;
     const format_arg* args_;
   };
 
-  bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
-
-  internal::type type(int index) const {
-    int shift = index * internal::packed_arg_bits;
-    unsigned int mask = (1 << internal::packed_arg_bits) - 1;
-    return static_cast<internal::type>((types_ >> shift) & mask);
+  bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; }
+  bool has_named_args() const {
+    return (desc_ & detail::has_named_args_bit) != 0;
   }
 
-  friend class internal::arg_map<Context>;
-
-  void set_data(const internal::value<Context>* values) { values_ = values; }
-  void set_data(const format_arg* args) { args_ = args; }
-
-  format_arg do_get(int index) const {
-    format_arg arg;
-    if (!is_packed()) {
-      auto num_args = max_size();
-      if (index < num_args) arg = args_[index];
-      return arg;
-    }
-    if (index > internal::max_packed_args) return arg;
-    arg.type_ = type(index);
-    if (arg.type_ == internal::type::none_type) return arg;
-    internal::value<Context>& val = arg.value_;
-    val = values_[index];
-    return arg;
+  detail::type type(int index) const {
+    int shift = index * detail::packed_arg_bits;
+    unsigned int mask = (1 << detail::packed_arg_bits) - 1;
+    return static_cast<detail::type>((desc_ >> shift) & mask);
   }
 
+  basic_format_args(unsigned long long desc,
+                    const detail::value<Context>* values)
+      : desc_(desc), values_(values) {}
+  basic_format_args(unsigned long long desc, const format_arg* args)
+      : desc_(desc), args_(args) {}
+
  public:
-  basic_format_args() : types_(0) {}
+  basic_format_args() : desc_(0) {}
 
   /**
    \rst
@@ -1526,10 +1672,8 @@ template <typename Context> class basic_format_args {
    \endrst
    */
   template <typename... Args>
-  basic_format_args(const format_arg_store<Context, Args...>& store)
-      : types_(store.types) {
-    set_data(store.data_);
-  }
+  FMT_INLINE basic_format_args(const format_arg_store<Context, Args...>& store)
+      : basic_format_args(store.desc, store.data_.args()) {}
 
   /**
    \rst
@@ -1537,10 +1681,8 @@ template <typename Context> class basic_format_args {
    `~fmt::dynamic_format_arg_store`.
    \endrst
    */
-  basic_format_args(const dynamic_format_arg_store<Context>& store)
-      : types_(store.get_types()) {
-    set_data(store.data_.data());
-  }
+  FMT_INLINE basic_format_args(const dynamic_format_arg_store<Context>& store)
+      : basic_format_args(store.get_types(), store.data()) {}
 
   /**
    \rst
@@ -1548,22 +1690,42 @@ template <typename Context> class basic_format_args {
    \endrst
    */
   basic_format_args(const format_arg* args, int count)
-      : types_(internal::is_unpacked_bit | internal::to_unsigned(count)) {
-    set_data(args);
-  }
+      : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
+                          args) {}
 
-  /** Returns the argument at specified index. */
-  format_arg get(int index) const {
-    format_arg arg = do_get(index);
-    if (arg.type_ == internal::type::named_arg_type)
-      arg = arg.value_.named_arg->template deserialize<Context>();
+  /** Returns the argument with the specified id. */
+  format_arg get(int id) const {
+    format_arg arg;
+    if (!is_packed()) {
+      if (id < max_size()) arg = args_[id];
+      return arg;
+    }
+    if (id >= detail::max_packed_args) return arg;
+    arg.type_ = type(id);
+    if (arg.type_ == detail::type::none_type) return arg;
+    arg.value_ = values_[id];
     return arg;
   }
 
+  template <typename Char> format_arg get(basic_string_view<Char> name) const {
+    int id = get_id(name);
+    return id >= 0 ? get(id) : format_arg();
+  }
+
+  template <typename Char> int get_id(basic_string_view<Char> name) const {
+    if (!has_named_args()) return {};
+    const auto& named_args =
+        (is_packed() ? values_[-1] : args_[-1].value_).named_args;
+    for (size_t i = 0; i < named_args.size; ++i) {
+      if (named_args.data[i].name == name) return named_args.data[i].id;
+    }
+    return -1;
+  }
+
   int max_size() const {
-    unsigned long long max_packed = internal::max_packed_args;
+    unsigned long long max_packed = detail::max_packed_args;
     return static_cast<int>(is_packed() ? max_packed
-                                        : types_ & ~internal::is_unpacked_bit);
+                                        : desc_ & ~detail::is_unpacked_bit);
   }
 };
 
@@ -1571,93 +1733,48 @@ template <typename Context> class basic_format_args {
 // It is a separate type rather than an alias to make symbols readable.
 struct format_args : basic_format_args<format_context> {
   template <typename... Args>
-  format_args(Args&&... args)
-      : basic_format_args<format_context>(static_cast<Args&&>(args)...) {}
+  FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {}
 };
 struct wformat_args : basic_format_args<wformat_context> {
-  template <typename... Args>
-  wformat_args(Args&&... args)
-      : basic_format_args<wformat_context>(static_cast<Args&&>(args)...) {}
+  using basic_format_args::basic_format_args;
 };
 
-template <typename Container> struct is_contiguous : std::false_type {};
-
-template <typename Char>
-struct is_contiguous<std::basic_string<Char>> : std::true_type {};
-
-template <typename Char>
-struct is_contiguous<internal::buffer<Char>> : std::true_type {};
-
-namespace internal {
-
-template <typename OutputIt>
-struct is_contiguous_back_insert_iterator : std::false_type {};
-template <typename Container>
-struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
-    : is_contiguous<Container> {};
-
-template <typename Char> struct named_arg_base {
-  basic_string_view<Char> name;
-
-  // Serialized value<context>.
-  mutable char data[sizeof(basic_format_arg<buffer_context<Char>>)];
-
-  named_arg_base(basic_string_view<Char> nm) : name(nm) {}
-
-  template <typename Context> basic_format_arg<Context> deserialize() const {
-    basic_format_arg<Context> arg;
-    std::memcpy(&arg, data, sizeof(basic_format_arg<Context>));
-    return arg;
-  }
-};
-
-struct view {};
-
-template <typename T, typename Char>
-struct named_arg : view, named_arg_base<Char> {
-  const T& value;
-
-  named_arg(basic_string_view<Char> name, const T& val)
-      : named_arg_base<Char>(name), value(val) {}
-};
+namespace detail {
 
+// Reports a compile-time error if S is not a valid format string.
 template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
-inline void check_format_string(const S&) {
-#if defined(FMT_ENFORCE_COMPILE_STRING)
+FMT_INLINE void check_format_string(const S&) {
+#ifdef FMT_ENFORCE_COMPILE_STRING
   static_assert(is_compile_string<S>::value,
-                "FMT_ENFORCE_COMPILE_STRING requires all format strings to "
-                "utilize FMT_STRING() or fmt().");
+                "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
+                "FMT_STRING.");
 #endif
 }
 template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
 void check_format_string(S);
 
-template <bool...> struct bool_pack;
-template <bool... Args>
-using all_true =
-    std::is_same<bool_pack<Args..., true>, bool_pack<true, Args...>>;
-
 template <typename... Args, typename S, typename Char = char_t<S>>
 inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
 make_args_checked(const S& format_str,
                   const remove_reference_t<Args>&... args) {
-  static_assert(
-      all_true<(!std::is_base_of<view, remove_reference_t<Args>>::value ||
-                !std::is_reference<Args>::value)...>::value,
-      "passing views as lvalues is disallowed");
+  static_assert(count<(std::is_base_of<view, remove_reference_t<Args>>::value &&
+                       std::is_reference<Args>::value)...>() == 0,
+                "passing views as lvalues is disallowed");
   check_format_string<Args...>(format_str);
   return {args...};
 }
 
-template <typename Char>
+template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 std::basic_string<Char> vformat(
     basic_string_view<Char> format_str,
     basic_format_args<buffer_context<type_identity_t<Char>>> args);
 
+FMT_API std::string vformat(string_view format_str, format_args args);
+
 template <typename Char>
-typename buffer_context<Char>::iterator vformat_to(
+typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
     buffer<Char>& buf, basic_string_view<Char> format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args);
+    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args);
 
 template <typename Char, typename Args,
           FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
@@ -1667,58 +1784,38 @@ FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
 #ifndef _WIN32
 inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
 #endif
-}  // namespace internal
-
-/**
-  \rst
-  Returns a named argument to be used in a formatting function. It should only
-  be used in a call to a formatting function.
-
-  **Example**::
-
-    fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
-  \endrst
- */
-template <typename S, typename T, typename Char = char_t<S>>
-inline internal::named_arg<T, Char> arg(const S& name, const T& arg) {
-  static_assert(internal::is_string<S>::value, "");
-  return {name, arg};
-}
-
-// Disable nested named arguments, e.g. ``arg("a", arg("b", 42))``.
-template <typename S, typename T, typename Char>
-void arg(S, internal::named_arg<T, Char>) = delete;
+}  // namespace detail
 
 /** Formats a string and writes the output to ``out``. */
 // GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
 // vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
-template <typename OutputIt, typename S, typename Char = char_t<S>,
-          FMT_ENABLE_IF(
-              internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
+template <
+    typename OutputIt, typename S, typename Char = char_t<S>,
+    FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
 OutputIt vformat_to(
     OutputIt out, const S& format_str,
     basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  using container = remove_reference_t<decltype(internal::get_container(out))>;
-  internal::container_buffer<container> buf((internal::get_container(out)));
-  internal::vformat_to(buf, to_string_view(format_str), args);
+  auto& c = detail::get_container(out);
+  detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
+  detail::vformat_to(buf, to_string_view(format_str), args);
   return out;
 }
 
 template <typename Container, typename S, typename... Args,
           FMT_ENABLE_IF(
-              is_contiguous<Container>::value&& internal::is_string<S>::value)>
+              is_contiguous<Container>::value&& detail::is_string<S>::value)>
 inline std::back_insert_iterator<Container> format_to(
     std::back_insert_iterator<Container> out, const S& format_str,
     Args&&... args) {
   return vformat_to(out, to_string_view(format_str),
-                    internal::make_args_checked<Args...>(format_str, args...));
+                    detail::make_args_checked<Args...>(format_str, args...));
 }
 
 template <typename S, typename Char = char_t<S>>
-inline std::basic_string<Char> vformat(
+FMT_INLINE std::basic_string<Char> vformat(
     const S& format_str,
     basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  return internal::vformat(to_string_view(format_str), args);
+  return detail::vformat(to_string_view(format_str), args);
 }
 
 /**
@@ -1734,10 +1831,9 @@ inline std::basic_string<Char> vformat(
 // Pass char_t as a default template parameter instead of using
 // std::basic_string<char_t<S>> to reduce the symbol size.
 template <typename S, typename... Args, typename Char = char_t<S>>
-inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
-  return internal::vformat(
-      to_string_view(format_str),
-      internal::make_args_checked<Args...>(format_str, args...));
+FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
+  const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
+  return detail::vformat(to_string_view(format_str), vargs);
 }
 
 FMT_API void vprint(string_view, format_args);
@@ -1756,12 +1852,10 @@ FMT_API void vprint(std::FILE*, string_view, format_args);
  */
 template <typename S, typename... Args, typename Char = char_t<S>>
 inline void print(std::FILE* f, const S& format_str, Args&&... args) {
-  return internal::is_unicode<Char>()
-             ? vprint(f, to_string_view(format_str),
-                      internal::make_args_checked<Args...>(format_str, args...))
-             : internal::vprint_mojibake(
-                   f, to_string_view(format_str),
-                   internal::make_args_checked<Args...>(format_str, args...));
+  const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
+  return detail::is_unicode<Char>()
+             ? vprint(f, to_string_view(format_str), vargs)
+             : detail::vprint_mojibake(f, to_string_view(format_str), vargs);
 }
 
 /**
@@ -1777,12 +1871,11 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
  */
 template <typename S, typename... Args, typename Char = char_t<S>>
 inline void print(const S& format_str, Args&&... args) {
-  return internal::is_unicode<Char>()
-             ? vprint(to_string_view(format_str),
-                      internal::make_args_checked<Args...>(format_str, args...))
-             : internal::vprint_mojibake(
-                   stdout, to_string_view(format_str),
-                   internal::make_args_checked<Args...>(format_str, args...));
+  const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
+  return detail::is_unicode<Char>()
+             ? vprint(to_string_view(format_str), vargs)
+             : detail::vprint_mojibake(stdout, to_string_view(format_str),
+                                       vargs);
 }
 FMT_END_NAMESPACE
 
index f632714d81e9ae9855a686f657bef1c861f9ea3c..d8c9c8a5ee58b4f1fd3073c5aea25ae370208ed5 100644 (file)
@@ -15,6 +15,7 @@
 #include <cstdarg>
 #include <cstring>  // for std::memmove
 #include <cwchar>
+#include <exception>
 
 #include "format.h"
 #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
 #endif
 
 #ifdef _WIN32
+#  if !defined(NOMINMAX) && !defined(WIN32_LEAN_AND_MEAN)
+#    define NOMINMAX
+#    define WIN32_LEAN_AND_MEAN
+#    include <windows.h>
+#    undef WIN32_LEAN_AND_MEAN
+#    undef NOMINMAX
+#  else
+#    include <windows.h>
+#  endif
 #  include <io.h>
-#  include <windows.h>
 #endif
 
 #ifdef _MSC_VER
 
 // 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 {}; }
-inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; }
+inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; }
+inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; }
 
 FMT_BEGIN_NAMESPACE
-namespace internal {
+namespace detail {
 
 FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
-  print(stderr, "{}:{}: assertion failed: {}", file, line, message);
-  std::abort();
+  // Use unchecked std::fprintf to avoid triggering another assertion when
+  // writing to stderr fails
+  std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
+  // Chosen instead of std::abort to satisfy Clang in CUDA mode during device
+  // code pass.
+  std::terminate();
 }
 
 #ifndef _MSC_VER
@@ -67,14 +80,14 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
 //   other  - failure
 // Buffer should be at least of size 1.
 FMT_FUNC int safe_strerror(int error_code, char*& buffer,
-                           std::size_t buffer_size) FMT_NOEXCEPT {
+                           size_t buffer_size) FMT_NOEXCEPT {
   FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
 
   class dispatcher {
    private:
     int error_code_;
     char*& buffer_;
-    std::size_t buffer_size_;
+    size_t buffer_size_;
 
     // A noop assignment operator to avoid bogus warnings.
     void operator=(const dispatcher&) {}
@@ -97,7 +110,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
 
     // Handle the case when strerror_r is not available.
     FMT_MAYBE_UNUSED
-    int handle(internal::null<>) {
+    int handle(detail::null<>) {
       return fallback(strerror_s(buffer_, buffer_size_, error_code_));
     }
 
@@ -111,7 +124,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
 
 #if !FMT_MSC_VER
     // Fallback to strerror if strerror_r and strerror_s are not available.
-    int fallback(internal::null<>) {
+    int fallback(detail::null<>) {
       errno = 0;
       buffer_ = strerror(error_code_);
       return errno;
@@ -119,7 +132,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
 #endif
 
    public:
-    dispatcher(int err_code, char*& buf, std::size_t buf_size)
+    dispatcher(int err_code, char*& buf, size_t buf_size)
         : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
 
     int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
@@ -127,7 +140,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
   return dispatcher(error_code, buffer, buffer_size).run();
 }
 
-FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
+FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
                                 string_view message) FMT_NOEXCEPT {
   // Report error code making sure that the output fits into
   // inline_buffer_size to avoid dynamic memory allocation and potential
@@ -136,20 +149,17 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
   static const char SEP[] = ": ";
   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;
+  size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
   auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
-  if (internal::is_negative(error_code)) {
+  if (detail::is_negative(error_code)) {
     abs_value = 0 - abs_value;
     ++error_code_size;
   }
-  error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
-  internal::writer w(out);
-  if (message.size() <= inline_buffer_size - error_code_size) {
-    w.write(message);
-    w.write(SEP);
-  }
-  w.write(ERROR_STR);
-  w.write(error_code);
+  error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
+  auto it = std::back_inserter(out);
+  if (message.size() <= inline_buffer_size - error_code_size)
+    format_to(it, "{}{}", message, SEP);
+  format_to(it, "{}{}", ERROR_STR, error_code);
   assert(out.size() <= inline_buffer_size);
 }
 
@@ -168,10 +178,10 @@ FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
   size_t written = std::fwrite(ptr, size, count, stream);
   if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
 }
-}  // namespace internal
+}  // namespace detail
 
 #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
-namespace internal {
+namespace detail {
 
 template <typename Locale>
 locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
@@ -194,18 +204,16 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
   return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
       .decimal_point();
 }
-}  // namespace internal
+}  // namespace detail
 #else
 template <typename Char>
-FMT_FUNC std::string internal::grouping_impl(locale_ref) {
+FMT_FUNC std::string detail::grouping_impl(locale_ref) {
   return "\03";
 }
-template <typename Char>
-FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
+template <typename Char> FMT_FUNC Char detail::thousands_sep_impl(locale_ref) {
   return FMT_STATIC_THOUSANDS_SEPARATOR;
 }
-template <typename Char>
-FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
+template <typename Char> FMT_FUNC Char detail::decimal_point_impl(locale_ref) {
   return '.';
 }
 #endif
@@ -222,9 +230,9 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
   base = std::runtime_error(to_string(buffer));
 }
 
-namespace internal {
+namespace detail {
 
-template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
+template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
   // fallback_uintptr is always stored in little endian.
   int i = static_cast<int>(sizeof(void*)) - 1;
   while (i > 0 && n.value[i] == 0) --i;
@@ -233,12 +241,27 @@ template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
 }
 
 template <typename T>
-const char basic_data<T>::digits[] =
-    "0001020304050607080910111213141516171819"
-    "2021222324252627282930313233343536373839"
-    "4041424344454647484950515253545556575859"
-    "6061626364656667686970717273747576777879"
-    "8081828384858687888990919293949596979899";
+const typename basic_data<T>::digit_pair basic_data<T>::digits[] = {
+    {'0', '0'},  {'0', '1'},  {'0', '2'},  {'0', '3'},  {'0', '4'},
+    {'0', '5'},  {'0', '6'},  {'0', '7'},  {'0', '8'},  {'0', '9'},
+    {'1', '0'},  {'1', '1'},  {'1', '2'},  {'1', '3'},  {'1', '4'},
+    {'1', '5'},  {'1', '6'},  {'1', '7'},  {'1', '8'},  {'1', '9'},
+    {'2', '0'},  {'2', '1'},  {'2', '2'},  {'2', '3'},  {'2', '4'},
+    {'2', '5'},  {'2', '6'},  {'2', '7'},  {'2', '8'},  {'2', '9'},
+    {'3', '0'},  {'3', '1'},  {'3', '2'},  {'3', '3'},  {'3', '4'},
+    {'3', '5'},  {'3', '6'},  {'3', '7'},  {'3', '8'},  {'3', '9'},
+    {'4', '0'},  {'4', '1'},  {'4', '2'},  {'4', '3'},  {'4', '4'},
+    {'4', '5'},  {'4', '6'},  {'4', '7'},  {'4', '8'},  {'4', '9'},
+    {'5', '0'},  {'5', '1'},  {'5', '2'},  {'5', '3'},  {'5', '4'},
+    {'5', '5'},  {'5', '6'},  {'5', '7'},  {'5', '8'},  {'5', '9'},
+    {'6', '0'},  {'6', '1'},  {'6', '2'},  {'6', '3'},  {'6', '4'},
+    {'6', '5'},  {'6', '6'},  {'6', '7'},  {'6', '8'},  {'6', '9'},
+    {'7', '0'},  {'7', '1'},  {'7', '2'},  {'7', '3'},  {'7', '4'},
+    {'7', '5'},  {'7', '6'},  {'7', '7'},  {'7', '8'},  {'7', '9'},
+    {'8', '0'},  {'8', '1'},  {'8', '2'},  {'8', '3'},  {'8', '4'},
+    {'8', '5'},  {'8', '6'},  {'8', '7'},  {'8', '8'},  {'8', '9'},
+    {'9', '0'},  {'9', '1'},  {'9', '2'},  {'9', '3'},  {'9', '4'},
+    {'9', '5'},  {'9', '6'},  {'9', '7'},  {'9', '8'},  {'9', '9'}};
 
 template <typename T>
 const char basic_data<T>::hex_digits[] = "0123456789abcdef";
@@ -317,6 +340,10 @@ 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>
+const char basic_data<T>::left_padding_shifts[] = {31, 31, 0, 1, 0};
+template <typename T>
+const char basic_data<T>::right_padding_shifts[] = {0, 31, 0, 1, 0};
 
 template <typename T> struct bits {
   static FMT_CONSTEXPR_DECL const int value =
@@ -576,9 +603,10 @@ class bigint {
   void operator=(const bigint&) = delete;
 
   void assign(const bigint& other) {
-    bigits_.resize(other.bigits_.size());
+    auto size = other.bigits_.size();
+    bigits_.resize(size);
     auto data = other.bigits_.data();
-    std::copy(data, data + other.bigits_.size(), bigits_.data());
+    std::copy(data, data + size, make_checked(bigits_.data(), size));
     exp_ = other.exp_;
   }
 
@@ -594,7 +622,7 @@ class bigint {
 
   int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
 
-  bigint& operator<<=(int shift) {
+  FMT_NOINLINE bigint& operator<<=(int shift) {
     assert(shift >= 0);
     exp_ += shift / bigit_bits;
     shift %= bigit_bits;
@@ -1125,7 +1153,7 @@ int snprintf_float(T value, int precision, float_specs specs,
     precision = (precision >= 0 ? precision : 6) - 1;
 
   // Build the format string.
-  enum { max_format_size = 7 };  // Ths longest format is "%#.*Le".
+  enum { max_format_size = 7 };  // The longest format is "%#.*Le".
   char format[max_format_size];
   char* format_ptr = format;
   *format_ptr++ = '%';
@@ -1145,13 +1173,13 @@ int snprintf_float(T value, int precision, float_specs specs,
   for (;;) {
     auto begin = buf.data() + offset;
     auto capacity = buf.capacity() - offset;
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+#ifdef FMT_FUZZ
     if (precision > 100000)
       throw std::runtime_error(
           "fuzz mode - avoid large allocation inside snprintf");
 #endif
     // Suppress the warning about a nonliteral format string.
-    // Cannot use auto becase of a bug in MinGW (#1532).
+    // Cannot use auto because of a bug in MinGW (#1532).
     int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
     int result = precision >= 0
                      ? snprintf_ptr(begin, capacity, format, precision, value)
@@ -1268,14 +1296,14 @@ FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
 
   return next;
 }
-}  // namespace internal
+}  // namespace detail
 
-template <> struct formatter<internal::bigint> {
+template <> struct formatter<detail::bigint> {
   format_parse_context::iterator parse(format_parse_context& ctx) {
     return ctx.begin();
   }
 
-  format_context::iterator format(const internal::bigint& n,
+  format_context::iterator format(const detail::bigint& n,
                                   format_context& ctx) {
     auto out = ctx.out();
     bool first = true;
@@ -1289,12 +1317,12 @@ template <> struct formatter<internal::bigint> {
       out = format_to(out, "{:08x}", value);
     }
     if (n.exp_ > 0)
-      out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits);
+      out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits);
     return out;
   }
 };
 
-FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
+FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
   auto transcode = [this](const char* p) {
     auto cp = uint32_t();
     auto error = 0;
@@ -1325,7 +1353,7 @@ FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
   buffer_.push_back(0);
 }
 
-FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
+FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
                                   string_view message) FMT_NOEXCEPT {
   FMT_TRY {
     memory_buffer buf;
@@ -1333,12 +1361,9 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
     for (;;) {
       char* system_message = &buf[0];
       int result =
-          internal::safe_strerror(error_code, system_message, buf.size());
+          detail::safe_strerror(error_code, system_message, buf.size());
       if (result == 0) {
-        internal::writer w(out);
-        w.write(message);
-        w.write(": ");
-        w.write(system_message);
+        format_to(std::back_inserter(out), "{}: {}", message, system_message);
         return;
       }
       if (result != ERANGE)
@@ -1350,7 +1375,7 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
   format_error_code(out, error_code, message);
 }
 
-FMT_FUNC void internal::error_handler::on_error(const char* message) {
+FMT_FUNC void detail::error_handler::on_error(const char* message) {
   FMT_THROW(format_error(message));
 }
 
@@ -1359,14 +1384,39 @@ FMT_FUNC void report_system_error(int error_code,
   report_error(format_system_error, error_code, message);
 }
 
+struct stringifier {
+  template <typename T> FMT_INLINE std::string operator()(T value) const {
+    return to_string(value);
+  }
+  std::string operator()(basic_format_arg<format_context>::handle h) const {
+    memory_buffer buf;
+    detail::buffer<char>& base = buf;
+    format_parse_context parse_ctx({});
+    format_context format_ctx(std::back_inserter(base), {}, {});
+    h.format(parse_ctx, format_ctx);
+    return to_string(buf);
+  }
+};
+
+FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) {
+  if (format_str.size() == 2 && equal2(format_str.data(), "{}")) {
+    auto arg = args.get(0);
+    if (!arg) error_handler().on_error("argument not found");
+    return visit_format_arg(stringifier(), arg);
+  }
+  memory_buffer buffer;
+  detail::vformat_to(buffer, format_str, args);
+  return to_string(buffer);
+}
+
 FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
   memory_buffer buffer;
-  internal::vformat_to(buffer, format_str,
-                       basic_format_args<buffer_context<char>>(args));
+  detail::vformat_to(buffer, format_str,
+                     basic_format_args<buffer_context<char>>(args));
 #ifdef _WIN32
   auto fd = _fileno(f);
   if (_isatty(fd)) {
-    internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
+    detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
     auto written = DWORD();
     if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
                        u16.c_str(), static_cast<DWORD>(u16.size()), &written,
@@ -1376,16 +1426,16 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
     return;
   }
 #endif
-  internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
+  detail::fwrite_fully(buffer.data(), 1, buffer.size(), f);
 }
 
 #ifdef _WIN32
 // Print assuming legacy (non-Unicode) encoding.
-FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
-                                        format_args args) {
+FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
+                                      format_args args) {
   memory_buffer buffer;
-  internal::vformat_to(buffer, format_str,
-                       basic_format_args<buffer_context<char>>(args));
+  detail::vformat_to(buffer, format_str,
+                     basic_format_args<buffer_context<char>>(args));
   fwrite_fully(buffer.data(), 1, buffer.size(), f);
 }
 #endif
index 4e96539fa2a078c093d77e9d30a83752339fda24..b1ba07136f5aaf8323a0749a8e9011e43cdf37c8 100644 (file)
 
 #include "core.h"
 
-#ifdef FMT_DEPRECATED_INCLUDE_OS
-#  include "os.h"
-#endif
-
 #ifdef __INTEL_COMPILER
 #  define FMT_ICC_VERSION __INTEL_COMPILER
 #elif defined(__ICL)
@@ -76,7 +72,8 @@
 #if __cplusplus == 201103L || __cplusplus == 201402L
 #  if defined(__clang__)
 #    define FMT_FALLTHROUGH [[clang::fallthrough]]
-#  elif FMT_GCC_VERSION >= 700 && !defined(__PGI)
+#  elif FMT_GCC_VERSION >= 700 && !defined(__PGI) && \
+      (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
 #    define FMT_FALLTHROUGH [[gnu::fallthrough]]
 #  else
 #    define FMT_FALLTHROUGH
 #  define FMT_FALLTHROUGH
 #endif
 
+#ifndef FMT_MAYBE_UNUSED
+#  if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
+#    define FMT_MAYBE_UNUSED [[maybe_unused]]
+#  else
+#    define FMT_MAYBE_UNUSED
+#  endif
+#endif
+
 #ifndef FMT_THROW
 #  if FMT_EXCEPTIONS
 #    if FMT_MSC_VER || FMT_NVCC
 FMT_BEGIN_NAMESPACE
-namespace internal {
+namespace detail {
 template <typename Exception> inline void do_throw(const Exception& x) {
   // Silence unreachable code warnings in MSVC and NVCC because these
   // are nearly impossible to fix in a generic code.
   volatile bool b = true;
   if (b) throw x;
 }
-}  // namespace internal
+}  // namespace detail
 FMT_END_NAMESPACE
-#      define FMT_THROW(x) internal::do_throw(x)
+#      define FMT_THROW(x) detail::do_throw(x)
 #    else
 #      define FMT_THROW(x) throw x
 #    endif
@@ -123,11 +128,10 @@ FMT_END_NAMESPACE
 #endif
 
 #ifndef FMT_USE_USER_DEFINED_LITERALS
-// For Intel and NVIDIA compilers both they and the system gcc/msc support UDLs.
-#  if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 ||      \
-       FMT_MSC_VER >= 1900) &&                                              \
-      (!(FMT_ICC_VERSION || FMT_CUDA_VERSION) || FMT_ICC_VERSION >= 1500 || \
-       FMT_CUDA_VERSION >= 700)
+// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
+#  if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
+       FMT_MSC_VER >= 1900) &&                                         \
+      (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
 #    define FMT_USE_USER_DEFINED_LITERALS 1
 #  else
 #    define FMT_USE_USER_DEFINED_LITERALS 0
@@ -135,12 +139,11 @@ FMT_END_NAMESPACE
 #endif
 
 #ifndef FMT_USE_UDL_TEMPLATE
-// EDG front end based compilers (icc, nvcc) and GCC < 6.4 do not propertly
+// EDG frontend based compilers (icc, nvcc, etc) and GCC < 6.4 do not properly
 // support UDL templates and GCC >= 9 warns about them.
-#  if FMT_USE_USER_DEFINED_LITERALS && FMT_ICC_VERSION == 0 && \
-      FMT_CUDA_VERSION == 0 &&                                 \
-      ((FMT_GCC_VERSION >= 604 && FMT_GCC_VERSION <= 900 &&    \
-        __cplusplus >= 201402L) ||                             \
+#  if FMT_USE_USER_DEFINED_LITERALS &&                         \
+      (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 501) && \
+      ((FMT_GCC_VERSION >= 604 && __cplusplus >= 201402L) ||   \
        FMT_CLANG_VERSION >= 304)
 #    define FMT_USE_UDL_TEMPLATE 1
 #  else
@@ -176,7 +179,7 @@ FMT_END_NAMESPACE
 #  include <intrin.h>  // _BitScanReverse, _BitScanReverse64
 
 FMT_BEGIN_NAMESPACE
-namespace internal {
+namespace detail {
 // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
 #  ifndef __clang__
 #    pragma intrinsic(_BitScanReverse)
@@ -189,10 +192,10 @@ inline uint32_t clz(uint32_t x) {
   // 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)
+  FMT_SUPPRESS_MSC_WARNING(6102)
   return 31 - r;
 }
-#  define FMT_BUILTIN_CLZ(n) internal::clz(n)
+#  define FMT_BUILTIN_CLZ(n) detail::clz(n)
 
 #  if defined(_WIN64) && !defined(__clang__)
 #    pragma intrinsic(_BitScanReverse64)
@@ -214,26 +217,21 @@ inline uint32_t clzll(uint64_t x) {
   // 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)
+  FMT_SUPPRESS_MSC_WARNING(6102)
   return 63 - r;
 }
-#  define FMT_BUILTIN_CLZLL(n) internal::clzll(n)
-}  // namespace internal
+#  define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
+}  // namespace detail
 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
+#ifndef FMT_DEPRECATED_NUMERIC_ALIGN
+#  define FMT_DEPRECATED_NUMERIC_ALIGN 0
 #endif
 
 FMT_BEGIN_NAMESPACE
-namespace internal {
+namespace detail {
 
 // An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
 // undefined behavior (e.g. due to type aliasing).
@@ -285,14 +283,31 @@ template <typename T> constexpr T max_value() {
 template <typename T> constexpr int num_bits() {
   return std::numeric_limits<T>::digits;
 }
+// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
+template <> constexpr int num_bits<int128_t>() { return 128; }
+template <> constexpr int num_bits<uint128_t>() { return 128; }
 template <> constexpr int num_bits<fallback_uintptr>() {
   return static_cast<int>(sizeof(void*) *
                           std::numeric_limits<unsigned char>::digits);
 }
 
+FMT_INLINE void assume(bool condition) {
+  (void)condition;
+#if FMT_HAS_BUILTIN(__builtin_assume)
+  __builtin_assume(condition);
+#endif
+}
+
+// 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; };
+
+template <typename... Ts>
+using void_t = typename detail::void_t_impl<Ts...>::type;
+
 // An approximation of iterator_t for pre-C++20 systems.
 template <typename T>
 using iterator_t = decltype(std::begin(std::declval<T&>()));
+template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
 
 // Detect the iterator category of *any* given type in a SFINAE-friendly way.
 // Unfortunately, older implementations of std::iterator_traits are not safe
@@ -339,25 +354,39 @@ inline typename Container::value_type* get_data(Container& c) {
 #if defined(_SECURE_SCL) && _SECURE_SCL
 // Make a checked iterator to avoid MSVC warnings.
 template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
-template <typename T> checked_ptr<T> make_checked(T* p, std::size_t size) {
+template <typename T> checked_ptr<T> make_checked(T* p, size_t size) {
   return {p, size};
 }
 #else
 template <typename T> using checked_ptr = T*;
-template <typename T> inline T* make_checked(T* p, std::size_t) { return p; }
+template <typename T> inline T* make_checked(T* p, size_t) { return p; }
 #endif
 
 template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
-inline checked_ptr<typename Container::value_type> reserve(
-    std::back_insert_iterator<Container>& it, std::size_t n) {
+#if FMT_CLANG_VERSION
+__attribute__((no_sanitize("undefined")))
+#endif
+inline checked_ptr<typename Container::value_type>
+reserve(std::back_insert_iterator<Container> it, size_t n) {
   Container& c = get_container(it);
-  std::size_t size = c.size();
+  size_t size = c.size();
   c.resize(size + n);
   return make_checked(get_data(c) + size, n);
 }
 
+template <typename Iterator> inline Iterator& reserve(Iterator& it, size_t) {
+  return it;
+}
+
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+inline std::back_insert_iterator<Container> base_iterator(
+    std::back_insert_iterator<Container>& it,
+    checked_ptr<typename Container::value_type>) {
+  return it;
+}
+
 template <typename Iterator>
-inline Iterator& reserve(Iterator& it, std::size_t) {
+inline Iterator base_iterator(Iterator, Iterator it) {
   return it;
 }
 
@@ -365,7 +394,7 @@ inline Iterator& reserve(Iterator& it, std::size_t) {
 // discards them.
 class counting_iterator {
  private:
-  std::size_t count_;
+  size_t count_;
 
  public:
   using iterator_category = std::output_iterator_tag;
@@ -380,7 +409,7 @@ class counting_iterator {
 
   counting_iterator() : count_(0) {}
 
-  std::size_t count() const { return count_; }
+  size_t count() const { return count_; }
 
   counting_iterator& operator++() {
     ++count_;
@@ -399,10 +428,10 @@ class counting_iterator {
 template <typename OutputIt> class truncating_iterator_base {
  protected:
   OutputIt out_;
-  std::size_t limit_;
-  std::size_t count_;
+  size_t limit_;
+  size_t count_;
 
-  truncating_iterator_base(OutputIt out, std::size_t limit)
+  truncating_iterator_base(OutputIt out, size_t limit)
       : out_(out), limit_(limit), count_(0) {}
 
  public:
@@ -415,7 +444,7 @@ template <typename OutputIt> class truncating_iterator_base {
       truncating_iterator_base;  // Mark iterator as checked.
 
   OutputIt base() const { return out_; }
-  std::size_t count() const { return count_; }
+  size_t count() const { return count_; }
 };
 
 // An output iterator that truncates the output and counts the number of objects
@@ -433,7 +462,7 @@ class truncating_iterator<OutputIt, std::false_type>
  public:
   using value_type = typename truncating_iterator_base<OutputIt>::value_type;
 
-  truncating_iterator(OutputIt out, std::size_t limit)
+  truncating_iterator(OutputIt out, size_t limit)
       : truncating_iterator_base<OutputIt>(out, limit) {}
 
   truncating_iterator& operator++() {
@@ -456,7 +485,7 @@ template <typename OutputIt>
 class truncating_iterator<OutputIt, std::true_type>
     : public truncating_iterator_base<OutputIt> {
  public:
-  truncating_iterator(OutputIt out, std::size_t limit)
+  truncating_iterator(OutputIt out, size_t limit)
       : truncating_iterator_base<OutputIt>(out, limit) {}
 
   template <typename T> truncating_iterator& operator=(T val) {
@@ -469,22 +498,6 @@ class truncating_iterator<OutputIt, std::true_type>
   truncating_iterator& operator*() { return *this; }
 };
 
-// A range with the specified output iterator and value type.
-template <typename OutputIt, typename T = typename OutputIt::value_type>
-class output_range {
- private:
-  OutputIt it_;
-
- public:
-  using value_type = T;
-  using iterator = OutputIt;
-  struct sentinel {};
-
-  explicit output_range(OutputIt it) : it_(it) {}
-  OutputIt begin() const { return it_; }
-  sentinel end() const { return {}; }  // Sentinel is not used yet.
-};
-
 template <typename Char>
 inline size_t count_code_points(basic_string_view<Char> s) {
   return s.size();
@@ -523,8 +536,6 @@ inline size_t code_point_index(basic_string_view<char8_type> s, size_t n) {
   return s.size();
 }
 
-inline char8_type to_char8_t(char c) { return static_cast<char8_type>(c); }
-
 template <typename InputIt, typename OutChar>
 using needs_conversion = bool_constant<
     std::is_same<typename std::iterator_traits<InputIt>::value_type,
@@ -540,7 +551,8 @@ OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
 template <typename OutChar, typename InputIt, typename OutputIt,
           FMT_ENABLE_IF(needs_conversion<InputIt, OutChar>::value)>
 OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
-  return std::transform(begin, end, it, to_char8_t);
+  return std::transform(begin, end, it,
+                        [](char c) { return static_cast<char8_type>(c); });
 }
 
 #ifndef FMT_USE_GRISU
@@ -555,43 +567,13 @@ template <typename T> constexpr bool use_grisu() {
 template <typename T>
 template <typename U>
 void buffer<T>::append(const U* begin, const U* end) {
-  std::size_t new_size = size_ + to_unsigned(end - begin);
+  size_t new_size = size_ + to_unsigned(end - begin);
   reserve(new_size);
-  std::uninitialized_copy(begin, end, make_checked(ptr_, capacity_) + size_);
+  std::uninitialized_copy(begin, end,
+                          make_checked(ptr_ + size_, capacity_ - size_));
   size_ = new_size;
 }
-}  // 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)) {}
-};
-
-class FMT_DEPRECATED u8string_view
-    : public basic_string_view<internal::char8_type> {
- public:
-  u8string_view(const char* s)
-      : basic_string_view<internal::char8_type>(
-            reinterpret_cast<const internal::char8_type*>(s)) {}
-  u8string_view(const char* s, size_t count) FMT_NOEXCEPT
-      : basic_string_view<internal::char8_type>(
-            reinterpret_cast<const internal::char8_type*>(s), count) {}
-};
-
-#if FMT_USE_USER_DEFINED_LITERALS
-inline namespace literals {
-FMT_DEPRECATED inline basic_string_view<internal::char8_type> operator"" _u(
-    const char* s, std::size_t n) {
-  return {reinterpret_cast<const internal::char8_type*>(s), n};
-}
-}  // namespace literals
-#endif
+}  // namespace detail
 
 // The number of characters to store in the basic_memory_buffer object itself
 // to avoid dynamic memory allocation.
@@ -626,27 +608,30 @@ enum { inline_buffer_size = 500 };
   The output can be converted to an ``std::string`` with ``to_string(out)``.
   \endrst
  */
-template <typename T, std::size_t SIZE = inline_buffer_size,
+template <typename T, size_t SIZE = inline_buffer_size,
           typename Allocator = std::allocator<T>>
-class basic_memory_buffer : private Allocator, public internal::buffer<T> {
+class basic_memory_buffer : public detail::buffer<T> {
  private:
   T store_[SIZE];
 
+  // Don't inherit from Allocator avoid generating type_info for it.
+  Allocator alloc_;
+
   // Deallocate memory allocated by the buffer.
   void deallocate() {
     T* data = this->data();
-    if (data != store_) Allocator::deallocate(data, this->capacity());
+    if (data != store_) alloc_.deallocate(data, this->capacity());
   }
 
  protected:
-  void grow(std::size_t size) FMT_OVERRIDE;
+  void grow(size_t size) FMT_OVERRIDE;
 
  public:
   using value_type = T;
   using const_reference = const T&;
 
   explicit basic_memory_buffer(const Allocator& alloc = Allocator())
-      : Allocator(alloc) {
+      : alloc_(alloc) {
     this->set(store_, SIZE);
   }
   ~basic_memory_buffer() FMT_OVERRIDE { deallocate(); }
@@ -654,14 +639,13 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
  private:
   // Move data from other to this buffer.
   void move(basic_memory_buffer& other) {
-    Allocator &this_alloc = *this, &other_alloc = other;
-    this_alloc = std::move(other_alloc);
+    alloc_ = std::move(other.alloc_);
     T* data = other.data();
-    std::size_t size = other.size(), capacity = other.capacity();
+    size_t size = other.size(), capacity = other.capacity();
     if (data == other.store_) {
       this->set(store_, capacity);
       std::uninitialized_copy(other.store_, other.store_ + size,
-                              internal::make_checked(store_, capacity));
+                              detail::make_checked(store_, capacity));
     } else {
       this->set(data, capacity);
       // Set pointer to the inline array so that delete is not called
@@ -693,32 +677,37 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
   }
 
   // Returns a copy of the allocator associated with this buffer.
-  Allocator get_allocator() const { return *this; }
+  Allocator get_allocator() const { return alloc_; }
 };
 
-template <typename T, std::size_t SIZE, typename Allocator>
-void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-  if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much");
+template <typename T, size_t SIZE, typename Allocator>
+void basic_memory_buffer<T, SIZE, Allocator>::grow(size_t size) {
+#ifdef FMT_FUZZ
+  if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much");
 #endif
-  std::size_t old_capacity = this->capacity();
-  std::size_t new_capacity = old_capacity + old_capacity / 2;
+  size_t old_capacity = this->capacity();
+  size_t new_capacity = old_capacity + old_capacity / 2;
   if (size > new_capacity) new_capacity = size;
   T* old_data = this->data();
-  T* new_data = std::allocator_traits<Allocator>::allocate(*this, new_capacity);
+  T* new_data =
+      std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
   // The following code doesn't throw, so the raw pointer above doesn't leak.
   std::uninitialized_copy(old_data, old_data + this->size(),
-                          internal::make_checked(new_data, new_capacity));
+                          detail::make_checked(new_data, new_capacity));
   this->set(new_data, new_capacity);
   // deallocate must not throw according to the standard, but even if it does,
   // the buffer already uses the new storage and will deallocate it in
   // destructor.
-  if (old_data != store_) Allocator::deallocate(old_data, old_capacity);
+  if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
 }
 
 using memory_buffer = basic_memory_buffer<char>;
 using wmemory_buffer = basic_memory_buffer<wchar_t>;
 
+template <typename T, size_t SIZE, typename Allocator>
+struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
+};
+
 /** A formatting error such as invalid format string. */
 FMT_CLASS_API
 class FMT_API format_error : public std::runtime_error {
@@ -733,7 +722,7 @@ class FMT_API format_error : public std::runtime_error {
   ~format_error() FMT_NOEXCEPT FMT_OVERRIDE;
 };
 
-namespace internal {
+namespace detail {
 
 // Returns true if value is negative, false otherwise.
 // Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
@@ -757,8 +746,8 @@ FMT_CONSTEXPR bool is_supported_floating_point(T) {
 // represent all values of T.
 template <typename 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>>;
+    num_bits<T>() <= 32, uint32_t,
+    conditional_t<num_bits<T>() <= 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 {
@@ -767,16 +756,22 @@ template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
   static const uint64_t zero_or_powers_of_10_64[];
   static const uint64_t pow10_significands[];
   static const int16_t pow10_exponents[];
-  static const char digits[];
+  // GCC generates slightly better code for pairs than chars.
+  using digit_pair = char[2];
+  static const digit_pair digits[];
   static const char hex_digits[];
   static const char foreground_color[];
   static const char background_color[];
   static const char reset_color[5];
   static const wchar_t wreset_color[5];
   static const char signs[];
+  static const char left_padding_shifts[5];
+  static const char right_padding_shifts[5];
 };
 
+#ifndef FMT_EXPORTED
 FMT_EXTERN template struct basic_data<void>;
+#endif
 
 // This is a struct rather than an alias to avoid shadowing warnings in gcc.
 struct data : basic_data<> {};
@@ -834,7 +829,7 @@ template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
   return num_digits;
 }
 
-template <> int count_digits<4>(internal::fallback_uintptr n);
+template <> int count_digits<4>(detail::fallback_uintptr n);
 
 #if FMT_GCC_VERSION || FMT_CLANG_VERSION
 #  define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
@@ -850,6 +845,12 @@ inline int count_digits(uint32_t n) {
 }
 #endif
 
+template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
+  return std::numeric_limits<Int>::digits10;
+}
+template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
+template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
+
 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);
@@ -874,57 +875,61 @@ template <> inline wchar_t decimal_point(locale_ref loc) {
   return decimal_point_impl<wchar_t>(loc);
 }
 
-// Formats a decimal unsigned integer value writing into buffer.
-// add_thousands_sep is called after writing each char to add a thousands
-// separator if necessary.
-template <typename UInt, typename Char, typename F>
-inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
-                            F add_thousands_sep) {
-  FMT_ASSERT(num_digits >= 0, "invalid digit count");
-  buffer += num_digits;
-  Char* end = buffer;
+// Compares two characters for equality.
+template <typename Char> bool equal2(const Char* lhs, const char* rhs) {
+  return lhs[0] == rhs[0] && lhs[1] == rhs[1];
+}
+inline bool equal2(const char* lhs, const char* rhs) {
+  return memcmp(lhs, rhs, 2) == 0;
+}
+
+// Copies two characters from src to dst.
+template <typename Char> void copy2(Char* dst, const char* src) {
+  *dst++ = static_cast<Char>(*src++);
+  *dst = static_cast<Char>(*src);
+}
+inline void copy2(char* dst, const char* src) { memcpy(dst, src, 2); }
+
+template <typename Iterator> struct format_decimal_result {
+  Iterator begin;
+  Iterator end;
+};
+
+// Formats a decimal unsigned integer value writing into out pointing to a
+// buffer of specified size. The caller must ensure that the buffer is large
+// enough.
+template <typename Char, typename UInt>
+inline format_decimal_result<Char*> format_decimal(Char* out, UInt value,
+                                                   int size) {
+  FMT_ASSERT(size >= count_digits(value), "invalid digit count");
+  out += size;
+  Char* end = out;
   while (value >= 100) {
     // 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.
-    auto index = static_cast<unsigned>((value % 100) * 2);
+    out -= 2;
+    copy2(out, data::digits[value % 100]);
     value /= 100;
-    *--buffer = static_cast<Char>(data::digits[index + 1]);
-    add_thousands_sep(buffer);
-    *--buffer = static_cast<Char>(data::digits[index]);
-    add_thousands_sep(buffer);
   }
   if (value < 10) {
-    *--buffer = static_cast<Char>('0' + value);
-    return end;
+    *--out = static_cast<Char>('0' + value);
+    return {out, end};
   }
-  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;
+  out -= 2;
+  copy2(out, data::digits[value]);
+  return {out, end};
 }
 
-template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
-  return std::numeric_limits<Int>::digits10;
-}
-template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
-template <> constexpr int digits10<uint128_t>() FMT_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");
+template <typename Char, typename UInt, typename Iterator,
+          FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
+inline format_decimal_result<Iterator> format_decimal(Iterator out, UInt value,
+                                                      int num_digits) {
   // Buffer should be large enough to hold all digits (<= digits10 + 1).
   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);
-}
-
-template <typename Char, typename It, typename UInt>
-inline It format_decimal(It out, UInt value, int num_digits) {
-  return format_decimal<Char>(out, value, num_digits, [](Char*) {});
+  auto end = format_decimal(buffer, value, num_digits).end;
+  return {out, detail::copy_str<Char>(buffer, end, out)};
 }
 
 template <unsigned BASE_BITS, typename Char, typename UInt>
@@ -942,7 +947,7 @@ inline Char* format_uint(Char* buffer, UInt value, int num_digits,
 }
 
 template <unsigned BASE_BITS, typename Char>
-Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits,
+Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits,
                   bool = false) {
   auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
   int start = (num_digits + char_digits - 1) / char_digits - 1;
@@ -968,7 +973,7 @@ 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[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);
+  return detail::copy_str<Char>(buffer, buffer + num_digits, out);
 }
 
 // A converter from UTF-8 to UTF-16.
@@ -1019,7 +1024,7 @@ template <typename Char> struct fill_t {
     return fill;
   }
 };
-}  // namespace internal
+}  // namespace detail
 
 // We cannot use enum classes as bit fields because of a gcc bug
 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
@@ -1041,7 +1046,7 @@ template <typename Char> struct basic_format_specs {
   align_t align : 4;
   sign_t sign : 3;
   bool alt : 1;  // Alternate form ('#').
-  internal::fill_t<Char> fill;
+  detail::fill_t<Char> fill;
 
   constexpr basic_format_specs()
       : width(0),
@@ -1050,12 +1055,12 @@ template <typename Char> struct basic_format_specs {
         align(align::none),
         sign(sign::none),
         alt(false),
-        fill(internal::fill_t<Char>::make()) {}
+        fill(detail::fill_t<Char>::make()) {}
 };
 
 using format_specs = basic_format_specs<char>;
 
-namespace internal {
+namespace detail {
 
 // A floating-point presentation format.
 enum class float_format : unsigned char {
@@ -1071,7 +1076,6 @@ struct float_specs {
   sign_t sign : 8;
   bool upper : 1;
   bool locale : 1;
-  bool percent : 1;
   bool binary32 : 1;
   bool use_grisu : 1;
   bool showpoint : 1;
@@ -1087,12 +1091,12 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
     *it++ = static_cast<Char>('+');
   }
   if (exp >= 100) {
-    const char* top = data::digits + (exp / 100) * 2;
+    const char* top = data::digits[exp / 100];
     if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
     *it++ = static_cast<Char>(top[1]);
     exp %= 100;
   }
-  const char* d = data::digits + exp * 2;
+  const char* d = data::digits[exp];
   *it++ = static_cast<Char>(d[0]);
   *it++ = static_cast<Char>(d[1]);
   return it;
@@ -1134,8 +1138,8 @@ template <typename Char> class float_writer {
             *it++ = static_cast<Char>('0');
           return it;
         }
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-        if (num_zeros > 1000)
+#ifdef FMT_FUZZ
+        if (num_zeros > 5000)
           throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
 #endif
         it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
@@ -1198,11 +1202,10 @@ template <typename Char> class float_writer {
   }
 
   size_t size() const { return size_; }
-  size_t width() const { return size(); }
 
-  template <typename It> void operator()(It&& it) {
+  template <typename It> It operator()(It it) const {
     if (specs_.sign) *it++ = static_cast<Char>(data::signs[specs_.sign]);
-    it = prettify(it);
+    return prettify(it);
   }
 };
 
@@ -1235,10 +1238,15 @@ FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
   case 'o':
     handler.on_oct();
     break;
+#ifdef FMT_DEPRECATED_N_SPECIFIER
   case 'n':
+#endif
   case 'L':
     handler.on_num();
     break;
+  case 'c':
+    handler.on_chr();
+    break;
   default:
     handler.on_error();
   }
@@ -1274,19 +1282,16 @@ FMT_CONSTEXPR float_specs parse_float_type_spec(
     result.format = float_format::fixed;
     result.showpoint |= specs.precision != 0;
     break;
-#if FMT_DEPRECATED_PERCENT
-  case '%':
-    result.format = float_format::fixed;
-    result.percent = true;
-    break;
-#endif
   case 'A':
     result.upper = true;
     FMT_FALLTHROUGH;
   case 'a':
     result.format = float_format::hex;
     break;
+#ifdef FMT_DEPRECATED_N_SPECIFIER
   case 'n':
+#endif
+  case 'L':
     result.locale = true;
     break;
   default:
@@ -1335,6 +1340,7 @@ template <typename ErrorHandler> class int_type_checker : private ErrorHandler {
   FMT_CONSTEXPR void on_bin() {}
   FMT_CONSTEXPR void on_oct() {}
   FMT_CONSTEXPR void on_num() {}
+  FMT_CONSTEXPR void on_chr() {}
 
   FMT_CONSTEXPR void on_error() {
     ErrorHandler::on_error("invalid type specifier");
@@ -1366,38 +1372,6 @@ class cstring_type_checker : public ErrorHandler {
   FMT_CONSTEXPR void on_pointer() {}
 };
 
-template <typename Context>
-void arg_map<Context>::init(const basic_format_args<Context>& args) {
-  if (map_) return;
-  map_ = new entry[internal::to_unsigned(args.max_size())];
-  if (args.is_packed()) {
-    for (int i = 0;; ++i) {
-      internal::type arg_type = args.type(i);
-      if (arg_type == internal::type::none_type) return;
-      if (arg_type == internal::type::named_arg_type)
-        push_back(args.values_[i]);
-    }
-  }
-  for (int i = 0, n = args.max_size(); i < n; ++i) {
-    auto type = args.args_[i].type_;
-    if (type == internal::type::named_arg_type) push_back(args.args_[i].value_);
-  }
-}
-
-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);
-  }
-};
-
 template <typename OutputIt, typename Char>
 FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& fill) {
   auto fill_size = fill.size();
@@ -1406,375 +1380,470 @@ FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& fill) {
   return it;
 }
 
-// This template provides operations for formatting and writing data into a
-// character range.
-template <typename Range> class basic_writer {
- public:
-  using char_type = typename Range::value_type;
-  using iterator = typename Range::iterator;
-  using format_specs = basic_format_specs<char_type>;
-
- private:
-  iterator out_;  // Output iterator.
-  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_.
-  auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
-    return internal::reserve(out_, n);
-  }
-
-  template <typename F> struct padded_int_writer {
-    size_t size_;
-    string_view prefix;
-    char_type fill;
-    std::size_t padding;
-    F f;
-
-    size_t size() const { return size_; }
-    size_t width() const { return size_; }
-
-    template <typename It> void operator()(It&& it) const {
-      if (prefix.size() != 0)
-        it = copy_str<char_type>(prefix.begin(), prefix.end(), it);
-      it = std::fill_n(it, padding, fill);
-      f(it);
-    }
-  };
-
-  // Writes an integer in the format
-  //   <left-padding><prefix><numeric-padding><digits><right-padding>
-  // 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() + to_unsigned(num_digits);
-    char_type fill = specs.fill[0];
-    std::size_t padding = 0;
+// Writes the output of f, padded according to format specifications in specs.
+// size: output size in code units.
+// width: output display width in (terminal) column positions.
+template <align::type align = align::left, typename OutputIt, typename Char,
+          typename F>
+inline OutputIt write_padded(OutputIt out,
+                             const basic_format_specs<Char>& specs, size_t size,
+                             size_t width, const F& f) {
+  static_assert(align == align::left || align == align::right, "");
+  unsigned spec_width = to_unsigned(specs.width);
+  size_t padding = spec_width > width ? spec_width - width : 0;
+  auto* shifts = align == align::left ? data::left_padding_shifts
+                                      : data::right_padding_shifts;
+  size_t left_padding = padding >> shifts[specs.align];
+  auto it = reserve(out, size + padding * specs.fill.size());
+  it = fill(it, left_padding, specs.fill);
+  it = f(it);
+  it = fill(it, padding - left_padding, specs.fill);
+  return base_iterator(out, it);
+}
+
+template <align::type align = align::left, typename OutputIt, typename Char,
+          typename F>
+inline OutputIt write_padded(OutputIt out,
+                             const basic_format_specs<Char>& specs, size_t size,
+                             const F& f) {
+  return write_padded<align>(out, specs, size, size, f);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write_bytes(OutputIt out, string_view bytes,
+                     const basic_format_specs<Char>& specs) {
+  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+  return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
+    const char* data = bytes.data();
+    return copy_str<Char>(data, data + bytes.size(), it);
+  });
+}
+
+// Data for write_int that doesn't depend on output iterator type. It is used to
+// avoid template code bloat.
+template <typename Char> struct write_int_data {
+  size_t size;
+  size_t padding;
+
+  write_int_data(int num_digits, string_view prefix,
+                 const basic_format_specs<Char>& specs)
+      : size(prefix.size() + to_unsigned(num_digits)), padding(0) {
     if (specs.align == align::numeric) {
-      auto unsiged_width = to_unsigned(specs.width);
-      if (unsiged_width > size) {
-        padding = unsiged_width - size;
-        size = unsiged_width;
+      auto width = to_unsigned(specs.width);
+      if (width > size) {
+        padding = width - size;
+        size = width;
       }
     } else if (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;
-    write_padded(specs, padded_int_writer<F>{size, prefix, fill, padding, f});
   }
+};
 
-  // Writes a decimal integer.
-  template <typename Int> void write_decimal(Int value) {
-    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_or_128_t<Int>;
-
-    basic_writer<Range>& writer;
-    const Specs& specs;
-    unsigned_type abs_value;
-    char prefix[4];
-    unsigned prefix_size;
-
-    string_view get_prefix() const { return string_view(prefix, prefix_size); }
-
-    int_writer(basic_writer<Range>& w, Int value, const Specs& s)
-        : writer(w),
-          specs(s),
-          abs_value(static_cast<unsigned_type>(value)),
-          prefix_size(0) {
-      if (is_negative(value)) {
-        prefix[0] = '-';
-        ++prefix_size;
-        abs_value = 0 - abs_value;
-      } else if (specs.sign != sign::none && specs.sign != sign::minus) {
-        prefix[0] = specs.sign == sign::plus ? '+' : ' ';
-        ++prefix_size;
-      }
+// Writes an integer in the format
+//   <left-padding><prefix><numeric-padding><digits><right-padding>
+// where <digits> are written by f(it).
+template <typename OutputIt, typename Char, typename F>
+OutputIt write_int(OutputIt out, int num_digits, string_view prefix,
+                   const basic_format_specs<Char>& specs, F f) {
+  auto data = write_int_data<Char>(num_digits, prefix, specs);
+  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+  return write_padded<align::right>(out, specs, data.size, [=](iterator it) {
+    if (prefix.size() != 0)
+      it = copy_str<Char>(prefix.begin(), prefix.end(), it);
+    it = std::fill_n(it, data.padding, static_cast<Char>('0'));
+    return f(it);
+  });
+}
+
+template <typename StrChar, typename Char, typename OutputIt>
+OutputIt write(OutputIt out, basic_string_view<StrChar> s,
+               const basic_format_specs<Char>& specs) {
+  auto data = s.data();
+  auto size = s.size();
+  if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
+    size = code_point_index(s, to_unsigned(specs.precision));
+  auto width = specs.width != 0
+                   ? count_code_points(basic_string_view<StrChar>(data, size))
+                   : 0;
+  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+  return write_padded(out, specs, size, width, [=](iterator it) {
+    return copy_str<Char>(data, data + size, it);
+  });
+}
+
+// The handle_int_type_spec handler that writes an integer.
+template <typename OutputIt, typename Char, typename UInt> struct int_writer {
+  OutputIt out;
+  locale_ref locale;
+  const basic_format_specs<Char>& specs;
+  UInt abs_value;
+  char prefix[4];
+  unsigned prefix_size;
+
+  using iterator =
+      remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
+
+  string_view get_prefix() const { return string_view(prefix, prefix_size); }
+
+  template <typename Int>
+  int_writer(OutputIt output, locale_ref loc, Int value,
+             const basic_format_specs<Char>& s)
+      : out(output),
+        locale(loc),
+        specs(s),
+        abs_value(static_cast<UInt>(value)),
+        prefix_size(0) {
+    static_assert(std::is_same<uint32_or_64_or_128_t<Int>, UInt>::value, "");
+    if (is_negative(value)) {
+      prefix[0] = '-';
+      ++prefix_size;
+      abs_value = 0 - abs_value;
+    } else if (specs.sign != sign::none && specs.sign != sign::minus) {
+      prefix[0] = specs.sign == sign::plus ? '+' : ' ';
+      ++prefix_size;
     }
+  }
 
-    struct dec_writer {
-      unsigned_type abs_value;
-      int num_digits;
-
-      template <typename It> void operator()(It&& it) const {
-        it = internal::format_decimal<char_type>(it, abs_value, num_digits);
-      }
-    };
+  void on_dec() {
+    auto num_digits = count_digits(abs_value);
+    out = write_int(
+        out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) {
+          return format_decimal<Char>(it, abs_value, num_digits).end;
+        });
+  }
 
-    void on_dec() {
-      int num_digits = count_digits(abs_value);
-      writer.write_int(num_digits, get_prefix(), specs,
-                       dec_writer{abs_value, num_digits});
+  void on_hex() {
+    if (specs.alt) {
+      prefix[prefix_size++] = '0';
+      prefix[prefix_size++] = specs.type;
     }
-
-    struct hex_writer {
-      int_writer& self;
-      int num_digits;
-
-      template <typename It> void operator()(It&& it) const {
-        it = format_uint<4, char_type>(it, self.abs_value, num_digits,
-                                       self.specs.type != 'x');
-      }
-    };
-
-    void on_hex() {
-      if (specs.alt) {
-        prefix[prefix_size++] = '0';
-        prefix[prefix_size++] = specs.type;
-      }
-      int num_digits = count_digits<4>(abs_value);
-      writer.write_int(num_digits, get_prefix(), specs,
-                       hex_writer{*this, num_digits});
+    int num_digits = count_digits<4>(abs_value);
+    out = write_int(out, num_digits, get_prefix(), specs,
+                    [this, num_digits](iterator it) {
+                      return format_uint<4, Char>(it, abs_value, num_digits,
+                                                  specs.type != 'x');
+                    });
+  }
+
+  void on_bin() {
+    if (specs.alt) {
+      prefix[prefix_size++] = '0';
+      prefix[prefix_size++] = static_cast<char>(specs.type);
     }
-
-    template <int BITS> struct bin_writer {
-      unsigned_type abs_value;
-      int num_digits;
-
-      template <typename It> void operator()(It&& it) const {
-        it = format_uint<BITS, char_type>(it, abs_value, num_digits);
-      }
-    };
-
-    void on_bin() {
-      if (specs.alt) {
-        prefix[prefix_size++] = '0';
-        prefix[prefix_size++] = static_cast<char>(specs.type);
-      }
-      int num_digits = count_digits<1>(abs_value);
-      writer.write_int(num_digits, get_prefix(), specs,
-                       bin_writer<1>{abs_value, num_digits});
+    int num_digits = count_digits<1>(abs_value);
+    out = write_int(out, num_digits, get_prefix(), specs,
+                    [this, num_digits](iterator it) {
+                      return format_uint<1, Char>(it, abs_value, num_digits);
+                    });
+  }
+
+  void on_oct() {
+    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';
     }
+    out = write_int(out, num_digits, get_prefix(), specs,
+                    [this, num_digits](iterator it) {
+                      return format_uint<3, Char>(it, abs_value, num_digits);
+                    });
+  }
 
-    void on_oct() {
-      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';
-      }
-      writer.write_int(num_digits, get_prefix(), specs,
-                       bin_writer<3>{abs_value, num_digits});
-    }
+  enum { sep_size = 1 };
 
-    enum { sep_size = 1 };
-
-    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.
-        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(),
-                                      make_checked(buffer, s.size()));
-            });
-      }
-    };
-
-    void on_num() {
-      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 = 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;
+  void on_num() {
+    std::string groups = grouping<Char>(locale);
+    if (groups.empty()) return on_dec();
+    auto sep = thousands_sep<Char>(locale);
+    if (!sep) return on_dec();
+    int num_digits = count_digits(abs_value);
+    int size = num_digits, n = num_digits;
+    std::string::const_iterator group = groups.cbegin();
+    while (group != groups.cend() && n > *group && *group > 0 &&
+           *group != max_value<char>()) {
+      size += sep_size;
+      n -= *group;
+      ++group;
+    }
+    if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back());
+    char digits[40];
+    format_decimal(digits, abs_value, num_digits);
+    basic_memory_buffer<Char> buffer;
+    size += prefix_size;
+    buffer.resize(size);
+    basic_string_view<Char> s(&sep, sep_size);
+    // Index of a decimal digit with the least significant digit having index 0.
+    int digit_index = 0;
+    group = groups.cbegin();
+    auto p = buffer.data() + size;
+    for (int i = num_digits - 1; i >= 0; --i) {
+      *--p = static_cast<Char>(digits[i]);
+      if (*group <= 0 || ++digit_index % *group != 0 ||
+          *group == max_value<char>())
+        continue;
+      if (group + 1 != groups.cend()) {
+        digit_index = 0;
         ++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, groups, sep});
-    }
-
-    FMT_NORETURN void on_error() {
-      FMT_THROW(format_error("invalid type specifier"));
+      p -= s.size();
+      std::uninitialized_copy(s.data(), s.data() + s.size(),
+                              make_checked(p, s.size()));
     }
-  };
+    if (prefix_size != 0) p[-1] = static_cast<Char>('-');
+    write(out, basic_string_view<Char>(buffer.data(), buffer.size()), specs);
+  }
 
-  template <typename Char> struct str_writer {
-    const Char* s;
-    size_t size_;
+  void on_chr() { *out++ = static_cast<Char>(abs_value); }
 
-    size_t size() const { return size_; }
-    size_t width() const {
-      return count_code_points(basic_string_view<Char>(s, size_));
-    }
+  FMT_NORETURN void on_error() {
+    FMT_THROW(format_error("invalid type specifier"));
+  }
+};
 
-    template <typename It> void operator()(It&& it) const {
-      it = copy_str<char_type>(s, s + size_, it);
-    }
+template <typename Char, typename OutputIt>
+OutputIt write_nonfinite(OutputIt out, bool isinf,
+                         const basic_format_specs<Char>& specs,
+                         const float_specs& fspecs) {
+  auto str =
+      isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan");
+  constexpr size_t str_size = 3;
+  auto sign = fspecs.sign;
+  auto size = str_size + (sign ? 1 : 0);
+  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+  return write_padded(out, specs, size, [=](iterator it) {
+    if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+    return copy_str<Char>(str, str + str_size, it);
+  });
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+OutputIt write(OutputIt out, T value, basic_format_specs<Char> specs,
+               locale_ref loc = {}) {
+  if (const_check(!is_supported_floating_point(value))) return out;
+  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;
+  }
+
+  if (!std::isfinite(value))
+    return write_nonfinite(out, std::isinf(value), specs, fspecs);
+
+  if (specs.align == align::numeric && fspecs.sign) {
+    auto it = reserve(out, 1);
+    *it++ = static_cast<Char>(data::signs[fspecs.sign]);
+    out = base_iterator(out, it);
+    fspecs.sign = sign::none;
+    if (specs.width != 0) --specs.width;
+  }
+
+  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);
+    return write_bytes(out, {buffer.data(), buffer.size()}, specs);
+  }
+  int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
+  if (fspecs.format == float_format::exp) {
+    if (precision == max_value<int>())
+      FMT_THROW(format_error("number is too big"));
+    else
+      ++precision;
+  }
+  if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+  fspecs.use_grisu = use_grisu<T>();
+  int exp = format_float(promote_float(value), precision, fspecs, buffer);
+  fspecs.precision = precision;
+  Char point =
+      fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.');
+  float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp,
+                       fspecs, point);
+  return write_padded<align::right>(out, specs, w.size(), w);
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+OutputIt write(OutputIt out, T value) {
+  if (const_check(!is_supported_floating_point(value))) return out;
+  auto fspecs = float_specs();
+  if (std::signbit(value)) {  // value < 0 is false for NaN so use signbit.
+    fspecs.sign = sign::minus;
+    value = -value;
+  }
+
+  auto specs = basic_format_specs<Char>();
+  if (!std::isfinite(value))
+    return write_nonfinite(out, std::isinf(value), specs, fspecs);
+
+  memory_buffer buffer;
+  int precision = -1;
+  if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+  fspecs.use_grisu = use_grisu<T>();
+  int exp = format_float(promote_float(value), precision, fspecs, buffer);
+  fspecs.precision = precision;
+  float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp,
+                       fspecs, static_cast<Char>('.'));
+  return base_iterator(out, w(reserve(out, w.size())));
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write_char(OutputIt out, Char value,
+                    const basic_format_specs<Char>& specs) {
+  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+  return write_padded(out, specs, 1, [=](iterator it) {
+    *it++ = value;
+    return it;
+  });
+}
+
+template <typename Char, typename OutputIt, typename UIntPtr>
+OutputIt write_ptr(OutputIt out, UIntPtr value,
+                   const basic_format_specs<Char>* specs) {
+  int num_digits = count_digits<4>(value);
+  auto size = to_unsigned(num_digits) + size_t(2);
+  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+  auto write = [=](iterator it) {
+    *it++ = static_cast<Char>('0');
+    *it++ = static_cast<Char>('x');
+    return format_uint<4, Char>(it, value, num_digits);
   };
+  return specs ? write_padded<align::right>(out, *specs, size, write)
+               : base_iterator(out, write(reserve(out, size)));
+}
 
-  struct bytes_writer {
-    string_view bytes;
+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 {};
 
-    size_t size() const { return bytes.size(); }
-    size_t width() const { return bytes.size(); }
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, monostate) {
+  FMT_ASSERT(false, "");
+  return out;
+}
 
-    template <typename It> void operator()(It&& it) const {
-      const char* data = bytes.data();
-      it = copy_str<char>(data, data + size(), it);
-    }
-  };
+template <typename Char, typename OutputIt,
+          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+OutputIt write(OutputIt out, string_view value) {
+  auto it = reserve(out, value.size());
+  it = copy_str<Char>(value.begin(), value.end(), it);
+  return base_iterator(out, it);
+}
 
-  template <typename UIntPtr> struct pointer_writer {
-    UIntPtr value;
-    int num_digits;
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, basic_string_view<Char> value) {
+  auto it = reserve(out, value.size());
+  it = std::copy(value.begin(), value.end(), it);
+  return base_iterator(out, it);
+}
 
-    size_t size() const { return to_unsigned(num_digits) + 2; }
-    size_t width() const { return size(); }
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_integral<T>::value &&
+                        !std::is_same<T, bool>::value &&
+                        !std::is_same<T, Char>::value)>
+OutputIt write(OutputIt out, T value) {
+  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(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(out, (negative ? 1 : 0) + static_cast<size_t>(num_digits));
+  if (negative) *it++ = static_cast<Char>('-');
+  it = format_decimal<Char>(it, abs_value, num_digits).end;
+  return base_iterator(out, it);
+}
 
-    template <typename It> void operator()(It&& it) const {
-      *it++ = static_cast<char_type>('0');
-      *it++ = static_cast<char_type>('x');
-      it = format_uint<4, char_type>(it, value, num_digits);
-    }
-  };
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, bool value) {
+  return write<Char>(out, string_view(value ? "true" : "false"));
+}
 
- public:
-  explicit basic_writer(Range out, locale_ref loc = locale_ref())
-      : out_(out.begin()), locale_(loc) {}
-
-  iterator out() const { return out_; }
-
-  // Writes a value in the format
-  //   <left-padding><value><right-padding>
-  // where <value> is written by f(it).
-  template <typename F> void write_padded(const format_specs& specs, F&& f) {
-    // User-perceived width (in code points).
-    unsigned width = to_unsigned(specs.width);
-    size_t size = f.size();  // The number of code units.
-    size_t num_code_points = width != 0 ? f.width() : size;
-    if (width <= num_code_points) return f(reserve(size));
-    size_t padding = width - num_code_points;
-    size_t fill_size = specs.fill.size();
-    auto&& it = reserve(size + padding * fill_size);
-    if (specs.align == align::right) {
-      it = fill(it, padding, specs.fill);
-      f(it);
-    } else if (specs.align == align::center) {
-      std::size_t left_padding = padding / 2;
-      it = fill(it, left_padding, specs.fill);
-      f(it);
-      it = fill(it, padding - left_padding, specs.fill);
-    } else {
-      f(it);
-      it = fill(it, padding, specs.fill);
-    }
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, Char value) {
+  auto it = reserve(out, 1);
+  *it++ = value;
+  return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, const Char* value) {
+  if (!value) {
+    FMT_THROW(format_error("string pointer is null"));
+  } else {
+    auto length = std::char_traits<Char>::length(value);
+    out = write(out, basic_string_view<Char>(value, length));
   }
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, const void* value) {
+  return write_ptr<Char>(out, to_uintptr(value), nullptr);
+}
 
-  void write(int value) { write_decimal(value); }
-  void write(long value) { write_decimal(value); }
-  void write(long long value) { write_decimal(value); }
+template <typename Char, typename OutputIt, typename T>
+auto write(OutputIt out, const T& value) -> typename std::enable_if<
+    mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value ==
+        type::custom_type,
+    OutputIt>::type {
+  basic_format_context<OutputIt, Char> ctx(out, {}, {});
+  return formatter<T>().format(value, ctx);
+}
 
-  void write(unsigned value) { write_decimal(value); }
-  void write(unsigned long value) { write_decimal(value); }
-  void write(unsigned long long value) { write_decimal(value); }
+// An argument visitor that formats the argument and writes it via the output
+// iterator. It's a class and not a generic lambda for compatibility with C++11.
+template <typename OutputIt, typename Char> struct default_arg_formatter {
+  using context = basic_format_context<OutputIt, Char>;
 
-#if FMT_USE_INT128
-  void write(int128_t value) { write_decimal(value); }
-  void write(uint128_t value) { write_decimal(value); }
-#endif
+  OutputIt out;
+  basic_format_args<context> args;
+  locale_ref loc;
 
-  template <typename T, typename Spec>
-  void write_int(T value, const Spec& spec) {
-    handle_int_type_spec(spec.type, int_writer<T, Spec>(*this, value, spec));
+  template <typename T> OutputIt operator()(T value) {
+    return write<Char>(out, value);
   }
 
-  template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
-  void write(T value, format_specs specs = {}) {
-    if (const_check(!is_supported_floating_point(value))) {
-      return;
-    }
-    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;
-    }
+  OutputIt operator()(typename basic_format_arg<context>::handle handle) {
+    basic_format_parse_context<Char> parse_ctx({});
+    basic_format_context<OutputIt, Char> format_ctx(out, args, loc);
+    handle.format(parse_ctx, format_ctx);
+    return format_ctx.out();
+  }
+};
 
-    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});
-    }
+template <typename OutputIt, typename Char,
+          typename ErrorHandler = error_handler>
+class arg_formatter_base {
+ public:
+  using iterator = OutputIt;
+  using char_type = Char;
+  using format_specs = basic_format_specs<Char>;
 
-    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;
-    }
+ private:
+  iterator out_;
+  locale_ref locale_;
+  format_specs* specs_;
 
-    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) {
-      if (precision == max_value<int>())
-        FMT_THROW(format_error("number is too big"));
-      else
-        ++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));
+  // Attempts to reserve space for n extra characters in the output range.
+  // Returns a pointer to the reserved range or a reference to out_.
+  auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) {
+    return detail::reserve(out_, n);
+  }
+
+  using reserve_iterator = remove_reference_t<decltype(
+      detail::reserve(std::declval<iterator&>(), 0))>;
+
+  template <typename T> void write_int(T value, const format_specs& spec) {
+    using uint_type = uint32_or_64_or_128_t<T>;
+    int_writer<iterator, Char, uint_type> w(out_, locale_, value, spec);
+    handle_int_type_spec(spec.type, w);
+    out_ = w.out;
   }
 
   void write(char value) {
@@ -1782,198 +1851,151 @@ template <typename Range> class basic_writer {
     *it++ = value;
   }
 
-  template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
-  void write(Char value) {
-    auto&& it = reserve(1);
-    *it++ = value;
+  template <typename Ch, FMT_ENABLE_IF(std::is_same<Ch, Char>::value)>
+  void write(Ch value) {
+    out_ = detail::write<Char>(out_, value);
   }
 
   void write(string_view value) {
     auto&& it = reserve(value.size());
-    it = copy_str<char_type>(value.begin(), value.end(), it);
+    it = copy_str<Char>(value.begin(), value.end(), it);
   }
   void write(wstring_view value) {
-    static_assert(std::is_same<char_type, wchar_t>::value, "");
+    static_assert(std::is_same<Char, wchar_t>::value, "");
     auto&& it = reserve(value.size());
     it = std::copy(value.begin(), value.end(), it);
   }
 
-  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 Ch>
+  void write(const Ch* s, size_t size, const format_specs& specs) {
+    auto width = specs.width != 0
+                     ? count_code_points(basic_string_view<Ch>(s, size))
+                     : 0;
+    out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) {
+      return copy_str<Char>(s, s + size, it);
+    });
   }
 
-  template <typename Char>
-  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 && to_unsigned(specs.precision) < size)
-      size = code_point_index(s, to_unsigned(specs.precision));
-    write(data, size, specs);
+  template <typename Ch>
+  void write(basic_string_view<Ch> s, const format_specs& specs = {}) {
+    out_ = detail::write(out_, s, specs);
   }
 
-  void write_bytes(string_view bytes, const format_specs& specs) {
-    write_padded(specs, bytes_writer{bytes});
-  }
-
-  template <typename UIntPtr>
-  void write_pointer(UIntPtr value, const format_specs* specs) {
-    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;
-    if (specs_copy.align == align::none) specs_copy.align = align::right;
-    write_padded(specs_copy, pw);
+  void write_pointer(const void* p) {
+    out_ = write_ptr<char_type>(out_, to_uintptr(p), specs_);
   }
-};
 
-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 {};
+  struct char_spec_handler : ErrorHandler {
+    arg_formatter_base& formatter;
+    Char value;
 
-template <typename Range, typename ErrorHandler = internal::error_handler>
-class arg_formatter_base {
- public:
-  using char_type = typename Range::value_type;
-  using iterator = typename Range::iterator;
-  using format_specs = basic_format_specs<char_type>;
+    char_spec_handler(arg_formatter_base& f, Char val)
+        : formatter(f), value(val) {}
 
- private:
-  using writer_type = basic_writer<Range>;
-  writer_type writer_;
-  format_specs* specs_;
+    void on_int() {
+      // char is only formatted as int if there are specs.
+      formatter.write_int(static_cast<int>(value), *formatter.specs_);
+    }
+    void on_char() {
+      if (formatter.specs_)
+        formatter.out_ = write_char(formatter.out_, value, *formatter.specs_);
+      else
+        formatter.write(value);
+    }
+  };
 
-  struct char_writer {
-    char_type value;
+  struct cstring_spec_handler : error_handler {
+    arg_formatter_base& formatter;
+    const Char* value;
 
-    size_t size() const { return 1; }
-    size_t width() const { return 1; }
+    cstring_spec_handler(arg_formatter_base& f, const Char* val)
+        : formatter(f), value(val) {}
 
-    template <typename It> void operator()(It&& it) const { *it++ = value; }
+    void on_string() { formatter.write(value); }
+    void on_pointer() { formatter.write_pointer(value); }
   };
 
-  void write_char(char_type value) {
-    if (specs_)
-      writer_.write_padded(*specs_, char_writer{value});
-    else
-      writer_.write(value);
-  }
-
-  void write_pointer(const void* p) {
-    writer_.write_pointer(internal::to_uintptr(p), specs_);
-  }
-
  protected:
-  writer_type& writer() { return writer_; }
-  FMT_DEPRECATED format_specs* spec() { return specs_; }
+  iterator out() { return out_; }
   format_specs* specs() { return specs_; }
-  iterator out() { return writer_.out(); }
 
   void write(bool value) {
-    string_view sv(value ? "true" : "false");
-    specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
+    if (specs_)
+      write(string_view(value ? "true" : "false"), *specs_);
+    else
+      out_ = detail::write<Char>(out_, value);
   }
 
-  void write(const char_type* value) {
+  void write(const Char* value) {
     if (!value) {
       FMT_THROW(format_error("string pointer is null"));
     } else {
       auto length = std::char_traits<char_type>::length(value);
       basic_string_view<char_type> sv(value, length);
-      specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
+      specs_ ? write(sv, *specs_) : write(sv);
     }
   }
 
  public:
-  arg_formatter_base(Range r, format_specs* s, locale_ref loc)
-      : writer_(r, loc), specs_(s) {}
+  arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc)
+      : out_(out), locale_(loc), specs_(s) {}
 
   iterator operator()(monostate) {
     FMT_ASSERT(false, "invalid argument type");
-    return out();
+    return out_;
   }
 
   template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
-  iterator operator()(T value) {
+  FMT_INLINE iterator operator()(T value) {
     if (specs_)
-      writer_.write_int(value, *specs_);
+      write_int(value, *specs_);
     else
-      writer_.write(value);
-    return out();
+      out_ = detail::write<Char>(out_, value);
+    return out_;
   }
 
-  iterator operator()(char_type value) {
-    internal::handle_char_specs(
-        specs_, char_spec_handler(*this, static_cast<char_type>(value)));
-    return out();
+  iterator operator()(Char value) {
+    handle_char_specs(specs_,
+                      char_spec_handler(*this, static_cast<Char>(value)));
+    return out_;
   }
 
   iterator operator()(bool value) {
     if (specs_ && specs_->type) return (*this)(value ? 1 : 0);
     write(value != 0);
-    return out();
+    return out_;
   }
 
   template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
   iterator operator()(T value) {
+    auto specs = specs_ ? *specs_ : format_specs();
     if (const_check(is_supported_floating_point(value)))
-      writer_.write(value, specs_ ? *specs_ : format_specs());
+      out_ = detail::write(out_, value, specs, locale_);
     else
       FMT_ASSERT(false, "unsupported float argument type");
-    return out();
+    return out_;
   }
 
-  struct char_spec_handler : ErrorHandler {
-    arg_formatter_base& formatter;
-    char_type value;
-
-    char_spec_handler(arg_formatter_base& f, char_type val)
-        : formatter(f), value(val) {}
-
-    void on_int() {
-      if (formatter.specs_)
-        formatter.writer_.write_int(value, *formatter.specs_);
-      else
-        formatter.writer_.write(value);
-    }
-    void on_char() { formatter.write_char(value); }
-  };
-
-  struct cstring_spec_handler : internal::error_handler {
-    arg_formatter_base& formatter;
-    const char_type* value;
-
-    cstring_spec_handler(arg_formatter_base& f, const char_type* val)
-        : formatter(f), value(val) {}
-
-    void on_string() { formatter.write(value); }
-    void on_pointer() { formatter.write_pointer(value); }
-  };
-
-  iterator operator()(const char_type* value) {
-    if (!specs_) return write(value), out();
-    internal::handle_cstring_type_spec(specs_->type,
-                                       cstring_spec_handler(*this, value));
-    return out();
+  iterator operator()(const Char* value) {
+    if (!specs_) return write(value), out_;
+    handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value));
+    return out_;
   }
 
-  iterator operator()(basic_string_view<char_type> value) {
+  iterator operator()(basic_string_view<Char> value) {
     if (specs_) {
-      internal::check_string_type_spec(specs_->type, internal::error_handler());
-      writer_.write(value, *specs_);
+      check_string_type_spec(specs_->type, error_handler());
+      write(value, *specs_);
     } else {
-      writer_.write(value);
+      write(value);
     }
-    return out();
+    return out_;
   }
 
   iterator operator()(const void* value) {
-    if (specs_)
-      check_pointer_type_spec(specs_->type, internal::error_handler());
+    if (specs_) check_pointer_type_spec(specs_->type, error_handler());
     write_pointer(value);
-    return out();
+    return out_;
   }
 };
 
@@ -2109,7 +2131,7 @@ template <typename Char> class specs_setter {
 
 template <typename ErrorHandler> class numeric_specs_checker {
  public:
-  FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, internal::type arg_type)
+  FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type)
       : error_handler_(eh), arg_type_(arg_type) {}
 
   FMT_CONSTEXPR void require_numeric_argument() {
@@ -2132,18 +2154,24 @@ template <typename ErrorHandler> class numeric_specs_checker {
 
  private:
   ErrorHandler& error_handler_;
-  internal::type arg_type_;
+  detail::type arg_type_;
 };
 
 // A format specifier handler that checks if specifiers are consistent with the
 // argument type.
 template <typename Handler> class specs_checker : public Handler {
+ private:
+  numeric_specs_checker<Handler> checker_;
+
+  // Suppress an MSVC warning about using this in initializer list.
+  FMT_CONSTEXPR Handler& error_handler() { return *this; }
+
  public:
-  FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type)
-      : Handler(handler), checker_(*this, arg_type) {}
+  FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
+      : Handler(handler), checker_(error_handler(), arg_type) {}
 
   FMT_CONSTEXPR specs_checker(const specs_checker& other)
-      : Handler(other), checker_(*this, other.arg_type_) {}
+      : Handler(other), checker_(error_handler(), other.arg_type_) {}
 
   FMT_CONSTEXPR void on_align(align_t align) {
     if (align == align::numeric) checker_.require_numeric_argument();
@@ -2176,9 +2204,6 @@ template <typename Handler> class specs_checker : public Handler {
   }
 
   FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
-
- private:
-  numeric_specs_checker<Handler> checker_;
 };
 
 template <template <typename> class Handler, typename FormatArg,
@@ -2191,10 +2216,10 @@ FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
 
 struct auto_id {};
 
-template <typename Context>
-FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, int id) {
+template <typename Context, typename ID>
+FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, ID id) {
   auto arg = ctx.arg(id);
-  if (!arg) ctx.on_error("argument index out of range");
+  if (!arg) ctx.on_error("argument not found");
   return arg;
 }
 
@@ -2227,17 +2252,17 @@ class specs_handler : public specs_setter<typename Context::char_type> {
   using format_arg = typename Context::format_arg;
 
   FMT_CONSTEXPR format_arg get_arg(auto_id) {
-    return internal::get_arg(context_, parse_context_.next_arg_id());
+    return detail::get_arg(context_, parse_context_.next_arg_id());
   }
 
   FMT_CONSTEXPR format_arg get_arg(int arg_id) {
     parse_context_.check_arg_id(arg_id);
-    return internal::get_arg(context_, arg_id);
+    return detail::get_arg(context_, arg_id);
   }
 
   FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
     parse_context_.check_arg_id(arg_id);
-    return context_.arg(arg_id);
+    return detail::get_arg(context_, arg_id);
   }
 
   ParseContext& parse_context_;
@@ -2424,7 +2449,7 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
     case '>':
       align = align::right;
       break;
-#if FMT_NUMERIC_ALIGN
+#if FMT_DEPRECATED_NUMERIC_ALIGN
     case '=':
       align = align::numeric;
       break;
@@ -2555,26 +2580,75 @@ template <>
 inline bool find<false, char>(const char* first, const char* last, char value,
                               const char*& out) {
   out = static_cast<const char*>(
-      std::memchr(first, value, internal::to_unsigned(last - first)));
+      std::memchr(first, value, detail::to_unsigned(last - first)));
   return out != nullptr;
 }
 
 template <typename Handler, typename Char> struct id_adapter {
-  FMT_CONSTEXPR void operator()() { handler.on_arg_id(); }
-  FMT_CONSTEXPR void operator()(int id) { handler.on_arg_id(id); }
+  Handler& handler;
+  int arg_id;
+
+  FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
+  FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
   FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
-    handler.on_arg_id(id);
+    arg_id = handler.on_arg_id(id);
   }
   FMT_CONSTEXPR void on_error(const char* message) {
     handler.on_error(message);
   }
-  Handler& handler;
 };
 
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin,
+                                                  const Char* end,
+                                                  Handler&& handler) {
+  ++begin;
+  if (begin == end) return handler.on_error("invalid format string"), end;
+  if (static_cast<char>(*begin) == '}') {
+    handler.on_replacement_field(handler.on_arg_id(), begin);
+  } else if (*begin == '{') {
+    handler.on_text(begin, begin + 1);
+  } else {
+    auto adapter = id_adapter<Handler, Char>{handler, 0};
+    begin = parse_arg_id(begin, end, adapter);
+    Char c = begin != end ? *begin : Char();
+    if (c == '}') {
+      handler.on_replacement_field(adapter.arg_id, begin);
+    } else if (c == ':') {
+      begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
+      if (begin == end || *begin != '}')
+        return handler.on_error("unknown format specifier"), end;
+    } else {
+      return handler.on_error("missing '}' in format string"), end;
+    }
+  }
+  return begin + 1;
+}
+
 template <bool IS_CONSTEXPR, typename Char, typename Handler>
-FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
-                                       Handler&& handler) {
-  struct pfs_writer {
+FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string(
+    basic_string_view<Char> format_str, Handler&& handler) {
+  auto begin = format_str.data();
+  auto end = begin + format_str.size();
+  if (end - begin < 32) {
+    // Use a simple loop instead of memchr for small strings.
+    const Char* p = begin;
+    while (p != end) {
+      auto c = *p++;
+      if (c == '{') {
+        handler.on_text(begin, p - 1);
+        begin = p = parse_replacement_field(p - 1, end, handler);
+      } else if (c == '}') {
+        if (p == end || *p != '}')
+          return handler.on_error("unmatched '}' in format string");
+        handler.on_text(begin, p);
+        begin = ++p;
+      }
+    }
+    handler.on_text(begin, end);
+    return;
+  }
+  struct writer {
     FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
       if (begin == end) return;
       for (;;) {
@@ -2590,8 +2664,6 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
     }
     Handler& handler_;
   } write{handler};
-  auto begin = format_str.data();
-  auto end = begin + format_str.size();
   while (begin != end) {
     // Doing two passes with memchr (one for '{' and another for '}') is up to
     // 2.5x faster than the naive one-pass implementation on big format strings.
@@ -2599,27 +2671,7 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
     if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
       return write(begin, end);
     write(begin, p);
-    ++p;
-    if (p == end) return handler.on_error("invalid format string");
-    if (static_cast<char>(*p) == '}') {
-      handler.on_arg_id();
-      handler.on_replacement_field(p);
-    } else if (*p == '{') {
-      handler.on_text(p, p + 1);
-    } else {
-      p = parse_arg_id(p, end, id_adapter<Handler, Char>{handler});
-      Char c = p != end ? *p : Char();
-      if (c == '}') {
-        handler.on_replacement_field(p);
-      } else if (c == ':') {
-        p = handler.on_format_specs(p + 1, end);
-        if (p == end || *p != '}')
-          return handler.on_error("unknown format specifier");
-      } else {
-        return handler.on_error("missing '}' in format string");
-      }
-    }
-    begin = p + 1;
+    begin = parse_replacement_field(p, end, handler);
   }
 }
 
@@ -2629,44 +2681,121 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
   using char_type = typename ParseContext::char_type;
   using context = buffer_context<char_type>;
   using mapped_type =
-      conditional_t<internal::mapped_type_constant<T, context>::value !=
+      conditional_t<detail::mapped_type_constant<T, context>::value !=
                         type::custom_type,
                     decltype(arg_mapper<context>().map(std::declval<T>())), T>;
   auto f = conditional_t<has_formatter<mapped_type, context>::value,
                          formatter<mapped_type, char_type>,
-                         internal::fallback_formatter<T, char_type>>();
+                         detail::fallback_formatter<T, char_type>>();
   return f.parse(ctx);
 }
 
+template <typename ArgFormatter, typename Char, typename Context>
+struct format_handler : detail::error_handler {
+  basic_format_parse_context<Char> parse_context;
+  Context context;
+
+  format_handler(typename ArgFormatter::iterator out,
+                 basic_string_view<Char> str,
+                 basic_format_args<Context> format_args, detail::locale_ref loc)
+      : parse_context(str), context(out, format_args, loc) {}
+
+  void on_text(const Char* begin, const Char* end) {
+    auto size = to_unsigned(end - begin);
+    auto out = context.out();
+    auto&& it = reserve(out, size);
+    it = std::copy_n(begin, size, it);
+    context.advance_to(out);
+  }
+
+  int on_arg_id() { return parse_context.next_arg_id(); }
+  int on_arg_id(int id) { return parse_context.check_arg_id(id), id; }
+  int on_arg_id(basic_string_view<Char> id) {
+    int arg_id = context.arg_id(id);
+    if (arg_id < 0) on_error("argument not found");
+    return arg_id;
+  }
+
+  FMT_INLINE void on_replacement_field(int id, const Char*) {
+    auto arg = get_arg(context, id);
+    context.advance_to(visit_format_arg(
+        default_arg_formatter<typename ArgFormatter::iterator, Char>{
+            context.out(), context.args(), context.locale()},
+        arg));
+  }
+
+  const Char* on_format_specs(int id, const Char* begin, const Char* end) {
+    advance_to(parse_context, begin);
+    auto arg = get_arg(context, id);
+    custom_formatter<Context> f(parse_context, context);
+    if (visit_format_arg(f, arg)) return parse_context.begin();
+    basic_format_specs<Char> specs;
+    using parse_context_t = basic_format_parse_context<Char>;
+    specs_checker<specs_handler<parse_context_t, Context>> handler(
+        specs_handler<parse_context_t, Context>(specs, parse_context, context),
+        arg.type());
+    begin = parse_format_specs(begin, end, handler);
+    if (begin == end || *begin != '}') on_error("missing '}' in format string");
+    advance_to(parse_context, begin);
+    context.advance_to(
+        visit_format_arg(ArgFormatter(context, &parse_context, &specs), arg));
+    return begin;
+  }
+};
+
+// A parse context with extra argument id checks. It is only used at compile
+// time because adding checks at runtime would introduce substantial overhead
+// and would be redundant since argument ids are checked when arguments are
+// retrieved anyway.
+template <typename Char, typename ErrorHandler = error_handler>
+class compile_parse_context
+    : public basic_format_parse_context<Char, ErrorHandler> {
+ private:
+  int num_args_;
+  using base = basic_format_parse_context<Char, ErrorHandler>;
+
+ public:
+  explicit FMT_CONSTEXPR compile_parse_context(
+      basic_string_view<Char> format_str, int num_args = max_value<int>(),
+      ErrorHandler eh = {})
+      : base(format_str, eh), num_args_(num_args) {}
+
+  FMT_CONSTEXPR int next_arg_id() {
+    int id = base::next_arg_id();
+    if (id >= num_args_) this->on_error("argument not found");
+    return id;
+  }
+
+  FMT_CONSTEXPR void check_arg_id(int id) {
+    base::check_arg_id(id);
+    if (id >= num_args_) this->on_error("argument not found");
+  }
+  using base::check_arg_id;
+};
+
 template <typename Char, typename ErrorHandler, typename... Args>
 class format_string_checker {
  public:
   explicit FMT_CONSTEXPR format_string_checker(
       basic_string_view<Char> format_str, ErrorHandler eh)
-      : arg_id_(-1),
-        context_(format_str, eh),
+      : context_(format_str, num_args, eh),
         parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
 
   FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
 
-  FMT_CONSTEXPR void on_arg_id() {
-    arg_id_ = context_.next_arg_id();
-    check_arg_id();
-  }
-  FMT_CONSTEXPR void on_arg_id(int id) {
-    arg_id_ = id;
-    context_.check_arg_id(id);
-    check_arg_id();
-  }
-  FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) {
+  FMT_CONSTEXPR int on_arg_id() { return context_.next_arg_id(); }
+  FMT_CONSTEXPR int on_arg_id(int id) { return context_.check_arg_id(id), id; }
+  FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
     on_error("compile-time checks don't support named arguments");
+    return 0;
   }
 
-  FMT_CONSTEXPR void on_replacement_field(const Char*) {}
+  FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
 
-  FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, const Char*) {
+  FMT_CONSTEXPR const Char* on_format_specs(int id, const Char* begin,
+                                            const Char*) {
     advance_to(context_, begin);
-    return arg_id_ < num_args ? parse_funcs_[arg_id_](context_) : begin;
+    return id < num_args ? parse_funcs_[id](context_) : begin;
   }
 
   FMT_CONSTEXPR void on_error(const char* message) {
@@ -2674,35 +2803,66 @@ class format_string_checker {
   }
 
  private:
-  using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
+  using parse_context_type = compile_parse_context<Char, ErrorHandler>;
   enum { num_args = sizeof...(Args) };
 
-  FMT_CONSTEXPR void check_arg_id() {
-    if (arg_id_ >= num_args) context_.on_error("argument index out of range");
-  }
-
   // Format specifier parsing function.
   using parse_func = const Char* (*)(parse_context_type&);
 
-  int arg_id_;
   parse_context_type context_;
   parse_func parse_funcs_[num_args > 0 ? num_args : 1];
 };
 
-template <typename Char, typename ErrorHandler, typename... Args>
-FMT_CONSTEXPR bool do_check_format_string(basic_string_view<Char> s,
-                                          ErrorHandler eh = ErrorHandler()) {
-  format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
-  parse_format_string<true>(s, checker);
-  return true;
+// Converts string literals to basic_string_view.
+template <typename Char, size_t N>
+FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
+    const Char (&s)[N]) {
+  // Remove trailing null character if needed. Won't be present if this is used
+  // with raw character array (i.e. not defined as a string).
+  return {s,
+          N - ((std::char_traits<Char>::to_int_type(s[N - 1]) == 0) ? 1 : 0)};
 }
 
+// Converts string_view to basic_string_view.
+template <typename Char>
+FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
+    const std_string_view<Char>& s) {
+  return {s.data(), s.size()};
+}
+
+#define FMT_STRING_IMPL(s, base)                                  \
+  [] {                                                            \
+    /* Use a macro-like name to avoid shadowing warnings. */      \
+    struct FMT_COMPILE_STRING : base {                            \
+      using char_type = fmt::remove_cvref_t<decltype(s[0])>;      \
+      FMT_MAYBE_UNUSED FMT_CONSTEXPR                              \
+      operator fmt::basic_string_view<char_type>() const {        \
+        return fmt::detail::compile_string_to_view<char_type>(s); \
+      }                                                           \
+    };                                                            \
+    return FMT_COMPILE_STRING();                                  \
+  }()
+
+/**
+  \rst
+  Constructs a compile-time format string from a string literal *s*.
+
+  **Example**::
+
+    // A compile-time error because 'd' is an invalid specifier for strings.
+    std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
+  \endrst
+ */
+#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string)
+
 template <typename... Args, typename S,
           enable_if_t<(is_compile_string<S>::value), int>>
 void check_format_string(S format_str) {
-  FMT_CONSTEXPR_DECL bool invalid_format = internal::do_check_format_string<
-      typename S::char_type, internal::error_handler,
-      remove_const_t<remove_reference_t<Args>>...>(to_string_view(format_str));
+  FMT_CONSTEXPR_DECL auto s = to_string_view(format_str);
+  using checker = format_string_checker<typename S::char_type, error_handler,
+                                        remove_cvref_t<Args>...>;
+  FMT_CONSTEXPR_DECL bool invalid_format =
+      (parse_format_string<true>(s, checker(s, {})), true);
   (void)invalid_format;
 }
 
@@ -2713,44 +2873,37 @@ void handle_dynamic_spec(int& value, arg_ref<typename Context::char_type> ref,
   case arg_id_kind::none:
     break;
   case arg_id_kind::index:
-    value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
-                                                ctx.error_handler());
+    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+                                              ctx.error_handler());
     break;
   case arg_id_kind::name:
-    value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
-                                                ctx.error_handler());
+    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+                                              ctx.error_handler());
     break;
   }
 }
 
-using format_func = void (*)(internal::buffer<char>&, int, string_view);
+using format_func = void (*)(detail::buffer<char>&, int, string_view);
 
 FMT_API void format_error_code(buffer<char>& out, int error_code,
                                string_view message) FMT_NOEXCEPT;
 
 FMT_API void report_error(format_func func, int error_code,
                           string_view message) FMT_NOEXCEPT;
-}  // namespace internal
-
-template <typename Range>
-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>
-class arg_formatter : public internal::arg_formatter_base<Range> {
+template <typename OutputIt, typename Char>
+class arg_formatter : public arg_formatter_base<OutputIt, Char> {
  private:
-  using char_type = typename Range::value_type;
-  using base = internal::arg_formatter_base<Range>;
-  using context_type = basic_format_context<typename base::iterator, char_type>;
+  using char_type = Char;
+  using base = arg_formatter_base<OutputIt, Char>;
+  using context_type = basic_format_context<OutputIt, Char>;
 
   context_type& ctx_;
   basic_format_parse_context<char_type>* parse_ctx_;
+  const Char* ptr_;
 
  public:
-  using range = Range;
   using iterator = typename base::iterator;
   using format_specs = typename base::format_specs;
 
@@ -2764,19 +2917,26 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
   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()),
+      format_specs* specs = nullptr, const Char* ptr = nullptr)
+      : base(ctx.out(), specs, ctx.locale()),
         ctx_(ctx),
-        parse_ctx_(parse_ctx) {}
+        parse_ctx_(parse_ctx),
+        ptr_(ptr) {}
 
   using base::operator();
 
   /** Formats an argument of a user-defined type. */
   iterator operator()(typename basic_format_arg<context_type>::handle handle) {
+    if (ptr_) advance_to(*parse_ctx_, ptr_);
     handle.format(*parse_ctx_, ctx_);
     return ctx_.out();
   }
 };
+}  // namespace detail
+
+template <typename OutputIt, typename Char>
+using arg_formatter FMT_DEPRECATED_ALIAS =
+  detail::arg_formatter<OutputIt, Char>;
 
 /**
  An error returned by an operating system or a language runtime,
@@ -2841,7 +3001,7 @@ class FMT_API system_error : public std::runtime_error {
   may look like "Unknown error -1" and is platform-dependent.
   \endrst
  */
-FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
+FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
                                  string_view message) FMT_NOEXCEPT;
 
 // Reports a system error without throwing an exception.
@@ -2858,47 +3018,32 @@ class format_int {
   mutable char buffer_[buffer_size];
   char* str_;
 
-  // Formats value in reverse and returns a pointer to the beginning.
-  char* format_decimal(unsigned long long value) {
-    char* ptr = buffer_ + (buffer_size - 1);  // Parens to workaround MSVC bug.
-    while (value >= 100) {
-      // 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.
-      auto index = static_cast<unsigned>((value % 100) * 2);
-      value /= 100;
-      *--ptr = internal::data::digits[index + 1];
-      *--ptr = internal::data::digits[index];
-    }
-    if (value < 10) {
-      *--ptr = static_cast<char>('0' + value);
-      return ptr;
-    }
-    auto index = static_cast<unsigned>(value * 2);
-    *--ptr = internal::data::digits[index + 1];
-    *--ptr = internal::data::digits[index];
-    return ptr;
+  template <typename UInt> char* format_unsigned(UInt value) {
+    auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
+    return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
   }
 
-  void format_signed(long long value) {
-    auto abs_value = static_cast<unsigned long long>(value);
+  template <typename Int> char* format_signed(Int value) {
+    auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
     bool negative = value < 0;
     if (negative) abs_value = 0 - abs_value;
-    str_ = format_decimal(abs_value);
-    if (negative) *--str_ = '-';
+    auto begin = format_unsigned(abs_value);
+    if (negative) *--begin = '-';
+    return begin;
   }
 
  public:
-  explicit format_int(int value) { format_signed(value); }
-  explicit format_int(long value) { format_signed(value); }
-  explicit format_int(long long value) { format_signed(value); }
-  explicit format_int(unsigned value) : str_(format_decimal(value)) {}
-  explicit format_int(unsigned long value) : str_(format_decimal(value)) {}
-  explicit format_int(unsigned long long value) : str_(format_decimal(value)) {}
+  explicit format_int(int value) : str_(format_signed(value)) {}
+  explicit format_int(long value) : str_(format_signed(value)) {}
+  explicit format_int(long long value) : str_(format_signed(value)) {}
+  explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
+  explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
+  explicit format_int(unsigned long long value)
+      : str_(format_unsigned(value)) {}
 
   /** Returns the number of characters written to the output buffer. */
-  std::size_t size() const {
-    return internal::to_unsigned(buffer_ - str_ + buffer_size - 1);
+  size_t size() const {
+    return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
   }
 
   /**
@@ -2924,75 +3069,71 @@ class format_int {
   std::string str() const { return std::string(str_, size()); }
 };
 
-// A formatter specialization for the core types corresponding to internal::type
+// A formatter specialization for the core types corresponding to detail::type
 // constants.
 template <typename T, typename Char>
 struct formatter<T, Char,
-                 enable_if_t<internal::type_constant<T, Char>::value !=
-                             internal::type::custom_type>> {
+                 enable_if_t<detail::type_constant<T, Char>::value !=
+                             detail::type::custom_type>> {
   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()) {
-    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),
-                                                  type);
+    using handler_type = detail::dynamic_specs_handler<ParseContext>;
+    auto type = detail::type_constant<T, Char>::value;
+    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+                                                type);
     auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
     auto eh = ctx.error_handler();
     switch (type) {
-    case internal::type::none_type:
-    case internal::type::named_arg_type:
+    case detail::type::none_type:
       FMT_ASSERT(false, "invalid argument type");
       break;
-    case internal::type::int_type:
-    case internal::type::uint_type:
-    case internal::type::long_long_type:
-    case internal::type::ulong_long_type:
-    case internal::type::int128_type:
-    case internal::type::uint128_type:
-    case internal::type::bool_type:
+    case detail::type::int_type:
+    case detail::type::uint_type:
+    case detail::type::long_long_type:
+    case detail::type::ulong_long_type:
+    case detail::type::int128_type:
+    case detail::type::uint128_type:
+    case detail::type::bool_type:
       handle_int_type_spec(specs_.type,
-                           internal::int_type_checker<decltype(eh)>(eh));
+                           detail::int_type_checker<decltype(eh)>(eh));
       break;
-    case internal::type::char_type:
+    case detail::type::char_type:
       handle_char_specs(
-          &specs_, internal::char_specs_checker<decltype(eh)>(specs_.type, eh));
+          &specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
       break;
-    case internal::type::float_type:
-      if (internal::const_check(FMT_USE_FLOAT)) {
-        internal::parse_float_type_spec(specs_, eh);
-      } else {
+    case detail::type::float_type:
+      if (detail::const_check(FMT_USE_FLOAT))
+        detail::parse_float_type_spec(specs_, eh);
+      else
         FMT_ASSERT(false, "float support disabled");
-      }
       break;
-    case internal::type::double_type:
-      if (internal::const_check(FMT_USE_DOUBLE)) {
-        internal::parse_float_type_spec(specs_, eh);
-      } else {
+    case detail::type::double_type:
+      if (detail::const_check(FMT_USE_DOUBLE))
+        detail::parse_float_type_spec(specs_, eh);
+      else
         FMT_ASSERT(false, "double support disabled");
-      }
       break;
-    case internal::type::long_double_type:
-      if (internal::const_check(FMT_USE_LONG_DOUBLE)) {
-        internal::parse_float_type_spec(specs_, eh);
-      } else {
+    case detail::type::long_double_type:
+      if (detail::const_check(FMT_USE_LONG_DOUBLE))
+        detail::parse_float_type_spec(specs_, eh);
+      else
         FMT_ASSERT(false, "long double support disabled");
-      }
       break;
-    case internal::type::cstring_type:
-      internal::handle_cstring_type_spec(
-          specs_.type, internal::cstring_type_checker<decltype(eh)>(eh));
+    case detail::type::cstring_type:
+      detail::handle_cstring_type_spec(
+          specs_.type, detail::cstring_type_checker<decltype(eh)>(eh));
       break;
-    case internal::type::string_type:
-      internal::check_string_type_spec(specs_.type, eh);
+    case detail::type::string_type:
+      detail::check_string_type_spec(specs_.type, eh);
       break;
-    case internal::type::pointer_type:
-      internal::check_pointer_type_spec(specs_.type, eh);
+    case detail::type::pointer_type:
+      detail::check_pointer_type_spec(specs_.type, eh);
       break;
-    case internal::type::custom_type:
+    case detail::type::custom_type:
       // Custom format specifiers should be checked in parse functions of
       // formatter specializations.
       break;
@@ -3002,19 +3143,18 @@ 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);
-    internal::handle_dynamic_spec<internal::precision_checker>(
+    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+                                                       specs_.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
         specs_.precision, specs_.precision_ref, ctx);
-    using range_type =
-        internal::output_range<typename FormatContext::iterator,
-                               typename FormatContext::char_type>;
-    return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
-                            internal::make_arg<FormatContext>(val));
+    using af = detail::arg_formatter<typename FormatContext::iterator,
+                                     typename FormatContext::char_type>;
+    return visit_format_arg(af(ctx, nullptr, &specs_),
+                            detail::make_arg<FormatContext>(val));
   }
 
  private:
-  internal::dynamic_format_specs<Char> specs_;
+  detail::dynamic_format_specs<Char> specs_;
 };
 
 #define FMT_FORMAT_AS(Type, Base)                                             \
@@ -3035,7 +3175,7 @@ FMT_FORMAT_AS(unsigned long, unsigned long long);
 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*);
-FMT_FORMAT_AS(internal::std_string_view<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
 
 template <typename Char>
 struct formatter<void*, Char> : formatter<const void*, Char> {
@@ -3065,7 +3205,7 @@ struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
 //   };
 template <typename Char = char> class dynamic_formatter {
  private:
-  struct null_handler : internal::error_handler {
+  struct null_handler : detail::error_handler {
     void on_align(align_t) {}
     void on_plus() {}
     void on_minus() {}
@@ -3078,16 +3218,15 @@ template <typename Char = char> class dynamic_formatter {
   auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
     format_str_ = ctx.begin();
     // Checks are deferred to formatting time when the argument type is known.
-    internal::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
+    detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
     return parse_format_specs(ctx.begin(), ctx.end(), handler);
   }
 
   template <typename T, typename FormatContext>
   auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
     handle_specs(ctx);
-    internal::specs_checker<null_handler> checker(
-        null_handler(),
-        internal::mapped_type_constant<T, FormatContext>::value);
+    detail::specs_checker<null_handler> checker(
+        null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
     checker.on_align(specs_.align);
     switch (specs_.sign) {
     case sign::none:
@@ -3104,104 +3243,47 @@ template <typename Char = char> class dynamic_formatter {
     }
     if (specs_.alt) checker.on_hash();
     if (specs_.precision >= 0) checker.end_precision();
-    using range = internal::output_range<typename FormatContext::iterator,
-                                         typename FormatContext::char_type>;
-    visit_format_arg(arg_formatter<range>(ctx, nullptr, &specs_),
-                     internal::make_arg<FormatContext>(val));
+    using af = detail::arg_formatter<typename FormatContext::iterator,
+                                     typename FormatContext::char_type>;
+    visit_format_arg(af(ctx, nullptr, &specs_),
+                     detail::make_arg<FormatContext>(val));
     return ctx.out();
   }
 
  private:
   template <typename Context> void handle_specs(Context& ctx) {
-    internal::handle_dynamic_spec<internal::width_checker>(
-        specs_.width, specs_.width_ref, ctx);
-    internal::handle_dynamic_spec<internal::precision_checker>(
+    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+                                                       specs_.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
         specs_.precision, specs_.precision_ref, ctx);
   }
 
-  internal::dynamic_format_specs<Char> specs_;
+  detail::dynamic_format_specs<Char> specs_;
   const Char* format_str_;
 };
 
-template <typename Range, typename Char>
-typename basic_format_context<Range, Char>::format_arg
-basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) {
-  map_.init(args_);
-  format_arg arg = map_.find(name);
-  if (arg.type() == internal::type::none_type)
-    this->on_error("argument not found");
-  return arg;
-}
-
 template <typename Char, typename ErrorHandler>
 FMT_CONSTEXPR void advance_to(
     basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
   ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
 }
 
-template <typename ArgFormatter, typename Char, typename Context>
-struct format_handler : internal::error_handler {
-  using range = typename ArgFormatter::range;
-
-  format_handler(range r, basic_string_view<Char> str,
-                 basic_format_args<Context> format_args,
-                 internal::locale_ref loc)
-      : parse_context(str), context(r.begin(), format_args, loc) {}
-
-  void on_text(const Char* begin, const Char* end) {
-    auto size = internal::to_unsigned(end - begin);
-    auto out = context.out();
-    auto&& it = internal::reserve(out, size);
-    it = std::copy_n(begin, size, it);
-    context.advance_to(out);
-  }
-
-  void get_arg(int id) { arg = internal::get_arg(context, id); }
-
-  void on_arg_id() { get_arg(parse_context.next_arg_id()); }
-  void on_arg_id(int id) {
-    parse_context.check_arg_id(id);
-    get_arg(id);
-  }
-  void on_arg_id(basic_string_view<Char> id) { arg = context.arg(id); }
-
-  void on_replacement_field(const Char* p) {
-    advance_to(parse_context, p);
-    context.advance_to(
-        visit_format_arg(ArgFormatter(context, &parse_context), arg));
-  }
-
-  const Char* on_format_specs(const Char* begin, const Char* end) {
-    advance_to(parse_context, begin);
-    internal::custom_formatter<Context> f(parse_context, context);
-    if (visit_format_arg(f, arg)) return parse_context.begin();
-    basic_format_specs<Char> specs;
-    using internal::specs_handler;
-    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());
-    begin = parse_format_specs(begin, end, handler);
-    if (begin == end || *begin != '}') on_error("missing '}' in format string");
-    advance_to(parse_context, begin);
-    context.advance_to(
-        visit_format_arg(ArgFormatter(context, &parse_context, &specs), arg));
-    return begin;
-  }
-
-  basic_format_parse_context<Char> parse_context;
-  Context context;
-  basic_format_arg<Context> arg;
-};
-
 /** Formats arguments and writes the output to the range. */
 template <typename ArgFormatter, typename Char, typename Context>
 typename Context::iterator vformat_to(
-    typename ArgFormatter::range out, basic_string_view<Char> format_str,
+    typename ArgFormatter::iterator out, basic_string_view<Char> format_str,
     basic_format_args<Context> args,
-    internal::locale_ref loc = internal::locale_ref()) {
-  format_handler<ArgFormatter, Char, Context> h(out, format_str, args, loc);
-  internal::parse_format_string<false>(format_str, h);
+    detail::locale_ref loc = detail::locale_ref()) {
+  if (format_str.size() == 2 && detail::equal2(format_str.data(), "{}")) {
+    auto arg = args.get(0);
+    if (!arg) detail::error_handler().on_error("argument not found");
+    using iterator = typename ArgFormatter::iterator;
+    return visit_format_arg(
+        detail::default_arg_formatter<iterator, Char>{out, args, loc}, arg);
+  }
+  detail::format_handler<ArgFormatter, Char, Context> h(out, format_str, args,
+                                                        loc);
+  detail::parse_format_string<false>(format_str, h);
   return h.context.out();
 }
 
@@ -3228,44 +3310,42 @@ class bytes {
 template <> struct formatter<bytes> {
   template <typename ParseContext>
   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    using handler_type = internal::dynamic_specs_handler<ParseContext>;
-    internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
-                                                  internal::type::string_type);
+    using handler_type = detail::dynamic_specs_handler<ParseContext>;
+    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+                                                detail::type::string_type);
     auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
-    internal::check_string_type_spec(specs_.type, ctx.error_handler());
+    detail::check_string_type_spec(specs_.type, ctx.error_handler());
     return it;
   }
 
   template <typename FormatContext>
   auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
-    internal::handle_dynamic_spec<internal::width_checker>(
-        specs_.width, specs_.width_ref, ctx);
-    internal::handle_dynamic_spec<internal::precision_checker>(
+    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+                                                       specs_.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
         specs_.precision, specs_.precision_ref, ctx);
-    using range_type =
-        internal::output_range<typename FormatContext::iterator, char>;
-    internal::basic_writer<range_type> writer(range_type(ctx.out()));
-    writer.write_bytes(b.data_, specs_);
-    return writer.out();
+    return detail::write_bytes(ctx.out(), b.data_, specs_);
   }
 
  private:
-  internal::dynamic_format_specs<char> specs_;
+  detail::dynamic_format_specs<char> specs_;
 };
 
-template <typename It, typename Char> struct arg_join : internal::view {
+template <typename It, typename Sentinel, typename Char>
+struct arg_join : detail::view {
   It begin;
-  It end;
+  Sentinel end;
   basic_string_view<Char> sep;
 
-  arg_join(It b, It e, basic_string_view<Char> s) : begin(b), end(e), sep(s) {}
+  arg_join(It b, Sentinel e, basic_string_view<Char> s)
+      : begin(b), end(e), sep(s) {}
 };
 
-template <typename It, typename Char>
-struct formatter<arg_join<It, Char>, Char>
+template <typename It, typename Sentinel, typename Char>
+struct formatter<arg_join<It, Sentinel, Char>, Char>
     : formatter<typename std::iterator_traits<It>::value_type, Char> {
   template <typename FormatContext>
-  auto format(const arg_join<It, Char>& value, FormatContext& ctx)
+  auto format(const arg_join<It, Sentinel, Char>& value, FormatContext& ctx)
       -> decltype(ctx.out()) {
     using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
     auto it = value.begin;
@@ -3286,13 +3366,13 @@ struct formatter<arg_join<It, Char>, Char>
   Returns an object that formats the iterator range `[begin, end)` with elements
   separated by `sep`.
  */
-template <typename It>
-arg_join<It, char> join(It begin, It end, string_view sep) {
+template <typename It, typename Sentinel>
+arg_join<It, Sentinel, char> join(It begin, Sentinel end, string_view sep) {
   return {begin, end, sep};
 }
 
-template <typename It>
-arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
+template <typename It, typename Sentinel>
+arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) {
   return {begin, end, sep};
 }
 
@@ -3313,14 +3393,15 @@ arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
   \endrst
  */
 template <typename Range>
-arg_join<internal::iterator_t<const Range>, char> join(const Range& range,
-                                                       string_view sep) {
+arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>, char>
+join(const Range& range, string_view sep) {
   return join(std::begin(range), std::end(range), sep);
 }
 
 template <typename Range>
-arg_join<internal::iterator_t<const Range>, wchar_t> join(const Range& range,
-                                                          wstring_view sep) {
+arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>,
+         wchar_t>
+join(const Range& range, wstring_view sep) {
   return join(std::begin(range), std::end(range), sep);
 }
 
@@ -3335,8 +3416,21 @@ arg_join<internal::iterator_t<const Range>, wchar_t> join(const Range& range,
     std::string answer = fmt::to_string(42);
   \endrst
  */
-template <typename T> inline std::string to_string(const T& value) {
-  return format("{}", value);
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+inline std::string to_string(const T& value) {
+  std::string result;
+  detail::write<char>(std::back_inserter(result), value);
+  return result;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline std::string to_string(T value) {
+  // The buffer should be large enough to store the number including the sign or
+  // "false" for bool.
+  constexpr int max_size = detail::digits10<T>() + 2;
+  char buffer[max_size > 5 ? max_size : 5];
+  char* begin = buffer;
+  return std::string(begin, detail::write<char>(begin, value));
 }
 
 /**
@@ -3346,36 +3440,65 @@ template <typename T> inline std::wstring to_wstring(const T& value) {
   return format(L"{}", value);
 }
 
-template <typename Char, std::size_t SIZE>
+template <typename Char, size_t SIZE>
 std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
-  return std::basic_string<Char>(buf.data(), buf.size());
+  auto size = buf.size();
+  detail::assume(size < std::basic_string<Char>().max_size());
+  return std::basic_string<Char>(buf.data(), size);
 }
 
 template <typename Char>
-typename buffer_context<Char>::iterator internal::vformat_to(
-    internal::buffer<Char>& buf, basic_string_view<Char> format_str,
+typename buffer_context<Char>::iterator detail::vformat_to(
+    detail::buffer<Char>& buf, basic_string_view<Char> format_str,
     basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  using range = buffer_range<Char>;
-  return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str),
-                                          args);
-}
+  using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
+  return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
+                        args);
+}
+
+#ifndef FMT_HEADER_ONLY
+extern template format_context::iterator detail::vformat_to(
+    detail::buffer<char>&, string_view, basic_format_args<format_context>);
+namespace detail {
+extern template FMT_API std::string grouping_impl<char>(locale_ref loc);
+extern template FMT_API std::string grouping_impl<wchar_t>(locale_ref loc);
+extern template FMT_API char thousands_sep_impl<char>(locale_ref loc);
+extern template FMT_API wchar_t thousands_sep_impl<wchar_t>(locale_ref loc);
+extern template FMT_API char decimal_point_impl(locale_ref loc);
+extern template FMT_API wchar_t decimal_point_impl(locale_ref loc);
+extern template int format_float<double>(double value, int precision,
+                                         float_specs specs, buffer<char>& buf);
+extern template int format_float<long double>(long double value, int precision,
+                                              float_specs specs,
+                                              buffer<char>& buf);
+int snprintf_float(float value, int precision, float_specs specs,
+                   buffer<char>& buf) = delete;
+extern template int snprintf_float<double>(double value, int precision,
+                                           float_specs specs,
+                                           buffer<char>& buf);
+extern template int snprintf_float<long double>(long double value,
+                                                int precision,
+                                                float_specs specs,
+                                                buffer<char>& buf);
+}  // namespace detail
+#endif
 
 template <typename S, typename Char = char_t<S>,
-          FMT_ENABLE_IF(internal::is_string<S>::value)>
-inline typename buffer_context<Char>::iterator vformat_to(
-    internal::buffer<Char>& buf, const S& format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  return internal::vformat_to(buf, to_string_view(format_str), args);
+          FMT_ENABLE_IF(detail::is_string<S>::value)>
+inline typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
+    detail::buffer<Char>& buf, const S& format_str,
+    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args) {
+  return detail::vformat_to(buf, to_string_view(format_str), args);
 }
 
-template <typename S, typename... Args, std::size_t SIZE = inline_buffer_size,
-          typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
+template <typename S, typename... Args, size_t SIZE = inline_buffer_size,
+          typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
 inline typename buffer_context<Char>::iterator format_to(
     basic_memory_buffer<Char, SIZE>& buf, const S& format_str, Args&&... args) {
-  internal::check_format_string<Args...>(format_str);
+  detail::check_format_string<Args...>(format_str);
   using context = buffer_context<Char>;
-  return internal::vformat_to(buf, to_string_view(format_str),
-                              make_format_args<context>(args...));
+  return detail::vformat_to(buf, to_string_view(format_str),
+                            make_format_args<context>(args...));
 }
 
 template <typename OutputIt, typename Char = char>
@@ -3384,16 +3507,15 @@ using format_context_t = basic_format_context<OutputIt, Char>;
 template <typename OutputIt, typename Char = char>
 using format_args_t = basic_format_args<format_context_t<OutputIt, Char>>;
 
-template <typename S, typename OutputIt, typename... Args,
-          FMT_ENABLE_IF(
-              internal::is_output_iterator<OutputIt>::value &&
-              !internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
+template <
+    typename S, typename OutputIt, typename... Args,
+    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value &&
+                  !detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
 inline OutputIt vformat_to(
     OutputIt out, const S& format_str,
     format_args_t<type_identity_t<OutputIt>, char_t<S>> args) {
-  using range = internal::output_range<OutputIt, char_t<S>>;
-  return vformat_to<arg_formatter<range>>(range(out),
-                                          to_string_view(format_str), args);
+  using af = detail::arg_formatter<OutputIt, char_t<S>>;
+  return vformat_to<af>(out, to_string_view(format_str), args);
 }
 
 /**
@@ -3409,11 +3531,11 @@ inline OutputIt vformat_to(
  */
 template <typename OutputIt, typename S, typename... Args,
           FMT_ENABLE_IF(
-              internal::is_output_iterator<OutputIt>::value &&
-              !internal::is_contiguous_back_insert_iterator<OutputIt>::value &&
-              internal::is_string<S>::value)>
+              detail::is_output_iterator<OutputIt>::value &&
+              !detail::is_contiguous_back_insert_iterator<OutputIt>::value &&
+              detail::is_string<S>::value)>
 inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) {
-  internal::check_format_string<Args...>(format_str);
+  detail::check_format_string<Args...>(format_str);
   using context = format_context_t<OutputIt, char_t<S>>;
   return vformat_to(out, to_string_view(format_str),
                     make_format_args<context>(args...));
@@ -3423,12 +3545,12 @@ template <typename OutputIt> struct format_to_n_result {
   /** Iterator past the end of the output range. */
   OutputIt out;
   /** Total (not truncated) output size. */
-  std::size_t size;
+  size_t size;
 };
 
 template <typename OutputIt, typename Char = typename OutputIt::value_type>
 using format_to_n_context =
-    format_context_t<internal::truncating_iterator<OutputIt>, Char>;
+    format_context_t<detail::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>>;
@@ -3441,11 +3563,11 @@ make_format_to_n_args(const Args&... args) {
 }
 
 template <typename OutputIt, typename Char, typename... Args,
-          FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value)>
 inline format_to_n_result<OutputIt> vformat_to_n(
-    OutputIt out, std::size_t n, basic_string_view<Char> format_str,
+    OutputIt out, size_t n, basic_string_view<Char> format_str,
     format_to_n_args<type_identity_t<OutputIt>, type_identity_t<Char>> args) {
-  auto it = vformat_to(internal::truncating_iterator<OutputIt>(out, n),
+  auto it = vformat_to(detail::truncating_iterator<OutputIt>(out, n),
                        format_str, args);
   return {it.base(), it.count()};
 }
@@ -3458,23 +3580,23 @@ inline format_to_n_result<OutputIt> vformat_to_n(
  \endrst
  */
 template <typename OutputIt, typename S, typename... Args,
-          FMT_ENABLE_IF(internal::is_string<S>::value&&
-                            internal::is_output_iterator<OutputIt>::value)>
-inline format_to_n_result<OutputIt> format_to_n(OutputIt out, std::size_t n,
+          FMT_ENABLE_IF(detail::is_string<S>::value&&
+                            detail::is_output_iterator<OutputIt>::value)>
+inline format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
                                                 const S& format_str,
                                                 const Args&... args) {
-  internal::check_format_string<Args...>(format_str);
+  detail::check_format_string<Args...>(format_str);
   using context = format_to_n_context<OutputIt, char_t<S>>;
   return vformat_to_n(out, n, to_string_view(format_str),
                       make_format_args<context>(args...));
 }
 
-template <typename Char>
-inline std::basic_string<Char> internal::vformat(
+template <typename Char, enable_if_t<(!std::is_same<Char, char>::value), int>>
+std::basic_string<Char> detail::vformat(
     basic_string_view<Char> format_str,
     basic_format_args<buffer_context<type_identity_t<Char>>> args) {
   basic_memory_buffer<Char> buffer;
-  internal::vformat_to(buffer, format_str, args);
+  detail::vformat_to(buffer, format_str, args);
   return to_string(buffer);
 }
 
@@ -3483,15 +3605,15 @@ inline std::basic_string<Char> internal::vformat(
   ``format(format_str, args...)``.
  */
 template <typename... Args>
-inline std::size_t formatted_size(string_view format_str, const Args&... args) {
-  return format_to(internal::counting_iterator(), format_str, args...).count();
+inline size_t formatted_size(string_view format_str, const Args&... args) {
+  return format_to(detail::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);
+  detail::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"));
@@ -3503,18 +3625,15 @@ void vprint(basic_string_view<Char> format_str, wformat_args args) {
 }
 
 #if FMT_USE_USER_DEFINED_LITERALS
-namespace internal {
+namespace detail {
 
 #  if FMT_USE_UDL_TEMPLATE
 template <typename Char, Char... CHARS> class udl_formatter {
  public:
   template <typename... Args>
   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, remove_cvref_t<Args>...>(
-            basic_string_view<Char>(s, sizeof...(CHARS)));
-    (void)invalid_format;
+    static FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
+    check_format_string<remove_cvref_t<Args>...>(FMT_STRING(s));
     return format(s, std::forward<Args>(args)...);
   }
 };
@@ -3530,39 +3649,23 @@ template <typename Char> struct udl_formatter {
 #  endif  // FMT_USE_UDL_TEMPLATE
 
 template <typename Char> struct udl_arg {
-  basic_string_view<Char> str;
+  const Char* str;
 
-  template <typename T> named_arg<T, Char> operator=(T&& value) const {
+  template <typename T> named_arg<Char, T> operator=(T&& value) const {
     return {str, std::forward<T>(value)};
   }
 };
-
-// Converts string literals to basic_string_view.
-template <typename Char, size_t N>
-FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
-    const Char (&s)[N]) {
-  // Remove trailing null character if needed. Won't be present if this is used
-  // with raw character array (i.e. not defined as a string).
-  return {s,
-          N - ((std::char_traits<Char>::to_int_type(s[N - 1]) == 0) ? 1 : 0)};
-}
-
-// Converts string_view to basic_string_view.
-template <typename Char>
-FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
-    const std_string_view<Char>& s) {
-  return {s.data(), s.size()};
-}
-}  // namespace internal
+}  // namespace detail
 
 inline namespace literals {
 #  if FMT_USE_UDL_TEMPLATE
 #    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wpedantic"
 #    if FMT_CLANG_VERSION
 #      pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
 #    endif
 template <typename Char, Char... CHARS>
-FMT_CONSTEXPR internal::udl_formatter<Char, CHARS...> operator""_format() {
+FMT_CONSTEXPR detail::udl_formatter<Char, CHARS...> operator""_format() {
   return {};
 }
 #    pragma GCC diagnostic pop
@@ -3577,12 +3680,12 @@ FMT_CONSTEXPR internal::udl_formatter<Char, CHARS...> operator""_format() {
     std::string message = "The answer is {}"_format(42);
   \endrst
  */
-FMT_CONSTEXPR internal::udl_formatter<char> operator"" _format(const char* s,
-                                                               std::size_t n) {
+FMT_CONSTEXPR detail::udl_formatter<char> operator"" _format(const char* s,
+                                                             size_t n) {
   return {{s, n}};
 }
-FMT_CONSTEXPR internal::udl_formatter<wchar_t> operator"" _format(
-    const wchar_t* s, std::size_t n) {
+FMT_CONSTEXPR detail::udl_formatter<wchar_t> operator"" _format(
+    const wchar_t* s, size_t n) {
   return {{s, n}};
 }
 #  endif  // FMT_USE_UDL_TEMPLATE
@@ -3597,47 +3700,16 @@ FMT_CONSTEXPR internal::udl_formatter<wchar_t> operator"" _format(
     fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
   \endrst
  */
-FMT_CONSTEXPR internal::udl_arg<char> operator"" _a(const char* s,
-                                                    std::size_t n) {
-  return {{s, n}};
+FMT_CONSTEXPR detail::udl_arg<char> operator"" _a(const char* s, size_t) {
+  return {s};
 }
-FMT_CONSTEXPR internal::udl_arg<wchar_t> operator"" _a(const wchar_t* s,
-                                                       std::size_t n) {
-  return {{s, n}};
+FMT_CONSTEXPR detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
+  return {s};
 }
 }  // namespace literals
 #endif  // FMT_USE_USER_DEFINED_LITERALS
 FMT_END_NAMESPACE
 
-#define FMT_STRING_IMPL(s, ...)                                     \
-  [] {                                                              \
-    /* Use a macro-like name to avoid shadowing warnings. */        \
-    struct FMT_COMPILE_STRING : fmt::compile_string {               \
-      using char_type = fmt::remove_cvref_t<decltype(s[0])>;        \
-      FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR                    \
-      operator fmt::basic_string_view<char_type>() const {          \
-        return fmt::internal::compile_string_to_view<char_type>(s); \
-      }                                                             \
-    };                                                              \
-    return FMT_COMPILE_STRING();                                    \
-  }()
-
-/**
-  \rst
-  Constructs a compile-time format string from a string literal *s*.
-
-  **Example**::
-
-    // A compile-time error because 'd' is an invalid specifier for strings.
-    std::string s = format(FMT_STRING("{:d}"), "foo");
-  \endrst
- */
-#define FMT_STRING(s) FMT_STRING_IMPL(s, )
-
-#if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS
-#  define fmt(s) FMT_STRING_IMPL(s, [[deprecated]])
-#endif
-
 #ifdef FMT_HEADER_ONLY
 #  define FMT_FUNC inline
 #  include "format-inl.h"
index 9a9abf8d0a7384021eb3013e2ae4177f3bf975d5..a64a1f3893a59297899f13d8382f9c79838c1ba7 100644 (file)
@@ -8,12 +8,12 @@
 #include "fmt/format-inl.h"
 
 FMT_BEGIN_NAMESPACE
-namespace internal {
+namespace detail {
 
 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
+#ifdef FMT_FUZZ
   if (precision > 100000)
     throw std::runtime_error(
         "fuzz mode - avoid large allocation inside snprintf");
@@ -23,154 +23,47 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
   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;
+}  // namespace detail
 
-  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_INSTANTIATION_DEF_API internal::basic_data<void>;
+template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
 
 // 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;
+int (*instantiate_format_float)(double, int, detail::float_specs,
+                                detail::buffer<char>&) = detail::format_float;
 
 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
-template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
-template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
+template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
+template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
 #endif
 
 // 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);
+template FMT_API std::string detail::grouping_impl<char>(locale_ref);
+template FMT_API char detail::thousands_sep_impl(locale_ref);
+template FMT_API char detail::decimal_point_impl(locale_ref);
 
-template FMT_API void internal::buffer<char>::append(const char*, const char*);
+template FMT_API void detail::buffer<char>::append(const char*, const char*);
 
-template FMT_API void internal::arg_map<format_context>::init(
-    const basic_format_args<format_context>& args);
+template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
+    detail::buffer<char>&, string_view,
+    basic_format_args<FMT_BUFFER_CONTEXT(char)>);
 
-template FMT_API std::string internal::vformat<char>(
-    string_view, basic_format_args<format_context>);
-
-template FMT_API format_context::iterator internal::vformat_to(
-    internal::buffer<char>&, string_view, basic_format_args<format_context>);
-
-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>&);
+template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
+                                            detail::buffer<char>&);
+template FMT_API int detail::snprintf_float(long double, int,
+                                            detail::float_specs,
+                                            detail::buffer<char>&);
+template FMT_API int detail::format_float(double, int, detail::float_specs,
+                                          detail::buffer<char>&);
+template FMT_API int detail::format_float(long double, int, detail::float_specs,
+                                          detail::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 std::string detail::grouping_impl<wchar_t>(locale_ref);
+template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
+template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
 
-template FMT_API std::wstring internal::vformat<wchar_t>(
-    wstring_view, basic_format_args<wformat_context>);
+template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
+                                                      const wchar_t*);
 FMT_END_NAMESPACE