summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Volk <f_l_k@t-online.de>2025-05-08 21:18:11 +0200
committerKhem Raj <raj.khem@gmail.com>2025-05-08 13:08:30 -0700
commit330ade641e519b4939ff7f965715cd28552b3cc7 (patch)
treeccebbe272e0abb79b15ba26cc6599197bab68db5
parentd5f076161b5f2cd66f70a73eb39f4ed09c4f3449 (diff)
downloadmeta-openembedded-330ade641e519b4939ff7f965715cd28552b3cc7.tar.gz
btop: update 1.4.0 -> 1.4.2
- remove submitted patch Fix process arguments appearing outside proc box by replacing ASCII control codes with blankspace, issue #1080 | @aristocratos Fix problems shown by clang-tidy's performance checks | @imwints Fix wrong error message and documentation of renamed option --utf-force | @t-webber @imwints CMake: Remove option to use mold | @imwints Update Terminus font link, fix typo, spelling, and grammar | @QinCai-rui Please clang with sanitizers | @bad-co-de Fix MacOS tree-mode + aggregate memory/thread scaling issue | @xaskii Fix typo: Mhz -> MHz | @NyCodeGHG v1.4.1 Various code fixes | @imwints Various code fixes | @bad-co-de Fixed typo | @polluks Move the config parser in it's own module | @imwints Adding a menu option to show bitrates in base 10 separate from the setting to show bytes/bits in base 10 | @georgev93 Allow MidnightBSD to build btop using the existing freebsd support. | @laffer1 Use XDG_STATE_HOME to save logs | @imwints Bump CMake version to 3.25 required for LINUX variable | @imwints Replace brackets with arrows in net and proc box | @taha-yassine Bump bundled fmt to 11.1.4 | @imwints cmake: link to CMAKE_DL_LIBS | @alalazo Fix phoenix-night.theme marked as executable | @sertonix Add Kanagawa-lotus and Kanagawa-wave themes | @philikarus Bump NetBSD version to 10.1 and FreeBSD version to 14.2. | @fraggerfox Add dark version of adwaita theme: adwaita-dark | @k0tran Resetting last selection on page navigation in optionsMenu to avoid unordered_map error | @seth-wood Share the CPU name trimming code between platforms | @yarrick Update Ryzen name trimming | @yarrick Drop macos 12 build, add v14 and v15 | @yarrick Fix cmake-macos workflow | @yarrick Bump version of deprecated upload-artifact step | @yarrick Update obsolete egrep call | @tywkeene Fix menu crash when GPU_SUPPORT=false, issue #989 | @aristocratos Add 'Everforest Ligth Medium' theme | @mstuttgart Support intel GPUs before Gen-6 (patch from upstream) | @w8jcik intel_name_lookup_shim.c (get_intel_device_name): Fix SEGFAULT | @artyom-poptsov Fix rsmi_measure_pcie_speeds not saving, issue #934 | @aristocratos Show GPU Watt fractions when below 100W | @aristocratos Signed-off-by: Markus Volk <f_l_k@t-online.de> Signed-off-by: Khem Raj <raj.khem@gmail.com>
-rw-r--r--meta-oe/recipes-support/btop/btop/0001-fmt-Update-headers-from-11.1.4.patch19338
-rw-r--r--meta-oe/recipes-support/btop/btop_1.4.2.bb (renamed from meta-oe/recipes-support/btop/btop_1.4.0.bb)3
2 files changed, 1 insertions, 19340 deletions
diff --git a/meta-oe/recipes-support/btop/btop/0001-fmt-Update-headers-from-11.1.4.patch b/meta-oe/recipes-support/btop/btop/0001-fmt-Update-headers-from-11.1.4.patch
deleted file mode 100644
index 50834e57e8..0000000000
--- a/meta-oe/recipes-support/btop/btop/0001-fmt-Update-headers-from-11.1.4.patch
+++ /dev/null
@@ -1,19338 +0,0 @@
1From 236f243428b898e3ababb611c648ec120abd2857 Mon Sep 17 00:00:00 2001
2From: Khem Raj <raj.khem@gmail.com>
3Date: Tue, 11 Mar 2025 20:02:10 -0700
4Subject: [PATCH] fmt: Update headers from 11.1.4+
5
6Newer compilers e.g. clang-20+ are not able to compile the fmt headers
7from old snapshop, therefore bring the latest from libfmt upstream
8
9Upstream-Status: Submitted [https://github.com/aristocratos/btop/pull/1063]
10Signed-off-by: Khem Raj <raj.khem@gmail.com>
11---
12 include/fmt/args.h | 174 +-
13 include/fmt/base.h | 2974 +++++++++++++++++++++++++++++++
14 include/fmt/chrono.h | 1534 ++++++++--------
15 include/fmt/color.h | 444 ++---
16 include/fmt/compile.h | 280 ++-
17 include/fmt/core.h | 2908 +-----------------------------
18 include/fmt/format-inl.h | 529 ++++--
19 include/fmt/format.h | 3617 +++++++++++++++++---------------------
20 include/fmt/os.h | 300 ++--
21 include/fmt/ostream.h | 189 +-
22 include/fmt/printf.h | 426 +++--
23 include/fmt/ranges.h | 624 ++++---
24 include/fmt/std.h | 677 +++++--
25 include/fmt/xchar.h | 275 ++-
26 14 files changed, 7656 insertions(+), 7295 deletions(-)
27 create mode 100644 include/fmt/base.h
28
29diff --git a/include/fmt/args.h b/include/fmt/args.h
30index a3966d1..3ff4788 100644
31--- a/include/fmt/args.h
32+++ b/include/fmt/args.h
33@@ -1,4 +1,4 @@
34-// Formatting library for C++ - dynamic format arguments
35+// Formatting library for C++ - dynamic argument lists
36 //
37 // Copyright (c) 2012 - present, Victor Zverovich
38 // All rights reserved.
39@@ -8,34 +8,39 @@
40 #ifndef FMT_ARGS_H_
41 #define FMT_ARGS_H_
42
43-#include <functional> // std::reference_wrapper
44-#include <memory> // std::unique_ptr
45-#include <vector>
46+#ifndef FMT_MODULE
47+# include <functional> // std::reference_wrapper
48+# include <memory> // std::unique_ptr
49+# include <vector>
50+#endif
51
52-#include "core.h"
53+#include "format.h" // std_string_view
54
55 FMT_BEGIN_NAMESPACE
56-
57 namespace detail {
58
59 template <typename T> struct is_reference_wrapper : std::false_type {};
60 template <typename T>
61 struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
62
63-template <typename T> const T& unwrap(const T& v) { return v; }
64-template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
65+template <typename T> auto unwrap(const T& v) -> const T& { return v; }
66+template <typename T>
67+auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
68 return static_cast<const T&>(v);
69 }
70
71-class dynamic_arg_list {
72- // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
73- // templates it doesn't complain about inability to deduce single translation
74- // unit for placing vtable. So storage_node_base is made a fake template.
75- template <typename = void> struct node {
76- virtual ~node() = default;
77- std::unique_ptr<node<>> next;
78- };
79+// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
80+// 2022 (v17.10.0).
81+//
82+// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
83+// templates it doesn't complain about inability to deduce single translation
84+// unit for placing vtable. So node is made a fake template.
85+template <typename = void> struct node {
86+ virtual ~node() = default;
87+ std::unique_ptr<node<>> next;
88+};
89
90+class dynamic_arg_list {
91 template <typename T> struct typed_node : node<> {
92 T value;
93
94@@ -50,7 +55,7 @@ class dynamic_arg_list {
95 std::unique_ptr<node<>> head_;
96
97 public:
98- template <typename T, typename Arg> const T& push(const Arg& arg) {
99+ template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
100 auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
101 auto& value = new_node->value;
102 new_node->next = std::move(head_);
103@@ -61,28 +66,18 @@ class dynamic_arg_list {
104 } // namespace detail
105
106 /**
107- \rst
108- A dynamic version of `fmt::format_arg_store`.
109- It's equipped with a storage to potentially temporary objects which lifetimes
110- could be shorter than the format arguments object.
111-
112- It can be implicitly converted into `~fmt::basic_format_args` for passing
113- into type-erased formatting functions such as `~fmt::vformat`.
114- \endrst
115+ * A dynamic list of formatting arguments with storage.
116+ *
117+ * It can be implicitly converted into `fmt::basic_format_args` for passing
118+ * into type-erased formatting functions such as `fmt::vformat`.
119 */
120-template <typename Context>
121-class dynamic_format_arg_store
122-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
123- // Workaround a GCC template argument substitution bug.
124- : public basic_format_args<Context>
125-#endif
126-{
127+template <typename Context> class dynamic_format_arg_store {
128 private:
129 using char_type = typename Context::char_type;
130
131 template <typename T> struct need_copy {
132 static constexpr detail::type mapped_type =
133- detail::mapped_type_constant<T, Context>::value;
134+ detail::mapped_type_constant<T, char_type>::value;
135
136 enum {
137 value = !(detail::is_reference_wrapper<T>::value ||
138@@ -95,7 +90,7 @@ class dynamic_format_arg_store
139 };
140
141 template <typename T>
142- using stored_type = conditional_t<
143+ using stored_t = conditional_t<
144 std::is_convertible<T, std::basic_string<char_type>>::value &&
145 !detail::is_reference_wrapper<T>::value,
146 std::basic_string<char_type>, T>;
147@@ -110,80 +105,72 @@ class dynamic_format_arg_store
148
149 friend class basic_format_args<Context>;
150
151- unsigned long long get_types() const {
152- return detail::is_unpacked_bit | data_.size() |
153- (named_info_.empty()
154- ? 0ULL
155- : static_cast<unsigned long long>(detail::has_named_args_bit));
156- }
157-
158- const basic_format_arg<Context>* data() const {
159+ auto data() const -> const basic_format_arg<Context>* {
160 return named_info_.empty() ? data_.data() : data_.data() + 1;
161 }
162
163 template <typename T> void emplace_arg(const T& arg) {
164- data_.emplace_back(detail::make_arg<Context>(arg));
165+ data_.emplace_back(arg);
166 }
167
168 template <typename T>
169 void emplace_arg(const detail::named_arg<char_type, T>& arg) {
170- if (named_info_.empty()) {
171- constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
172- data_.insert(data_.begin(), {zero_ptr, 0});
173- }
174- data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
175+ if (named_info_.empty())
176+ data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
177+ data_.emplace_back(detail::unwrap(arg.value));
178 auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
179 data->pop_back();
180 };
181 std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
182 guard{&data_, pop_one};
183 named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
184- data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
185+ data_[0] = {named_info_.data(), named_info_.size()};
186 guard.release();
187 }
188
189 public:
190 constexpr dynamic_format_arg_store() = default;
191
192+ operator basic_format_args<Context>() const {
193+ return basic_format_args<Context>(data(), static_cast<int>(data_.size()),
194+ !named_info_.empty());
195+ }
196+
197 /**
198- \rst
199- Adds an argument into the dynamic store for later passing to a formatting
200- function.
201-
202- Note that custom types and string types (but not string views) are copied
203- into the store dynamically allocating memory if necessary.
204-
205- **Example**::
206-
207- fmt::dynamic_format_arg_store<fmt::format_context> store;
208- store.push_back(42);
209- store.push_back("abc");
210- store.push_back(1.5f);
211- std::string result = fmt::vformat("{} and {} and {}", store);
212- \endrst
213- */
214+ * Adds an argument into the dynamic store for later passing to a formatting
215+ * function.
216+ *
217+ * Note that custom types and string types (but not string views) are copied
218+ * into the store dynamically allocating memory if necessary.
219+ *
220+ * **Example**:
221+ *
222+ * fmt::dynamic_format_arg_store<fmt::format_context> store;
223+ * store.push_back(42);
224+ * store.push_back("abc");
225+ * store.push_back(1.5f);
226+ * std::string result = fmt::vformat("{} and {} and {}", store);
227+ */
228 template <typename T> void push_back(const T& arg) {
229 if (detail::const_check(need_copy<T>::value))
230- emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
231+ emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
232 else
233 emplace_arg(detail::unwrap(arg));
234 }
235
236 /**
237- \rst
238- Adds a reference to the argument into the dynamic store for later passing to
239- a formatting function.
240-
241- **Example**::
242-
243- fmt::dynamic_format_arg_store<fmt::format_context> store;
244- char band[] = "Rolling Stones";
245- store.push_back(std::cref(band));
246- band[9] = 'c'; // Changing str affects the output.
247- std::string result = fmt::vformat("{}", store);
248- // result == "Rolling Scones"
249- \endrst
250- */
251+ * Adds a reference to the argument into the dynamic store for later passing
252+ * to a formatting function.
253+ *
254+ * **Example**:
255+ *
256+ * fmt::dynamic_format_arg_store<fmt::format_context> store;
257+ * char band[] = "Rolling Stones";
258+ * store.push_back(std::cref(band));
259+ * band[9] = 'c'; // Changing str affects the output.
260+ * std::string result = fmt::vformat("{}", store);
261+ * // result == "Rolling Scones"
262+ */
263 template <typename T> void push_back(std::reference_wrapper<T> arg) {
264 static_assert(
265 need_copy<T>::value,
266@@ -192,41 +179,40 @@ class dynamic_format_arg_store
267 }
268
269 /**
270- Adds named argument into the dynamic store for later passing to a formatting
271- function. ``std::reference_wrapper`` is supported to avoid copying of the
272- argument. The name is always copied into the store.
273- */
274+ * Adds named argument into the dynamic store for later passing to a
275+ * formatting function. `std::reference_wrapper` is supported to avoid
276+ * copying of the argument. The name is always copied into the store.
277+ */
278 template <typename T>
279 void push_back(const detail::named_arg<char_type, T>& arg) {
280 const char_type* arg_name =
281 dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
282 if (detail::const_check(need_copy<T>::value)) {
283 emplace_arg(
284- fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
285+ fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
286 } else {
287 emplace_arg(fmt::arg(arg_name, arg.value));
288 }
289 }
290
291- /** Erase all elements from the store */
292+ /// Erase all elements from the store.
293 void clear() {
294 data_.clear();
295 named_info_.clear();
296- dynamic_args_ = detail::dynamic_arg_list();
297+ dynamic_args_ = {};
298 }
299
300- /**
301- \rst
302- Reserves space to store at least *new_cap* arguments including
303- *new_cap_named* named arguments.
304- \endrst
305- */
306+ /// Reserves space to store at least `new_cap` arguments including
307+ /// `new_cap_named` named arguments.
308 void reserve(size_t new_cap, size_t new_cap_named) {
309 FMT_ASSERT(new_cap >= new_cap_named,
310- "Set of arguments includes set of named arguments");
311+ "set of arguments includes set of named arguments");
312 data_.reserve(new_cap);
313 named_info_.reserve(new_cap_named);
314 }
315+
316+ /// Returns the number of elements in the store.
317+ size_t size() const noexcept { return data_.size(); }
318 };
319
320 FMT_END_NAMESPACE
321diff --git a/include/fmt/base.h b/include/fmt/base.h
322new file mode 100644
323index 0000000..fe73e37
324--- /dev/null
325+++ b/include/fmt/base.h
326@@ -0,0 +1,2974 @@
327+// Formatting library for C++ - the base API for char/UTF-8
328+//
329+// Copyright (c) 2012 - present, Victor Zverovich
330+// All rights reserved.
331+//
332+// For the license information refer to format.h.
333+
334+#ifndef FMT_BASE_H_
335+#define FMT_BASE_H_
336+
337+#if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE)
338+# define FMT_MODULE
339+#endif
340+
341+#ifndef FMT_MODULE
342+# include <limits.h> // CHAR_BIT
343+# include <stdio.h> // FILE
344+# include <string.h> // memcmp
345+
346+# include <type_traits> // std::enable_if
347+#endif
348+
349+// The fmt library version in the form major * 10000 + minor * 100 + patch.
350+#define FMT_VERSION 110104
351+
352+// Detect compiler versions.
353+#if defined(__clang__) && !defined(__ibmxl__)
354+# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
355+#else
356+# define FMT_CLANG_VERSION 0
357+#endif
358+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
359+# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
360+#else
361+# define FMT_GCC_VERSION 0
362+#endif
363+#if defined(__ICL)
364+# define FMT_ICC_VERSION __ICL
365+#elif defined(__INTEL_COMPILER)
366+# define FMT_ICC_VERSION __INTEL_COMPILER
367+#else
368+# define FMT_ICC_VERSION 0
369+#endif
370+#if defined(_MSC_VER)
371+# define FMT_MSC_VERSION _MSC_VER
372+#else
373+# define FMT_MSC_VERSION 0
374+#endif
375+
376+// Detect standard library versions.
377+#ifdef _GLIBCXX_RELEASE
378+# define FMT_GLIBCXX_RELEASE _GLIBCXX_RELEASE
379+#else
380+# define FMT_GLIBCXX_RELEASE 0
381+#endif
382+#ifdef _LIBCPP_VERSION
383+# define FMT_LIBCPP_VERSION _LIBCPP_VERSION
384+#else
385+# define FMT_LIBCPP_VERSION 0
386+#endif
387+
388+#ifdef _MSVC_LANG
389+# define FMT_CPLUSPLUS _MSVC_LANG
390+#else
391+# define FMT_CPLUSPLUS __cplusplus
392+#endif
393+
394+// Detect __has_*.
395+#ifdef __has_feature
396+# define FMT_HAS_FEATURE(x) __has_feature(x)
397+#else
398+# define FMT_HAS_FEATURE(x) 0
399+#endif
400+#ifdef __has_include
401+# define FMT_HAS_INCLUDE(x) __has_include(x)
402+#else
403+# define FMT_HAS_INCLUDE(x) 0
404+#endif
405+#ifdef __has_builtin
406+# define FMT_HAS_BUILTIN(x) __has_builtin(x)
407+#else
408+# define FMT_HAS_BUILTIN(x) 0
409+#endif
410+#ifdef __has_cpp_attribute
411+# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
412+#else
413+# define FMT_HAS_CPP_ATTRIBUTE(x) 0
414+#endif
415+
416+#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
417+ (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
418+
419+#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
420+ (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
421+
422+// Detect C++14 relaxed constexpr.
423+#ifdef FMT_USE_CONSTEXPR
424+// Use the provided definition.
425+#elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L
426+// GCC only allows constexpr member functions in non-literal types since 7.2:
427+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297.
428+# define FMT_USE_CONSTEXPR 1
429+#elif FMT_ICC_VERSION
430+# define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628
431+#elif FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912
432+# define FMT_USE_CONSTEXPR 1
433+#else
434+# define FMT_USE_CONSTEXPR 0
435+#endif
436+#if FMT_USE_CONSTEXPR
437+# define FMT_CONSTEXPR constexpr
438+#else
439+# define FMT_CONSTEXPR
440+#endif
441+
442+// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated.
443+#if !defined(__cpp_lib_is_constant_evaluated)
444+# define FMT_USE_CONSTEVAL 0
445+#elif FMT_CPLUSPLUS < 201709L
446+# define FMT_USE_CONSTEVAL 0
447+#elif FMT_GLIBCXX_RELEASE && FMT_GLIBCXX_RELEASE < 10
448+# define FMT_USE_CONSTEVAL 0
449+#elif FMT_LIBCPP_VERSION && FMT_LIBCPP_VERSION < 10000
450+# define FMT_USE_CONSTEVAL 0
451+#elif defined(__apple_build_version__) && __apple_build_version__ < 14000029L
452+# define FMT_USE_CONSTEVAL 0 // consteval is broken in Apple clang < 14.
453+#elif FMT_MSC_VERSION && FMT_MSC_VERSION < 1929
454+# define FMT_USE_CONSTEVAL 0 // consteval is broken in MSVC VS2019 < 16.10.
455+#elif defined(__cpp_consteval)
456+# define FMT_USE_CONSTEVAL 1
457+#elif FMT_GCC_VERSION >= 1002 || FMT_CLANG_VERSION >= 1101
458+# define FMT_USE_CONSTEVAL 1
459+#else
460+# define FMT_USE_CONSTEVAL 0
461+#endif
462+#if FMT_USE_CONSTEVAL
463+# define FMT_CONSTEVAL consteval
464+# define FMT_CONSTEXPR20 constexpr
465+#else
466+# define FMT_CONSTEVAL
467+# define FMT_CONSTEXPR20
468+#endif
469+
470+// Check if exceptions are disabled.
471+#ifdef FMT_USE_EXCEPTIONS
472+// Use the provided definition.
473+#elif defined(__GNUC__) && !defined(__EXCEPTIONS)
474+# define FMT_USE_EXCEPTIONS 0
475+#elif defined(__clang__) && !defined(__cpp_exceptions)
476+# define FMT_USE_EXCEPTIONS 0
477+#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS
478+# define FMT_USE_EXCEPTIONS 0
479+#else
480+# define FMT_USE_EXCEPTIONS 1
481+#endif
482+#if FMT_USE_EXCEPTIONS
483+# define FMT_TRY try
484+# define FMT_CATCH(x) catch (x)
485+#else
486+# define FMT_TRY if (true)
487+# define FMT_CATCH(x) if (false)
488+#endif
489+
490+#ifdef FMT_NO_UNIQUE_ADDRESS
491+// Use the provided definition.
492+#elif FMT_CPLUSPLUS < 202002L
493+// Not supported.
494+#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
495+# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]]
496+// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485).
497+#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION
498+# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
499+#endif
500+#ifndef FMT_NO_UNIQUE_ADDRESS
501+# define FMT_NO_UNIQUE_ADDRESS
502+#endif
503+
504+#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
505+# define FMT_FALLTHROUGH [[fallthrough]]
506+#elif defined(__clang__)
507+# define FMT_FALLTHROUGH [[clang::fallthrough]]
508+#elif FMT_GCC_VERSION >= 700 && \
509+ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
510+# define FMT_FALLTHROUGH [[gnu::fallthrough]]
511+#else
512+# define FMT_FALLTHROUGH
513+#endif
514+
515+// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings.
516+#if FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__)
517+# define FMT_NORETURN [[noreturn]]
518+#else
519+# define FMT_NORETURN
520+#endif
521+
522+#ifdef FMT_NODISCARD
523+// Use the provided definition.
524+#elif FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
525+# define FMT_NODISCARD [[nodiscard]]
526+#else
527+# define FMT_NODISCARD
528+#endif
529+
530+#ifdef FMT_DEPRECATED
531+// Use the provided definition.
532+#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
533+# define FMT_DEPRECATED [[deprecated]]
534+#else
535+# define FMT_DEPRECATED /* deprecated */
536+#endif
537+
538+#ifdef FMT_ALWAYS_INLINE
539+// Use the provided definition.
540+#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
541+# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
542+#else
543+# define FMT_ALWAYS_INLINE inline
544+#endif
545+// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
546+#ifdef NDEBUG
547+# define FMT_INLINE FMT_ALWAYS_INLINE
548+#else
549+# define FMT_INLINE inline
550+#endif
551+
552+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
553+# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
554+#else
555+# define FMT_VISIBILITY(value)
556+#endif
557+
558+// Detect pragmas.
559+#define FMT_PRAGMA_IMPL(x) _Pragma(#x)
560+#if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER)
561+// Workaround a _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884
562+// and an nvhpc warning: https://github.com/fmtlib/fmt/pull/2582.
563+# define FMT_PRAGMA_GCC(x) FMT_PRAGMA_IMPL(GCC x)
564+#else
565+# define FMT_PRAGMA_GCC(x)
566+#endif
567+#if FMT_CLANG_VERSION
568+# define FMT_PRAGMA_CLANG(x) FMT_PRAGMA_IMPL(clang x)
569+#else
570+# define FMT_PRAGMA_CLANG(x)
571+#endif
572+#if FMT_MSC_VERSION
573+# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
574+#else
575+# define FMT_MSC_WARNING(...)
576+#endif
577+
578+#ifndef FMT_BEGIN_NAMESPACE
579+# define FMT_BEGIN_NAMESPACE \
580+ namespace fmt { \
581+ inline namespace v11 {
582+# define FMT_END_NAMESPACE \
583+ } \
584+ }
585+#endif
586+
587+#ifndef FMT_EXPORT
588+# define FMT_EXPORT
589+# define FMT_BEGIN_EXPORT
590+# define FMT_END_EXPORT
591+#endif
592+
593+#ifdef _WIN32
594+# define FMT_WIN32 1
595+#else
596+# define FMT_WIN32 0
597+#endif
598+
599+#if !defined(FMT_HEADER_ONLY) && FMT_WIN32
600+# if defined(FMT_LIB_EXPORT)
601+# define FMT_API __declspec(dllexport)
602+# elif defined(FMT_SHARED)
603+# define FMT_API __declspec(dllimport)
604+# endif
605+#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
606+# define FMT_API FMT_VISIBILITY("default")
607+#endif
608+#ifndef FMT_API
609+# define FMT_API
610+#endif
611+
612+#ifndef FMT_OPTIMIZE_SIZE
613+# define FMT_OPTIMIZE_SIZE 0
614+#endif
615+
616+// FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher
617+// per-call binary size by passing built-in types through the extension API.
618+#ifndef FMT_BUILTIN_TYPES
619+# define FMT_BUILTIN_TYPES 1
620+#endif
621+
622+#define FMT_APPLY_VARIADIC(expr) \
623+ using unused = int[]; \
624+ (void)unused { 0, (expr, 0)... }
625+
626+// Enable minimal optimizations for more compact code in debug mode.
627+FMT_PRAGMA_GCC(push_options)
628+#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
629+FMT_PRAGMA_GCC(optimize("Og"))
630+#endif
631+FMT_PRAGMA_CLANG(diagnostic push)
632+
633+FMT_BEGIN_NAMESPACE
634+
635+// Implementations of enable_if_t and other metafunctions for older systems.
636+template <bool B, typename T = void>
637+using enable_if_t = typename std::enable_if<B, T>::type;
638+template <bool B, typename T, typename F>
639+using conditional_t = typename std::conditional<B, T, F>::type;
640+template <bool B> using bool_constant = std::integral_constant<bool, B>;
641+template <typename T>
642+using remove_reference_t = typename std::remove_reference<T>::type;
643+template <typename T>
644+using remove_const_t = typename std::remove_const<T>::type;
645+template <typename T>
646+using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
647+template <typename T>
648+using make_unsigned_t = typename std::make_unsigned<T>::type;
649+template <typename T>
650+using underlying_t = typename std::underlying_type<T>::type;
651+template <typename T> using decay_t = typename std::decay<T>::type;
652+using nullptr_t = decltype(nullptr);
653+
654+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
655+// A workaround for gcc 4.9 to make void_t work in a SFINAE context.
656+template <typename...> struct void_t_impl {
657+ using type = void;
658+};
659+template <typename... T> using void_t = typename void_t_impl<T...>::type;
660+#else
661+template <typename...> using void_t = void;
662+#endif
663+
664+struct monostate {
665+ constexpr monostate() {}
666+};
667+
668+// An enable_if helper to be used in template parameters which results in much
669+// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
670+// to workaround a bug in MSVC 2019 (see #1140 and #1186).
671+#ifdef FMT_DOC
672+# define FMT_ENABLE_IF(...)
673+#else
674+# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
675+#endif
676+
677+template <typename T> constexpr auto min_of(T a, T b) -> T {
678+ return a < b ? a : b;
679+}
680+template <typename T> constexpr auto max_of(T a, T b) -> T {
681+ return a > b ? a : b;
682+}
683+
684+namespace detail {
685+// Suppresses "unused variable" warnings with the method described in
686+// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
687+// (void)var does not work on many Intel compilers.
688+template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
689+
690+constexpr auto is_constant_evaluated(bool default_value = false) noexcept
691+ -> bool {
692+// Workaround for incompatibility between clang 14 and libstdc++ consteval-based
693+// std::is_constant_evaluated: https://github.com/fmtlib/fmt/issues/3247.
694+#if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \
695+ (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
696+ ignore_unused(default_value);
697+ return __builtin_is_constant_evaluated();
698+#elif defined(__cpp_lib_is_constant_evaluated)
699+ ignore_unused(default_value);
700+ return std::is_constant_evaluated();
701+#else
702+ return default_value;
703+#endif
704+}
705+
706+// Suppresses "conditional expression is constant" warnings.
707+template <typename T> FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T {
708+ return val;
709+}
710+
711+FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
712+ const char* message);
713+
714+#if defined(FMT_ASSERT)
715+// Use the provided definition.
716+#elif defined(NDEBUG)
717+// FMT_ASSERT is not empty to avoid -Wempty-body.
718+# define FMT_ASSERT(condition, message) \
719+ fmt::detail::ignore_unused((condition), (message))
720+#else
721+# define FMT_ASSERT(condition, message) \
722+ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
723+ ? (void)0 \
724+ : fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
725+#endif
726+
727+#ifdef FMT_USE_INT128
728+// Use the provided definition.
729+#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
730+ !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
731+# define FMT_USE_INT128 1
732+using int128_opt = __int128_t; // An optional native 128-bit integer.
733+using uint128_opt = __uint128_t;
734+inline auto map(int128_opt x) -> int128_opt { return x; }
735+inline auto map(uint128_opt x) -> uint128_opt { return x; }
736+#else
737+# define FMT_USE_INT128 0
738+#endif
739+#if !FMT_USE_INT128
740+enum class int128_opt {};
741+enum class uint128_opt {};
742+// Reduce template instantiations.
743+inline auto map(int128_opt) -> monostate { return {}; }
744+inline auto map(uint128_opt) -> monostate { return {}; }
745+#endif
746+
747+#ifndef FMT_USE_BITINT
748+# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500)
749+#endif
750+
751+#if FMT_USE_BITINT
752+FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")
753+template <int N> using bitint = _BitInt(N);
754+template <int N> using ubitint = unsigned _BitInt(N);
755+#else
756+template <int N> struct bitint {};
757+template <int N> struct ubitint {};
758+#endif // FMT_USE_BITINT
759+
760+// Casts a nonnegative integer to unsigned.
761+template <typename Int>
762+FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t<Int> {
763+ FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
764+ return static_cast<make_unsigned_t<Int>>(value);
765+}
766+
767+template <typename Char>
768+using unsigned_char = conditional_t<sizeof(Char) == 1, unsigned char, unsigned>;
769+
770+// A heuristic to detect std::string and std::[experimental::]string_view.
771+// It is mainly used to avoid dependency on <[experimental/]string_view>.
772+template <typename T, typename Enable = void>
773+struct is_std_string_like : std::false_type {};
774+template <typename T>
775+struct is_std_string_like<T, void_t<decltype(std::declval<T>().find_first_of(
776+ typename T::value_type(), 0))>>
777+ : std::is_convertible<decltype(std::declval<T>().data()),
778+ const typename T::value_type*> {};
779+
780+// Check if the literal encoding is UTF-8.
781+enum { is_utf8_enabled = "\u00A7"[1] == '\xA7' };
782+enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
783+
784+#ifndef FMT_UNICODE
785+# define FMT_UNICODE 1
786+#endif
787+
788+static_assert(!FMT_UNICODE || use_utf8,
789+ "Unicode support requires compiling with /utf-8");
790+
791+template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
792+constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
793+
794+template <typename Char>
795+FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
796+ -> int {
797+ if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
798+ for (; n != 0; ++s1, ++s2, --n) {
799+ if (*s1 < *s2) return -1;
800+ if (*s1 > *s2) return 1;
801+ }
802+ return 0;
803+}
804+
805+namespace adl {
806+using namespace std;
807+
808+template <typename Container>
809+auto invoke_back_inserter()
810+ -> decltype(back_inserter(std::declval<Container&>()));
811+} // namespace adl
812+
813+template <typename It, typename Enable = std::true_type>
814+struct is_back_insert_iterator : std::false_type {};
815+
816+template <typename It>
817+struct is_back_insert_iterator<
818+ It, bool_constant<std::is_same<
819+ decltype(adl::invoke_back_inserter<typename It::container_type>()),
820+ It>::value>> : std::true_type {};
821+
822+// Extracts a reference to the container from *insert_iterator.
823+template <typename OutputIt>
824+inline FMT_CONSTEXPR20 auto get_container(OutputIt it) ->
825+ typename OutputIt::container_type& {
826+ struct accessor : OutputIt {
827+ FMT_CONSTEXPR20 accessor(OutputIt base) : OutputIt(base) {}
828+ using OutputIt::container;
829+ };
830+ return *accessor(it).container;
831+}
832+} // namespace detail
833+
834+// Parsing-related public API and forward declarations.
835+FMT_BEGIN_EXPORT
836+
837+/**
838+ * An implementation of `std::basic_string_view` for pre-C++17. It provides a
839+ * subset of the API. `fmt::basic_string_view` is used for format strings even
840+ * if `std::basic_string_view` is available to prevent issues when a library is
841+ * compiled with a different `-std` option than the client code (which is not
842+ * recommended).
843+ */
844+template <typename Char> class basic_string_view {
845+ private:
846+ const Char* data_;
847+ size_t size_;
848+
849+ public:
850+ using value_type = Char;
851+ using iterator = const Char*;
852+
853+ constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
854+
855+ /// Constructs a string reference object from a C string and a size.
856+ constexpr basic_string_view(const Char* s, size_t count) noexcept
857+ : data_(s), size_(count) {}
858+
859+ constexpr basic_string_view(nullptr_t) = delete;
860+
861+ /// Constructs a string reference object from a C string.
862+#if FMT_GCC_VERSION
863+ FMT_ALWAYS_INLINE
864+#endif
865+ FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
866+#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
867+ if (std::is_same<Char, char>::value) {
868+ size_ = __builtin_strlen(detail::narrow(s));
869+ return;
870+ }
871+#endif
872+ size_t len = 0;
873+ while (*s++) ++len;
874+ size_ = len;
875+ }
876+
877+ /// Constructs a string reference from a `std::basic_string` or a
878+ /// `std::basic_string_view` object.
879+ template <typename S,
880+ FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same<
881+ typename S::value_type, Char>::value)>
882+ FMT_CONSTEXPR basic_string_view(const S& s) noexcept
883+ : data_(s.data()), size_(s.size()) {}
884+
885+ /// Returns a pointer to the string data.
886+ constexpr auto data() const noexcept -> const Char* { return data_; }
887+
888+ /// Returns the string size.
889+ constexpr auto size() const noexcept -> size_t { return size_; }
890+
891+ constexpr auto begin() const noexcept -> iterator { return data_; }
892+ constexpr auto end() const noexcept -> iterator { return data_ + size_; }
893+
894+ constexpr auto operator[](size_t pos) const noexcept -> const Char& {
895+ return data_[pos];
896+ }
897+
898+ FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
899+ data_ += n;
900+ size_ -= n;
901+ }
902+
903+ FMT_CONSTEXPR auto starts_with(basic_string_view<Char> sv) const noexcept
904+ -> bool {
905+ return size_ >= sv.size_ && detail::compare(data_, sv.data_, sv.size_) == 0;
906+ }
907+ FMT_CONSTEXPR auto starts_with(Char c) const noexcept -> bool {
908+ return size_ >= 1 && *data_ == c;
909+ }
910+ FMT_CONSTEXPR auto starts_with(const Char* s) const -> bool {
911+ return starts_with(basic_string_view<Char>(s));
912+ }
913+
914+ // Lexicographically compare this string reference to other.
915+ FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
916+ int result =
917+ detail::compare(data_, other.data_, min_of(size_, other.size_));
918+ if (result != 0) return result;
919+ return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
920+ }
921+
922+ FMT_CONSTEXPR friend auto operator==(basic_string_view lhs,
923+ basic_string_view rhs) -> bool {
924+ return lhs.compare(rhs) == 0;
925+ }
926+ friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
927+ return lhs.compare(rhs) != 0;
928+ }
929+ friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
930+ return lhs.compare(rhs) < 0;
931+ }
932+ friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
933+ return lhs.compare(rhs) <= 0;
934+ }
935+ friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
936+ return lhs.compare(rhs) > 0;
937+ }
938+ friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
939+ return lhs.compare(rhs) >= 0;
940+ }
941+};
942+
943+using string_view = basic_string_view<char>;
944+
945+/// Specifies if `T` is an extended character type. Can be specialized by users.
946+template <typename T> struct is_xchar : std::false_type {};
947+template <> struct is_xchar<wchar_t> : std::true_type {};
948+template <> struct is_xchar<char16_t> : std::true_type {};
949+template <> struct is_xchar<char32_t> : std::true_type {};
950+#ifdef __cpp_char8_t
951+template <> struct is_xchar<char8_t> : std::true_type {};
952+#endif
953+
954+// DEPRECATED! Will be replaced with an alias to prevent specializations.
955+template <typename T> struct is_char : is_xchar<T> {};
956+template <> struct is_char<char> : std::true_type {};
957+
958+template <typename T> class basic_appender;
959+using appender = basic_appender<char>;
960+
961+// Checks whether T is a container with contiguous storage.
962+template <typename T> struct is_contiguous : std::false_type {};
963+
964+class context;
965+template <typename OutputIt, typename Char> class generic_context;
966+template <typename Char> class parse_context;
967+
968+// Longer aliases for C++20 compatibility.
969+template <typename Char> using basic_format_parse_context = parse_context<Char>;
970+using format_parse_context = parse_context<char>;
971+template <typename OutputIt, typename Char>
972+using basic_format_context =
973+ conditional_t<std::is_same<OutputIt, appender>::value, context,
974+ generic_context<OutputIt, Char>>;
975+using format_context = context;
976+
977+template <typename Char>
978+using buffered_context =
979+ conditional_t<std::is_same<Char, char>::value, context,
980+ generic_context<basic_appender<Char>, Char>>;
981+
982+template <typename Context> class basic_format_arg;
983+template <typename Context> class basic_format_args;
984+
985+// A separate type would result in shorter symbols but break ABI compatibility
986+// between clang and gcc on ARM (#1919).
987+using format_args = basic_format_args<context>;
988+
989+// A formatter for objects of type T.
990+template <typename T, typename Char = char, typename Enable = void>
991+struct formatter {
992+ // A deleted default constructor indicates a disabled formatter.
993+ formatter() = delete;
994+};
995+
996+/// Reports a format error at compile time or, via a `format_error` exception,
997+/// at runtime.
998+// This function is intentionally not constexpr to give a compile-time error.
999+FMT_NORETURN FMT_API void report_error(const char* message);
1000+
1001+enum class presentation_type : unsigned char {
1002+ // Common specifiers:
1003+ none = 0,
1004+ debug = 1, // '?'
1005+ string = 2, // 's' (string, bool)
1006+
1007+ // Integral, bool and character specifiers:
1008+ dec = 3, // 'd'
1009+ hex, // 'x' or 'X'
1010+ oct, // 'o'
1011+ bin, // 'b' or 'B'
1012+ chr, // 'c'
1013+
1014+ // String and pointer specifiers:
1015+ pointer = 3, // 'p'
1016+
1017+ // Floating-point specifiers:
1018+ exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation)
1019+ fixed, // 'f' or 'F'
1020+ general, // 'g' or 'G'
1021+ hexfloat // 'a' or 'A'
1022+};
1023+
1024+enum class align { none, left, right, center, numeric };
1025+enum class sign { none, minus, plus, space };
1026+enum class arg_id_kind { none, index, name };
1027+
1028+// Basic format specifiers for built-in and string types.
1029+class basic_specs {
1030+ private:
1031+ // Data is arranged as follows:
1032+ //
1033+ // 0 1 2 3
1034+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1035+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1036+ // |type |align| w | p | s |u|#|L| f | unused |
1037+ // +-----+-----+---+---+---+-+-+-+-----+---------------------------+
1038+ //
1039+ // w - dynamic width info
1040+ // p - dynamic precision info
1041+ // s - sign
1042+ // u - uppercase (e.g. 'X' for 'x')
1043+ // # - alternate form ('#')
1044+ // L - localized
1045+ // f - fill size
1046+ //
1047+ // Bitfields are not used because of compiler bugs such as gcc bug 61414.
1048+ enum : unsigned {
1049+ type_mask = 0x00007,
1050+ align_mask = 0x00038,
1051+ width_mask = 0x000C0,
1052+ precision_mask = 0x00300,
1053+ sign_mask = 0x00C00,
1054+ uppercase_mask = 0x01000,
1055+ alternate_mask = 0x02000,
1056+ localized_mask = 0x04000,
1057+ fill_size_mask = 0x38000,
1058+
1059+ align_shift = 3,
1060+ width_shift = 6,
1061+ precision_shift = 8,
1062+ sign_shift = 10,
1063+ fill_size_shift = 15,
1064+
1065+ max_fill_size = 4
1066+ };
1067+
1068+ unsigned data_ = 1 << fill_size_shift;
1069+ static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, "");
1070+
1071+ // Character (code unit) type is erased to prevent template bloat.
1072+ char fill_data_[max_fill_size] = {' '};
1073+
1074+ FMT_CONSTEXPR void set_fill_size(size_t size) {
1075+ data_ = (data_ & ~fill_size_mask) |
1076+ (static_cast<unsigned>(size) << fill_size_shift);
1077+ }
1078+
1079+ public:
1080+ constexpr auto type() const -> presentation_type {
1081+ return static_cast<presentation_type>(data_ & type_mask);
1082+ }
1083+ FMT_CONSTEXPR void set_type(presentation_type t) {
1084+ data_ = (data_ & ~type_mask) | static_cast<unsigned>(t);
1085+ }
1086+
1087+ constexpr auto align() const -> align {
1088+ return static_cast<fmt::align>((data_ & align_mask) >> align_shift);
1089+ }
1090+ FMT_CONSTEXPR void set_align(fmt::align a) {
1091+ data_ = (data_ & ~align_mask) | (static_cast<unsigned>(a) << align_shift);
1092+ }
1093+
1094+ constexpr auto dynamic_width() const -> arg_id_kind {
1095+ return static_cast<arg_id_kind>((data_ & width_mask) >> width_shift);
1096+ }
1097+ FMT_CONSTEXPR void set_dynamic_width(arg_id_kind w) {
1098+ data_ = (data_ & ~width_mask) | (static_cast<unsigned>(w) << width_shift);
1099+ }
1100+
1101+ FMT_CONSTEXPR auto dynamic_precision() const -> arg_id_kind {
1102+ return static_cast<arg_id_kind>((data_ & precision_mask) >>
1103+ precision_shift);
1104+ }
1105+ FMT_CONSTEXPR void set_dynamic_precision(arg_id_kind p) {
1106+ data_ = (data_ & ~precision_mask) |
1107+ (static_cast<unsigned>(p) << precision_shift);
1108+ }
1109+
1110+ constexpr bool dynamic() const {
1111+ return (data_ & (width_mask | precision_mask)) != 0;
1112+ }
1113+
1114+ constexpr auto sign() const -> sign {
1115+ return static_cast<fmt::sign>((data_ & sign_mask) >> sign_shift);
1116+ }
1117+ FMT_CONSTEXPR void set_sign(fmt::sign s) {
1118+ data_ = (data_ & ~sign_mask) | (static_cast<unsigned>(s) << sign_shift);
1119+ }
1120+
1121+ constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; }
1122+ FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; }
1123+
1124+ constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; }
1125+ FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; }
1126+ FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; }
1127+
1128+ constexpr auto localized() const -> bool {
1129+ return (data_ & localized_mask) != 0;
1130+ }
1131+ FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; }
1132+
1133+ constexpr auto fill_size() const -> size_t {
1134+ return (data_ & fill_size_mask) >> fill_size_shift;
1135+ }
1136+
1137+ template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
1138+ constexpr auto fill() const -> const Char* {
1139+ return fill_data_;
1140+ }
1141+ template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
1142+ constexpr auto fill() const -> const Char* {
1143+ return nullptr;
1144+ }
1145+
1146+ template <typename Char> constexpr auto fill_unit() const -> Char {
1147+ using uchar = unsigned char;
1148+ return static_cast<Char>(static_cast<uchar>(fill_data_[0]) |
1149+ (static_cast<uchar>(fill_data_[1]) << 8) |
1150+ (static_cast<uchar>(fill_data_[2]) << 16));
1151+ }
1152+
1153+ FMT_CONSTEXPR void set_fill(char c) {
1154+ fill_data_[0] = c;
1155+ set_fill_size(1);
1156+ }
1157+
1158+ template <typename Char>
1159+ FMT_CONSTEXPR void set_fill(basic_string_view<Char> s) {
1160+ auto size = s.size();
1161+ set_fill_size(size);
1162+ if (size == 1) {
1163+ unsigned uchar = static_cast<detail::unsigned_char<Char>>(s[0]);
1164+ fill_data_[0] = static_cast<char>(uchar);
1165+ fill_data_[1] = static_cast<char>(uchar >> 8);
1166+ fill_data_[2] = static_cast<char>(uchar >> 16);
1167+ return;
1168+ }
1169+ FMT_ASSERT(size <= max_fill_size, "invalid fill");
1170+ for (size_t i = 0; i < size; ++i)
1171+ fill_data_[i & 3] = static_cast<char>(s[i]);
1172+ }
1173+
1174+ FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) {
1175+ set_fill_size(specs.fill_size());
1176+ for (size_t i = 0; i < max_fill_size; ++i)
1177+ fill_data_[i] = specs.fill_data_[i];
1178+ }
1179+};
1180+
1181+// Format specifiers for built-in and string types.
1182+struct format_specs : basic_specs {
1183+ int width;
1184+ int precision;
1185+
1186+ constexpr format_specs() : width(0), precision(-1) {}
1187+};
1188+
1189+/**
1190+ * Parsing context consisting of a format string range being parsed and an
1191+ * argument counter for automatic indexing.
1192+ */
1193+template <typename Char = char> class parse_context {
1194+ private:
1195+ basic_string_view<Char> fmt_;
1196+ int next_arg_id_;
1197+
1198+ enum { use_constexpr_cast = !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200 };
1199+
1200+ FMT_CONSTEXPR void do_check_arg_id(int arg_id);
1201+
1202+ public:
1203+ using char_type = Char;
1204+ using iterator = const Char*;
1205+
1206+ constexpr explicit parse_context(basic_string_view<Char> fmt,
1207+ int next_arg_id = 0)
1208+ : fmt_(fmt), next_arg_id_(next_arg_id) {}
1209+
1210+ /// Returns an iterator to the beginning of the format string range being
1211+ /// parsed.
1212+ constexpr auto begin() const noexcept -> iterator { return fmt_.begin(); }
1213+
1214+ /// Returns an iterator past the end of the format string range being parsed.
1215+ constexpr auto end() const noexcept -> iterator { return fmt_.end(); }
1216+
1217+ /// Advances the begin iterator to `it`.
1218+ FMT_CONSTEXPR void advance_to(iterator it) {
1219+ fmt_.remove_prefix(detail::to_unsigned(it - begin()));
1220+ }
1221+
1222+ /// Reports an error if using the manual argument indexing; otherwise returns
1223+ /// the next argument index and switches to the automatic indexing.
1224+ FMT_CONSTEXPR auto next_arg_id() -> int {
1225+ if (next_arg_id_ < 0) {
1226+ report_error("cannot switch from manual to automatic argument indexing");
1227+ return 0;
1228+ }
1229+ int id = next_arg_id_++;
1230+ do_check_arg_id(id);
1231+ return id;
1232+ }
1233+
1234+ /// Reports an error if using the automatic argument indexing; otherwise
1235+ /// switches to the manual indexing.
1236+ FMT_CONSTEXPR void check_arg_id(int id) {
1237+ if (next_arg_id_ > 0) {
1238+ report_error("cannot switch from automatic to manual argument indexing");
1239+ return;
1240+ }
1241+ next_arg_id_ = -1;
1242+ do_check_arg_id(id);
1243+ }
1244+ FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {
1245+ next_arg_id_ = -1;
1246+ }
1247+ FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
1248+};
1249+
1250+FMT_END_EXPORT
1251+
1252+namespace detail {
1253+
1254+// Constructs fmt::basic_string_view<Char> from types implicitly convertible
1255+// to it, deducing Char. Explicitly convertible types such as the ones returned
1256+// from FMT_STRING are intentionally excluded.
1257+template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
1258+constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
1259+ return s;
1260+}
1261+template <typename T, FMT_ENABLE_IF(is_std_string_like<T>::value)>
1262+constexpr auto to_string_view(const T& s)
1263+ -> basic_string_view<typename T::value_type> {
1264+ return s;
1265+}
1266+template <typename Char>
1267+constexpr auto to_string_view(basic_string_view<Char> s)
1268+ -> basic_string_view<Char> {
1269+ return s;
1270+}
1271+
1272+template <typename T, typename Enable = void>
1273+struct has_to_string_view : std::false_type {};
1274+// detail:: is intentional since to_string_view is not an extension point.
1275+template <typename T>
1276+struct has_to_string_view<
1277+ T, void_t<decltype(detail::to_string_view(std::declval<T>()))>>
1278+ : std::true_type {};
1279+
1280+/// String's character (code unit) type. detail:: is intentional to prevent ADL.
1281+template <typename S,
1282+ typename V = decltype(detail::to_string_view(std::declval<S>()))>
1283+using char_t = typename V::value_type;
1284+
1285+enum class type {
1286+ none_type,
1287+ // Integer types should go first,
1288+ int_type,
1289+ uint_type,
1290+ long_long_type,
1291+ ulong_long_type,
1292+ int128_type,
1293+ uint128_type,
1294+ bool_type,
1295+ char_type,
1296+ last_integer_type = char_type,
1297+ // followed by floating-point types.
1298+ float_type,
1299+ double_type,
1300+ long_double_type,
1301+ last_numeric_type = long_double_type,
1302+ cstring_type,
1303+ string_type,
1304+ pointer_type,
1305+ custom_type
1306+};
1307+
1308+// Maps core type T to the corresponding type enum constant.
1309+template <typename T, typename Char>
1310+struct type_constant : std::integral_constant<type, type::custom_type> {};
1311+
1312+#define FMT_TYPE_CONSTANT(Type, constant) \
1313+ template <typename Char> \
1314+ struct type_constant<Type, Char> \
1315+ : std::integral_constant<type, type::constant> {}
1316+
1317+FMT_TYPE_CONSTANT(int, int_type);
1318+FMT_TYPE_CONSTANT(unsigned, uint_type);
1319+FMT_TYPE_CONSTANT(long long, long_long_type);
1320+FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
1321+FMT_TYPE_CONSTANT(int128_opt, int128_type);
1322+FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
1323+FMT_TYPE_CONSTANT(bool, bool_type);
1324+FMT_TYPE_CONSTANT(Char, char_type);
1325+FMT_TYPE_CONSTANT(float, float_type);
1326+FMT_TYPE_CONSTANT(double, double_type);
1327+FMT_TYPE_CONSTANT(long double, long_double_type);
1328+FMT_TYPE_CONSTANT(const Char*, cstring_type);
1329+FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
1330+FMT_TYPE_CONSTANT(const void*, pointer_type);
1331+
1332+constexpr auto is_integral_type(type t) -> bool {
1333+ return t > type::none_type && t <= type::last_integer_type;
1334+}
1335+constexpr auto is_arithmetic_type(type t) -> bool {
1336+ return t > type::none_type && t <= type::last_numeric_type;
1337+}
1338+
1339+constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
1340+constexpr auto in(type t, int set) -> bool {
1341+ return ((set >> static_cast<int>(t)) & 1) != 0;
1342+}
1343+
1344+// Bitsets of types.
1345+enum {
1346+ sint_set =
1347+ set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
1348+ uint_set = set(type::uint_type) | set(type::ulong_long_type) |
1349+ set(type::uint128_type),
1350+ bool_set = set(type::bool_type),
1351+ char_set = set(type::char_type),
1352+ float_set = set(type::float_type) | set(type::double_type) |
1353+ set(type::long_double_type),
1354+ string_set = set(type::string_type),
1355+ cstring_set = set(type::cstring_type),
1356+ pointer_set = set(type::pointer_type)
1357+};
1358+
1359+struct view {};
1360+
1361+template <typename Char, typename T> struct named_arg;
1362+template <typename T> struct is_named_arg : std::false_type {};
1363+template <typename T> struct is_static_named_arg : std::false_type {};
1364+
1365+template <typename Char, typename T>
1366+struct is_named_arg<named_arg<Char, T>> : std::true_type {};
1367+
1368+template <typename Char, typename T> struct named_arg : view {
1369+ const Char* name;
1370+ const T& value;
1371+
1372+ named_arg(const Char* n, const T& v) : name(n), value(v) {}
1373+ static_assert(!is_named_arg<T>::value, "nested named arguments");
1374+};
1375+
1376+template <bool B = false> constexpr auto count() -> int { return B ? 1 : 0; }
1377+template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
1378+ return (B1 ? 1 : 0) + count<B2, Tail...>();
1379+}
1380+
1381+template <typename... Args> constexpr auto count_named_args() -> int {
1382+ return count<is_named_arg<Args>::value...>();
1383+}
1384+template <typename... Args> constexpr auto count_static_named_args() -> int {
1385+ return count<is_static_named_arg<Args>::value...>();
1386+}
1387+
1388+template <typename Char> struct named_arg_info {
1389+ const Char* name;
1390+ int id;
1391+};
1392+
1393+// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13.
1394+template <typename Char>
1395+FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
1396+ int named_arg_index,
1397+ basic_string_view<Char> arg_name) {
1398+ for (int i = 0; i < named_arg_index; ++i) {
1399+ if (named_args[i].name == arg_name) report_error("duplicate named arg");
1400+ }
1401+}
1402+
1403+template <typename Char, typename T, FMT_ENABLE_IF(!is_named_arg<T>::value)>
1404+void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
1405+ ++arg_index;
1406+}
1407+template <typename Char, typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
1408+void init_named_arg(named_arg_info<Char>* named_args, int& arg_index,
1409+ int& named_arg_index, const T& arg) {
1410+ check_for_duplicate<Char>(named_args, named_arg_index, arg.name);
1411+ named_args[named_arg_index++] = {arg.name, arg_index++};
1412+}
1413+
1414+template <typename T, typename Char,
1415+ FMT_ENABLE_IF(!is_static_named_arg<T>::value)>
1416+FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>*, int& arg_index,
1417+ int&) {
1418+ ++arg_index;
1419+}
1420+template <typename T, typename Char,
1421+ FMT_ENABLE_IF(is_static_named_arg<T>::value)>
1422+FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>* named_args,
1423+ int& arg_index, int& named_arg_index) {
1424+ check_for_duplicate<Char>(named_args, named_arg_index, T::name);
1425+ named_args[named_arg_index++] = {T::name, arg_index++};
1426+}
1427+
1428+// To minimize the number of types we need to deal with, long is translated
1429+// either to int or to long long depending on its size.
1430+enum { long_short = sizeof(long) == sizeof(int) };
1431+using long_type = conditional_t<long_short, int, long long>;
1432+using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
1433+
1434+template <typename T>
1435+using format_as_result =
1436+ remove_cvref_t<decltype(format_as(std::declval<const T&>()))>;
1437+template <typename T>
1438+using format_as_member_result =
1439+ remove_cvref_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>;
1440+
1441+template <typename T, typename Enable = std::true_type>
1442+struct use_format_as : std::false_type {};
1443+// format_as member is only used to avoid injection into the std namespace.
1444+template <typename T, typename Enable = std::true_type>
1445+struct use_format_as_member : std::false_type {};
1446+
1447+// Only map owning types because mapping views can be unsafe.
1448+template <typename T>
1449+struct use_format_as<
1450+ T, bool_constant<std::is_arithmetic<format_as_result<T>>::value>>
1451+ : std::true_type {};
1452+template <typename T>
1453+struct use_format_as_member<
1454+ T, bool_constant<std::is_arithmetic<format_as_member_result<T>>::value>>
1455+ : std::true_type {};
1456+
1457+template <typename T, typename U = remove_const_t<T>>
1458+using use_formatter =
1459+ bool_constant<(std::is_class<T>::value || std::is_enum<T>::value ||
1460+ std::is_union<T>::value || std::is_array<T>::value) &&
1461+ !has_to_string_view<T>::value && !is_named_arg<T>::value &&
1462+ !use_format_as<T>::value && !use_format_as_member<U>::value>;
1463+
1464+template <typename Char, typename T, typename U = remove_const_t<T>>
1465+auto has_formatter_impl(T* p, buffered_context<Char>* ctx = nullptr)
1466+ -> decltype(formatter<U, Char>().format(*p, *ctx), std::true_type());
1467+template <typename Char> auto has_formatter_impl(...) -> std::false_type;
1468+
1469+// T can be const-qualified to check if it is const-formattable.
1470+template <typename T, typename Char> constexpr auto has_formatter() -> bool {
1471+ return decltype(has_formatter_impl<Char>(static_cast<T*>(nullptr)))::value;
1472+}
1473+
1474+// Maps formatting argument types to natively supported types or user-defined
1475+// types with formatters. Returns void on errors to be SFINAE-friendly.
1476+template <typename Char> struct type_mapper {
1477+ static auto map(signed char) -> int;
1478+ static auto map(unsigned char) -> unsigned;
1479+ static auto map(short) -> int;
1480+ static auto map(unsigned short) -> unsigned;
1481+ static auto map(int) -> int;
1482+ static auto map(unsigned) -> unsigned;
1483+ static auto map(long) -> long_type;
1484+ static auto map(unsigned long) -> ulong_type;
1485+ static auto map(long long) -> long long;
1486+ static auto map(unsigned long long) -> unsigned long long;
1487+ static auto map(int128_opt) -> int128_opt;
1488+ static auto map(uint128_opt) -> uint128_opt;
1489+ static auto map(bool) -> bool;
1490+
1491+ template <int N>
1492+ static auto map(bitint<N>) -> conditional_t<N <= 64, long long, void>;
1493+ template <int N>
1494+ static auto map(ubitint<N>)
1495+ -> conditional_t<N <= 64, unsigned long long, void>;
1496+
1497+ template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
1498+ static auto map(T) -> conditional_t<
1499+ std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
1500+
1501+ static auto map(float) -> float;
1502+ static auto map(double) -> double;
1503+ static auto map(long double) -> long double;
1504+
1505+ static auto map(Char*) -> const Char*;
1506+ static auto map(const Char*) -> const Char*;
1507+ template <typename T, typename C = char_t<T>,
1508+ FMT_ENABLE_IF(!std::is_pointer<T>::value)>
1509+ static auto map(const T&) -> conditional_t<std::is_same<C, Char>::value,
1510+ basic_string_view<C>, void>;
1511+
1512+ static auto map(void*) -> const void*;
1513+ static auto map(const void*) -> const void*;
1514+ static auto map(volatile void*) -> const void*;
1515+ static auto map(const volatile void*) -> const void*;
1516+ static auto map(nullptr_t) -> const void*;
1517+ template <typename T, FMT_ENABLE_IF(std::is_pointer<T>::value ||
1518+ std::is_member_pointer<T>::value)>
1519+ static auto map(const T&) -> void;
1520+
1521+ template <typename T, FMT_ENABLE_IF(use_format_as<T>::value)>
1522+ static auto map(const T& x) -> decltype(map(format_as(x)));
1523+ template <typename T, FMT_ENABLE_IF(use_format_as_member<T>::value)>
1524+ static auto map(const T& x) -> decltype(map(formatter<T>::format_as(x)));
1525+
1526+ template <typename T, FMT_ENABLE_IF(use_formatter<T>::value)>
1527+ static auto map(T&) -> conditional_t<has_formatter<T, Char>(), T&, void>;
1528+
1529+ template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
1530+ static auto map(const T& named_arg) -> decltype(map(named_arg.value));
1531+};
1532+
1533+// detail:: is used to workaround a bug in MSVC 2017.
1534+template <typename T, typename Char>
1535+using mapped_t = decltype(detail::type_mapper<Char>::map(std::declval<T&>()));
1536+
1537+// A type constant after applying type_mapper.
1538+template <typename T, typename Char = char>
1539+using mapped_type_constant = type_constant<mapped_t<T, Char>, Char>;
1540+
1541+template <typename T, typename Context,
1542+ type TYPE =
1543+ mapped_type_constant<T, typename Context::char_type>::value>
1544+using stored_type_constant = std::integral_constant<
1545+ type, Context::builtin_types || TYPE == type::int_type ? TYPE
1546+ : type::custom_type>;
1547+// A parse context with extra data used only in compile-time checks.
1548+template <typename Char>
1549+class compile_parse_context : public parse_context<Char> {
1550+ private:
1551+ int num_args_;
1552+ const type* types_;
1553+ using base = parse_context<Char>;
1554+
1555+ public:
1556+ FMT_CONSTEXPR explicit compile_parse_context(basic_string_view<Char> fmt,
1557+ int num_args, const type* types,
1558+ int next_arg_id = 0)
1559+ : base(fmt, next_arg_id), num_args_(num_args), types_(types) {}
1560+
1561+ constexpr auto num_args() const -> int { return num_args_; }
1562+ constexpr auto arg_type(int id) const -> type { return types_[id]; }
1563+
1564+ FMT_CONSTEXPR auto next_arg_id() -> int {
1565+ int id = base::next_arg_id();
1566+ if (id >= num_args_) report_error("argument not found");
1567+ return id;
1568+ }
1569+
1570+ FMT_CONSTEXPR void check_arg_id(int id) {
1571+ base::check_arg_id(id);
1572+ if (id >= num_args_) report_error("argument not found");
1573+ }
1574+ using base::check_arg_id;
1575+
1576+ FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
1577+ ignore_unused(arg_id);
1578+ if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
1579+ report_error("width/precision is not integer");
1580+ }
1581+};
1582+
1583+// An argument reference.
1584+template <typename Char> union arg_ref {
1585+ FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {}
1586+ FMT_CONSTEXPR arg_ref(basic_string_view<Char> n) : name(n) {}
1587+
1588+ int index;
1589+ basic_string_view<Char> name;
1590+};
1591+
1592+// Format specifiers with width and precision resolved at formatting rather
1593+// than parsing time to allow reusing the same parsed specifiers with
1594+// different sets of arguments (precompilation of format strings).
1595+template <typename Char = char> struct dynamic_format_specs : format_specs {
1596+ arg_ref<Char> width_ref;
1597+ arg_ref<Char> precision_ref;
1598+};
1599+
1600+// Converts a character to ASCII. Returns '\0' on conversion failure.
1601+template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
1602+constexpr auto to_ascii(Char c) -> char {
1603+ return c <= 0xff ? static_cast<char>(c) : '\0';
1604+}
1605+
1606+// Returns the number of code units in a code point or 1 on error.
1607+template <typename Char>
1608+FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
1609+ if (const_check(sizeof(Char) != 1)) return 1;
1610+ auto c = static_cast<unsigned char>(*begin);
1611+ return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1;
1612+}
1613+
1614+// Parses the range [begin, end) as an unsigned integer. This function assumes
1615+// that the range is non-empty and the first character is a digit.
1616+template <typename Char>
1617+FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
1618+ int error_value) noexcept -> int {
1619+ FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
1620+ unsigned value = 0, prev = 0;
1621+ auto p = begin;
1622+ do {
1623+ prev = value;
1624+ value = value * 10 + unsigned(*p - '0');
1625+ ++p;
1626+ } while (p != end && '0' <= *p && *p <= '9');
1627+ auto num_digits = p - begin;
1628+ begin = p;
1629+ int digits10 = static_cast<int>(sizeof(int) * CHAR_BIT * 3 / 10);
1630+ if (num_digits <= digits10) return static_cast<int>(value);
1631+ // Check for overflow.
1632+ unsigned max = INT_MAX;
1633+ return num_digits == digits10 + 1 &&
1634+ prev * 10ull + unsigned(p[-1] - '0') <= max
1635+ ? static_cast<int>(value)
1636+ : error_value;
1637+}
1638+
1639+FMT_CONSTEXPR inline auto parse_align(char c) -> align {
1640+ switch (c) {
1641+ case '<': return align::left;
1642+ case '>': return align::right;
1643+ case '^': return align::center;
1644+ }
1645+ return align::none;
1646+}
1647+
1648+template <typename Char> constexpr auto is_name_start(Char c) -> bool {
1649+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
1650+}
1651+
1652+template <typename Char, typename Handler>
1653+FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end,
1654+ Handler&& handler) -> const Char* {
1655+ Char c = *begin;
1656+ if (c >= '0' && c <= '9') {
1657+ int index = 0;
1658+ if (c != '0')
1659+ index = parse_nonnegative_int(begin, end, INT_MAX);
1660+ else
1661+ ++begin;
1662+ if (begin == end || (*begin != '}' && *begin != ':'))
1663+ report_error("invalid format string");
1664+ else
1665+ handler.on_index(index);
1666+ return begin;
1667+ }
1668+ if (FMT_OPTIMIZE_SIZE > 1 || !is_name_start(c)) {
1669+ report_error("invalid format string");
1670+ return begin;
1671+ }
1672+ auto it = begin;
1673+ do {
1674+ ++it;
1675+ } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
1676+ handler.on_name({begin, to_unsigned(it - begin)});
1677+ return it;
1678+}
1679+
1680+template <typename Char> struct dynamic_spec_handler {
1681+ parse_context<Char>& ctx;
1682+ arg_ref<Char>& ref;
1683+ arg_id_kind& kind;
1684+
1685+ FMT_CONSTEXPR void on_index(int id) {
1686+ ref = id;
1687+ kind = arg_id_kind::index;
1688+ ctx.check_arg_id(id);
1689+ ctx.check_dynamic_spec(id);
1690+ }
1691+ FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
1692+ ref = id;
1693+ kind = arg_id_kind::name;
1694+ ctx.check_arg_id(id);
1695+ }
1696+};
1697+
1698+template <typename Char> struct parse_dynamic_spec_result {
1699+ const Char* end;
1700+ arg_id_kind kind;
1701+};
1702+
1703+// Parses integer | "{" [arg_id] "}".
1704+template <typename Char>
1705+FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
1706+ int& value, arg_ref<Char>& ref,
1707+ parse_context<Char>& ctx)
1708+ -> parse_dynamic_spec_result<Char> {
1709+ FMT_ASSERT(begin != end, "");
1710+ auto kind = arg_id_kind::none;
1711+ if ('0' <= *begin && *begin <= '9') {
1712+ int val = parse_nonnegative_int(begin, end, -1);
1713+ if (val == -1) report_error("number is too big");
1714+ value = val;
1715+ } else {
1716+ if (*begin == '{') {
1717+ ++begin;
1718+ if (begin != end) {
1719+ Char c = *begin;
1720+ if (c == '}' || c == ':') {
1721+ int id = ctx.next_arg_id();
1722+ ref = id;
1723+ kind = arg_id_kind::index;
1724+ ctx.check_dynamic_spec(id);
1725+ } else {
1726+ begin = parse_arg_id(begin, end,
1727+ dynamic_spec_handler<Char>{ctx, ref, kind});
1728+ }
1729+ }
1730+ if (begin != end && *begin == '}') return {++begin, kind};
1731+ }
1732+ report_error("invalid format string");
1733+ }
1734+ return {begin, kind};
1735+}
1736+
1737+template <typename Char>
1738+FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
1739+ format_specs& specs, arg_ref<Char>& width_ref,
1740+ parse_context<Char>& ctx) -> const Char* {
1741+ auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
1742+ specs.set_dynamic_width(result.kind);
1743+ return result.end;
1744+}
1745+
1746+template <typename Char>
1747+FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
1748+ format_specs& specs,
1749+ arg_ref<Char>& precision_ref,
1750+ parse_context<Char>& ctx) -> const Char* {
1751+ ++begin;
1752+ if (begin == end) {
1753+ report_error("invalid precision");
1754+ return begin;
1755+ }
1756+ auto result =
1757+ parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx);
1758+ specs.set_dynamic_precision(result.kind);
1759+ return result.end;
1760+}
1761+
1762+enum class state { start, align, sign, hash, zero, width, precision, locale };
1763+
1764+// Parses standard format specifiers.
1765+template <typename Char>
1766+FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
1767+ dynamic_format_specs<Char>& specs,
1768+ parse_context<Char>& ctx, type arg_type)
1769+ -> const Char* {
1770+ auto c = '\0';
1771+ if (end - begin > 1) {
1772+ auto next = to_ascii(begin[1]);
1773+ c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
1774+ } else {
1775+ if (begin == end) return begin;
1776+ c = to_ascii(*begin);
1777+ }
1778+
1779+ struct {
1780+ state current_state = state::start;
1781+ FMT_CONSTEXPR void operator()(state s, bool valid = true) {
1782+ if (current_state >= s || !valid)
1783+ report_error("invalid format specifier");
1784+ current_state = s;
1785+ }
1786+ } enter_state;
1787+
1788+ using pres = presentation_type;
1789+ constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
1790+ struct {
1791+ const Char*& begin;
1792+ format_specs& specs;
1793+ type arg_type;
1794+
1795+ FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* {
1796+ if (!in(arg_type, set)) report_error("invalid format specifier");
1797+ specs.set_type(pres_type);
1798+ return begin + 1;
1799+ }
1800+ } parse_presentation_type{begin, specs, arg_type};
1801+
1802+ for (;;) {
1803+ switch (c) {
1804+ case '<':
1805+ case '>':
1806+ case '^':
1807+ enter_state(state::align);
1808+ specs.set_align(parse_align(c));
1809+ ++begin;
1810+ break;
1811+ case '+':
1812+ case ' ':
1813+ specs.set_sign(c == ' ' ? sign::space : sign::plus);
1814+ FMT_FALLTHROUGH;
1815+ case '-':
1816+ enter_state(state::sign, in(arg_type, sint_set | float_set));
1817+ ++begin;
1818+ break;
1819+ case '#':
1820+ enter_state(state::hash, is_arithmetic_type(arg_type));
1821+ specs.set_alt();
1822+ ++begin;
1823+ break;
1824+ case '0':
1825+ enter_state(state::zero);
1826+ if (!is_arithmetic_type(arg_type))
1827+ report_error("format specifier requires numeric argument");
1828+ if (specs.align() == align::none) {
1829+ // Ignore 0 if align is specified for compatibility with std::format.
1830+ specs.set_align(align::numeric);
1831+ specs.set_fill('0');
1832+ }
1833+ ++begin;
1834+ break;
1835+ // clang-format off
1836+ case '1': case '2': case '3': case '4': case '5':
1837+ case '6': case '7': case '8': case '9': case '{':
1838+ // clang-format on
1839+ enter_state(state::width);
1840+ begin = parse_width(begin, end, specs, specs.width_ref, ctx);
1841+ break;
1842+ case '.':
1843+ enter_state(state::precision,
1844+ in(arg_type, float_set | string_set | cstring_set));
1845+ begin = parse_precision(begin, end, specs, specs.precision_ref, ctx);
1846+ break;
1847+ case 'L':
1848+ enter_state(state::locale, is_arithmetic_type(arg_type));
1849+ specs.set_localized();
1850+ ++begin;
1851+ break;
1852+ case 'd': return parse_presentation_type(pres::dec, integral_set);
1853+ case 'X': specs.set_upper(); FMT_FALLTHROUGH;
1854+ case 'x': return parse_presentation_type(pres::hex, integral_set);
1855+ case 'o': return parse_presentation_type(pres::oct, integral_set);
1856+ case 'B': specs.set_upper(); FMT_FALLTHROUGH;
1857+ case 'b': return parse_presentation_type(pres::bin, integral_set);
1858+ case 'E': specs.set_upper(); FMT_FALLTHROUGH;
1859+ case 'e': return parse_presentation_type(pres::exp, float_set);
1860+ case 'F': specs.set_upper(); FMT_FALLTHROUGH;
1861+ case 'f': return parse_presentation_type(pres::fixed, float_set);
1862+ case 'G': specs.set_upper(); FMT_FALLTHROUGH;
1863+ case 'g': return parse_presentation_type(pres::general, float_set);
1864+ case 'A': specs.set_upper(); FMT_FALLTHROUGH;
1865+ case 'a': return parse_presentation_type(pres::hexfloat, float_set);
1866+ case 'c':
1867+ if (arg_type == type::bool_type) report_error("invalid format specifier");
1868+ return parse_presentation_type(pres::chr, integral_set);
1869+ case 's':
1870+ return parse_presentation_type(pres::string,
1871+ bool_set | string_set | cstring_set);
1872+ case 'p':
1873+ return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
1874+ case '?':
1875+ return parse_presentation_type(pres::debug,
1876+ char_set | string_set | cstring_set);
1877+ case '}': return begin;
1878+ default: {
1879+ if (*begin == '}') return begin;
1880+ // Parse fill and alignment.
1881+ auto fill_end = begin + code_point_length(begin);
1882+ if (end - fill_end <= 0) {
1883+ report_error("invalid format specifier");
1884+ return begin;
1885+ }
1886+ if (*begin == '{') {
1887+ report_error("invalid fill character '{'");
1888+ return begin;
1889+ }
1890+ auto alignment = parse_align(to_ascii(*fill_end));
1891+ enter_state(state::align, alignment != align::none);
1892+ specs.set_fill(
1893+ basic_string_view<Char>(begin, to_unsigned(fill_end - begin)));
1894+ specs.set_align(alignment);
1895+ begin = fill_end + 1;
1896+ }
1897+ }
1898+ if (begin == end) return begin;
1899+ c = to_ascii(*begin);
1900+ }
1901+}
1902+
1903+template <typename Char, typename Handler>
1904+FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin,
1905+ const Char* end,
1906+ Handler&& handler)
1907+ -> const Char* {
1908+ ++begin;
1909+ if (begin == end) {
1910+ handler.on_error("invalid format string");
1911+ return end;
1912+ }
1913+ int arg_id = 0;
1914+ switch (*begin) {
1915+ case '}':
1916+ handler.on_replacement_field(handler.on_arg_id(), begin);
1917+ return begin + 1;
1918+ case '{': handler.on_text(begin, begin + 1); return begin + 1;
1919+ case ':': arg_id = handler.on_arg_id(); break;
1920+ default: {
1921+ struct id_adapter {
1922+ Handler& handler;
1923+ int arg_id;
1924+
1925+ FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
1926+ FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
1927+ arg_id = handler.on_arg_id(id);
1928+ }
1929+ } adapter = {handler, 0};
1930+ begin = parse_arg_id(begin, end, adapter);
1931+ arg_id = adapter.arg_id;
1932+ Char c = begin != end ? *begin : Char();
1933+ if (c == '}') {
1934+ handler.on_replacement_field(arg_id, begin);
1935+ return begin + 1;
1936+ }
1937+ if (c != ':') {
1938+ handler.on_error("missing '}' in format string");
1939+ return end;
1940+ }
1941+ break;
1942+ }
1943+ }
1944+ begin = handler.on_format_specs(arg_id, begin + 1, end);
1945+ if (begin == end || *begin != '}')
1946+ return handler.on_error("unknown format specifier"), end;
1947+ return begin + 1;
1948+}
1949+
1950+template <typename Char, typename Handler>
1951+FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> fmt,
1952+ Handler&& handler) {
1953+ auto begin = fmt.data(), end = begin + fmt.size();
1954+ auto p = begin;
1955+ while (p != end) {
1956+ auto c = *p++;
1957+ if (c == '{') {
1958+ handler.on_text(begin, p - 1);
1959+ begin = p = parse_replacement_field(p - 1, end, handler);
1960+ } else if (c == '}') {
1961+ if (p == end || *p != '}')
1962+ return handler.on_error("unmatched '}' in format string");
1963+ handler.on_text(begin, p);
1964+ begin = ++p;
1965+ }
1966+ }
1967+ handler.on_text(begin, end);
1968+}
1969+
1970+// Checks char specs and returns true iff the presentation type is char-like.
1971+FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool {
1972+ auto type = specs.type();
1973+ if (type != presentation_type::none && type != presentation_type::chr &&
1974+ type != presentation_type::debug) {
1975+ return false;
1976+ }
1977+ if (specs.align() == align::numeric || specs.sign() != sign::none ||
1978+ specs.alt()) {
1979+ report_error("invalid format specifier for char");
1980+ }
1981+ return true;
1982+}
1983+
1984+// A base class for compile-time strings.
1985+struct compile_string {};
1986+
1987+template <typename T, typename Char>
1988+FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769).
1989+FMT_CONSTEXPR auto invoke_parse(parse_context<Char>& ctx) -> const Char* {
1990+ using mapped_type = remove_cvref_t<mapped_t<T, Char>>;
1991+ constexpr bool formattable =
1992+ std::is_constructible<formatter<mapped_type, Char>>::value;
1993+ if (!formattable) return ctx.begin(); // Error is reported in the value ctor.
1994+ using formatted_type = conditional_t<formattable, mapped_type, int>;
1995+ return formatter<formatted_type, Char>().parse(ctx);
1996+}
1997+
1998+template <typename... T> struct arg_pack {};
1999+
2000+template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
2001+class format_string_checker {
2002+ private:
2003+ type types_[max_of(1, NUM_ARGS)];
2004+ named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
2005+ compile_parse_context<Char> context_;
2006+
2007+ using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
2008+ parse_func parse_funcs_[max_of(1, NUM_ARGS)];
2009+
2010+ public:
2011+ template <typename... T>
2012+ FMT_CONSTEXPR explicit format_string_checker(basic_string_view<Char> fmt,
2013+ arg_pack<T...>)
2014+ : types_{mapped_type_constant<T, Char>::value...},
2015+ named_args_{},
2016+ context_(fmt, NUM_ARGS, types_),
2017+ parse_funcs_{&invoke_parse<T, Char>...} {
2018+ int arg_index = 0, named_arg_index = 0;
2019+ FMT_APPLY_VARIADIC(
2020+ init_static_named_arg<T>(named_args_, arg_index, named_arg_index));
2021+ ignore_unused(arg_index, named_arg_index);
2022+ }
2023+
2024+ FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
2025+
2026+ FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
2027+ FMT_CONSTEXPR auto on_arg_id(int id) -> int {
2028+ context_.check_arg_id(id);
2029+ return id;
2030+ }
2031+ FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
2032+ for (int i = 0; i < NUM_NAMED_ARGS; ++i) {
2033+ if (named_args_[i].name == id) return named_args_[i].id;
2034+ }
2035+ if (!DYNAMIC_NAMES) on_error("argument not found");
2036+ return -1;
2037+ }
2038+
2039+ FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) {
2040+ on_format_specs(id, begin, begin); // Call parse() on empty specs.
2041+ }
2042+
2043+ FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end)
2044+ -> const Char* {
2045+ context_.advance_to(begin);
2046+ if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_);
2047+ while (begin != end && *begin != '}') ++begin;
2048+ return begin;
2049+ }
2050+
2051+ FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) {
2052+ report_error(message);
2053+ }
2054+};
2055+
2056+/// A contiguous memory buffer with an optional growing ability. It is an
2057+/// internal class and shouldn't be used directly, only via `memory_buffer`.
2058+template <typename T> class buffer {
2059+ private:
2060+ T* ptr_;
2061+ size_t size_;
2062+ size_t capacity_;
2063+
2064+ using grow_fun = void (*)(buffer& buf, size_t capacity);
2065+ grow_fun grow_;
2066+
2067+ protected:
2068+ // Don't initialize ptr_ since it is not accessed to save a few cycles.
2069+ FMT_MSC_WARNING(suppress : 26495)
2070+ FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept
2071+ : size_(sz), capacity_(sz), grow_(grow) {}
2072+
2073+ constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0,
2074+ size_t cap = 0) noexcept
2075+ : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {}
2076+
2077+ FMT_CONSTEXPR20 ~buffer() = default;
2078+ buffer(buffer&&) = default;
2079+
2080+ /// Sets the buffer data and capacity.
2081+ FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
2082+ ptr_ = buf_data;
2083+ capacity_ = buf_capacity;
2084+ }
2085+
2086+ public:
2087+ using value_type = T;
2088+ using const_reference = const T&;
2089+
2090+ buffer(const buffer&) = delete;
2091+ void operator=(const buffer&) = delete;
2092+
2093+ auto begin() noexcept -> T* { return ptr_; }
2094+ auto end() noexcept -> T* { return ptr_ + size_; }
2095+
2096+ auto begin() const noexcept -> const T* { return ptr_; }
2097+ auto end() const noexcept -> const T* { return ptr_ + size_; }
2098+
2099+ /// Returns the size of this buffer.
2100+ constexpr auto size() const noexcept -> size_t { return size_; }
2101+
2102+ /// Returns the capacity of this buffer.
2103+ constexpr auto capacity() const noexcept -> size_t { return capacity_; }
2104+
2105+ /// Returns a pointer to the buffer data (not null-terminated).
2106+ FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
2107+ FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
2108+
2109+ /// Clears this buffer.
2110+ FMT_CONSTEXPR void clear() { size_ = 0; }
2111+
2112+ // Tries resizing the buffer to contain `count` elements. If T is a POD type
2113+ // the new elements may not be initialized.
2114+ FMT_CONSTEXPR void try_resize(size_t count) {
2115+ try_reserve(count);
2116+ size_ = min_of(count, capacity_);
2117+ }
2118+
2119+ // Tries increasing the buffer capacity to `new_capacity`. It can increase the
2120+ // capacity by a smaller amount than requested but guarantees there is space
2121+ // for at least one additional element either by increasing the capacity or by
2122+ // flushing the buffer if it is full.
2123+ FMT_CONSTEXPR void try_reserve(size_t new_capacity) {
2124+ if (new_capacity > capacity_) grow_(*this, new_capacity);
2125+ }
2126+
2127+ FMT_CONSTEXPR void push_back(const T& value) {
2128+ try_reserve(size_ + 1);
2129+ ptr_[size_++] = value;
2130+ }
2131+
2132+ /// Appends data to the end of the buffer.
2133+ template <typename U>
2134+// Workaround for MSVC2019 to fix error C2893: Failed to specialize function
2135+// template 'void fmt::v11::detail::buffer<T>::append(const U *,const U *)'.
2136+#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940
2137+ FMT_CONSTEXPR20
2138+#endif
2139+ void
2140+ append(const U* begin, const U* end) {
2141+ while (begin != end) {
2142+ auto count = to_unsigned(end - begin);
2143+ try_reserve(size_ + count);
2144+ auto free_cap = capacity_ - size_;
2145+ if (free_cap < count) count = free_cap;
2146+ // A loop is faster than memcpy on small sizes.
2147+ T* out = ptr_ + size_;
2148+ for (size_t i = 0; i < count; ++i) out[i] = begin[i];
2149+ size_ += count;
2150+ begin += count;
2151+ }
2152+ }
2153+
2154+ template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
2155+ return ptr_[index];
2156+ }
2157+ template <typename Idx>
2158+ FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
2159+ return ptr_[index];
2160+ }
2161+};
2162+
2163+struct buffer_traits {
2164+ constexpr explicit buffer_traits(size_t) {}
2165+ constexpr auto count() const -> size_t { return 0; }
2166+ constexpr auto limit(size_t size) const -> size_t { return size; }
2167+};
2168+
2169+class fixed_buffer_traits {
2170+ private:
2171+ size_t count_ = 0;
2172+ size_t limit_;
2173+
2174+ public:
2175+ constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
2176+ constexpr auto count() const -> size_t { return count_; }
2177+ FMT_CONSTEXPR auto limit(size_t size) -> size_t {
2178+ size_t n = limit_ > count_ ? limit_ - count_ : 0;
2179+ count_ += size;
2180+ return min_of(size, n);
2181+ }
2182+};
2183+
2184+// A buffer that writes to an output iterator when flushed.
2185+template <typename OutputIt, typename T, typename Traits = buffer_traits>
2186+class iterator_buffer : public Traits, public buffer<T> {
2187+ private:
2188+ OutputIt out_;
2189+ enum { buffer_size = 256 };
2190+ T data_[buffer_size];
2191+
2192+ static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
2193+ if (buf.size() == buffer_size) static_cast<iterator_buffer&>(buf).flush();
2194+ }
2195+
2196+ void flush() {
2197+ auto size = this->size();
2198+ this->clear();
2199+ const T* begin = data_;
2200+ const T* end = begin + this->limit(size);
2201+ while (begin != end) *out_++ = *begin++;
2202+ }
2203+
2204+ public:
2205+ explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
2206+ : Traits(n), buffer<T>(grow, data_, 0, buffer_size), out_(out) {}
2207+ iterator_buffer(iterator_buffer&& other) noexcept
2208+ : Traits(other),
2209+ buffer<T>(grow, data_, 0, buffer_size),
2210+ out_(other.out_) {}
2211+ ~iterator_buffer() {
2212+ // Don't crash if flush fails during unwinding.
2213+ FMT_TRY { flush(); }
2214+ FMT_CATCH(...) {}
2215+ }
2216+
2217+ auto out() -> OutputIt {
2218+ flush();
2219+ return out_;
2220+ }
2221+ auto count() const -> size_t { return Traits::count() + this->size(); }
2222+};
2223+
2224+template <typename T>
2225+class iterator_buffer<T*, T, fixed_buffer_traits> : public fixed_buffer_traits,
2226+ public buffer<T> {
2227+ private:
2228+ T* out_;
2229+ enum { buffer_size = 256 };
2230+ T data_[buffer_size];
2231+
2232+ static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
2233+ if (buf.size() == buf.capacity())
2234+ static_cast<iterator_buffer&>(buf).flush();
2235+ }
2236+
2237+ void flush() {
2238+ size_t n = this->limit(this->size());
2239+ if (this->data() == out_) {
2240+ out_ += n;
2241+ this->set(data_, buffer_size);
2242+ }
2243+ this->clear();
2244+ }
2245+
2246+ public:
2247+ explicit iterator_buffer(T* out, size_t n = buffer_size)
2248+ : fixed_buffer_traits(n), buffer<T>(grow, out, 0, n), out_(out) {}
2249+ iterator_buffer(iterator_buffer&& other) noexcept
2250+ : fixed_buffer_traits(other),
2251+ buffer<T>(static_cast<iterator_buffer&&>(other)),
2252+ out_(other.out_) {
2253+ if (this->data() != out_) {
2254+ this->set(data_, buffer_size);
2255+ this->clear();
2256+ }
2257+ }
2258+ ~iterator_buffer() { flush(); }
2259+
2260+ auto out() -> T* {
2261+ flush();
2262+ return out_;
2263+ }
2264+ auto count() const -> size_t {
2265+ return fixed_buffer_traits::count() + this->size();
2266+ }
2267+};
2268+
2269+template <typename T> class iterator_buffer<T*, T> : public buffer<T> {
2270+ public:
2271+ explicit iterator_buffer(T* out, size_t = 0)
2272+ : buffer<T>([](buffer<T>&, size_t) {}, out, 0, ~size_t()) {}
2273+
2274+ auto out() -> T* { return &*this->end(); }
2275+};
2276+
2277+template <typename Container>
2278+class container_buffer : public buffer<typename Container::value_type> {
2279+ private:
2280+ using value_type = typename Container::value_type;
2281+
2282+ static FMT_CONSTEXPR void grow(buffer<value_type>& buf, size_t capacity) {
2283+ auto& self = static_cast<container_buffer&>(buf);
2284+ self.container.resize(capacity);
2285+ self.set(&self.container[0], capacity);
2286+ }
2287+
2288+ public:
2289+ Container& container;
2290+
2291+ explicit container_buffer(Container& c)
2292+ : buffer<value_type>(grow, c.size()), container(c) {}
2293+};
2294+
2295+// A buffer that writes to a container with the contiguous storage.
2296+template <typename OutputIt>
2297+class iterator_buffer<
2298+ OutputIt,
2299+ enable_if_t<is_back_insert_iterator<OutputIt>::value &&
2300+ is_contiguous<typename OutputIt::container_type>::value,
2301+ typename OutputIt::container_type::value_type>>
2302+ : public container_buffer<typename OutputIt::container_type> {
2303+ private:
2304+ using base = container_buffer<typename OutputIt::container_type>;
2305+
2306+ public:
2307+ explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {}
2308+ explicit iterator_buffer(OutputIt out, size_t = 0)
2309+ : base(get_container(out)) {}
2310+
2311+ auto out() -> OutputIt { return OutputIt(this->container); }
2312+};
2313+
2314+// A buffer that counts the number of code units written discarding the output.
2315+template <typename T = char> class counting_buffer : public buffer<T> {
2316+ private:
2317+ enum { buffer_size = 256 };
2318+ T data_[buffer_size];
2319+ size_t count_ = 0;
2320+
2321+ static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
2322+ if (buf.size() != buffer_size) return;
2323+ static_cast<counting_buffer&>(buf).count_ += buf.size();
2324+ buf.clear();
2325+ }
2326+
2327+ public:
2328+ FMT_CONSTEXPR counting_buffer() : buffer<T>(grow, data_, 0, buffer_size) {}
2329+
2330+ constexpr auto count() const noexcept -> size_t {
2331+ return count_ + this->size();
2332+ }
2333+};
2334+
2335+template <typename T>
2336+struct is_back_insert_iterator<basic_appender<T>> : std::true_type {};
2337+
2338+template <typename OutputIt, typename InputIt, typename = void>
2339+struct has_back_insert_iterator_container_append : std::false_type {};
2340+template <typename OutputIt, typename InputIt>
2341+struct has_back_insert_iterator_container_append<
2342+ OutputIt, InputIt,
2343+ void_t<decltype(get_container(std::declval<OutputIt>())
2344+ .append(std::declval<InputIt>(),
2345+ std::declval<InputIt>()))>> : std::true_type {};
2346+
2347+// An optimized version of std::copy with the output value type (T).
2348+template <typename T, typename InputIt, typename OutputIt,
2349+ FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
2350+ has_back_insert_iterator_container_append<
2351+ OutputIt, InputIt>::value)>
2352+FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
2353+ -> OutputIt {
2354+ get_container(out).append(begin, end);
2355+ return out;
2356+}
2357+
2358+template <typename T, typename InputIt, typename OutputIt,
2359+ FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
2360+ !has_back_insert_iterator_container_append<
2361+ OutputIt, InputIt>::value)>
2362+FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
2363+ -> OutputIt {
2364+ auto& c = get_container(out);
2365+ c.insert(c.end(), begin, end);
2366+ return out;
2367+}
2368+
2369+template <typename T, typename InputIt, typename OutputIt,
2370+ FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
2371+FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
2372+ while (begin != end) *out++ = static_cast<T>(*begin++);
2373+ return out;
2374+}
2375+
2376+template <typename T, typename V, typename OutputIt>
2377+FMT_CONSTEXPR auto copy(basic_string_view<V> s, OutputIt out) -> OutputIt {
2378+ return copy<T>(s.begin(), s.end(), out);
2379+}
2380+
2381+template <typename It, typename Enable = std::true_type>
2382+struct is_buffer_appender : std::false_type {};
2383+template <typename It>
2384+struct is_buffer_appender<
2385+ It, bool_constant<
2386+ is_back_insert_iterator<It>::value &&
2387+ std::is_base_of<buffer<typename It::container_type::value_type>,
2388+ typename It::container_type>::value>>
2389+ : std::true_type {};
2390+
2391+// Maps an output iterator to a buffer.
2392+template <typename T, typename OutputIt,
2393+ FMT_ENABLE_IF(!is_buffer_appender<OutputIt>::value)>
2394+auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
2395+ return iterator_buffer<OutputIt, T>(out);
2396+}
2397+template <typename T, typename OutputIt,
2398+ FMT_ENABLE_IF(is_buffer_appender<OutputIt>::value)>
2399+auto get_buffer(OutputIt out) -> buffer<T>& {
2400+ return get_container(out);
2401+}
2402+
2403+template <typename Buf, typename OutputIt>
2404+auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
2405+ return buf.out();
2406+}
2407+template <typename T, typename OutputIt>
2408+auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
2409+ return out;
2410+}
2411+
2412+// This type is intentionally undefined, only used for errors.
2413+template <typename T, typename Char> struct type_is_unformattable_for;
2414+
2415+template <typename Char> struct string_value {
2416+ const Char* data;
2417+ size_t size;
2418+ auto str() const -> basic_string_view<Char> { return {data, size}; }
2419+};
2420+
2421+template <typename Context> struct custom_value {
2422+ using char_type = typename Context::char_type;
2423+ void* value;
2424+ void (*format)(void* arg, parse_context<char_type>& parse_ctx, Context& ctx);
2425+};
2426+
2427+template <typename Char> struct named_arg_value {
2428+ const named_arg_info<Char>* data;
2429+ size_t size;
2430+};
2431+
2432+struct custom_tag {};
2433+
2434+#if !FMT_BUILTIN_TYPES
2435+# define FMT_BUILTIN , monostate
2436+#else
2437+# define FMT_BUILTIN
2438+#endif
2439+
2440+// A formatting argument value.
2441+template <typename Context> class value {
2442+ public:
2443+ using char_type = typename Context::char_type;
2444+
2445+ union {
2446+ monostate no_value;
2447+ int int_value;
2448+ unsigned uint_value;
2449+ long long long_long_value;
2450+ unsigned long long ulong_long_value;
2451+ int128_opt int128_value;
2452+ uint128_opt uint128_value;
2453+ bool bool_value;
2454+ char_type char_value;
2455+ float float_value;
2456+ double double_value;
2457+ long double long_double_value;
2458+ const void* pointer;
2459+ string_value<char_type> string;
2460+ custom_value<Context> custom;
2461+ named_arg_value<char_type> named_args;
2462+ };
2463+
2464+ constexpr FMT_INLINE value() : no_value() {}
2465+ constexpr FMT_INLINE value(signed char x) : int_value(x) {}
2466+ constexpr FMT_INLINE value(unsigned char x FMT_BUILTIN) : uint_value(x) {}
2467+ constexpr FMT_INLINE value(signed short x) : int_value(x) {}
2468+ constexpr FMT_INLINE value(unsigned short x FMT_BUILTIN) : uint_value(x) {}
2469+ constexpr FMT_INLINE value(int x) : int_value(x) {}
2470+ constexpr FMT_INLINE value(unsigned x FMT_BUILTIN) : uint_value(x) {}
2471+ FMT_CONSTEXPR FMT_INLINE value(long x FMT_BUILTIN) : value(long_type(x)) {}
2472+ FMT_CONSTEXPR FMT_INLINE value(unsigned long x FMT_BUILTIN)
2473+ : value(ulong_type(x)) {}
2474+ constexpr FMT_INLINE value(long long x FMT_BUILTIN) : long_long_value(x) {}
2475+ constexpr FMT_INLINE value(unsigned long long x FMT_BUILTIN)
2476+ : ulong_long_value(x) {}
2477+ FMT_INLINE value(int128_opt x FMT_BUILTIN) : int128_value(x) {}
2478+ FMT_INLINE value(uint128_opt x FMT_BUILTIN) : uint128_value(x) {}
2479+ constexpr FMT_INLINE value(bool x FMT_BUILTIN) : bool_value(x) {}
2480+
2481+ template <int N>
2482+ constexpr FMT_INLINE value(bitint<N> x FMT_BUILTIN) : long_long_value(x) {
2483+ static_assert(N <= 64, "unsupported _BitInt");
2484+ }
2485+ template <int N>
2486+ constexpr FMT_INLINE value(ubitint<N> x FMT_BUILTIN) : ulong_long_value(x) {
2487+ static_assert(N <= 64, "unsupported _BitInt");
2488+ }
2489+
2490+ template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
2491+ constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
2492+ static_assert(
2493+ std::is_same<T, char>::value || std::is_same<T, char_type>::value,
2494+ "mixing character types is disallowed");
2495+ }
2496+
2497+ constexpr FMT_INLINE value(float x FMT_BUILTIN) : float_value(x) {}
2498+ constexpr FMT_INLINE value(double x FMT_BUILTIN) : double_value(x) {}
2499+ FMT_INLINE value(long double x FMT_BUILTIN) : long_double_value(x) {}
2500+
2501+ FMT_CONSTEXPR FMT_INLINE value(char_type* x FMT_BUILTIN) {
2502+ string.data = x;
2503+ if (is_constant_evaluated()) string.size = 0;
2504+ }
2505+ FMT_CONSTEXPR FMT_INLINE value(const char_type* x FMT_BUILTIN) {
2506+ string.data = x;
2507+ if (is_constant_evaluated()) string.size = 0;
2508+ }
2509+ template <typename T, typename C = char_t<T>,
2510+ FMT_ENABLE_IF(!std::is_pointer<T>::value)>
2511+ FMT_CONSTEXPR value(const T& x FMT_BUILTIN) {
2512+ static_assert(std::is_same<C, char_type>::value,
2513+ "mixing character types is disallowed");
2514+ auto sv = to_string_view(x);
2515+ string.data = sv.data();
2516+ string.size = sv.size();
2517+ }
2518+ FMT_INLINE value(void* x FMT_BUILTIN) : pointer(x) {}
2519+ FMT_INLINE value(const void* x FMT_BUILTIN) : pointer(x) {}
2520+ FMT_INLINE value(volatile void* x FMT_BUILTIN)
2521+ : pointer(const_cast<const void*>(x)) {}
2522+ FMT_INLINE value(const volatile void* x FMT_BUILTIN)
2523+ : pointer(const_cast<const void*>(x)) {}
2524+ FMT_INLINE value(nullptr_t) : pointer(nullptr) {}
2525+
2526+ template <typename T, FMT_ENABLE_IF(std::is_pointer<T>::value ||
2527+ std::is_member_pointer<T>::value)>
2528+ value(const T&) {
2529+ // Formatting of arbitrary pointers is disallowed. If you want to format a
2530+ // pointer cast it to `void*` or `const void*`. In particular, this forbids
2531+ // formatting of `[const] volatile char*` printed as bool by iostreams.
2532+ static_assert(sizeof(T) == 0,
2533+ "formatting of non-void pointers is disallowed");
2534+ }
2535+
2536+ template <typename T, FMT_ENABLE_IF(use_format_as<T>::value)>
2537+ value(const T& x) : value(format_as(x)) {}
2538+ template <typename T, FMT_ENABLE_IF(use_format_as_member<T>::value)>
2539+ value(const T& x) : value(formatter<T>::format_as(x)) {}
2540+
2541+ template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
2542+ value(const T& named_arg) : value(named_arg.value) {}
2543+
2544+ template <typename T,
2545+ FMT_ENABLE_IF(use_formatter<T>::value || !FMT_BUILTIN_TYPES)>
2546+ FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {}
2547+
2548+ FMT_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
2549+ : named_args{args, size} {}
2550+
2551+ private:
2552+ template <typename T, FMT_ENABLE_IF(has_formatter<T, char_type>())>
2553+ FMT_CONSTEXPR value(T& x, custom_tag) {
2554+ using value_type = remove_const_t<T>;
2555+ // T may overload operator& e.g. std::vector<bool>::reference in libc++.
2556+ if (!is_constant_evaluated()) {
2557+ custom.value =
2558+ const_cast<char*>(&reinterpret_cast<const volatile char&>(x));
2559+ } else {
2560+ custom.value = nullptr;
2561+#if defined(__cpp_if_constexpr)
2562+ if constexpr (std::is_same<decltype(&x), remove_reference_t<T>*>::value)
2563+ custom.value = const_cast<value_type*>(&x);
2564+#endif
2565+ }
2566+ custom.format = format_custom<value_type, formatter<value_type, char_type>>;
2567+ }
2568+
2569+ template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
2570+ FMT_CONSTEXPR value(const T&, custom_tag) {
2571+ // Cannot format an argument; to make type T formattable provide a
2572+ // formatter<T> specialization: https://fmt.dev/latest/api.html#udt.
2573+ type_is_unformattable_for<T, char_type> _;
2574+ }
2575+
2576+ // Formats an argument of a custom type, such as a user-defined class.
2577+ template <typename T, typename Formatter>
2578+ static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
2579+ Context& ctx) {
2580+ auto f = Formatter();
2581+ parse_ctx.advance_to(f.parse(parse_ctx));
2582+ using qualified_type =
2583+ conditional_t<has_formatter<const T, char_type>(), const T, T>;
2584+ // format must be const for compatibility with std::format and compilation.
2585+ const auto& cf = f;
2586+ ctx.advance_to(cf.format(*static_cast<qualified_type*>(arg), ctx));
2587+ }
2588+};
2589+
2590+enum { packed_arg_bits = 4 };
2591+// Maximum number of arguments with packed types.
2592+enum { max_packed_args = 62 / packed_arg_bits };
2593+enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
2594+enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
2595+
2596+template <typename It, typename T, typename Enable = void>
2597+struct is_output_iterator : std::false_type {};
2598+
2599+template <> struct is_output_iterator<appender, char> : std::true_type {};
2600+
2601+template <typename It, typename T>
2602+struct is_output_iterator<
2603+ It, T,
2604+ enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
2605+ T>::value>> : std::true_type {};
2606+
2607+#ifndef FMT_USE_LOCALE
2608+# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
2609+#endif
2610+
2611+// A type-erased reference to an std::locale to avoid a heavy <locale> include.
2612+class locale_ref {
2613+#if FMT_USE_LOCALE
2614+ private:
2615+ const void* locale_; // A type-erased pointer to std::locale.
2616+
2617+ public:
2618+ constexpr locale_ref() : locale_(nullptr) {}
2619+ template <typename Locale> locale_ref(const Locale& loc);
2620+
2621+ inline explicit operator bool() const noexcept { return locale_ != nullptr; }
2622+#endif // FMT_USE_LOCALE
2623+
2624+ public:
2625+ template <typename Locale> auto get() const -> Locale;
2626+};
2627+
2628+template <typename> constexpr auto encode_types() -> unsigned long long {
2629+ return 0;
2630+}
2631+
2632+template <typename Context, typename Arg, typename... Args>
2633+constexpr auto encode_types() -> unsigned long long {
2634+ return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
2635+ (encode_types<Context, Args...>() << packed_arg_bits);
2636+}
2637+
2638+template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
2639+constexpr auto make_descriptor() -> unsigned long long {
2640+ return NUM_ARGS <= max_packed_args ? encode_types<Context, T...>()
2641+ : is_unpacked_bit | NUM_ARGS;
2642+}
2643+
2644+template <typename Context, int NUM_ARGS>
2645+using arg_t = conditional_t<NUM_ARGS <= max_packed_args, value<Context>,
2646+ basic_format_arg<Context>>;
2647+
2648+template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
2649+ unsigned long long DESC>
2650+struct named_arg_store {
2651+ // args_[0].named_args points to named_args to avoid bloating format_args.
2652+ arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
2653+ named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
2654+
2655+ template <typename... T>
2656+ FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
2657+ : args{{named_args, NUM_NAMED_ARGS}, values...} {
2658+ int arg_index = 0, named_arg_index = 0;
2659+ FMT_APPLY_VARIADIC(
2660+ init_named_arg(named_args, arg_index, named_arg_index, values));
2661+ }
2662+
2663+ named_arg_store(named_arg_store&& rhs) {
2664+ args[0] = {named_args, NUM_NAMED_ARGS};
2665+ for (size_t i = 1; i < sizeof(args) / sizeof(*args); ++i)
2666+ args[i] = rhs.args[i];
2667+ for (size_t i = 0; i < NUM_NAMED_ARGS; ++i)
2668+ named_args[i] = rhs.named_args[i];
2669+ }
2670+
2671+ named_arg_store(const named_arg_store& rhs) = delete;
2672+ named_arg_store& operator=(const named_arg_store& rhs) = delete;
2673+ named_arg_store& operator=(named_arg_store&& rhs) = delete;
2674+ operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
2675+};
2676+
2677+// An array of references to arguments. It can be implicitly converted to
2678+// `basic_format_args` for passing into type-erased formatting functions
2679+// such as `vformat`. It is a plain struct to reduce binary size in debug mode.
2680+template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
2681+ unsigned long long DESC>
2682+struct format_arg_store {
2683+ // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
2684+ using type =
2685+ conditional_t<NUM_NAMED_ARGS == 0,
2686+ arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
2687+ named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
2688+ type args;
2689+};
2690+
2691+// TYPE can be different from type_constant<T>, e.g. for __float128.
2692+template <typename T, typename Char, type TYPE> struct native_formatter {
2693+ private:
2694+ dynamic_format_specs<Char> specs_;
2695+
2696+ public:
2697+ using nonlocking = void;
2698+
2699+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
2700+ if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
2701+ auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE);
2702+ if (const_check(TYPE == type::char_type)) check_char_specs(specs_);
2703+ return end;
2704+ }
2705+
2706+ template <type U = TYPE,
2707+ FMT_ENABLE_IF(U == type::string_type || U == type::cstring_type ||
2708+ U == type::char_type)>
2709+ FMT_CONSTEXPR void set_debug_format(bool set = true) {
2710+ specs_.set_type(set ? presentation_type::debug : presentation_type::none);
2711+ }
2712+
2713+ FMT_PRAGMA_CLANG(diagnostic ignored "-Wundefined-inline")
2714+ template <typename FormatContext>
2715+ FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
2716+ -> decltype(ctx.out());
2717+};
2718+
2719+template <typename T, typename Enable = void>
2720+struct locking
2721+ : bool_constant<mapped_type_constant<T>::value == type::custom_type> {};
2722+template <typename T>
2723+struct locking<T, void_t<typename formatter<remove_cvref_t<T>>::nonlocking>>
2724+ : std::false_type {};
2725+
2726+template <typename T = int> FMT_CONSTEXPR inline auto is_locking() -> bool {
2727+ return locking<T>::value;
2728+}
2729+template <typename T1, typename T2, typename... Tail>
2730+FMT_CONSTEXPR inline auto is_locking() -> bool {
2731+ return locking<T1>::value || is_locking<T2, Tail...>();
2732+}
2733+
2734+FMT_API void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
2735+ locale_ref loc = {});
2736+
2737+#if FMT_WIN32
2738+FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool);
2739+#else // format_args is passed by reference since it is defined later.
2740+inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {}
2741+#endif
2742+} // namespace detail
2743+
2744+// The main public API.
2745+
2746+template <typename Char>
2747+FMT_CONSTEXPR void parse_context<Char>::do_check_arg_id(int arg_id) {
2748+ // Argument id is only checked at compile time during parsing because
2749+ // formatting has its own validation.
2750+ if (detail::is_constant_evaluated() && use_constexpr_cast) {
2751+ auto ctx = static_cast<detail::compile_parse_context<Char>*>(this);
2752+ if (arg_id >= ctx->num_args()) report_error("argument not found");
2753+ }
2754+}
2755+
2756+template <typename Char>
2757+FMT_CONSTEXPR void parse_context<Char>::check_dynamic_spec(int arg_id) {
2758+ using detail::compile_parse_context;
2759+ if (detail::is_constant_evaluated() && use_constexpr_cast)
2760+ static_cast<compile_parse_context<Char>*>(this)->check_dynamic_spec(arg_id);
2761+}
2762+
2763+FMT_BEGIN_EXPORT
2764+
2765+// An output iterator that appends to a buffer. It is used instead of
2766+// back_insert_iterator to reduce symbol sizes and avoid <iterator> dependency.
2767+template <typename T> class basic_appender {
2768+ protected:
2769+ detail::buffer<T>* container;
2770+
2771+ public:
2772+ using container_type = detail::buffer<T>;
2773+
2774+ FMT_CONSTEXPR basic_appender(detail::buffer<T>& buf) : container(&buf) {}
2775+
2776+ FMT_CONSTEXPR20 auto operator=(T c) -> basic_appender& {
2777+ container->push_back(c);
2778+ return *this;
2779+ }
2780+ FMT_CONSTEXPR20 auto operator*() -> basic_appender& { return *this; }
2781+ FMT_CONSTEXPR20 auto operator++() -> basic_appender& { return *this; }
2782+ FMT_CONSTEXPR20 auto operator++(int) -> basic_appender { return *this; }
2783+};
2784+
2785+// A formatting argument. Context is a template parameter for the compiled API
2786+// where output can be unbuffered.
2787+template <typename Context> class basic_format_arg {
2788+ private:
2789+ detail::value<Context> value_;
2790+ detail::type type_;
2791+
2792+ friend class basic_format_args<Context>;
2793+
2794+ using char_type = typename Context::char_type;
2795+
2796+ public:
2797+ class handle {
2798+ private:
2799+ detail::custom_value<Context> custom_;
2800+
2801+ public:
2802+ explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
2803+
2804+ void format(parse_context<char_type>& parse_ctx, Context& ctx) const {
2805+ custom_.format(custom_.value, parse_ctx, ctx);
2806+ }
2807+ };
2808+
2809+ constexpr basic_format_arg() : type_(detail::type::none_type) {}
2810+ basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
2811+ : value_(args, size) {}
2812+ template <typename T>
2813+ basic_format_arg(T&& val)
2814+ : value_(val), type_(detail::stored_type_constant<T, Context>::value) {}
2815+
2816+ constexpr explicit operator bool() const noexcept {
2817+ return type_ != detail::type::none_type;
2818+ }
2819+ auto type() const -> detail::type { return type_; }
2820+
2821+ /**
2822+ * Visits an argument dispatching to the appropriate visit method based on
2823+ * the argument type. For example, if the argument type is `double` then
2824+ * `vis(value)` will be called with the value of type `double`.
2825+ */
2826+ template <typename Visitor>
2827+ FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) {
2828+ using detail::map;
2829+ switch (type_) {
2830+ case detail::type::none_type: break;
2831+ case detail::type::int_type: return vis(value_.int_value);
2832+ case detail::type::uint_type: return vis(value_.uint_value);
2833+ case detail::type::long_long_type: return vis(value_.long_long_value);
2834+ case detail::type::ulong_long_type: return vis(value_.ulong_long_value);
2835+ case detail::type::int128_type: return vis(map(value_.int128_value));
2836+ case detail::type::uint128_type: return vis(map(value_.uint128_value));
2837+ case detail::type::bool_type: return vis(value_.bool_value);
2838+ case detail::type::char_type: return vis(value_.char_value);
2839+ case detail::type::float_type: return vis(value_.float_value);
2840+ case detail::type::double_type: return vis(value_.double_value);
2841+ case detail::type::long_double_type: return vis(value_.long_double_value);
2842+ case detail::type::cstring_type: return vis(value_.string.data);
2843+ case detail::type::string_type: return vis(value_.string.str());
2844+ case detail::type::pointer_type: return vis(value_.pointer);
2845+ case detail::type::custom_type: return vis(handle(value_.custom));
2846+ }
2847+ return vis(monostate());
2848+ }
2849+
2850+ auto format_custom(const char_type* parse_begin,
2851+ parse_context<char_type>& parse_ctx, Context& ctx)
2852+ -> bool {
2853+ if (type_ != detail::type::custom_type) return false;
2854+ parse_ctx.advance_to(parse_begin);
2855+ value_.custom.format(value_.custom.value, parse_ctx, ctx);
2856+ return true;
2857+ }
2858+};
2859+
2860+/**
2861+ * A view of a collection of formatting arguments. To avoid lifetime issues it
2862+ * should only be used as a parameter type in type-erased functions such as
2863+ * `vformat`:
2864+ *
2865+ * void vlog(fmt::string_view fmt, fmt::format_args args); // OK
2866+ * fmt::format_args args = fmt::make_format_args(); // Dangling reference
2867+ */
2868+template <typename Context> class basic_format_args {
2869+ private:
2870+ // A descriptor that contains information about formatting arguments.
2871+ // If the number of arguments is less or equal to max_packed_args then
2872+ // argument types are passed in the descriptor. This reduces binary code size
2873+ // per formatting function call.
2874+ unsigned long long desc_;
2875+ union {
2876+ // If is_packed() returns true then argument values are stored in values_;
2877+ // otherwise they are stored in args_. This is done to improve cache
2878+ // locality and reduce compiled code size since storing larger objects
2879+ // may require more code (at least on x86-64) even if the same amount of
2880+ // data is actually copied to stack. It saves ~10% on the bloat test.
2881+ const detail::value<Context>* values_;
2882+ const basic_format_arg<Context>* args_;
2883+ };
2884+
2885+ constexpr auto is_packed() const -> bool {
2886+ return (desc_ & detail::is_unpacked_bit) == 0;
2887+ }
2888+ constexpr auto has_named_args() const -> bool {
2889+ return (desc_ & detail::has_named_args_bit) != 0;
2890+ }
2891+
2892+ FMT_CONSTEXPR auto type(int index) const -> detail::type {
2893+ int shift = index * detail::packed_arg_bits;
2894+ unsigned mask = (1 << detail::packed_arg_bits) - 1;
2895+ return static_cast<detail::type>((desc_ >> shift) & mask);
2896+ }
2897+
2898+ template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC>
2899+ using store =
2900+ detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>;
2901+
2902+ public:
2903+ using format_arg = basic_format_arg<Context>;
2904+
2905+ constexpr basic_format_args() : desc_(0), args_(nullptr) {}
2906+
2907+ /// Constructs a `basic_format_args` object from `format_arg_store`.
2908+ template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
2909+ FMT_ENABLE_IF(NUM_ARGS <= detail::max_packed_args)>
2910+ constexpr FMT_ALWAYS_INLINE basic_format_args(
2911+ const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
2912+ : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
2913+ values_(s.args) {}
2914+
2915+ template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
2916+ FMT_ENABLE_IF(NUM_ARGS > detail::max_packed_args)>
2917+ constexpr basic_format_args(const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
2918+ : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
2919+ args_(s.args) {}
2920+
2921+ /// Constructs a `basic_format_args` object from a dynamic list of arguments.
2922+ constexpr basic_format_args(const format_arg* args, int count,
2923+ bool has_named = false)
2924+ : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) |
2925+ (has_named ? +detail::has_named_args_bit : 0)),
2926+ args_(args) {}
2927+
2928+ /// Returns the argument with the specified id.
2929+ FMT_CONSTEXPR auto get(int id) const -> format_arg {
2930+ auto arg = format_arg();
2931+ if (!is_packed()) {
2932+ if (id < max_size()) arg = args_[id];
2933+ return arg;
2934+ }
2935+ if (static_cast<unsigned>(id) >= detail::max_packed_args) return arg;
2936+ arg.type_ = type(id);
2937+ if (arg.type_ != detail::type::none_type) arg.value_ = values_[id];
2938+ return arg;
2939+ }
2940+
2941+ template <typename Char>
2942+ auto get(basic_string_view<Char> name) const -> format_arg {
2943+ int id = get_id(name);
2944+ return id >= 0 ? get(id) : format_arg();
2945+ }
2946+
2947+ template <typename Char>
2948+ FMT_CONSTEXPR auto get_id(basic_string_view<Char> name) const -> int {
2949+ if (!has_named_args()) return -1;
2950+ const auto& named_args =
2951+ (is_packed() ? values_[-1] : args_[-1].value_).named_args;
2952+ for (size_t i = 0; i < named_args.size; ++i) {
2953+ if (named_args.data[i].name == name) return named_args.data[i].id;
2954+ }
2955+ return -1;
2956+ }
2957+
2958+ auto max_size() const -> int {
2959+ unsigned long long max_packed = detail::max_packed_args;
2960+ return static_cast<int>(is_packed() ? max_packed
2961+ : desc_ & ~detail::is_unpacked_bit);
2962+ }
2963+};
2964+
2965+// A formatting context.
2966+class context {
2967+ private:
2968+ appender out_;
2969+ format_args args_;
2970+ FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
2971+
2972+ public:
2973+ /// The character type for the output.
2974+ using char_type = char;
2975+
2976+ using iterator = appender;
2977+ using format_arg = basic_format_arg<context>;
2978+ using parse_context_type FMT_DEPRECATED = parse_context<>;
2979+ template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>;
2980+ enum { builtin_types = FMT_BUILTIN_TYPES };
2981+
2982+ /// Constructs a `context` object. References to the arguments are stored
2983+ /// in the object so make sure they have appropriate lifetimes.
2984+ FMT_CONSTEXPR context(iterator out, format_args args,
2985+ detail::locale_ref loc = {})
2986+ : out_(out), args_(args), loc_(loc) {}
2987+ context(context&&) = default;
2988+ context(const context&) = delete;
2989+ void operator=(const context&) = delete;
2990+
2991+ FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); }
2992+ inline auto arg(string_view name) const -> format_arg {
2993+ return args_.get(name);
2994+ }
2995+ FMT_CONSTEXPR auto arg_id(string_view name) const -> int {
2996+ return args_.get_id(name);
2997+ }
2998+ auto args() const -> const format_args& { return args_; }
2999+
3000+ // Returns an iterator to the beginning of the output range.
3001+ FMT_CONSTEXPR auto out() const -> iterator { return out_; }
3002+
3003+ // Advances the begin iterator to `it`.
3004+ FMT_CONSTEXPR void advance_to(iterator) {}
3005+
3006+ FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
3007+};
3008+
3009+template <typename Char = char> struct runtime_format_string {
3010+ basic_string_view<Char> str;
3011+};
3012+
3013+/**
3014+ * Creates a runtime format string.
3015+ *
3016+ * **Example**:
3017+ *
3018+ * // Check format string at runtime instead of compile-time.
3019+ * fmt::print(fmt::runtime("{:d}"), "I am not a number");
3020+ */
3021+inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
3022+
3023+/// A compile-time format string. Use `format_string` in the public API to
3024+/// prevent type deduction.
3025+template <typename... T> struct fstring {
3026+ private:
3027+ static constexpr int num_static_named_args =
3028+ detail::count_static_named_args<T...>();
3029+
3030+ using checker = detail::format_string_checker<
3031+ char, static_cast<int>(sizeof...(T)), num_static_named_args,
3032+ num_static_named_args != detail::count_named_args<T...>()>;
3033+
3034+ using arg_pack = detail::arg_pack<T...>;
3035+
3036+ public:
3037+ string_view str;
3038+ using t = fstring;
3039+
3040+ // Reports a compile-time error if S is not a valid format string for T.
3041+ template <size_t N>
3042+ FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
3043+ using namespace detail;
3044+ static_assert(count<(std::is_base_of<view, remove_reference_t<T>>::value &&
3045+ std::is_reference<T>::value)...>() == 0,
3046+ "passing views as lvalues is disallowed");
3047+ if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
3048+#ifdef FMT_ENFORCE_COMPILE_STRING
3049+ static_assert(
3050+ FMT_USE_CONSTEVAL && sizeof(s) != 0,
3051+ "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING");
3052+#endif
3053+ }
3054+ template <typename S,
3055+ FMT_ENABLE_IF(std::is_convertible<const S&, string_view>::value)>
3056+ FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) {
3057+ auto sv = string_view(str);
3058+ if (FMT_USE_CONSTEVAL)
3059+ detail::parse_format_string<char>(sv, checker(sv, arg_pack()));
3060+#ifdef FMT_ENFORCE_COMPILE_STRING
3061+ static_assert(
3062+ FMT_USE_CONSTEVAL && sizeof(s) != 0,
3063+ "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING");
3064+#endif
3065+ }
3066+ template <typename S,
3067+ FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
3068+ std::is_same<typename S::char_type, char>::value)>
3069+ FMT_ALWAYS_INLINE fstring(const S&) : str(S()) {
3070+ FMT_CONSTEXPR auto sv = string_view(S());
3071+ FMT_CONSTEXPR int unused =
3072+ (parse_format_string(sv, checker(sv, arg_pack())), 0);
3073+ detail::ignore_unused(unused);
3074+ }
3075+ fstring(runtime_format_string<> fmt) : str(fmt.str) {}
3076+
3077+ // Returning by reference generates better code in debug mode.
3078+ FMT_ALWAYS_INLINE operator const string_view&() const { return str; }
3079+ auto get() const -> string_view { return str; }
3080+};
3081+
3082+template <typename... T> using format_string = typename fstring<T...>::t;
3083+
3084+template <typename T, typename Char = char>
3085+using is_formattable = bool_constant<!std::is_same<
3086+ detail::mapped_t<conditional_t<std::is_void<T>::value, int*, T>, Char>,
3087+ void>::value>;
3088+#ifdef __cpp_concepts
3089+template <typename T, typename Char = char>
3090+concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
3091+#endif
3092+
3093+template <typename T, typename Char>
3094+using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
3095+
3096+// A formatter specialization for natively supported types.
3097+template <typename T, typename Char>
3098+struct formatter<T, Char,
3099+ enable_if_t<detail::type_constant<T, Char>::value !=
3100+ detail::type::custom_type>>
3101+ : detail::native_formatter<T, Char, detail::type_constant<T, Char>::value> {
3102+};
3103+
3104+/**
3105+ * Constructs an object that stores references to arguments and can be
3106+ * implicitly converted to `format_args`. `Context` can be omitted in which case
3107+ * it defaults to `context`. See `arg` for lifetime considerations.
3108+ */
3109+// Take arguments by lvalue references to avoid some lifetime issues, e.g.
3110+// auto args = make_format_args(std::string());
3111+template <typename Context = context, typename... T,
3112+ int NUM_ARGS = sizeof...(T),
3113+ int NUM_NAMED_ARGS = detail::count_named_args<T...>(),
3114+ unsigned long long DESC = detail::make_descriptor<Context, T...>()>
3115+constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
3116+ -> detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
3117+ // Suppress warnings for pathological types convertible to detail::value.
3118+ FMT_PRAGMA_GCC(diagnostic ignored "-Wconversion")
3119+ return {{args...}};
3120+}
3121+
3122+template <typename... T>
3123+using vargs =
3124+ detail::format_arg_store<context, sizeof...(T),
3125+ detail::count_named_args<T...>(),
3126+ detail::make_descriptor<context, T...>()>;
3127+
3128+/**
3129+ * Returns a named argument to be used in a formatting function.
3130+ * It should only be used in a call to a formatting function.
3131+ *
3132+ * **Example**:
3133+ *
3134+ * fmt::print("The answer is {answer}.", fmt::arg("answer", 42));
3135+ */
3136+template <typename Char, typename T>
3137+inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
3138+ return {name, arg};
3139+}
3140+
3141+/// Formats a string and writes the output to `out`.
3142+template <typename OutputIt,
3143+ FMT_ENABLE_IF(detail::is_output_iterator<remove_cvref_t<OutputIt>,
3144+ char>::value)>
3145+auto vformat_to(OutputIt&& out, string_view fmt, format_args args)
3146+ -> remove_cvref_t<OutputIt> {
3147+ auto&& buf = detail::get_buffer<char>(out);
3148+ detail::vformat_to(buf, fmt, args, {});
3149+ return detail::get_iterator(buf, out);
3150+}
3151+
3152+/**
3153+ * Formats `args` according to specifications in `fmt`, writes the result to
3154+ * the output iterator `out` and returns the iterator past the end of the output
3155+ * range. `format_to` does not append a terminating null character.
3156+ *
3157+ * **Example**:
3158+ *
3159+ * auto out = std::vector<char>();
3160+ * fmt::format_to(std::back_inserter(out), "{}", 42);
3161+ */
3162+template <typename OutputIt, typename... T,
3163+ FMT_ENABLE_IF(detail::is_output_iterator<remove_cvref_t<OutputIt>,
3164+ char>::value)>
3165+FMT_INLINE auto format_to(OutputIt&& out, format_string<T...> fmt, T&&... args)
3166+ -> remove_cvref_t<OutputIt> {
3167+ return vformat_to(out, fmt.str, vargs<T...>{{args...}});
3168+}
3169+
3170+template <typename OutputIt> struct format_to_n_result {
3171+ /// Iterator past the end of the output range.
3172+ OutputIt out;
3173+ /// Total (not truncated) output size.
3174+ size_t size;
3175+};
3176+
3177+template <typename OutputIt, typename... T,
3178+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
3179+auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
3180+ -> format_to_n_result<OutputIt> {
3181+ using traits = detail::fixed_buffer_traits;
3182+ auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
3183+ detail::vformat_to(buf, fmt, args, {});
3184+ return {buf.out(), buf.count()};
3185+}
3186+
3187+/**
3188+ * Formats `args` according to specifications in `fmt`, writes up to `n`
3189+ * characters of the result to the output iterator `out` and returns the total
3190+ * (not truncated) output size and the iterator past the end of the output
3191+ * range. `format_to_n` does not append a terminating null character.
3192+ */
3193+template <typename OutputIt, typename... T,
3194+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
3195+FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
3196+ T&&... args) -> format_to_n_result<OutputIt> {
3197+ return vformat_to_n(out, n, fmt.str, vargs<T...>{{args...}});
3198+}
3199+
3200+struct format_to_result {
3201+ /// Pointer to just after the last successful write in the array.
3202+ char* out;
3203+ /// Specifies if the output was truncated.
3204+ bool truncated;
3205+
3206+ FMT_CONSTEXPR operator char*() const {
3207+ // Report truncation to prevent silent data loss.
3208+ if (truncated) report_error("output is truncated");
3209+ return out;
3210+ }
3211+};
3212+
3213+template <size_t N>
3214+auto vformat_to(char (&out)[N], string_view fmt, format_args args)
3215+ -> format_to_result {
3216+ auto result = vformat_to_n(out, N, fmt, args);
3217+ return {result.out, result.size > N};
3218+}
3219+
3220+template <size_t N, typename... T>
3221+FMT_INLINE auto format_to(char (&out)[N], format_string<T...> fmt, T&&... args)
3222+ -> format_to_result {
3223+ auto result = vformat_to_n(out, N, fmt.str, vargs<T...>{{args...}});
3224+ return {result.out, result.size > N};
3225+}
3226+
3227+/// Returns the number of chars in the output of `format(fmt, args...)`.
3228+template <typename... T>
3229+FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
3230+ T&&... args) -> size_t {
3231+ auto buf = detail::counting_buffer<>();
3232+ detail::vformat_to(buf, fmt.str, vargs<T...>{{args...}}, {});
3233+ return buf.count();
3234+}
3235+
3236+FMT_API void vprint(string_view fmt, format_args args);
3237+FMT_API void vprint(FILE* f, string_view fmt, format_args args);
3238+FMT_API void vprintln(FILE* f, string_view fmt, format_args args);
3239+FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args);
3240+
3241+/**
3242+ * Formats `args` according to specifications in `fmt` and writes the output
3243+ * to `stdout`.
3244+ *
3245+ * **Example**:
3246+ *
3247+ * fmt::print("The answer is {}.", 42);
3248+ */
3249+template <typename... T>
3250+FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
3251+ vargs<T...> va = {{args...}};
3252+ if (detail::const_check(!detail::use_utf8))
3253+ return detail::vprint_mojibake(stdout, fmt.str, va, false);
3254+ return detail::is_locking<T...>() ? vprint_buffered(stdout, fmt.str, va)
3255+ : vprint(fmt.str, va);
3256+}
3257+
3258+/**
3259+ * Formats `args` according to specifications in `fmt` and writes the
3260+ * output to the file `f`.
3261+ *
3262+ * **Example**:
3263+ *
3264+ * fmt::print(stderr, "Don't {}!", "panic");
3265+ */
3266+template <typename... T>
3267+FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
3268+ vargs<T...> va = {{args...}};
3269+ if (detail::const_check(!detail::use_utf8))
3270+ return detail::vprint_mojibake(f, fmt.str, va, false);
3271+ return detail::is_locking<T...>() ? vprint_buffered(f, fmt.str, va)
3272+ : vprint(f, fmt.str, va);
3273+}
3274+
3275+/// Formats `args` according to specifications in `fmt` and writes the output
3276+/// to the file `f` followed by a newline.
3277+template <typename... T>
3278+FMT_INLINE void println(FILE* f, format_string<T...> fmt, T&&... args) {
3279+ vargs<T...> va = {{args...}};
3280+ return detail::const_check(detail::use_utf8)
3281+ ? vprintln(f, fmt.str, va)
3282+ : detail::vprint_mojibake(f, fmt.str, va, true);
3283+}
3284+
3285+/// Formats `args` according to specifications in `fmt` and writes the output
3286+/// to `stdout` followed by a newline.
3287+template <typename... T>
3288+FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
3289+ return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
3290+}
3291+
3292+FMT_END_EXPORT
3293+FMT_PRAGMA_CLANG(diagnostic pop)
3294+FMT_PRAGMA_GCC(pop_options)
3295+FMT_END_NAMESPACE
3296+
3297+#ifdef FMT_HEADER_ONLY
3298+# include "format.h"
3299+#endif
3300+#endif // FMT_BASE_H_
3301diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h
3302index 43daeeb..50c777c 100644
3303--- a/include/fmt/chrono.h
3304+++ b/include/fmt/chrono.h
3305@@ -8,51 +8,36 @@
3306 #ifndef FMT_CHRONO_H_
3307 #define FMT_CHRONO_H_
3308
3309-#include <algorithm>
3310-#include <chrono>
3311-#include <cmath> // std::isfinite
3312-#include <cstring> // std::memcpy
3313-#include <ctime>
3314-#include <iterator>
3315-#include <locale>
3316-#include <ostream>
3317-#include <type_traits>
3318+#ifndef FMT_MODULE
3319+# include <algorithm>
3320+# include <chrono>
3321+# include <cmath> // std::isfinite
3322+# include <cstring> // std::memcpy
3323+# include <ctime>
3324+# include <iterator>
3325+# include <locale>
3326+# include <ostream>
3327+# include <type_traits>
3328+#endif
3329
3330 #include "format.h"
3331
3332-FMT_BEGIN_NAMESPACE
3333-
3334-// Check if std::chrono::local_t is available.
3335-#ifndef FMT_USE_LOCAL_TIME
3336-# ifdef __cpp_lib_chrono
3337-# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
3338-# else
3339-# define FMT_USE_LOCAL_TIME 0
3340-# endif
3341-#endif
3342+namespace fmt_detail {
3343+struct time_zone {
3344+ template <typename Duration, typename T>
3345+ auto to_sys(T)
3346+ -> std::chrono::time_point<std::chrono::system_clock, Duration> {
3347+ return {};
3348+ }
3349+};
3350+template <typename... T> inline auto current_zone(T...) -> time_zone* {
3351+ return nullptr;
3352+}
3353
3354-// Check if std::chrono::utc_timestamp is available.
3355-#ifndef FMT_USE_UTC_TIME
3356-# ifdef __cpp_lib_chrono
3357-# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
3358-# else
3359-# define FMT_USE_UTC_TIME 0
3360-# endif
3361-#endif
3362+template <typename... T> inline void _tzset(T...) {}
3363+} // namespace fmt_detail
3364
3365-// Enable tzset.
3366-#ifndef FMT_USE_TZSET
3367-// UWP doesn't provide _tzset.
3368-# if FMT_HAS_INCLUDE("winapifamily.h")
3369-# include <winapifamily.h>
3370-# endif
3371-# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
3372- (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
3373-# define FMT_USE_TZSET 1
3374-# else
3375-# define FMT_USE_TZSET 0
3376-# endif
3377-#endif
3378+FMT_BEGIN_NAMESPACE
3379
3380 // Enable safe chrono durations, unless explicitly disabled.
3381 #ifndef FMT_SAFE_DURATION_CAST
3382@@ -72,7 +57,8 @@ template <typename To, typename From,
3383 FMT_ENABLE_IF(!std::is_same<From, To>::value &&
3384 std::numeric_limits<From>::is_signed ==
3385 std::numeric_limits<To>::is_signed)>
3386-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
3387+FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
3388+ -> To {
3389 ec = 0;
3390 using F = std::numeric_limits<From>;
3391 using T = std::numeric_limits<To>;
3392@@ -93,15 +79,14 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
3393 return static_cast<To>(from);
3394 }
3395
3396-/**
3397- * converts From to To, without loss. If the dynamic value of from
3398- * can't be converted to To without loss, ec is set.
3399- */
3400+/// Converts From to To, without loss. If the dynamic value of from
3401+/// can't be converted to To without loss, ec is set.
3402 template <typename To, typename From,
3403 FMT_ENABLE_IF(!std::is_same<From, To>::value &&
3404 std::numeric_limits<From>::is_signed !=
3405 std::numeric_limits<To>::is_signed)>
3406-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
3407+FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
3408+ -> To {
3409 ec = 0;
3410 using F = std::numeric_limits<From>;
3411 using T = std::numeric_limits<To>;
3412@@ -133,7 +118,8 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
3413
3414 template <typename To, typename From,
3415 FMT_ENABLE_IF(std::is_same<From, To>::value)>
3416-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
3417+FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
3418+ -> To {
3419 ec = 0;
3420 return from;
3421 } // function
3422@@ -154,7 +140,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
3423 // clang-format on
3424 template <typename To, typename From,
3425 FMT_ENABLE_IF(!std::is_same<From, To>::value)>
3426-FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
3427+FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
3428 ec = 0;
3429 using T = std::numeric_limits<To>;
3430 static_assert(std::is_floating_point<From>::value, "From must be floating");
3431@@ -176,72 +162,18 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
3432
3433 template <typename To, typename From,
3434 FMT_ENABLE_IF(std::is_same<From, To>::value)>
3435-FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
3436+FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
3437 ec = 0;
3438 static_assert(std::is_floating_point<From>::value, "From must be floating");
3439 return from;
3440 }
3441
3442-/**
3443- * safe duration cast between integral durations
3444- */
3445-template <typename To, typename FromRep, typename FromPeriod,
3446- FMT_ENABLE_IF(std::is_integral<FromRep>::value),
3447- FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
3448-To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
3449- int& ec) {
3450- using From = std::chrono::duration<FromRep, FromPeriod>;
3451- ec = 0;
3452- // the basic idea is that we need to convert from count() in the from type
3453- // to count() in the To type, by multiplying it with this:
3454- struct Factor
3455- : std::ratio_divide<typename From::period, typename To::period> {};
3456-
3457- static_assert(Factor::num > 0, "num must be positive");
3458- static_assert(Factor::den > 0, "den must be positive");
3459-
3460- // the conversion is like this: multiply from.count() with Factor::num
3461- // /Factor::den and convert it to To::rep, all this without
3462- // overflow/underflow. let's start by finding a suitable type that can hold
3463- // both To, From and Factor::num
3464- using IntermediateRep =
3465- typename std::common_type<typename From::rep, typename To::rep,
3466- decltype(Factor::num)>::type;
3467-
3468- // safe conversion to IntermediateRep
3469- IntermediateRep count =
3470- lossless_integral_conversion<IntermediateRep>(from.count(), ec);
3471- if (ec) return {};
3472- // multiply with Factor::num without overflow or underflow
3473- if (detail::const_check(Factor::num != 1)) {
3474- const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
3475- if (count > max1) {
3476- ec = 1;
3477- return {};
3478- }
3479- const auto min1 =
3480- (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
3481- if (detail::const_check(!std::is_unsigned<IntermediateRep>::value) &&
3482- count < min1) {
3483- ec = 1;
3484- return {};
3485- }
3486- count *= Factor::num;
3487- }
3488-
3489- if (detail::const_check(Factor::den != 1)) count /= Factor::den;
3490- auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
3491- return ec ? To() : To(tocount);
3492-}
3493-
3494-/**
3495- * safe duration_cast between floating point durations
3496- */
3497+/// Safe duration_cast between floating point durations
3498 template <typename To, typename FromRep, typename FromPeriod,
3499 FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
3500 FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
3501-To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
3502- int& ec) {
3503+auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
3504+ int& ec) -> To {
3505 using From = std::chrono::duration<FromRep, FromPeriod>;
3506 ec = 0;
3507 if (std::isnan(from.count())) {
3508@@ -315,18 +247,95 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
3509 } // namespace safe_duration_cast
3510 #endif
3511
3512+namespace detail {
3513+
3514+// Check if std::chrono::utc_time is available.
3515+#ifdef FMT_USE_UTC_TIME
3516+// Use the provided definition.
3517+#elif defined(__cpp_lib_chrono)
3518+# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
3519+#else
3520+# define FMT_USE_UTC_TIME 0
3521+#endif
3522+#if FMT_USE_UTC_TIME
3523+using utc_clock = std::chrono::utc_clock;
3524+#else
3525+struct utc_clock {
3526+ template <typename T> void to_sys(T);
3527+};
3528+#endif
3529+
3530+// Check if std::chrono::local_time is available.
3531+#ifdef FMT_USE_LOCAL_TIME
3532+// Use the provided definition.
3533+#elif defined(__cpp_lib_chrono)
3534+# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
3535+#else
3536+# define FMT_USE_LOCAL_TIME 0
3537+#endif
3538+#if FMT_USE_LOCAL_TIME
3539+using local_t = std::chrono::local_t;
3540+#else
3541+struct local_t {};
3542+#endif
3543+
3544+} // namespace detail
3545+
3546+template <typename Duration>
3547+using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
3548+
3549+template <typename Duration>
3550+using utc_time = std::chrono::time_point<detail::utc_clock, Duration>;
3551+
3552+template <class Duration>
3553+using local_time = std::chrono::time_point<detail::local_t, Duration>;
3554+
3555+namespace detail {
3556+
3557 // Prevents expansion of a preceding token as a function-style macro.
3558 // Usage: f FMT_NOMACRO()
3559 #define FMT_NOMACRO
3560
3561-namespace detail {
3562 template <typename T = void> struct null {};
3563-inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
3564-inline null<> localtime_s(...) { return null<>(); }
3565-inline null<> gmtime_r(...) { return null<>(); }
3566-inline null<> gmtime_s(...) { return null<>(); }
3567+inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
3568+inline auto localtime_s(...) -> null<> { return null<>(); }
3569+inline auto gmtime_r(...) -> null<> { return null<>(); }
3570+inline auto gmtime_s(...) -> null<> { return null<>(); }
3571+
3572+// It is defined here and not in ostream.h because the latter has expensive
3573+// includes.
3574+template <typename StreamBuf> class formatbuf : public StreamBuf {
3575+ private:
3576+ using char_type = typename StreamBuf::char_type;
3577+ using streamsize = decltype(std::declval<StreamBuf>().sputn(nullptr, 0));
3578+ using int_type = typename StreamBuf::int_type;
3579+ using traits_type = typename StreamBuf::traits_type;
3580
3581-inline const std::locale& get_classic_locale() {
3582+ buffer<char_type>& buffer_;
3583+
3584+ public:
3585+ explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
3586+
3587+ protected:
3588+ // The put area is always empty. This makes the implementation simpler and has
3589+ // the advantage that the streambuf and the buffer are always in sync and
3590+ // sputc never writes into uninitialized memory. A disadvantage is that each
3591+ // call to sputc always results in a (virtual) call to overflow. There is no
3592+ // disadvantage here for sputn since this always results in a call to xsputn.
3593+
3594+ auto overflow(int_type ch) -> int_type override {
3595+ if (!traits_type::eq_int_type(ch, traits_type::eof()))
3596+ buffer_.push_back(static_cast<char_type>(ch));
3597+ return ch;
3598+ }
3599+
3600+ auto xsputn(const char_type* s, streamsize count) -> streamsize override {
3601+ buffer_.append(s, s + count);
3602+ return count;
3603+ }
3604+};
3605+
3606+inline auto get_classic_locale() -> const std::locale& {
3607 static const auto& locale = std::locale::classic();
3608 return locale;
3609 }
3610@@ -336,24 +345,18 @@ template <typename CodeUnit> struct codecvt_result {
3611 CodeUnit buf[max_size];
3612 CodeUnit* end;
3613 };
3614-template <typename CodeUnit>
3615-constexpr const size_t codecvt_result<CodeUnit>::max_size;
3616
3617 template <typename CodeUnit>
3618-void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
3619+void write_codecvt(codecvt_result<CodeUnit>& out, string_view in,
3620 const std::locale& loc) {
3621-#if FMT_CLANG_VERSION
3622-# pragma clang diagnostic push
3623-# pragma clang diagnostic ignored "-Wdeprecated"
3624+ FMT_PRAGMA_CLANG(diagnostic push)
3625+ FMT_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated")
3626 auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
3627-# pragma clang diagnostic pop
3628-#else
3629- auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
3630-#endif
3631+ FMT_PRAGMA_CLANG(diagnostic pop)
3632 auto mb = std::mbstate_t();
3633 const char* from_next = nullptr;
3634- auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next,
3635- std::begin(out.buf), std::end(out.buf), out.end);
3636+ auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf),
3637+ std::end(out.buf), out.end);
3638 if (result != std::codecvt_base::ok)
3639 FMT_THROW(format_error("failed to format time"));
3640 }
3641@@ -361,11 +364,12 @@ void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
3642 template <typename OutputIt>
3643 auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
3644 -> OutputIt {
3645- if (detail::is_utf8() && loc != get_classic_locale()) {
3646+ if (const_check(detail::use_utf8) && loc != get_classic_locale()) {
3647 // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
3648 // gcc-4.
3649-#if FMT_MSC_VERSION != 0 || \
3650- (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
3651+#if FMT_MSC_VERSION != 0 || \
3652+ (defined(__GLIBCXX__) && \
3653+ (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0))
3654 // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
3655 // and newer.
3656 using code_unit = wchar_t;
3657@@ -381,9 +385,9 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
3658 to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>();
3659 if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
3660 FMT_THROW(format_error("failed to format time"));
3661- return copy_str<char>(u.c_str(), u.c_str() + u.size(), out);
3662+ return copy<char>(u.c_str(), u.c_str() + u.size(), out);
3663 }
3664- return copy_str<char>(in.data(), in.data() + in.size(), out);
3665+ return copy<char>(in.data(), in.data() + in.size(), out);
3666 }
3667
3668 template <typename Char, typename OutputIt,
3669@@ -392,7 +396,7 @@ auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
3670 -> OutputIt {
3671 codecvt_result<Char> unit;
3672 write_codecvt(unit, sv, loc);
3673- return copy_str<Char>(unit.buf, unit.end, out);
3674+ return copy<Char>(unit.buf, unit.end, out);
3675 }
3676
3677 template <typename Char, typename OutputIt,
3678@@ -408,8 +412,7 @@ inline void do_write(buffer<Char>& buf, const std::tm& time,
3679 auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
3680 auto&& os = std::basic_ostream<Char>(&format_buf);
3681 os.imbue(loc);
3682- using iterator = std::ostreambuf_iterator<Char>;
3683- const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
3684+ const auto& facet = std::use_facet<std::time_put<Char>>(loc);
3685 auto end = facet.put(os, os, Char(' '), &time, format, modifier);
3686 if (end.failed()) FMT_THROW(format_error("failed to format time"));
3687 }
3688@@ -432,38 +435,129 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
3689 return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
3690 }
3691
3692+template <typename Rep1, typename Rep2>
3693+struct is_same_arithmetic_type
3694+ : public std::integral_constant<bool,
3695+ (std::is_integral<Rep1>::value &&
3696+ std::is_integral<Rep2>::value) ||
3697+ (std::is_floating_point<Rep1>::value &&
3698+ std::is_floating_point<Rep2>::value)> {
3699+};
3700+
3701+FMT_NORETURN inline void throw_duration_error() {
3702+ FMT_THROW(format_error("cannot format duration"));
3703+}
3704+
3705+// Cast one integral duration to another with an overflow check.
3706+template <typename To, typename FromRep, typename FromPeriod,
3707+ FMT_ENABLE_IF(std::is_integral<FromRep>::value&&
3708+ std::is_integral<typename To::rep>::value)>
3709+auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
3710+#if !FMT_SAFE_DURATION_CAST
3711+ return std::chrono::duration_cast<To>(from);
3712+#else
3713+ // The conversion factor: to.count() == factor * from.count().
3714+ using factor = std::ratio_divide<FromPeriod, typename To::period>;
3715+
3716+ using common_rep = typename std::common_type<FromRep, typename To::rep,
3717+ decltype(factor::num)>::type;
3718+
3719+ int ec = 0;
3720+ auto count = safe_duration_cast::lossless_integral_conversion<common_rep>(
3721+ from.count(), ec);
3722+ if (ec) throw_duration_error();
3723+
3724+ // Multiply from.count() by factor and check for overflow.
3725+ if (const_check(factor::num != 1)) {
3726+ if (count > max_value<common_rep>() / factor::num) throw_duration_error();
3727+ const auto min = (std::numeric_limits<common_rep>::min)() / factor::num;
3728+ if (const_check(!std::is_unsigned<common_rep>::value) && count < min)
3729+ throw_duration_error();
3730+ count *= factor::num;
3731+ }
3732+ if (const_check(factor::den != 1)) count /= factor::den;
3733+ auto to =
3734+ To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
3735+ count, ec));
3736+ if (ec) throw_duration_error();
3737+ return to;
3738+#endif
3739+}
3740+
3741+template <typename To, typename FromRep, typename FromPeriod,
3742+ FMT_ENABLE_IF(std::is_floating_point<FromRep>::value&&
3743+ std::is_floating_point<typename To::rep>::value)>
3744+auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
3745+#if FMT_SAFE_DURATION_CAST
3746+ // Throwing version of safe_duration_cast is only available for
3747+ // integer to integer or float to float casts.
3748+ int ec;
3749+ To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
3750+ if (ec) throw_duration_error();
3751+ return to;
3752+#else
3753+ // Standard duration cast, may overflow.
3754+ return std::chrono::duration_cast<To>(from);
3755+#endif
3756+}
3757+
3758+template <
3759+ typename To, typename FromRep, typename FromPeriod,
3760+ FMT_ENABLE_IF(!is_same_arithmetic_type<FromRep, typename To::rep>::value)>
3761+auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
3762+ // Mixed integer <-> float cast is not supported by safe_duration_cast.
3763+ return std::chrono::duration_cast<To>(from);
3764+}
3765+
3766+template <typename Duration>
3767+auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
3768+ // Cannot use std::chrono::system_clock::to_time_t since this would first
3769+ // require a cast to std::chrono::system_clock::time_point, which could
3770+ // overflow.
3771+ return detail::duration_cast<std::chrono::duration<std::time_t>>(
3772+ time_point.time_since_epoch())
3773+ .count();
3774+}
3775+
3776+// Workaround a bug in libstdc++ which sets __cpp_lib_chrono to 201907 without
3777+// providing current_zone(): https://github.com/fmtlib/fmt/issues/4160.
3778+template <typename T> FMT_CONSTEXPR auto has_current_zone() -> bool {
3779+ using namespace std::chrono;
3780+ using namespace fmt_detail;
3781+ return !std::is_same<decltype(current_zone()), fmt_detail::time_zone*>::value;
3782+}
3783 } // namespace detail
3784
3785 FMT_BEGIN_EXPORT
3786
3787 /**
3788- Converts given time since epoch as ``std::time_t`` value into calendar time,
3789- expressed in local time. Unlike ``std::localtime``, this function is
3790- thread-safe on most platforms.
3791+ * Converts given time since epoch as `std::time_t` value into calendar time,
3792+ * expressed in local time. Unlike `std::localtime`, this function is
3793+ * thread-safe on most platforms.
3794 */
3795-inline std::tm localtime(std::time_t time) {
3796+inline auto localtime(std::time_t time) -> std::tm {
3797 struct dispatcher {
3798 std::time_t time_;
3799 std::tm tm_;
3800
3801- dispatcher(std::time_t t) : time_(t) {}
3802+ inline dispatcher(std::time_t t) : time_(t) {}
3803
3804- bool run() {
3805+ inline auto run() -> bool {
3806 using namespace fmt::detail;
3807 return handle(localtime_r(&time_, &tm_));
3808 }
3809
3810- bool handle(std::tm* tm) { return tm != nullptr; }
3811+ inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
3812
3813- bool handle(detail::null<>) {
3814+ inline auto handle(detail::null<>) -> bool {
3815 using namespace fmt::detail;
3816 return fallback(localtime_s(&tm_, &time_));
3817 }
3818
3819- bool fallback(int res) { return res == 0; }
3820+ inline auto fallback(int res) -> bool { return res == 0; }
3821
3822 #if !FMT_MSC_VERSION
3823- bool fallback(detail::null<>) {
3824+ inline auto fallback(detail::null<>) -> bool {
3825 using namespace fmt::detail;
3826 std::tm* tm = std::localtime(&time_);
3827 if (tm) tm_ = *tm;
3828@@ -478,102 +572,61 @@ inline std::tm localtime(std::time_t time) {
3829 }
3830
3831 #if FMT_USE_LOCAL_TIME
3832-template <typename Duration>
3833+template <typename Duration,
3834+ FMT_ENABLE_IF(detail::has_current_zone<Duration>())>
3835 inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
3836- return localtime(std::chrono::system_clock::to_time_t(
3837- std::chrono::current_zone()->to_sys(time)));
3838+ using namespace std::chrono;
3839+ using namespace fmt_detail;
3840+ return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
3841 }
3842 #endif
3843
3844 /**
3845- Converts given time since epoch as ``std::time_t`` value into calendar time,
3846- expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
3847- function is thread-safe on most platforms.
3848+ * Converts given time since epoch as `std::time_t` value into calendar time,
3849+ * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
3850+ * function is thread-safe on most platforms.
3851 */
3852-inline std::tm gmtime(std::time_t time) {
3853+inline auto gmtime(std::time_t time) -> std::tm {
3854 struct dispatcher {
3855 std::time_t time_;
3856 std::tm tm_;
3857
3858- dispatcher(std::time_t t) : time_(t) {}
3859+ inline dispatcher(std::time_t t) : time_(t) {}
3860
3861- bool run() {
3862+ inline auto run() -> bool {
3863 using namespace fmt::detail;
3864 return handle(gmtime_r(&time_, &tm_));
3865 }
3866
3867- bool handle(std::tm* tm) { return tm != nullptr; }
3868+ inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
3869
3870- bool handle(detail::null<>) {
3871+ inline auto handle(detail::null<>) -> bool {
3872 using namespace fmt::detail;
3873 return fallback(gmtime_s(&tm_, &time_));
3874 }
3875
3876- bool fallback(int res) { return res == 0; }
3877+ inline auto fallback(int res) -> bool { return res == 0; }
3878
3879 #if !FMT_MSC_VERSION
3880- bool fallback(detail::null<>) {
3881+ inline auto fallback(detail::null<>) -> bool {
3882 std::tm* tm = std::gmtime(&time_);
3883 if (tm) tm_ = *tm;
3884 return tm != nullptr;
3885 }
3886 #endif
3887 };
3888- dispatcher gt(time);
3889+ auto gt = dispatcher(time);
3890 // Too big time values may be unsupported.
3891 if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
3892 return gt.tm_;
3893 }
3894
3895-inline std::tm gmtime(
3896- std::chrono::time_point<std::chrono::system_clock> time_point) {
3897- return gmtime(std::chrono::system_clock::to_time_t(time_point));
3898+template <typename Duration>
3899+inline auto gmtime(sys_time<Duration> time_point) -> std::tm {
3900+ return gmtime(detail::to_time_t(time_point));
3901 }
3902
3903-FMT_BEGIN_DETAIL_NAMESPACE
3904-
3905-// DEPRECATED!
3906-template <typename Char>
3907-FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
3908- format_specs<Char>& specs) -> const Char* {
3909- FMT_ASSERT(begin != end, "");
3910- auto align = align::none;
3911- auto p = begin + code_point_length(begin);
3912- if (end - p <= 0) p = begin;
3913- for (;;) {
3914- switch (to_ascii(*p)) {
3915- case '<':
3916- align = align::left;
3917- break;
3918- case '>':
3919- align = align::right;
3920- break;
3921- case '^':
3922- align = align::center;
3923- break;
3924- }
3925- if (align != align::none) {
3926- if (p != begin) {
3927- auto c = *begin;
3928- if (c == '}') return begin;
3929- if (c == '{') {
3930- throw_format_error("invalid fill character '{'");
3931- return begin;
3932- }
3933- specs.fill = {begin, to_unsigned(p - begin)};
3934- begin = p + 1;
3935- } else {
3936- ++begin;
3937- }
3938- break;
3939- } else if (p == begin) {
3940- break;
3941- }
3942- p = begin;
3943- }
3944- specs.align = align;
3945- return begin;
3946-}
3947+namespace detail {
3948
3949 // Writes two-digit numbers a, b and c separated by sep to buf.
3950 // The method by Pavel Novikov based on
3951@@ -609,12 +662,14 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
3952 }
3953 }
3954
3955-template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
3956+template <typename Period>
3957+FMT_CONSTEXPR inline auto get_units() -> const char* {
3958 if (std::is_same<Period, std::atto>::value) return "as";
3959 if (std::is_same<Period, std::femto>::value) return "fs";
3960 if (std::is_same<Period, std::pico>::value) return "ps";
3961 if (std::is_same<Period, std::nano>::value) return "ns";
3962- if (std::is_same<Period, std::micro>::value) return "µs";
3963+ if (std::is_same<Period, std::micro>::value)
3964+ return detail::use_utf8 ? "µs" : "us";
3965 if (std::is_same<Period, std::milli>::value) return "ms";
3966 if (std::is_same<Period, std::centi>::value) return "cs";
3967 if (std::is_same<Period, std::deci>::value) return "ds";
3968@@ -627,8 +682,9 @@ template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
3969 if (std::is_same<Period, std::tera>::value) return "Ts";
3970 if (std::is_same<Period, std::peta>::value) return "Ps";
3971 if (std::is_same<Period, std::exa>::value) return "Es";
3972- if (std::is_same<Period, std::ratio<60>>::value) return "m";
3973+ if (std::is_same<Period, std::ratio<60>>::value) return "min";
3974 if (std::is_same<Period, std::ratio<3600>>::value) return "h";
3975+ if (std::is_same<Period, std::ratio<86400>>::value) return "d";
3976 return nullptr;
3977 }
3978
3979@@ -640,12 +696,10 @@ enum class numeric_system {
3980
3981 // Glibc extensions for formatting numeric values.
3982 enum class pad_type {
3983- unspecified,
3984+ // Pad a numeric result string with zeros (the default).
3985+ zero,
3986 // Do not pad a numeric result string.
3987 none,
3988- // Pad a numeric result string with zeros even if the conversion specifier
3989- // character uses space-padding by default.
3990- zero,
3991 // Pad a numeric result string with spaces.
3992 space,
3993 };
3994@@ -653,7 +707,7 @@ enum class pad_type {
3995 template <typename OutputIt>
3996 auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt {
3997 if (pad == pad_type::none) return out;
3998- return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0');
3999+ return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0');
4000 }
4001
4002 template <typename OutputIt>
4003@@ -664,14 +718,13 @@ auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
4004
4005 // Parses a put_time-like format string and invokes handler actions.
4006 template <typename Char, typename Handler>
4007-FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
4008- const Char* end,
4009- Handler&& handler) {
4010+FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end,
4011+ Handler&& handler) -> const Char* {
4012 if (begin == end || *begin == '}') return begin;
4013 if (*begin != '%') FMT_THROW(format_error("invalid format"));
4014 auto ptr = begin;
4015- pad_type pad = pad_type::unspecified;
4016 while (ptr != end) {
4017+ pad_type pad = pad_type::zero;
4018 auto c = *ptr;
4019 if (c == '}') break;
4020 if (c != '%') {
4021@@ -691,17 +744,11 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
4022 pad = pad_type::none;
4023 ++ptr;
4024 break;
4025- case '0':
4026- pad = pad_type::zero;
4027- ++ptr;
4028- break;
4029 }
4030 if (ptr == end) FMT_THROW(format_error("invalid format"));
4031 c = *ptr++;
4032 switch (c) {
4033- case '%':
4034- handler.on_text(ptr - 1, ptr);
4035- break;
4036+ case '%': handler.on_text(ptr - 1, ptr); break;
4037 case 'n': {
4038 const Char newline[] = {'\n'};
4039 handler.on_text(newline, newline + 1);
4040@@ -713,145 +760,66 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
4041 break;
4042 }
4043 // Year:
4044- case 'Y':
4045- handler.on_year(numeric_system::standard);
4046- break;
4047- case 'y':
4048- handler.on_short_year(numeric_system::standard);
4049- break;
4050- case 'C':
4051- handler.on_century(numeric_system::standard);
4052- break;
4053- case 'G':
4054- handler.on_iso_week_based_year();
4055- break;
4056- case 'g':
4057- handler.on_iso_week_based_short_year();
4058- break;
4059+ case 'Y': handler.on_year(numeric_system::standard, pad); break;
4060+ case 'y': handler.on_short_year(numeric_system::standard); break;
4061+ case 'C': handler.on_century(numeric_system::standard); break;
4062+ case 'G': handler.on_iso_week_based_year(); break;
4063+ case 'g': handler.on_iso_week_based_short_year(); break;
4064 // Day of the week:
4065- case 'a':
4066- handler.on_abbr_weekday();
4067- break;
4068- case 'A':
4069- handler.on_full_weekday();
4070- break;
4071- case 'w':
4072- handler.on_dec0_weekday(numeric_system::standard);
4073- break;
4074- case 'u':
4075- handler.on_dec1_weekday(numeric_system::standard);
4076- break;
4077+ case 'a': handler.on_abbr_weekday(); break;
4078+ case 'A': handler.on_full_weekday(); break;
4079+ case 'w': handler.on_dec0_weekday(numeric_system::standard); break;
4080+ case 'u': handler.on_dec1_weekday(numeric_system::standard); break;
4081 // Month:
4082 case 'b':
4083- case 'h':
4084- handler.on_abbr_month();
4085- break;
4086- case 'B':
4087- handler.on_full_month();
4088- break;
4089- case 'm':
4090- handler.on_dec_month(numeric_system::standard);
4091- break;
4092+ case 'h': handler.on_abbr_month(); break;
4093+ case 'B': handler.on_full_month(); break;
4094+ case 'm': handler.on_dec_month(numeric_system::standard, pad); break;
4095 // Day of the year/month:
4096 case 'U':
4097- handler.on_dec0_week_of_year(numeric_system::standard);
4098+ handler.on_dec0_week_of_year(numeric_system::standard, pad);
4099 break;
4100 case 'W':
4101- handler.on_dec1_week_of_year(numeric_system::standard);
4102- break;
4103- case 'V':
4104- handler.on_iso_week_of_year(numeric_system::standard);
4105- break;
4106- case 'j':
4107- handler.on_day_of_year();
4108- break;
4109- case 'd':
4110- handler.on_day_of_month(numeric_system::standard);
4111+ handler.on_dec1_week_of_year(numeric_system::standard, pad);
4112 break;
4113+ case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad); break;
4114+ case 'j': handler.on_day_of_year(pad); break;
4115+ case 'd': handler.on_day_of_month(numeric_system::standard, pad); break;
4116 case 'e':
4117- handler.on_day_of_month_space(numeric_system::standard);
4118+ handler.on_day_of_month(numeric_system::standard, pad_type::space);
4119 break;
4120 // Hour, minute, second:
4121- case 'H':
4122- handler.on_24_hour(numeric_system::standard, pad);
4123- break;
4124- case 'I':
4125- handler.on_12_hour(numeric_system::standard, pad);
4126- break;
4127- case 'M':
4128- handler.on_minute(numeric_system::standard, pad);
4129- break;
4130- case 'S':
4131- handler.on_second(numeric_system::standard, pad);
4132- break;
4133+ case 'H': handler.on_24_hour(numeric_system::standard, pad); break;
4134+ case 'I': handler.on_12_hour(numeric_system::standard, pad); break;
4135+ case 'M': handler.on_minute(numeric_system::standard, pad); break;
4136+ case 'S': handler.on_second(numeric_system::standard, pad); break;
4137 // Other:
4138- case 'c':
4139- handler.on_datetime(numeric_system::standard);
4140- break;
4141- case 'x':
4142- handler.on_loc_date(numeric_system::standard);
4143- break;
4144- case 'X':
4145- handler.on_loc_time(numeric_system::standard);
4146- break;
4147- case 'D':
4148- handler.on_us_date();
4149- break;
4150- case 'F':
4151- handler.on_iso_date();
4152- break;
4153- case 'r':
4154- handler.on_12_hour_time();
4155- break;
4156- case 'R':
4157- handler.on_24_hour_time();
4158- break;
4159- case 'T':
4160- handler.on_iso_time();
4161- break;
4162- case 'p':
4163- handler.on_am_pm();
4164- break;
4165- case 'Q':
4166- handler.on_duration_value();
4167- break;
4168- case 'q':
4169- handler.on_duration_unit();
4170- break;
4171- case 'z':
4172- handler.on_utc_offset(numeric_system::standard);
4173- break;
4174- case 'Z':
4175- handler.on_tz_name();
4176- break;
4177+ case 'c': handler.on_datetime(numeric_system::standard); break;
4178+ case 'x': handler.on_loc_date(numeric_system::standard); break;
4179+ case 'X': handler.on_loc_time(numeric_system::standard); break;
4180+ case 'D': handler.on_us_date(); break;
4181+ case 'F': handler.on_iso_date(); break;
4182+ case 'r': handler.on_12_hour_time(); break;
4183+ case 'R': handler.on_24_hour_time(); break;
4184+ case 'T': handler.on_iso_time(); break;
4185+ case 'p': handler.on_am_pm(); break;
4186+ case 'Q': handler.on_duration_value(); break;
4187+ case 'q': handler.on_duration_unit(); break;
4188+ case 'z': handler.on_utc_offset(numeric_system::standard); break;
4189+ case 'Z': handler.on_tz_name(); break;
4190 // Alternative representation:
4191 case 'E': {
4192 if (ptr == end) FMT_THROW(format_error("invalid format"));
4193 c = *ptr++;
4194 switch (c) {
4195- case 'Y':
4196- handler.on_year(numeric_system::alternative);
4197- break;
4198- case 'y':
4199- handler.on_offset_year();
4200- break;
4201- case 'C':
4202- handler.on_century(numeric_system::alternative);
4203- break;
4204- case 'c':
4205- handler.on_datetime(numeric_system::alternative);
4206- break;
4207- case 'x':
4208- handler.on_loc_date(numeric_system::alternative);
4209- break;
4210- case 'X':
4211- handler.on_loc_time(numeric_system::alternative);
4212- break;
4213- case 'z':
4214- handler.on_utc_offset(numeric_system::alternative);
4215- break;
4216- default:
4217- FMT_THROW(format_error("invalid format"));
4218+ case 'Y': handler.on_year(numeric_system::alternative, pad); break;
4219+ case 'y': handler.on_offset_year(); break;
4220+ case 'C': handler.on_century(numeric_system::alternative); break;
4221+ case 'c': handler.on_datetime(numeric_system::alternative); break;
4222+ case 'x': handler.on_loc_date(numeric_system::alternative); break;
4223+ case 'X': handler.on_loc_time(numeric_system::alternative); break;
4224+ case 'z': handler.on_utc_offset(numeric_system::alternative); break;
4225+ default: FMT_THROW(format_error("invalid format"));
4226 }
4227 break;
4228 }
4229@@ -859,54 +827,34 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
4230 if (ptr == end) FMT_THROW(format_error("invalid format"));
4231 c = *ptr++;
4232 switch (c) {
4233- case 'y':
4234- handler.on_short_year(numeric_system::alternative);
4235- break;
4236- case 'm':
4237- handler.on_dec_month(numeric_system::alternative);
4238- break;
4239+ case 'y': handler.on_short_year(numeric_system::alternative); break;
4240+ case 'm': handler.on_dec_month(numeric_system::alternative, pad); break;
4241 case 'U':
4242- handler.on_dec0_week_of_year(numeric_system::alternative);
4243+ handler.on_dec0_week_of_year(numeric_system::alternative, pad);
4244 break;
4245 case 'W':
4246- handler.on_dec1_week_of_year(numeric_system::alternative);
4247+ handler.on_dec1_week_of_year(numeric_system::alternative, pad);
4248 break;
4249 case 'V':
4250- handler.on_iso_week_of_year(numeric_system::alternative);
4251+ handler.on_iso_week_of_year(numeric_system::alternative, pad);
4252 break;
4253 case 'd':
4254- handler.on_day_of_month(numeric_system::alternative);
4255+ handler.on_day_of_month(numeric_system::alternative, pad);
4256 break;
4257 case 'e':
4258- handler.on_day_of_month_space(numeric_system::alternative);
4259- break;
4260- case 'w':
4261- handler.on_dec0_weekday(numeric_system::alternative);
4262- break;
4263- case 'u':
4264- handler.on_dec1_weekday(numeric_system::alternative);
4265- break;
4266- case 'H':
4267- handler.on_24_hour(numeric_system::alternative, pad);
4268+ handler.on_day_of_month(numeric_system::alternative, pad_type::space);
4269 break;
4270- case 'I':
4271- handler.on_12_hour(numeric_system::alternative, pad);
4272- break;
4273- case 'M':
4274- handler.on_minute(numeric_system::alternative, pad);
4275- break;
4276- case 'S':
4277- handler.on_second(numeric_system::alternative, pad);
4278- break;
4279- case 'z':
4280- handler.on_utc_offset(numeric_system::alternative);
4281- break;
4282- default:
4283- FMT_THROW(format_error("invalid format"));
4284+ case 'w': handler.on_dec0_weekday(numeric_system::alternative); break;
4285+ case 'u': handler.on_dec1_weekday(numeric_system::alternative); break;
4286+ case 'H': handler.on_24_hour(numeric_system::alternative, pad); break;
4287+ case 'I': handler.on_12_hour(numeric_system::alternative, pad); break;
4288+ case 'M': handler.on_minute(numeric_system::alternative, pad); break;
4289+ case 'S': handler.on_second(numeric_system::alternative, pad); break;
4290+ case 'z': handler.on_utc_offset(numeric_system::alternative); break;
4291+ default: FMT_THROW(format_error("invalid format"));
4292 }
4293 break;
4294- default:
4295- FMT_THROW(format_error("invalid format"));
4296+ default: FMT_THROW(format_error("invalid format"));
4297 }
4298 begin = ptr;
4299 }
4300@@ -918,7 +866,7 @@ template <typename Derived> struct null_chrono_spec_handler {
4301 FMT_CONSTEXPR void unsupported() {
4302 static_cast<Derived*>(this)->unsupported();
4303 }
4304- FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
4305+ FMT_CONSTEXPR void on_year(numeric_system, pad_type) { unsupported(); }
4306 FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
4307 FMT_CONSTEXPR void on_offset_year() { unsupported(); }
4308 FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
4309@@ -930,13 +878,20 @@ template <typename Derived> struct null_chrono_spec_handler {
4310 FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
4311 FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
4312 FMT_CONSTEXPR void on_full_month() { unsupported(); }
4313- FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
4314- FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
4315- FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
4316- FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
4317- FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
4318- FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
4319- FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
4320+ FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) { unsupported(); }
4321+ FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {
4322+ unsupported();
4323+ }
4324+ FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {
4325+ unsupported();
4326+ }
4327+ FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {
4328+ unsupported();
4329+ }
4330+ FMT_CONSTEXPR void on_day_of_year(pad_type) { unsupported(); }
4331+ FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {
4332+ unsupported();
4333+ }
4334 FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
4335 FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
4336 FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
4337@@ -957,11 +912,13 @@ template <typename Derived> struct null_chrono_spec_handler {
4338 };
4339
4340 struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
4341- FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
4342+ FMT_NORETURN inline void unsupported() {
4343+ FMT_THROW(format_error("no format"));
4344+ }
4345
4346 template <typename Char>
4347 FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
4348- FMT_CONSTEXPR void on_year(numeric_system) {}
4349+ FMT_CONSTEXPR void on_year(numeric_system, pad_type) {}
4350 FMT_CONSTEXPR void on_short_year(numeric_system) {}
4351 FMT_CONSTEXPR void on_offset_year() {}
4352 FMT_CONSTEXPR void on_century(numeric_system) {}
4353@@ -973,13 +930,12 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
4354 FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
4355 FMT_CONSTEXPR void on_abbr_month() {}
4356 FMT_CONSTEXPR void on_full_month() {}
4357- FMT_CONSTEXPR void on_dec_month(numeric_system) {}
4358- FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
4359- FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
4360- FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
4361- FMT_CONSTEXPR void on_day_of_year() {}
4362- FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
4363- FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
4364+ FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) {}
4365+ FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {}
4366+ FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {}
4367+ FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {}
4368+ FMT_CONSTEXPR void on_day_of_year(pad_type) {}
4369+ FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {}
4370 FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
4371 FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
4372 FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
4373@@ -997,25 +953,25 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
4374 FMT_CONSTEXPR void on_tz_name() {}
4375 };
4376
4377-inline const char* tm_wday_full_name(int wday) {
4378+inline auto tm_wday_full_name(int wday) -> const char* {
4379 static constexpr const char* full_name_list[] = {
4380 "Sunday", "Monday", "Tuesday", "Wednesday",
4381 "Thursday", "Friday", "Saturday"};
4382 return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
4383 }
4384-inline const char* tm_wday_short_name(int wday) {
4385+inline auto tm_wday_short_name(int wday) -> const char* {
4386 static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
4387 "Thu", "Fri", "Sat"};
4388 return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
4389 }
4390
4391-inline const char* tm_mon_full_name(int mon) {
4392+inline auto tm_mon_full_name(int mon) -> const char* {
4393 static constexpr const char* full_name_list[] = {
4394 "January", "February", "March", "April", "May", "June",
4395 "July", "August", "September", "October", "November", "December"};
4396 return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
4397 }
4398-inline const char* tm_mon_short_name(int mon) {
4399+inline auto tm_mon_short_name(int mon) -> const char* {
4400 static constexpr const char* short_name_list[] = {
4401 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
4402 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
4403@@ -1035,33 +991,33 @@ template <typename T>
4404 struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
4405 : std::true_type {};
4406
4407-#if FMT_USE_TZSET
4408 inline void tzset_once() {
4409- static bool init = []() -> bool {
4410+ static bool init = []() {
4411+ using namespace fmt_detail;
4412 _tzset();
4413- return true;
4414+ return false;
4415 }();
4416 ignore_unused(init);
4417 }
4418-#endif
4419
4420 // Converts value to Int and checks that it's in the range [0, upper).
4421 template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
4422-inline Int to_nonnegative_int(T value, Int upper) {
4423- FMT_ASSERT(std::is_unsigned<Int>::value ||
4424- (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
4425- "invalid value");
4426- (void)upper;
4427+inline auto to_nonnegative_int(T value, Int upper) -> Int {
4428+ if (!std::is_unsigned<Int>::value &&
4429+ (value < 0 || to_unsigned(value) > to_unsigned(upper))) {
4430+ FMT_THROW(fmt::format_error("chrono value is out of range"));
4431+ }
4432 return static_cast<Int>(value);
4433 }
4434 template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
4435-inline Int to_nonnegative_int(T value, Int upper) {
4436- if (value < 0 || value > static_cast<T>(upper))
4437+inline auto to_nonnegative_int(T value, Int upper) -> Int {
4438+ auto int_value = static_cast<Int>(value);
4439+ if (int_value < 0 || value > static_cast<T>(upper))
4440 FMT_THROW(format_error("invalid value"));
4441- return static_cast<Int>(value);
4442+ return int_value;
4443 }
4444
4445-constexpr long long pow10(std::uint32_t n) {
4446+constexpr auto pow10(std::uint32_t n) -> long long {
4447 return n == 0 ? 1 : 10 * pow10(n - 1);
4448 }
4449
4450@@ -1093,17 +1049,16 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
4451 using subsecond_precision = std::chrono::duration<
4452 typename std::common_type<typename Duration::rep,
4453 std::chrono::seconds::rep>::type,
4454- std::ratio<1, detail::pow10(num_fractional_digits)>>;
4455+ std::ratio<1, pow10(num_fractional_digits)>>;
4456
4457- const auto fractional =
4458- d - std::chrono::duration_cast<std::chrono::seconds>(d);
4459+ const auto fractional = d - detail::duration_cast<std::chrono::seconds>(d);
4460 const auto subseconds =
4461 std::chrono::treat_as_floating_point<
4462 typename subsecond_precision::rep>::value
4463 ? fractional.count()
4464- : std::chrono::duration_cast<subsecond_precision>(fractional).count();
4465+ : detail::duration_cast<subsecond_precision>(fractional).count();
4466 auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
4467- const int num_digits = detail::count_digits(n);
4468+ const int num_digits = count_digits(n);
4469
4470 int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits);
4471 if (precision < 0) {
4472@@ -1111,22 +1066,25 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
4473 if (std::ratio_less<typename subsecond_precision::period,
4474 std::chrono::seconds::period>::value) {
4475 *out++ = '.';
4476- out = std::fill_n(out, leading_zeroes, '0');
4477- out = format_decimal<Char>(out, n, num_digits).end;
4478+ out = detail::fill_n(out, leading_zeroes, '0');
4479+ out = format_decimal<Char>(out, n, num_digits);
4480 }
4481- } else {
4482+ } else if (precision > 0) {
4483 *out++ = '.';
4484- leading_zeroes = (std::min)(leading_zeroes, precision);
4485- out = std::fill_n(out, leading_zeroes, '0');
4486+ leading_zeroes = min_of(leading_zeroes, precision);
4487 int remaining = precision - leading_zeroes;
4488- if (remaining != 0 && remaining < num_digits) {
4489- n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining)));
4490- out = format_decimal<Char>(out, n, remaining).end;
4491+ out = detail::fill_n(out, leading_zeroes, '0');
4492+ if (remaining < num_digits) {
4493+ int num_truncated_digits = num_digits - remaining;
4494+ n /= to_unsigned(pow10(to_unsigned(num_truncated_digits)));
4495+ if (n != 0) out = format_decimal<Char>(out, n, remaining);
4496 return;
4497 }
4498- out = format_decimal<Char>(out, n, num_digits).end;
4499- remaining -= num_digits;
4500- out = std::fill_n(out, remaining, '0');
4501+ if (n != 0) {
4502+ out = format_decimal<Char>(out, n, num_digits);
4503+ remaining -= num_digits;
4504+ }
4505+ out = detail::fill_n(out, remaining, '0');
4506 }
4507 }
4508
4509@@ -1152,11 +1110,11 @@ void write_floating_seconds(memory_buffer& buf, Duration duration,
4510 num_fractional_digits = 6;
4511 }
4512
4513- format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
4514- std::fmod(val * static_cast<rep>(Duration::period::num) /
4515- static_cast<rep>(Duration::period::den),
4516- static_cast<rep>(60)),
4517- num_fractional_digits);
4518+ fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
4519+ std::fmod(val * static_cast<rep>(Duration::period::num) /
4520+ static_cast<rep>(Duration::period::den),
4521+ static_cast<rep>(60)),
4522+ num_fractional_digits);
4523 }
4524
4525 template <typename OutputIt, typename Char,
4526@@ -1217,8 +1175,7 @@ class tm_writer {
4527 return static_cast<int>(l);
4528 }
4529
4530- // Algorithm:
4531- // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
4532+ // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
4533 auto iso_year_weeks(long long curr_year) const noexcept -> int {
4534 const auto prev_year = curr_year - 1;
4535 const auto curr_p =
4536@@ -1268,29 +1225,28 @@ class tm_writer {
4537 }
4538 }
4539
4540- void write_year_extended(long long year) {
4541+ void write_year_extended(long long year, pad_type pad) {
4542 // At least 4 characters.
4543 int width = 4;
4544- if (year < 0) {
4545- *out_++ = '-';
4546+ bool negative = year < 0;
4547+ if (negative) {
4548 year = 0 - year;
4549 --width;
4550 }
4551 uint32_or_64_or_128_t<long long> n = to_unsigned(year);
4552 const int num_digits = count_digits(n);
4553- if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0');
4554- out_ = format_decimal<Char>(out_, n, num_digits).end;
4555- }
4556- void write_year(long long year) {
4557- if (year >= 0 && year < 10000) {
4558- write2(static_cast<int>(year / 100));
4559- write2(static_cast<int>(year % 100));
4560- } else {
4561- write_year_extended(year);
4562+ if (negative && pad == pad_type::zero) *out_++ = '-';
4563+ if (width > num_digits) {
4564+ out_ = detail::write_padding(out_, pad, width - num_digits);
4565 }
4566+ if (negative && pad != pad_type::zero) *out_++ = '-';
4567+ out_ = format_decimal<Char>(out_, n, num_digits);
4568+ }
4569+ void write_year(long long year, pad_type pad) {
4570+ write_year_extended(year, pad);
4571 }
4572
4573- void write_utc_offset(long offset, numeric_system ns) {
4574+ void write_utc_offset(long long offset, numeric_system ns) {
4575 if (offset < 0) {
4576 *out_++ = '-';
4577 offset = -offset;
4578@@ -1302,6 +1258,7 @@ class tm_writer {
4579 if (ns != numeric_system::standard) *out_++ = ':';
4580 write2(static_cast<int>(offset % 60));
4581 }
4582+
4583 template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
4584 void format_utc_offset_impl(const T& tm, numeric_system ns) {
4585 write_utc_offset(tm.tm_gmtoff, ns);
4586@@ -1309,9 +1266,7 @@ class tm_writer {
4587 template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
4588 void format_utc_offset_impl(const T& tm, numeric_system ns) {
4589 #if defined(_WIN32) && defined(_UCRT)
4590-# if FMT_USE_TZSET
4591 tzset_once();
4592-# endif
4593 long offset = 0;
4594 _get_timezone(&offset);
4595 if (tm.tm_isdst) {
4596@@ -1328,7 +1283,7 @@ class tm_writer {
4597 std::time_t gt = std::mktime(&gtm);
4598 std::tm ltm = gmtime(gt);
4599 std::time_t lt = std::mktime(&ltm);
4600- long offset = gt - lt;
4601+ long long offset = gt - lt;
4602 write_utc_offset(offset, ns);
4603 #endif
4604 }
4605@@ -1358,10 +1313,10 @@ class tm_writer {
4606 subsecs_(subsecs),
4607 tm_(tm) {}
4608
4609- OutputIt out() const { return out_; }
4610+ auto out() const -> OutputIt { return out_; }
4611
4612 FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
4613- out_ = copy_str<Char>(begin, end, out_);
4614+ out_ = copy<Char>(begin, end, out_);
4615 }
4616
4617 void on_abbr_weekday() {
4618@@ -1408,11 +1363,11 @@ class tm_writer {
4619 *out_++ = ' ';
4620 on_abbr_month();
4621 *out_++ = ' ';
4622- on_day_of_month_space(numeric_system::standard);
4623+ on_day_of_month(numeric_system::standard, pad_type::space);
4624 *out_++ = ' ';
4625 on_iso_time();
4626 *out_++ = ' ';
4627- on_year(numeric_system::standard);
4628+ on_year(numeric_system::standard, pad_type::space);
4629 } else {
4630 format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
4631 }
4632@@ -1434,31 +1389,31 @@ class tm_writer {
4633 write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
4634 to_unsigned(tm_mday()),
4635 to_unsigned(split_year_lower(tm_year())), '/');
4636- out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
4637+ out_ = copy<Char>(std::begin(buf), std::end(buf), out_);
4638 }
4639 void on_iso_date() {
4640 auto year = tm_year();
4641 char buf[10];
4642 size_t offset = 0;
4643 if (year >= 0 && year < 10000) {
4644- copy2(buf, digits2(static_cast<size_t>(year / 100)));
4645+ write2digits(buf, static_cast<size_t>(year / 100));
4646 } else {
4647 offset = 4;
4648- write_year_extended(year);
4649+ write_year_extended(year, pad_type::zero);
4650 year = 0;
4651 }
4652 write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
4653 to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
4654 '-');
4655- out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
4656+ out_ = copy<Char>(std::begin(buf) + offset, std::end(buf), out_);
4657 }
4658
4659 void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
4660 void on_tz_name() { format_tz_name_impl(tm_); }
4661
4662- void on_year(numeric_system ns) {
4663+ void on_year(numeric_system ns, pad_type pad) {
4664 if (is_classic_ || ns == numeric_system::standard)
4665- return write_year(tm_year());
4666+ return write_year(tm_year(), pad);
4667 format_localized('Y', 'E');
4668 }
4669 void on_short_year(numeric_system ns) {
4670@@ -1489,56 +1444,57 @@ class tm_writer {
4671 }
4672 }
4673
4674- void on_dec_month(numeric_system ns) {
4675+ void on_dec_month(numeric_system ns, pad_type pad) {
4676 if (is_classic_ || ns == numeric_system::standard)
4677- return write2(tm_mon() + 1);
4678+ return write2(tm_mon() + 1, pad);
4679 format_localized('m', 'O');
4680 }
4681
4682- void on_dec0_week_of_year(numeric_system ns) {
4683+ void on_dec0_week_of_year(numeric_system ns, pad_type pad) {
4684 if (is_classic_ || ns == numeric_system::standard)
4685- return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
4686+ return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week,
4687+ pad);
4688 format_localized('U', 'O');
4689 }
4690- void on_dec1_week_of_year(numeric_system ns) {
4691+ void on_dec1_week_of_year(numeric_system ns, pad_type pad) {
4692 if (is_classic_ || ns == numeric_system::standard) {
4693 auto wday = tm_wday();
4694 write2((tm_yday() + days_per_week -
4695 (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
4696- days_per_week);
4697+ days_per_week,
4698+ pad);
4699 } else {
4700 format_localized('W', 'O');
4701 }
4702 }
4703- void on_iso_week_of_year(numeric_system ns) {
4704+ void on_iso_week_of_year(numeric_system ns, pad_type pad) {
4705 if (is_classic_ || ns == numeric_system::standard)
4706- return write2(tm_iso_week_of_year());
4707+ return write2(tm_iso_week_of_year(), pad);
4708 format_localized('V', 'O');
4709 }
4710
4711- void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
4712+ void on_iso_week_based_year() {
4713+ write_year(tm_iso_week_year(), pad_type::zero);
4714+ }
4715 void on_iso_week_based_short_year() {
4716 write2(split_year_lower(tm_iso_week_year()));
4717 }
4718
4719- void on_day_of_year() {
4720+ void on_day_of_year(pad_type pad) {
4721 auto yday = tm_yday() + 1;
4722- write1(yday / 100);
4723- write2(yday % 100);
4724- }
4725- void on_day_of_month(numeric_system ns) {
4726- if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday());
4727- format_localized('d', 'O');
4728- }
4729- void on_day_of_month_space(numeric_system ns) {
4730- if (is_classic_ || ns == numeric_system::standard) {
4731- auto mday = to_unsigned(tm_mday()) % 100;
4732- const char* d2 = digits2(mday);
4733- *out_++ = mday < 10 ? ' ' : d2[0];
4734- *out_++ = d2[1];
4735+ auto digit1 = yday / 100;
4736+ if (digit1 != 0) {
4737+ write1(digit1);
4738 } else {
4739- format_localized('e', 'O');
4740+ out_ = detail::write_padding(out_, pad);
4741 }
4742+ write2(yday % 100, pad);
4743+ }
4744+
4745+ void on_day_of_month(numeric_system ns, pad_type pad) {
4746+ if (is_classic_ || ns == numeric_system::standard)
4747+ return write2(tm_mday(), pad);
4748+ format_localized('d', 'O');
4749 }
4750
4751 void on_24_hour(numeric_system ns, pad_type pad) {
4752@@ -1566,7 +1522,7 @@ class tm_writer {
4753 write_floating_seconds(buf, *subsecs_);
4754 if (buf.size() > 1) {
4755 // Remove the leading "0", write something like ".123".
4756- out_ = std::copy(buf.begin() + 1, buf.end(), out_);
4757+ out_ = copy<Char>(buf.begin() + 1, buf.end(), out_);
4758 }
4759 } else {
4760 write_fractional_seconds<Char>(out_, *subsecs_);
4761@@ -1583,7 +1539,7 @@ class tm_writer {
4762 char buf[8];
4763 write_digit2_separated(buf, to_unsigned(tm_hour12()),
4764 to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
4765- out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
4766+ out_ = copy<Char>(std::begin(buf), std::end(buf), out_);
4767 *out_++ = ' ';
4768 on_am_pm();
4769 } else {
4770@@ -1598,7 +1554,7 @@ class tm_writer {
4771 void on_iso_time() {
4772 on_24_hour_time();
4773 *out_++ = ':';
4774- on_second(numeric_system::standard, pad_type::unspecified);
4775+ on_second(numeric_system::standard, pad_type::zero);
4776 }
4777
4778 void on_am_pm() {
4779@@ -1618,10 +1574,11 @@ class tm_writer {
4780 struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
4781 bool has_precision_integral = false;
4782
4783- FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
4784+ FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); }
4785
4786 template <typename Char>
4787 FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
4788+ FMT_CONSTEXPR void on_day_of_year(pad_type) {}
4789 FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
4790 FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
4791 FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
4792@@ -1631,25 +1588,24 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
4793 FMT_CONSTEXPR void on_iso_time() {}
4794 FMT_CONSTEXPR void on_am_pm() {}
4795 FMT_CONSTEXPR void on_duration_value() const {
4796- if (has_precision_integral) {
4797+ if (has_precision_integral)
4798 FMT_THROW(format_error("precision not allowed for this argument type"));
4799- }
4800 }
4801 FMT_CONSTEXPR void on_duration_unit() {}
4802 };
4803
4804 template <typename T,
4805 FMT_ENABLE_IF(std::is_integral<T>::value&& has_isfinite<T>::value)>
4806-inline bool isfinite(T) {
4807+inline auto isfinite(T) -> bool {
4808 return true;
4809 }
4810
4811 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
4812-inline T mod(T x, int y) {
4813+inline auto mod(T x, int y) -> T {
4814 return x % static_cast<T>(y);
4815 }
4816 template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
4817-inline T mod(T x, int y) {
4818+inline auto mod(T x, int y) -> T {
4819 return std::fmod(x, static_cast<T>(y));
4820 }
4821
4822@@ -1664,71 +1620,60 @@ template <typename T> struct make_unsigned_or_unchanged<T, true> {
4823 using type = typename std::make_unsigned<T>::type;
4824 };
4825
4826-#if FMT_SAFE_DURATION_CAST
4827-// throwing version of safe_duration_cast
4828-template <typename To, typename FromRep, typename FromPeriod>
4829-To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
4830- int ec;
4831- To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
4832- if (ec) FMT_THROW(format_error("cannot format duration"));
4833- return to;
4834-}
4835-#endif
4836-
4837 template <typename Rep, typename Period,
4838 FMT_ENABLE_IF(std::is_integral<Rep>::value)>
4839-inline std::chrono::duration<Rep, std::milli> get_milliseconds(
4840- std::chrono::duration<Rep, Period> d) {
4841+inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
4842+ -> std::chrono::duration<Rep, std::milli> {
4843 // this may overflow and/or the result may not fit in the
4844 // target type.
4845 #if FMT_SAFE_DURATION_CAST
4846 using CommonSecondsType =
4847 typename std::common_type<decltype(d), std::chrono::seconds>::type;
4848- const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
4849+ const auto d_as_common = detail::duration_cast<CommonSecondsType>(d);
4850 const auto d_as_whole_seconds =
4851- fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
4852+ detail::duration_cast<std::chrono::seconds>(d_as_common);
4853 // this conversion should be nonproblematic
4854 const auto diff = d_as_common - d_as_whole_seconds;
4855 const auto ms =
4856- fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
4857+ detail::duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
4858 return ms;
4859 #else
4860- auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
4861- return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
4862+ auto s = detail::duration_cast<std::chrono::seconds>(d);
4863+ return detail::duration_cast<std::chrono::milliseconds>(d - s);
4864 #endif
4865 }
4866
4867 template <typename Char, typename Rep, typename OutputIt,
4868 FMT_ENABLE_IF(std::is_integral<Rep>::value)>
4869-OutputIt format_duration_value(OutputIt out, Rep val, int) {
4870+auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt {
4871 return write<Char>(out, val);
4872 }
4873
4874 template <typename Char, typename Rep, typename OutputIt,
4875 FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
4876-OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
4877- auto specs = format_specs<Char>();
4878+auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt {
4879+ auto specs = format_specs();
4880 specs.precision = precision;
4881- specs.type = precision >= 0 ? presentation_type::fixed_lower
4882- : presentation_type::general_lower;
4883+ specs.set_type(precision >= 0 ? presentation_type::fixed
4884+ : presentation_type::general);
4885 return write<Char>(out, val, specs);
4886 }
4887
4888 template <typename Char, typename OutputIt>
4889-OutputIt copy_unit(string_view unit, OutputIt out, Char) {
4890- return std::copy(unit.begin(), unit.end(), out);
4891+auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt {
4892+ return copy<Char>(unit.begin(), unit.end(), out);
4893 }
4894
4895 template <typename OutputIt>
4896-OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
4897+auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt {
4898 // This works when wchar_t is UTF-32 because units only contain characters
4899 // that have the same representation in UTF-16 and UTF-32.
4900 utf8_to_utf16 u(unit);
4901- return std::copy(u.c_str(), u.c_str() + u.size(), out);
4902+ return copy<wchar_t>(u.c_str(), u.c_str() + u.size(), out);
4903 }
4904
4905 template <typename Char, typename Period, typename OutputIt>
4906-OutputIt format_duration_unit(OutputIt out) {
4907+auto format_duration_unit(OutputIt out) -> OutputIt {
4908 if (const char* unit = get_units<Period>())
4909 return copy_unit(string_view(unit), out, Char());
4910 *out++ = '[';
4911@@ -1750,14 +1695,14 @@ class get_locale {
4912 bool has_locale_ = false;
4913
4914 public:
4915- get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
4916+ inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
4917 if (localized)
4918 ::new (&locale_) std::locale(loc.template get<std::locale>());
4919 }
4920- ~get_locale() {
4921+ inline ~get_locale() {
4922 if (has_locale_) locale_.~locale();
4923 }
4924- operator const std::locale&() const {
4925+ inline operator const std::locale&() const {
4926 return has_locale_ ? locale_ : get_classic_locale();
4927 }
4928 };
4929@@ -1795,18 +1740,12 @@ struct chrono_formatter {
4930
4931 // this may overflow and/or the result may not fit in the
4932 // target type.
4933-#if FMT_SAFE_DURATION_CAST
4934 // might need checked conversion (rep!=Rep)
4935- auto tmpval = std::chrono::duration<rep, Period>(val);
4936- s = fmt_safe_duration_cast<seconds>(tmpval);
4937-#else
4938- s = std::chrono::duration_cast<seconds>(
4939- std::chrono::duration<rep, Period>(val));
4940-#endif
4941+ s = detail::duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
4942 }
4943
4944 // returns true if nan or inf, writes to out.
4945- bool handle_nan_inf() {
4946+ auto handle_nan_inf() -> bool {
4947 if (isfinite(val)) {
4948 return false;
4949 }
4950@@ -1823,17 +1762,22 @@ struct chrono_formatter {
4951 return true;
4952 }
4953
4954- Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
4955+ auto days() const -> Rep { return static_cast<Rep>(s.count() / 86400); }
4956+ auto hour() const -> Rep {
4957+ return static_cast<Rep>(mod((s.count() / 3600), 24));
4958+ }
4959
4960- Rep hour12() const {
4961+ auto hour12() const -> Rep {
4962 Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
4963 return hour <= 0 ? 12 : hour;
4964 }
4965
4966- Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
4967- Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
4968+ auto minute() const -> Rep {
4969+ return static_cast<Rep>(mod((s.count() / 60), 60));
4970+ }
4971+ auto second() const -> Rep { return static_cast<Rep>(mod(s.count(), 60)); }
4972
4973- std::tm time() const {
4974+ auto time() const -> std::tm {
4975 auto time = std::tm();
4976 time.tm_hour = to_nonnegative_int(hour(), 24);
4977 time.tm_min = to_nonnegative_int(minute(), 60);
4978@@ -1848,7 +1792,7 @@ struct chrono_formatter {
4979 }
4980 }
4981
4982- void write(Rep value, int width, pad_type pad = pad_type::unspecified) {
4983+ void write(Rep value, int width, pad_type pad = pad_type::zero) {
4984 write_sign();
4985 if (isnan(value)) return write_nan();
4986 uint32_or_64_or_128_t<int> n =
4987@@ -1857,7 +1801,7 @@ struct chrono_formatter {
4988 if (width > num_digits) {
4989 out = detail::write_padding(out, pad, width - num_digits);
4990 }
4991- out = format_decimal<char_type>(out, n, num_digits).end;
4992+ out = format_decimal<char_type>(out, n, num_digits);
4993 }
4994
4995 void write_nan() { std::copy_n("nan", 3, out); }
4996@@ -1874,7 +1818,7 @@ struct chrono_formatter {
4997 }
4998
4999 void on_text(const char_type* begin, const char_type* end) {
5000- std::copy(begin, end, out);
5001+ copy<char_type>(begin, end, out);
5002 }
5003
5004 // These are not implemented because durations don't have date information.
5005@@ -1891,19 +1835,22 @@ struct chrono_formatter {
5006 void on_iso_date() {}
5007 void on_utc_offset(numeric_system) {}
5008 void on_tz_name() {}
5009- void on_year(numeric_system) {}
5010+ void on_year(numeric_system, pad_type) {}
5011 void on_short_year(numeric_system) {}
5012 void on_offset_year() {}
5013 void on_century(numeric_system) {}
5014 void on_iso_week_based_year() {}
5015 void on_iso_week_based_short_year() {}
5016- void on_dec_month(numeric_system) {}
5017- void on_dec0_week_of_year(numeric_system) {}
5018- void on_dec1_week_of_year(numeric_system) {}
5019- void on_iso_week_of_year(numeric_system) {}
5020- void on_day_of_year() {}
5021- void on_day_of_month(numeric_system) {}
5022- void on_day_of_month_space(numeric_system) {}
5023+ void on_dec_month(numeric_system, pad_type) {}
5024+ void on_dec0_week_of_year(numeric_system, pad_type) {}
5025+ void on_dec1_week_of_year(numeric_system, pad_type) {}
5026+ void on_iso_week_of_year(numeric_system, pad_type) {}
5027+ void on_day_of_month(numeric_system, pad_type) {}
5028+
5029+ void on_day_of_year(pad_type) {
5030+ if (handle_nan_inf()) return;
5031+ write(days(), 0);
5032+ }
5033
5034 void on_24_hour(numeric_system ns, pad_type pad) {
5035 if (handle_nan_inf()) return;
5036@@ -1944,7 +1891,7 @@ struct chrono_formatter {
5037 if (buf.size() < 2 || buf[1] == '.') {
5038 out = detail::write_padding(out, pad);
5039 }
5040- out = std::copy(buf.begin(), buf.end(), out);
5041+ out = copy<char_type>(buf.begin(), buf.end(), out);
5042 } else {
5043 write(second(), 2, pad);
5044 write_fractional_seconds<char_type>(
5045@@ -1978,7 +1925,7 @@ struct chrono_formatter {
5046 on_24_hour_time();
5047 *out++ = ':';
5048 if (handle_nan_inf()) return;
5049- on_second(numeric_system::standard, pad_type::unspecified);
5050+ on_second(numeric_system::standard, pad_type::zero);
5051 }
5052
5053 void on_am_pm() {
5054@@ -1997,268 +1944,391 @@ struct chrono_formatter {
5055 }
5056 };
5057
5058-FMT_END_DETAIL_NAMESPACE
5059+} // namespace detail
5060
5061 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
5062 using weekday = std::chrono::weekday;
5063+using day = std::chrono::day;
5064+using month = std::chrono::month;
5065+using year = std::chrono::year;
5066+using year_month_day = std::chrono::year_month_day;
5067 #else
5068 // A fallback version of weekday.
5069 class weekday {
5070 private:
5071- unsigned char value;
5072+ unsigned char value_;
5073
5074 public:
5075 weekday() = default;
5076- explicit constexpr weekday(unsigned wd) noexcept
5077- : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
5078- constexpr unsigned c_encoding() const noexcept { return value; }
5079+ constexpr explicit weekday(unsigned wd) noexcept
5080+ : value_(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
5081+ constexpr auto c_encoding() const noexcept -> unsigned { return value_; }
5082+};
5083+
5084+class day {
5085+ private:
5086+ unsigned char value_;
5087+
5088+ public:
5089+ day() = default;
5090+ constexpr explicit day(unsigned d) noexcept
5091+ : value_(static_cast<unsigned char>(d)) {}
5092+ constexpr explicit operator unsigned() const noexcept { return value_; }
5093+};
5094+
5095+class month {
5096+ private:
5097+ unsigned char value_;
5098+
5099+ public:
5100+ month() = default;
5101+ constexpr explicit month(unsigned m) noexcept
5102+ : value_(static_cast<unsigned char>(m)) {}
5103+ constexpr explicit operator unsigned() const noexcept { return value_; }
5104+};
5105+
5106+class year {
5107+ private:
5108+ int value_;
5109+
5110+ public:
5111+ year() = default;
5112+ constexpr explicit year(int y) noexcept : value_(y) {}
5113+ constexpr explicit operator int() const noexcept { return value_; }
5114 };
5115
5116-class year_month_day {};
5117+class year_month_day {
5118+ private:
5119+ fmt::year year_;
5120+ fmt::month month_;
5121+ fmt::day day_;
5122+
5123+ public:
5124+ year_month_day() = default;
5125+ constexpr year_month_day(const year& y, const month& m, const day& d) noexcept
5126+ : year_(y), month_(m), day_(d) {}
5127+ constexpr auto year() const noexcept -> fmt::year { return year_; }
5128+ constexpr auto month() const noexcept -> fmt::month { return month_; }
5129+ constexpr auto day() const noexcept -> fmt::day { return day_; }
5130+};
5131 #endif
5132
5133-// A rudimentary weekday formatter.
5134-template <typename Char> struct formatter<weekday, Char> {
5135+template <typename Char>
5136+struct formatter<weekday, Char> : private formatter<std::tm, Char> {
5137 private:
5138- bool localized = false;
5139+ bool localized_ = false;
5140+ bool use_tm_formatter_ = false;
5141
5142 public:
5143- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
5144- -> decltype(ctx.begin()) {
5145- auto begin = ctx.begin(), end = ctx.end();
5146- if (begin != end && *begin == 'L') {
5147- ++begin;
5148- localized = true;
5149+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
5150+ auto it = ctx.begin(), end = ctx.end();
5151+ if (it != end && *it == 'L') {
5152+ ++it;
5153+ localized_ = true;
5154+ return it;
5155 }
5156- return begin;
5157+ use_tm_formatter_ = it != end && *it != '}';
5158+ return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
5159 }
5160
5161 template <typename FormatContext>
5162 auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
5163 auto time = std::tm();
5164 time.tm_wday = static_cast<int>(wd.c_encoding());
5165- detail::get_locale loc(localized, ctx.locale());
5166+ if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
5167+ detail::get_locale loc(localized_, ctx.locale());
5168 auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
5169 w.on_abbr_weekday();
5170 return w.out();
5171 }
5172 };
5173
5174+template <typename Char>
5175+struct formatter<day, Char> : private formatter<std::tm, Char> {
5176+ private:
5177+ bool use_tm_formatter_ = false;
5178+
5179+ public:
5180+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
5181+ auto it = ctx.begin(), end = ctx.end();
5182+ use_tm_formatter_ = it != end && *it != '}';
5183+ return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
5184+ }
5185+
5186+ template <typename FormatContext>
5187+ auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) {
5188+ auto time = std::tm();
5189+ time.tm_mday = static_cast<int>(static_cast<unsigned>(d));
5190+ if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
5191+ detail::get_locale loc(false, ctx.locale());
5192+ auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
5193+ w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero);
5194+ return w.out();
5195+ }
5196+};
5197+
5198+template <typename Char>
5199+struct formatter<month, Char> : private formatter<std::tm, Char> {
5200+ private:
5201+ bool localized_ = false;
5202+ bool use_tm_formatter_ = false;
5203+
5204+ public:
5205+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
5206+ auto it = ctx.begin(), end = ctx.end();
5207+ if (it != end && *it == 'L') {
5208+ ++it;
5209+ localized_ = true;
5210+ return it;
5211+ }
5212+ use_tm_formatter_ = it != end && *it != '}';
5213+ return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
5214+ }
5215+
5216+ template <typename FormatContext>
5217+ auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) {
5218+ auto time = std::tm();
5219+ time.tm_mon = static_cast<int>(static_cast<unsigned>(m)) - 1;
5220+ if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
5221+ detail::get_locale loc(localized_, ctx.locale());
5222+ auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
5223+ w.on_abbr_month();
5224+ return w.out();
5225+ }
5226+};
5227+
5228+template <typename Char>
5229+struct formatter<year, Char> : private formatter<std::tm, Char> {
5230+ private:
5231+ bool use_tm_formatter_ = false;
5232+
5233+ public:
5234+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
5235+ auto it = ctx.begin(), end = ctx.end();
5236+ use_tm_formatter_ = it != end && *it != '}';
5237+ return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
5238+ }
5239+
5240+ template <typename FormatContext>
5241+ auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) {
5242+ auto time = std::tm();
5243+ time.tm_year = static_cast<int>(y) - 1900;
5244+ if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
5245+ detail::get_locale loc(false, ctx.locale());
5246+ auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
5247+ w.on_year(detail::numeric_system::standard, detail::pad_type::zero);
5248+ return w.out();
5249+ }
5250+};
5251+
5252+template <typename Char>
5253+struct formatter<year_month_day, Char> : private formatter<std::tm, Char> {
5254+ private:
5255+ bool use_tm_formatter_ = false;
5256+
5257+ public:
5258+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
5259+ auto it = ctx.begin(), end = ctx.end();
5260+ use_tm_formatter_ = it != end && *it != '}';
5261+ return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
5262+ }
5263+
5264+ template <typename FormatContext>
5265+ auto format(year_month_day val, FormatContext& ctx) const
5266+ -> decltype(ctx.out()) {
5267+ auto time = std::tm();
5268+ time.tm_year = static_cast<int>(val.year()) - 1900;
5269+ time.tm_mon = static_cast<int>(static_cast<unsigned>(val.month())) - 1;
5270+ time.tm_mday = static_cast<int>(static_cast<unsigned>(val.day()));
5271+ if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
5272+ detail::get_locale loc(true, ctx.locale());
5273+ auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
5274+ w.on_iso_date();
5275+ return w.out();
5276+ }
5277+};
5278+
5279 template <typename Rep, typename Period, typename Char>
5280 struct formatter<std::chrono::duration<Rep, Period>, Char> {
5281 private:
5282- format_specs<Char> specs;
5283- int precision = -1;
5284- using arg_ref_type = detail::arg_ref<Char>;
5285- arg_ref_type width_ref;
5286- arg_ref_type precision_ref;
5287- bool localized = false;
5288- basic_string_view<Char> format_str;
5289- using duration = std::chrono::duration<Rep, Period>;
5290+ format_specs specs_;
5291+ detail::arg_ref<Char> width_ref_;
5292+ detail::arg_ref<Char> precision_ref_;
5293+ bool localized_ = false;
5294+ basic_string_view<Char> fmt_;
5295
5296- using iterator = typename basic_format_parse_context<Char>::iterator;
5297- struct parse_range {
5298- iterator begin;
5299- iterator end;
5300- };
5301-
5302- FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
5303- auto begin = ctx.begin(), end = ctx.end();
5304- if (begin == end || *begin == '}') return {begin, begin};
5305+ public:
5306+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
5307+ auto it = ctx.begin(), end = ctx.end();
5308+ if (it == end || *it == '}') return it;
5309
5310- begin = detail::parse_align(begin, end, specs);
5311- if (begin == end) return {begin, begin};
5312+ it = detail::parse_align(it, end, specs_);
5313+ if (it == end) return it;
5314
5315- begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
5316- if (begin == end) return {begin, begin};
5317+ Char c = *it;
5318+ if ((c >= '0' && c <= '9') || c == '{') {
5319+ it = detail::parse_width(it, end, specs_, width_ref_, ctx);
5320+ if (it == end) return it;
5321+ }
5322
5323 auto checker = detail::chrono_format_checker();
5324- if (*begin == '.') {
5325+ if (*it == '.') {
5326 checker.has_precision_integral = !std::is_floating_point<Rep>::value;
5327- begin =
5328- detail::parse_precision(begin, end, precision, precision_ref, ctx);
5329+ it = detail::parse_precision(it, end, specs_, precision_ref_, ctx);
5330 }
5331- if (begin != end && *begin == 'L') {
5332- ++begin;
5333- localized = true;
5334+ if (it != end && *it == 'L') {
5335+ localized_ = true;
5336+ ++it;
5337 }
5338- end = detail::parse_chrono_format(begin, end, checker);
5339- return {begin, end};
5340- }
5341-
5342- public:
5343- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
5344- -> decltype(ctx.begin()) {
5345- auto range = do_parse(ctx);
5346- format_str = basic_string_view<Char>(
5347- &*range.begin, detail::to_unsigned(range.end - range.begin));
5348- return range.end;
5349+ end = detail::parse_chrono_format(it, end, checker);
5350+ fmt_ = {it, detail::to_unsigned(end - it)};
5351+ return end;
5352 }
5353
5354 template <typename FormatContext>
5355- auto format(const duration& d, FormatContext& ctx) const
5356+ auto format(std::chrono::duration<Rep, Period> d, FormatContext& ctx) const
5357 -> decltype(ctx.out()) {
5358- auto specs_copy = specs;
5359- auto precision_copy = precision;
5360- auto begin = format_str.begin(), end = format_str.end();
5361+ auto specs = specs_;
5362+ auto precision = specs.precision;
5363+ specs.precision = -1;
5364+ auto begin = fmt_.begin(), end = fmt_.end();
5365 // As a possible future optimization, we could avoid extra copying if width
5366 // is not specified.
5367- basic_memory_buffer<Char> buf;
5368- auto out = std::back_inserter(buf);
5369- detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
5370- width_ref, ctx);
5371- detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
5372- precision_ref, ctx);
5373+ auto buf = basic_memory_buffer<Char>();
5374+ auto out = basic_appender<Char>(buf);
5375+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
5376+ ctx);
5377+ detail::handle_dynamic_spec(specs.dynamic_precision(), precision,
5378+ precision_ref_, ctx);
5379 if (begin == end || *begin == '}') {
5380- out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
5381+ out = detail::format_duration_value<Char>(out, d.count(), precision);
5382 detail::format_duration_unit<Char, Period>(out);
5383 } else {
5384- detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
5385- ctx, out, d);
5386- f.precision = precision_copy;
5387- f.localized = localized;
5388+ using chrono_formatter =
5389+ detail::chrono_formatter<FormatContext, decltype(out), Rep, Period>;
5390+ auto f = chrono_formatter(ctx, out, d);
5391+ f.precision = precision;
5392+ f.localized = localized_;
5393 detail::parse_chrono_format(begin, end, f);
5394 }
5395 return detail::write(
5396- ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
5397+ ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
5398 }
5399 };
5400
5401-template <typename Char, typename Duration>
5402-struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
5403- Char> : formatter<std::tm, Char> {
5404- FMT_CONSTEXPR formatter() {
5405- this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
5406+template <typename Char> struct formatter<std::tm, Char> {
5407+ private:
5408+ format_specs specs_;
5409+ detail::arg_ref<Char> width_ref_;
5410+
5411+ protected:
5412+ basic_string_view<Char> fmt_;
5413+
5414+ template <typename Duration, typename FormatContext>
5415+ auto do_format(const std::tm& tm, FormatContext& ctx,
5416+ const Duration* subsecs) const -> decltype(ctx.out()) {
5417+ auto specs = specs_;
5418+ auto buf = basic_memory_buffer<Char>();
5419+ auto out = basic_appender<Char>(buf);
5420+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
5421+ ctx);
5422+
5423+ auto loc_ref = ctx.locale();
5424+ detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
5425+ auto w =
5426+ detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
5427+ detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
5428+ return detail::write(
5429+ ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
5430 }
5431
5432- template <typename FormatContext>
5433- auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
5434- FormatContext& ctx) const -> decltype(ctx.out()) {
5435- using period = typename Duration::period;
5436- if (period::num != 1 || period::den != 1 ||
5437- std::is_floating_point<typename Duration::rep>::value) {
5438- const auto epoch = val.time_since_epoch();
5439- auto subsecs = std::chrono::duration_cast<Duration>(
5440- epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
5441-
5442- if (subsecs.count() < 0) {
5443- auto second = std::chrono::duration_cast<Duration>(
5444- std::chrono::seconds(1));
5445- if (epoch.count() < ((Duration::min)() + second).count())
5446- FMT_THROW(format_error("duration is too small"));
5447- subsecs += second;
5448- val -= second;
5449- }
5450+ public:
5451+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
5452+ auto it = ctx.begin(), end = ctx.end();
5453+ if (it == end || *it == '}') return it;
5454
5455- return formatter<std::tm, Char>::do_format(
5456- gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
5457- &subsecs);
5458+ it = detail::parse_align(it, end, specs_);
5459+ if (it == end) return it;
5460+
5461+ Char c = *it;
5462+ if ((c >= '0' && c <= '9') || c == '{') {
5463+ it = detail::parse_width(it, end, specs_, width_ref_, ctx);
5464+ if (it == end) return it;
5465 }
5466
5467- return formatter<std::tm, Char>::format(
5468- gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
5469+ end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
5470+ // Replace the default format string only if the new spec is not empty.
5471+ if (end != it) fmt_ = {it, detail::to_unsigned(end - it)};
5472+ return end;
5473+ }
5474+
5475+ template <typename FormatContext>
5476+ auto format(const std::tm& tm, FormatContext& ctx) const
5477+ -> decltype(ctx.out()) {
5478+ return do_format<std::chrono::seconds>(tm, ctx, nullptr);
5479 }
5480 };
5481
5482-#if FMT_USE_LOCAL_TIME
5483 template <typename Char, typename Duration>
5484-struct formatter<std::chrono::local_time<Duration>, Char>
5485- : formatter<std::tm, Char> {
5486+struct formatter<sys_time<Duration>, Char> : formatter<std::tm, Char> {
5487 FMT_CONSTEXPR formatter() {
5488- this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
5489+ this->fmt_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
5490 }
5491
5492 template <typename FormatContext>
5493- auto format(std::chrono::local_time<Duration> val, FormatContext& ctx) const
5494+ auto format(sys_time<Duration> val, FormatContext& ctx) const
5495 -> decltype(ctx.out()) {
5496+ std::tm tm = gmtime(val);
5497 using period = typename Duration::period;
5498- if (period::num != 1 || period::den != 1 ||
5499- std::is_floating_point<typename Duration::rep>::value) {
5500- const auto epoch = val.time_since_epoch();
5501- const auto subsecs = std::chrono::duration_cast<Duration>(
5502- epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
5503-
5504- return formatter<std::tm, Char>::do_format(
5505- localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
5506- ctx, &subsecs);
5507+ if (detail::const_check(
5508+ period::num == 1 && period::den == 1 &&
5509+ !std::is_floating_point<typename Duration::rep>::value)) {
5510+ return formatter<std::tm, Char>::format(tm, ctx);
5511 }
5512-
5513- return formatter<std::tm, Char>::format(
5514- localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
5515- ctx);
5516+ Duration epoch = val.time_since_epoch();
5517+ Duration subsecs = detail::duration_cast<Duration>(
5518+ epoch - detail::duration_cast<std::chrono::seconds>(epoch));
5519+ if (subsecs.count() < 0) {
5520+ auto second = detail::duration_cast<Duration>(std::chrono::seconds(1));
5521+ if (tm.tm_sec != 0)
5522+ --tm.tm_sec;
5523+ else
5524+ tm = gmtime(val - second);
5525+ subsecs += detail::duration_cast<Duration>(std::chrono::seconds(1));
5526+ }
5527+ return formatter<std::tm, Char>::do_format(tm, ctx, &subsecs);
5528 }
5529 };
5530-#endif
5531
5532-#if FMT_USE_UTC_TIME
5533-template <typename Char, typename Duration>
5534-struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
5535- Char>
5536- : formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
5537- Char> {
5538+template <typename Duration, typename Char>
5539+struct formatter<utc_time<Duration>, Char>
5540+ : formatter<sys_time<Duration>, Char> {
5541 template <typename FormatContext>
5542- auto format(std::chrono::time_point<std::chrono::utc_clock, Duration> val,
5543- FormatContext& ctx) const -> decltype(ctx.out()) {
5544- return formatter<
5545- std::chrono::time_point<std::chrono::system_clock, Duration>,
5546- Char>::format(std::chrono::utc_clock::to_sys(val), ctx);
5547+ auto format(utc_time<Duration> val, FormatContext& ctx) const
5548+ -> decltype(ctx.out()) {
5549+ return formatter<sys_time<Duration>, Char>::format(
5550+ detail::utc_clock::to_sys(val), ctx);
5551 }
5552 };
5553-#endif
5554-
5555-template <typename Char> struct formatter<std::tm, Char> {
5556- private:
5557- format_specs<Char> specs;
5558- detail::arg_ref<Char> width_ref;
5559-
5560- protected:
5561- basic_string_view<Char> format_str;
5562-
5563- FMT_CONSTEXPR auto do_parse(basic_format_parse_context<Char>& ctx)
5564- -> decltype(ctx.begin()) {
5565- auto begin = ctx.begin(), end = ctx.end();
5566- if (begin == end || *begin == '}') return begin;
5567-
5568- begin = detail::parse_align(begin, end, specs);
5569- if (begin == end) return end;
5570-
5571- begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
5572- if (begin == end) return end;
5573-
5574- end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
5575- // Replace default format_str only if the new spec is not empty.
5576- if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)};
5577- return end;
5578- }
5579-
5580- template <typename FormatContext, typename Duration>
5581- auto do_format(const std::tm& tm, FormatContext& ctx,
5582- const Duration* subsecs) const -> decltype(ctx.out()) {
5583- auto specs_copy = specs;
5584- basic_memory_buffer<Char> buf;
5585- auto out = std::back_inserter(buf);
5586- detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
5587- width_ref, ctx);
5588
5589- const auto loc_ref = ctx.locale();
5590- detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
5591- auto w =
5592- detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
5593- detail::parse_chrono_format(format_str.begin(), format_str.end(), w);
5594- return detail::write(
5595- ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
5596- }
5597-
5598- public:
5599- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
5600- -> decltype(ctx.begin()) {
5601- return this->do_parse(ctx);
5602+template <typename Duration, typename Char>
5603+struct formatter<local_time<Duration>, Char> : formatter<std::tm, Char> {
5604+ FMT_CONSTEXPR formatter() {
5605+ this->fmt_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
5606 }
5607
5608 template <typename FormatContext>
5609- auto format(const std::tm& tm, FormatContext& ctx) const
5610+ auto format(local_time<Duration> val, FormatContext& ctx) const
5611 -> decltype(ctx.out()) {
5612- return do_format<FormatContext, std::chrono::seconds>(tm, ctx, nullptr);
5613+ using period = typename Duration::period;
5614+ if (period::num == 1 && period::den == 1 &&
5615+ !std::is_floating_point<typename Duration::rep>::value) {
5616+ return formatter<std::tm, Char>::format(localtime(val), ctx);
5617+ }
5618+ auto epoch = val.time_since_epoch();
5619+ auto subsecs = detail::duration_cast<Duration>(
5620+ epoch - detail::duration_cast<std::chrono::seconds>(epoch));
5621+ return formatter<std::tm, Char>::do_format(localtime(val), ctx, &subsecs);
5622 }
5623 };
5624
5625diff --git a/include/fmt/color.h b/include/fmt/color.h
5626index d175448..638f15b 100644
5627--- a/include/fmt/color.h
5628+++ b/include/fmt/color.h
5629@@ -190,11 +190,11 @@ enum class emphasis : uint8_t {
5630 // rgb is a struct for red, green and blue colors.
5631 // Using the name "rgb" makes some editors show the color in a tooltip.
5632 struct rgb {
5633- FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
5634- FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
5635- FMT_CONSTEXPR rgb(uint32_t hex)
5636+ constexpr rgb() : r(0), g(0), b(0) {}
5637+ constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
5638+ constexpr rgb(uint32_t hex)
5639 : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
5640- FMT_CONSTEXPR rgb(color hex)
5641+ constexpr rgb(color hex)
5642 : r((uint32_t(hex) >> 16) & 0xFF),
5643 g((uint32_t(hex) >> 8) & 0xFF),
5644 b(uint32_t(hex) & 0xFF) {}
5645@@ -203,136 +203,174 @@ struct rgb {
5646 uint8_t b;
5647 };
5648
5649-FMT_BEGIN_DETAIL_NAMESPACE
5650+namespace detail {
5651
5652-// color is a struct of either a rgb color or a terminal color.
5653+// A bit-packed variant of an RGB color, a terminal color, or unset color.
5654+// see text_style for the bit-packing scheme.
5655 struct color_type {
5656- FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
5657- FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
5658- value.rgb_color = static_cast<uint32_t>(rgb_color);
5659+ constexpr color_type() noexcept = default;
5660+ constexpr color_type(color rgb_color) noexcept
5661+ : value_(static_cast<uint32_t>(rgb_color) | (1 << 24)) {}
5662+ constexpr color_type(rgb rgb_color) noexcept
5663+ : color_type(static_cast<color>(
5664+ (static_cast<uint32_t>(rgb_color.r) << 16) |
5665+ (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b)) {}
5666+ constexpr color_type(terminal_color term_color) noexcept
5667+ : value_(static_cast<uint32_t>(term_color) | (3 << 24)) {}
5668+
5669+ constexpr auto is_terminal_color() const noexcept -> bool {
5670+ return (value_ & (1 << 25)) != 0;
5671 }
5672- FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
5673- value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
5674- (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
5675- }
5676- FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
5677- : is_rgb(), value{} {
5678- value.term_color = static_cast<uint8_t>(term_color);
5679+
5680+ constexpr auto value() const noexcept -> uint32_t {
5681+ return value_ & 0xFFFFFF;
5682 }
5683- bool is_rgb;
5684- union color_union {
5685- uint8_t term_color;
5686- uint32_t rgb_color;
5687- } value;
5688-};
5689
5690-FMT_END_DETAIL_NAMESPACE
5691+ constexpr color_type(uint32_t value) noexcept : value_(value) {}
5692+
5693+ uint32_t value_ = 0;
5694+};
5695+} // namespace detail
5696
5697-/** A text style consisting of foreground and background colors and emphasis. */
5698+/// A text style consisting of foreground and background colors and emphasis.
5699 class text_style {
5700+ // The information is packed as follows:
5701+ // ┌──┐
5702+ // │ 0│─┐
5703+ // │..│ ├── foreground color value
5704+ // │23│─┘
5705+ // ├──┤
5706+ // │24│─┬── discriminator for the above value. 00 if unset, 01 if it's
5707+ // │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused)
5708+ // ├──┤
5709+ // │26│──── overflow bit, always zero (see below)
5710+ // ├──┤
5711+ // │27│─┐
5712+ // │..│ │
5713+ // │50│ │
5714+ // ├──┤ │
5715+ // │51│ ├── background color (same format as the foreground color)
5716+ // │52│ │
5717+ // ├──┤ │
5718+ // │53│─┘
5719+ // ├──┤
5720+ // │54│─┐
5721+ // │..│ ├── emphases
5722+ // │61│─┘
5723+ // ├──┤
5724+ // │62│─┬── unused
5725+ // │63│─┘
5726+ // └──┘
5727+ // The overflow bits are there to make operator|= efficient.
5728+ // When ORing, we must throw if, for either the foreground or background,
5729+ // one style specifies a terminal color and the other specifies any color
5730+ // (terminal or RGB); in other words, if one discriminator is 11 and the
5731+ // other is 11 or 01.
5732+ //
5733+ // We do that check by adding the styles. Consider what adding does to each
5734+ // possible pair of discriminators:
5735+ // 00 + 00 = 000
5736+ // 01 + 00 = 001
5737+ // 11 + 00 = 011
5738+ // 01 + 01 = 010
5739+ // 11 + 01 = 100 (!!)
5740+ // 11 + 11 = 110 (!!)
5741+ // In the last two cases, the ones we want to catch, the third bit——the
5742+ // overflow bit——is set. Bingo.
5743+ //
5744+ // We must take into account the possible carry bit from the bits
5745+ // before the discriminator. The only potentially problematic case is
5746+ // 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry
5747+ // bit is impossible in that case, because 00 (unset color) means the
5748+ // 24 bits that precede the discriminator are all zero.
5749+ //
5750+ // This test can be applied to both colors simultaneously.
5751+
5752 public:
5753 FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
5754- : set_foreground_color(), set_background_color(), ems(em) {}
5755-
5756- FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
5757- if (!set_foreground_color) {
5758- set_foreground_color = rhs.set_foreground_color;
5759- foreground_color = rhs.foreground_color;
5760- } else if (rhs.set_foreground_color) {
5761- if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
5762- FMT_THROW(format_error("can't OR a terminal color"));
5763- foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
5764- }
5765+ : style_(static_cast<uint64_t>(em) << 54) {}
5766
5767- if (!set_background_color) {
5768- set_background_color = rhs.set_background_color;
5769- background_color = rhs.background_color;
5770- } else if (rhs.set_background_color) {
5771- if (!background_color.is_rgb || !rhs.background_color.is_rgb)
5772- FMT_THROW(format_error("can't OR a terminal color"));
5773- background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
5774- }
5775-
5776- ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
5777- static_cast<uint8_t>(rhs.ems));
5778+ FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& {
5779+ if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0)
5780+ report_error("can't OR a terminal color");
5781+ style_ |= rhs.style_;
5782 return *this;
5783 }
5784
5785- friend FMT_CONSTEXPR text_style operator|(text_style lhs,
5786- const text_style& rhs) {
5787+ friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs)
5788+ -> text_style {
5789 return lhs |= rhs;
5790 }
5791
5792- FMT_CONSTEXPR bool has_foreground() const noexcept {
5793- return set_foreground_color;
5794+ FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool {
5795+ return style_ == rhs.style_;
5796+ }
5797+
5798+ FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool {
5799+ return !(*this == rhs);
5800 }
5801- FMT_CONSTEXPR bool has_background() const noexcept {
5802- return set_background_color;
5803+
5804+ FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
5805+ return (style_ & (1 << 24)) != 0;
5806 }
5807- FMT_CONSTEXPR bool has_emphasis() const noexcept {
5808- return static_cast<uint8_t>(ems) != 0;
5809+ FMT_CONSTEXPR auto has_background() const noexcept -> bool {
5810+ return (style_ & (1ULL << 51)) != 0;
5811 }
5812- FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
5813+ FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
5814+ return (style_ >> 54) != 0;
5815+ }
5816+ FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
5817 FMT_ASSERT(has_foreground(), "no foreground specified for this style");
5818- return foreground_color;
5819+ return style_ & 0x3FFFFFF;
5820 }
5821- FMT_CONSTEXPR detail::color_type get_background() const noexcept {
5822+ FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
5823 FMT_ASSERT(has_background(), "no background specified for this style");
5824- return background_color;
5825+ return (style_ >> 27) & 0x3FFFFFF;
5826 }
5827- FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
5828+ FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
5829 FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
5830- return ems;
5831+ return static_cast<emphasis>(style_ >> 54);
5832 }
5833
5834 private:
5835- FMT_CONSTEXPR text_style(bool is_foreground,
5836- detail::color_type text_color) noexcept
5837- : set_foreground_color(), set_background_color(), ems() {
5838- if (is_foreground) {
5839- foreground_color = text_color;
5840- set_foreground_color = true;
5841- } else {
5842- background_color = text_color;
5843- set_background_color = true;
5844- }
5845- }
5846+ FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {}
5847
5848- friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept;
5849+ friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
5850+ -> text_style;
5851
5852- friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
5853+ friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
5854+ -> text_style;
5855
5856- detail::color_type foreground_color;
5857- detail::color_type background_color;
5858- bool set_foreground_color;
5859- bool set_background_color;
5860- emphasis ems;
5861+ uint64_t style_ = 0;
5862 };
5863
5864-/** Creates a text style from the foreground (text) color. */
5865-FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
5866- return text_style(true, foreground);
5867+/// Creates a text style from the foreground (text) color.
5868+FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
5869+ -> text_style {
5870+ return foreground.value_;
5871 }
5872
5873-/** Creates a text style from the background color. */
5874-FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
5875- return text_style(false, background);
5876+/// Creates a text style from the background color.
5877+FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
5878+ -> text_style {
5879+ return static_cast<uint64_t>(background.value_) << 27;
5880 }
5881
5882-FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
5883+FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
5884+ -> text_style {
5885 return text_style(lhs) | rhs;
5886 }
5887
5888-FMT_BEGIN_DETAIL_NAMESPACE
5889+namespace detail {
5890
5891 template <typename Char> struct ansi_color_escape {
5892- FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
5893+ FMT_CONSTEXPR ansi_color_escape(color_type text_color,
5894 const char* esc) noexcept {
5895 // If we have a terminal color, we need to output another escape code
5896 // sequence.
5897- if (!text_color.is_rgb) {
5898+ if (text_color.is_terminal_color()) {
5899 bool is_background = esc == string_view("\x1b[48;2;");
5900- uint32_t value = text_color.value.term_color;
5901+ uint32_t value = text_color.value();
5902 // Background ASCII codes are the same as the foreground ones but with
5903 // 10 more.
5904 if (is_background) value += 10u;
5905@@ -356,7 +394,7 @@ template <typename Char> struct ansi_color_escape {
5906 for (int i = 0; i < 7; i++) {
5907 buffer[i] = static_cast<Char>(esc[i]);
5908 }
5909- rgb color(text_color.value.rgb_color);
5910+ rgb color(text_color.value());
5911 to_esc(color.r, buffer + 7, ';');
5912 to_esc(color.g, buffer + 11, ';');
5913 to_esc(color.b, buffer + 15, 'm');
5914@@ -385,9 +423,9 @@ template <typename Char> struct ansi_color_escape {
5915 }
5916 FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
5917
5918- FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
5919- FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
5920- return buffer + std::char_traits<Char>::length(buffer);
5921+ FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
5922+ FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
5923+ return buffer + basic_string_view<Char>(buffer).size();
5924 }
5925
5926 private:
5927@@ -401,25 +439,27 @@ template <typename Char> struct ansi_color_escape {
5928 out[2] = static_cast<Char>('0' + c % 10);
5929 out[3] = static_cast<Char>(delimiter);
5930 }
5931- static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
5932+ static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept
5933+ -> bool {
5934 return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
5935 }
5936 };
5937
5938 template <typename Char>
5939-FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
5940- detail::color_type foreground) noexcept {
5941+FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept
5942+ -> ansi_color_escape<Char> {
5943 return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
5944 }
5945
5946 template <typename Char>
5947-FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
5948- detail::color_type background) noexcept {
5949+FMT_CONSTEXPR auto make_background_color(color_type background) noexcept
5950+ -> ansi_color_escape<Char> {
5951 return ansi_color_escape<Char>(background, "\x1b[48;2;");
5952 }
5953
5954 template <typename Char>
5955-FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
5956+FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
5957+ -> ansi_color_escape<Char> {
5958 return ansi_color_escape<Char>(em);
5959 }
5960
5961@@ -428,149 +468,116 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
5962 buffer.append(reset_color.begin(), reset_color.end());
5963 }
5964
5965-template <typename T> struct styled_arg {
5966+template <typename T> struct styled_arg : view {
5967 const T& value;
5968 text_style style;
5969+ styled_arg(const T& v, text_style s) : value(v), style(s) {}
5970 };
5971
5972 template <typename Char>
5973-void vformat_to(buffer<Char>& buf, const text_style& ts,
5974- basic_string_view<Char> format_str,
5975- basic_format_args<buffer_context<type_identity_t<Char>>> args) {
5976- bool has_style = false;
5977+void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
5978+ basic_format_args<buffered_context<Char>> args) {
5979 if (ts.has_emphasis()) {
5980- has_style = true;
5981- auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
5982+ auto emphasis = make_emphasis<Char>(ts.get_emphasis());
5983 buf.append(emphasis.begin(), emphasis.end());
5984 }
5985 if (ts.has_foreground()) {
5986- has_style = true;
5987- auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
5988+ auto foreground = make_foreground_color<Char>(ts.get_foreground());
5989 buf.append(foreground.begin(), foreground.end());
5990 }
5991 if (ts.has_background()) {
5992- has_style = true;
5993- auto background = detail::make_background_color<Char>(ts.get_background());
5994+ auto background = make_background_color<Char>(ts.get_background());
5995 buf.append(background.begin(), background.end());
5996 }
5997- detail::vformat_to(buf, format_str, args, {});
5998- if (has_style) detail::reset_color<Char>(buf);
5999+ vformat_to(buf, fmt, args);
6000+ if (ts != text_style()) reset_color<Char>(buf);
6001 }
6002+} // namespace detail
6003
6004-FMT_END_DETAIL_NAMESPACE
6005-
6006-inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
6007- format_args args) {
6008- // Legacy wide streams are not supported.
6009+inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) {
6010 auto buf = memory_buffer();
6011 detail::vformat_to(buf, ts, fmt, args);
6012- if (detail::is_utf8()) {
6013- detail::print(f, string_view(buf.begin(), buf.size()));
6014- return;
6015- }
6016- buf.push_back('\0');
6017- int result = std::fputs(buf.data(), f);
6018- if (result < 0)
6019- FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
6020+ print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
6021 }
6022
6023 /**
6024- \rst
6025- Formats a string and prints it to the specified file stream using ANSI
6026- escape sequences to specify text formatting.
6027-
6028- **Example**::
6029-
6030- fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
6031- "Elapsed time: {0:.2f} seconds", 1.23);
6032- \endrst
6033+ * Formats a string and prints it to the specified file stream using ANSI
6034+ * escape sequences to specify text formatting.
6035+ *
6036+ * **Example**:
6037+ *
6038+ * fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
6039+ * "Elapsed time: {0:.2f} seconds", 1.23);
6040 */
6041-template <typename S, typename... Args,
6042- FMT_ENABLE_IF(detail::is_string<S>::value)>
6043-void print(std::FILE* f, const text_style& ts, const S& format_str,
6044- const Args&... args) {
6045- vprint(f, ts, format_str,
6046- fmt::make_format_args<buffer_context<char_t<S>>>(args...));
6047+template <typename... T>
6048+void print(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
6049+ vprint(f, ts, fmt.str, vargs<T...>{{args...}});
6050 }
6051
6052 /**
6053- \rst
6054- Formats a string and prints it to stdout using ANSI escape sequences to
6055- specify text formatting.
6056-
6057- **Example**::
6058-
6059- fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
6060- "Elapsed time: {0:.2f} seconds", 1.23);
6061- \endrst
6062+ * Formats a string and prints it to stdout using ANSI escape sequences to
6063+ * specify text formatting.
6064+ *
6065+ * **Example**:
6066+ *
6067+ * fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
6068+ * "Elapsed time: {0:.2f} seconds", 1.23);
6069 */
6070-template <typename S, typename... Args,
6071- FMT_ENABLE_IF(detail::is_string<S>::value)>
6072-void print(const text_style& ts, const S& format_str, const Args&... args) {
6073- return print(stdout, ts, format_str, args...);
6074+template <typename... T>
6075+void print(text_style ts, format_string<T...> fmt, T&&... args) {
6076+ return print(stdout, ts, fmt, std::forward<T>(args)...);
6077 }
6078
6079-template <typename S, typename Char = char_t<S>>
6080-inline std::basic_string<Char> vformat(
6081- const text_style& ts, const S& format_str,
6082- basic_format_args<buffer_context<type_identity_t<Char>>> args) {
6083- basic_memory_buffer<Char> buf;
6084- detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
6085+inline auto vformat(text_style ts, string_view fmt, format_args args)
6086+ -> std::string {
6087+ auto buf = memory_buffer();
6088+ detail::vformat_to(buf, ts, fmt, args);
6089 return fmt::to_string(buf);
6090 }
6091
6092 /**
6093- \rst
6094- Formats arguments and returns the result as a string using ANSI
6095- escape sequences to specify text formatting.
6096-
6097- **Example**::
6098-
6099- #include <fmt/color.h>
6100- std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
6101- "The answer is {}", 42);
6102- \endrst
6103-*/
6104-template <typename S, typename... Args, typename Char = char_t<S>>
6105-inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
6106- const Args&... args) {
6107- return fmt::vformat(ts, detail::to_string_view(format_str),
6108- fmt::make_format_args<buffer_context<Char>>(args...));
6109+ * Formats arguments and returns the result as a string using ANSI escape
6110+ * sequences to specify text formatting.
6111+ *
6112+ * **Example**:
6113+ *
6114+ * ```
6115+ * #include <fmt/color.h>
6116+ * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
6117+ * "The answer is {}", 42);
6118+ * ```
6119+ */
6120+template <typename... T>
6121+inline auto format(text_style ts, format_string<T...> fmt, T&&... args)
6122+ -> std::string {
6123+ return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
6124 }
6125
6126-/**
6127- Formats a string with the given text_style and writes the output to ``out``.
6128- */
6129-template <typename OutputIt, typename Char,
6130- FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
6131-OutputIt vformat_to(
6132- OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
6133- basic_format_args<buffer_context<type_identity_t<Char>>> args) {
6134- auto&& buf = detail::get_buffer<Char>(out);
6135- detail::vformat_to(buf, ts, format_str, args);
6136+/// Formats a string with the given text_style and writes the output to `out`.
6137+template <typename OutputIt,
6138+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
6139+auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args)
6140+ -> OutputIt {
6141+ auto&& buf = detail::get_buffer<char>(out);
6142+ detail::vformat_to(buf, ts, fmt, args);
6143 return detail::get_iterator(buf, out);
6144 }
6145
6146 /**
6147- \rst
6148- Formats arguments with the given text_style, writes the result to the output
6149- iterator ``out`` and returns the iterator past the end of the output range.
6150-
6151- **Example**::
6152-
6153- std::vector<char> out;
6154- fmt::format_to(std::back_inserter(out),
6155- fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
6156- \endrst
6157-*/
6158-template <typename OutputIt, typename S, typename... Args,
6159- bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
6160- detail::is_string<S>::value>
6161-inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
6162- Args&&... args) ->
6163- typename std::enable_if<enable, OutputIt>::type {
6164- return vformat_to(out, ts, detail::to_string_view(format_str),
6165- fmt::make_format_args<buffer_context<char_t<S>>>(args...));
6166+ * Formats arguments with the given text style, writes the result to the output
6167+ * iterator `out` and returns the iterator past the end of the output range.
6168+ *
6169+ * **Example**:
6170+ *
6171+ * std::vector<char> out;
6172+ * fmt::format_to(std::back_inserter(out),
6173+ * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
6174+ */
6175+template <typename OutputIt, typename... T,
6176+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
6177+inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt,
6178+ T&&... args) -> OutputIt {
6179+ return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
6180 }
6181
6182 template <typename T, typename Char>
6183@@ -579,47 +586,44 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
6184 auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
6185 -> decltype(ctx.out()) {
6186 const auto& ts = arg.style;
6187- const auto& value = arg.value;
6188 auto out = ctx.out();
6189
6190 bool has_style = false;
6191 if (ts.has_emphasis()) {
6192 has_style = true;
6193 auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
6194- out = std::copy(emphasis.begin(), emphasis.end(), out);
6195+ out = detail::copy<Char>(emphasis.begin(), emphasis.end(), out);
6196 }
6197 if (ts.has_foreground()) {
6198 has_style = true;
6199 auto foreground =
6200 detail::make_foreground_color<Char>(ts.get_foreground());
6201- out = std::copy(foreground.begin(), foreground.end(), out);
6202+ out = detail::copy<Char>(foreground.begin(), foreground.end(), out);
6203 }
6204 if (ts.has_background()) {
6205 has_style = true;
6206 auto background =
6207 detail::make_background_color<Char>(ts.get_background());
6208- out = std::copy(background.begin(), background.end(), out);
6209+ out = detail::copy<Char>(background.begin(), background.end(), out);
6210 }
6211- out = formatter<T, Char>::format(value, ctx);
6212+ out = formatter<T, Char>::format(arg.value, ctx);
6213 if (has_style) {
6214 auto reset_color = string_view("\x1b[0m");
6215- out = std::copy(reset_color.begin(), reset_color.end(), out);
6216+ out = detail::copy<Char>(reset_color.begin(), reset_color.end(), out);
6217 }
6218 return out;
6219 }
6220 };
6221
6222 /**
6223- \rst
6224- Returns an argument that will be formatted using ANSI escape sequences,
6225- to be used in a formatting function.
6226-
6227- **Example**::
6228-
6229- fmt::print("Elapsed time: {0:.2f} seconds",
6230- fmt::styled(1.23, fmt::fg(fmt::color::green) |
6231- fmt::bg(fmt::color::blue)));
6232- \endrst
6233+ * Returns an argument that will be formatted using ANSI escape sequences,
6234+ * to be used in a formatting function.
6235+ *
6236+ * **Example**:
6237+ *
6238+ * fmt::print("Elapsed time: {0:.2f} seconds",
6239+ * fmt::styled(1.23, fmt::fg(fmt::color::green) |
6240+ * fmt::bg(fmt::color::blue)));
6241 */
6242 template <typename T>
6243 FMT_CONSTEXPR auto styled(const T& value, text_style ts)
6244diff --git a/include/fmt/compile.h b/include/fmt/compile.h
6245index 34a581d..08d9427 100644
6246--- a/include/fmt/compile.h
6247+++ b/include/fmt/compile.h
6248@@ -8,134 +8,41 @@
6249 #ifndef FMT_COMPILE_H_
6250 #define FMT_COMPILE_H_
6251
6252+#ifndef FMT_MODULE
6253+# include <iterator> // std::back_inserter
6254+#endif
6255+
6256 #include "format.h"
6257
6258 FMT_BEGIN_NAMESPACE
6259-namespace detail {
6260-
6261-template <typename Char, typename InputIt>
6262-FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
6263- counting_iterator it) {
6264- return it + (end - begin);
6265-}
6266-
6267-template <typename OutputIt> class truncating_iterator_base {
6268- protected:
6269- OutputIt out_;
6270- size_t limit_;
6271- size_t count_ = 0;
6272-
6273- truncating_iterator_base() : out_(), limit_(0) {}
6274-
6275- truncating_iterator_base(OutputIt out, size_t limit)
6276- : out_(out), limit_(limit) {}
6277-
6278- public:
6279- using iterator_category = std::output_iterator_tag;
6280- using value_type = typename std::iterator_traits<OutputIt>::value_type;
6281- using difference_type = std::ptrdiff_t;
6282- using pointer = void;
6283- using reference = void;
6284- FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
6285-
6286- OutputIt base() const { return out_; }
6287- size_t count() const { return count_; }
6288-};
6289-
6290-// An output iterator that truncates the output and counts the number of objects
6291-// written to it.
6292-template <typename OutputIt,
6293- typename Enable = typename std::is_void<
6294- typename std::iterator_traits<OutputIt>::value_type>::type>
6295-class truncating_iterator;
6296-
6297-template <typename OutputIt>
6298-class truncating_iterator<OutputIt, std::false_type>
6299- : public truncating_iterator_base<OutputIt> {
6300- mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
6301-
6302- public:
6303- using value_type = typename truncating_iterator_base<OutputIt>::value_type;
6304-
6305- truncating_iterator() = default;
6306-
6307- truncating_iterator(OutputIt out, size_t limit)
6308- : truncating_iterator_base<OutputIt>(out, limit) {}
6309-
6310- truncating_iterator& operator++() {
6311- if (this->count_++ < this->limit_) ++this->out_;
6312- return *this;
6313- }
6314-
6315- truncating_iterator operator++(int) {
6316- auto it = *this;
6317- ++*this;
6318- return it;
6319- }
6320-
6321- value_type& operator*() const {
6322- return this->count_ < this->limit_ ? *this->out_ : blackhole_;
6323- }
6324-};
6325-
6326-template <typename OutputIt>
6327-class truncating_iterator<OutputIt, std::true_type>
6328- : public truncating_iterator_base<OutputIt> {
6329- public:
6330- truncating_iterator() = default;
6331-
6332- truncating_iterator(OutputIt out, size_t limit)
6333- : truncating_iterator_base<OutputIt>(out, limit) {}
6334-
6335- template <typename T> truncating_iterator& operator=(T val) {
6336- if (this->count_++ < this->limit_) *this->out_++ = val;
6337- return *this;
6338- }
6339-
6340- truncating_iterator& operator++() { return *this; }
6341- truncating_iterator& operator++(int) { return *this; }
6342- truncating_iterator& operator*() { return *this; }
6343-};
6344
6345 // A compile-time string which is compiled into fast formatting code.
6346-class compiled_string {};
6347+FMT_EXPORT class compiled_string {};
6348
6349 template <typename S>
6350 struct is_compiled_string : std::is_base_of<compiled_string, S> {};
6351
6352-/**
6353- \rst
6354- Converts a string literal *s* into a format string that will be parsed at
6355- compile time and converted into efficient formatting code. Requires C++17
6356- ``constexpr if`` compiler support.
6357-
6358- **Example**::
6359+namespace detail {
6360
6361- // Converts 42 into std::string using the most efficient method and no
6362- // runtime format string processing.
6363- std::string s = fmt::format(FMT_COMPILE("{}"), 42);
6364- \endrst
6365+/**
6366+ * Converts a string literal `s` into a format string that will be parsed at
6367+ * compile time and converted into efficient formatting code. Requires C++17
6368+ * `constexpr if` compiler support.
6369+ *
6370+ * **Example**:
6371+ *
6372+ * // Converts 42 into std::string using the most efficient method and no
6373+ * // runtime format string processing.
6374+ * std::string s = fmt::format(FMT_COMPILE("{}"), 42);
6375 */
6376 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
6377-# define FMT_COMPILE(s) \
6378- FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
6379+# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string)
6380 #else
6381 # define FMT_COMPILE(s) FMT_STRING(s)
6382 #endif
6383
6384-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
6385-template <typename Char, size_t N,
6386- fmt::detail_exported::fixed_string<Char, N> Str>
6387-struct udl_compiled_string : compiled_string {
6388- using char_type = Char;
6389- explicit constexpr operator basic_string_view<char_type>() const {
6390- return {Str.data, N - 1};
6391- }
6392-};
6393-#endif
6394-
6395 template <typename T, typename... Tail>
6396-const T& first(const T& value, const Tail&...) {
6397+auto first(const T& value, const Tail&...) -> const T& {
6398 return value;
6399 }
6400
6401@@ -153,6 +60,29 @@ constexpr const auto& get([[maybe_unused]] const T& first,
6402 return detail::get<N - 1>(rest...);
6403 }
6404
6405+# if FMT_USE_NONTYPE_TEMPLATE_ARGS
6406+template <int N, typename T, typename... Args, typename Char>
6407+constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
6408+ if constexpr (is_static_named_arg<T>()) {
6409+ if (name == T::name) return N;
6410+ }
6411+ if constexpr (sizeof...(Args) > 0)
6412+ return get_arg_index_by_name<N + 1, Args...>(name);
6413+ (void)name; // Workaround an MSVC bug about "unused" parameter.
6414+ return -1;
6415+}
6416+# endif
6417+
6418+template <typename... Args, typename Char>
6419+FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
6420+# if FMT_USE_NONTYPE_TEMPLATE_ARGS
6421+ if constexpr (sizeof...(Args) > 0)
6422+ return get_arg_index_by_name<0, Args...>(name);
6423+# endif
6424+ (void)name;
6425+ return -1;
6426+}
6427+
6428 template <typename Char, typename... Args>
6429 constexpr int get_arg_index_by_name(basic_string_view<Char> name,
6430 type_list<Args...>) {
6431@@ -196,7 +126,8 @@ template <typename Char> struct code_unit {
6432
6433 template <typename OutputIt, typename... Args>
6434 constexpr OutputIt format(OutputIt out, const Args&...) const {
6435- return write<Char>(out, value);
6436+ *out++ = value;
6437+ return out;
6438 }
6439 };
6440
6441@@ -220,7 +151,13 @@ template <typename Char, typename T, int N> struct field {
6442
6443 template <typename OutputIt, typename... Args>
6444 constexpr OutputIt format(OutputIt out, const Args&... args) const {
6445- return write<Char>(out, get_arg_checked<T, N>(args...));
6446+ const T& arg = get_arg_checked<T, N>(args...);
6447+ if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
6448+ auto s = basic_string_view<Char>(arg);
6449+ return copy<Char>(s.begin(), s.end(), out);
6450+ } else {
6451+ return write<Char>(out, arg);
6452+ }
6453 }
6454 };
6455
6456@@ -308,13 +245,12 @@ constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
6457 }
6458
6459 template <typename Args, size_t POS, int ID, typename S>
6460-constexpr auto compile_format_string(S format_str);
6461+constexpr auto compile_format_string(S fmt);
6462
6463 template <typename Args, size_t POS, int ID, typename T, typename S>
6464-constexpr auto parse_tail(T head, S format_str) {
6465- if constexpr (POS !=
6466- basic_string_view<typename S::char_type>(format_str).size()) {
6467- constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
6468+constexpr auto parse_tail(T head, S fmt) {
6469+ if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
6470+ constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
6471 if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
6472 unknown_format>())
6473 return tail;
6474@@ -346,6 +282,7 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
6475 }
6476
6477 template <typename Char> struct arg_id_handler {
6478+ arg_id_kind kind;
6479 arg_ref<Char> arg_id;
6480
6481 constexpr int on_auto() {
6482@@ -353,25 +290,28 @@ template <typename Char> struct arg_id_handler {
6483 return 0;
6484 }
6485 constexpr int on_index(int id) {
6486+ kind = arg_id_kind::index;
6487 arg_id = arg_ref<Char>(id);
6488 return 0;
6489 }
6490 constexpr int on_name(basic_string_view<Char> id) {
6491+ kind = arg_id_kind::name;
6492 arg_id = arg_ref<Char>(id);
6493 return 0;
6494 }
6495 };
6496
6497 template <typename Char> struct parse_arg_id_result {
6498+ arg_id_kind kind;
6499 arg_ref<Char> arg_id;
6500 const Char* arg_id_end;
6501 };
6502
6503 template <int ID, typename Char>
6504 constexpr auto parse_arg_id(const Char* begin, const Char* end) {
6505- auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
6506+ auto handler = arg_id_handler<Char>{arg_id_kind::none, arg_ref<Char>{}};
6507 auto arg_id_end = parse_arg_id(begin, end, handler);
6508- return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
6509+ return parse_arg_id_result<Char>{handler.kind, handler.arg_id, arg_id_end};
6510 }
6511
6512 template <typename T, typename Enable = void> struct field_type {
6513@@ -385,14 +325,13 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
6514
6515 template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
6516 typename S>
6517-constexpr auto parse_replacement_field_then_tail(S format_str) {
6518+constexpr auto parse_replacement_field_then_tail(S fmt) {
6519 using char_type = typename S::char_type;
6520- constexpr auto str = basic_string_view<char_type>(format_str);
6521+ constexpr auto str = basic_string_view<char_type>(fmt);
6522 constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
6523 if constexpr (c == '}') {
6524 return parse_tail<Args, END_POS + 1, NEXT_ID>(
6525- field<char_type, typename field_type<T>::type, ARG_INDEX>(),
6526- format_str);
6527+ field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
6528 } else if constexpr (c != ':') {
6529 FMT_THROW(format_error("expected ':'"));
6530 } else {
6531@@ -405,7 +344,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) {
6532 return parse_tail<Args, result.end + 1, result.next_arg_id>(
6533 spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
6534 result.fmt},
6535- format_str);
6536+ fmt);
6537 }
6538 }
6539 }
6540@@ -413,22 +352,21 @@ constexpr auto parse_replacement_field_then_tail(S format_str) {
6541 // Compiles a non-empty format string and returns the compiled representation
6542 // or unknown_format() on unrecognized input.
6543 template <typename Args, size_t POS, int ID, typename S>
6544-constexpr auto compile_format_string(S format_str) {
6545+constexpr auto compile_format_string(S fmt) {
6546 using char_type = typename S::char_type;
6547- constexpr auto str = basic_string_view<char_type>(format_str);
6548+ constexpr auto str = basic_string_view<char_type>(fmt);
6549 if constexpr (str[POS] == '{') {
6550 if constexpr (POS + 1 == str.size())
6551 FMT_THROW(format_error("unmatched '{' in format string"));
6552 if constexpr (str[POS + 1] == '{') {
6553- return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
6554+ return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
6555 } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
6556 static_assert(ID != manual_indexing_id,
6557 "cannot switch from manual to automatic argument indexing");
6558 constexpr auto next_id =
6559 ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
6560 return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
6561- POS + 1, ID, next_id>(
6562- format_str);
6563+ POS + 1, ID, next_id>(fmt);
6564 } else {
6565 constexpr auto arg_id_result =
6566 parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
6567@@ -436,28 +374,27 @@ constexpr auto compile_format_string(S format_str) {
6568 constexpr char_type c =
6569 arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
6570 static_assert(c == '}' || c == ':', "missing '}' in format string");
6571- if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
6572+ if constexpr (arg_id_result.kind == arg_id_kind::index) {
6573 static_assert(
6574 ID == manual_indexing_id || ID == 0,
6575 "cannot switch from automatic to manual argument indexing");
6576- constexpr auto arg_index = arg_id_result.arg_id.val.index;
6577+ constexpr auto arg_index = arg_id_result.arg_id.index;
6578 return parse_replacement_field_then_tail<get_type<arg_index, Args>,
6579 Args, arg_id_end_pos,
6580 arg_index, manual_indexing_id>(
6581- format_str);
6582- } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
6583+ fmt);
6584+ } else if constexpr (arg_id_result.kind == arg_id_kind::name) {
6585 constexpr auto arg_index =
6586- get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
6587+ get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
6588 if constexpr (arg_index >= 0) {
6589 constexpr auto next_id =
6590 ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
6591 return parse_replacement_field_then_tail<
6592 decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
6593- arg_index, next_id>(format_str);
6594+ arg_index, next_id>(fmt);
6595 } else if constexpr (c == '}') {
6596 return parse_tail<Args, arg_id_end_pos + 1, ID>(
6597- runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
6598- format_str);
6599+ runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
6600 } else if constexpr (c == ':') {
6601 return unknown_format(); // no type info for specs parsing
6602 }
6603@@ -466,29 +403,26 @@ constexpr auto compile_format_string(S format_str) {
6604 } else if constexpr (str[POS] == '}') {
6605 if constexpr (POS + 1 == str.size())
6606 FMT_THROW(format_error("unmatched '}' in format string"));
6607- return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
6608+ return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
6609 } else {
6610 constexpr auto end = parse_text(str, POS + 1);
6611 if constexpr (end - POS > 1) {
6612- return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
6613- format_str);
6614+ return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
6615 } else {
6616- return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
6617- format_str);
6618+ return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
6619 }
6620 }
6621 }
6622
6623 template <typename... Args, typename S,
6624- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
6625-constexpr auto compile(S format_str) {
6626- constexpr auto str = basic_string_view<typename S::char_type>(format_str);
6627+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
6628+constexpr auto compile(S fmt) {
6629+ constexpr auto str = basic_string_view<typename S::char_type>(fmt);
6630 if constexpr (str.size() == 0) {
6631 return detail::make_text(str, 0, 0);
6632 } else {
6633 constexpr auto result =
6634- detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
6635- format_str);
6636+ detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
6637 return result;
6638 }
6639 }
6640@@ -517,7 +451,7 @@ constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
6641 }
6642
6643 template <typename S, typename... Args,
6644- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
6645+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
6646 FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
6647 Args&&... args) {
6648 if constexpr (std::is_same<typename S::char_type, char>::value) {
6649@@ -544,7 +478,7 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
6650 }
6651
6652 template <typename OutputIt, typename S, typename... Args,
6653- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
6654+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
6655 FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
6656 constexpr auto compiled = detail::compile<Args...>(S());
6657 if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
6658@@ -559,42 +493,42 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
6659 #endif
6660
6661 template <typename OutputIt, typename S, typename... Args,
6662- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
6663-format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
6664- const S& format_str, Args&&... args) {
6665- auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
6666- format_str, std::forward<Args>(args)...);
6667- return {it.base(), it.count()};
6668+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
6669+auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
6670+ -> format_to_n_result<OutputIt> {
6671+ using traits = detail::fixed_buffer_traits;
6672+ auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
6673+ fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
6674+ return {buf.out(), buf.count()};
6675 }
6676
6677 template <typename S, typename... Args,
6678- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
6679-FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
6680- const Args&... args) {
6681- return fmt::format_to(detail::counting_iterator(), format_str, args...)
6682- .count();
6683+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
6684+FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
6685+ -> size_t {
6686+ auto buf = detail::counting_buffer<>();
6687+ fmt::format_to(appender(buf), fmt, args...);
6688+ return buf.count();
6689 }
6690
6691 template <typename S, typename... Args,
6692- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
6693-void print(std::FILE* f, const S& format_str, const Args&... args) {
6694- memory_buffer buffer;
6695- fmt::format_to(std::back_inserter(buffer), format_str, args...);
6696- detail::print(f, {buffer.data(), buffer.size()});
6697+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
6698+void print(std::FILE* f, const S& fmt, const Args&... args) {
6699+ auto buf = memory_buffer();
6700+ fmt::format_to(appender(buf), fmt, args...);
6701+ detail::print(f, {buf.data(), buf.size()});
6702 }
6703
6704 template <typename S, typename... Args,
6705- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
6706-void print(const S& format_str, const Args&... args) {
6707- print(stdout, format_str, args...);
6708+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
6709+void print(const S& fmt, const Args&... args) {
6710+ print(stdout, fmt, args...);
6711 }
6712
6713 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
6714 inline namespace literals {
6715-template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
6716- using char_t = remove_cvref_t<decltype(Str.data[0])>;
6717- return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
6718- Str>();
6719+template <detail::fixed_string Str> constexpr auto operator""_cf() {
6720+ return FMT_COMPILE(Str.data);
6721 }
6722 } // namespace literals
6723 #endif
6724diff --git a/include/fmt/core.h b/include/fmt/core.h
6725index 4c31964..8ca735f 100644
6726--- a/include/fmt/core.h
6727+++ b/include/fmt/core.h
6728@@ -1,2905 +1,5 @@
6729-// Formatting library for C++ - the core API for char/UTF-8
6730-//
6731-// Copyright (c) 2012 - present, Victor Zverovich
6732-// All rights reserved.
6733-//
6734-// For the license information refer to format.h.
6735+// This file is only provided for compatibility and may be removed in future
6736+// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h
6737+// otherwise.
6738
6739-#ifndef FMT_CORE_H_
6740-#define FMT_CORE_H_
6741-
6742-#include <cstddef> // std::byte
6743-#include <cstdio> // std::FILE
6744-#include <cstring> // std::strlen
6745-#include <iterator>
6746-#include <limits>
6747-#include <string>
6748-#include <type_traits>
6749-
6750-// The fmt library version in the form major * 10000 + minor * 100 + patch.
6751-#define FMT_VERSION 100001
6752-
6753-#if defined(__clang__) && !defined(__ibmxl__)
6754-# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
6755-#else
6756-# define FMT_CLANG_VERSION 0
6757-#endif
6758-
6759-#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \
6760- !defined(__NVCOMPILER)
6761-# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
6762-#else
6763-# define FMT_GCC_VERSION 0
6764-#endif
6765-
6766-#ifndef FMT_GCC_PRAGMA
6767-// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
6768-# if FMT_GCC_VERSION >= 504
6769-# define FMT_GCC_PRAGMA(arg) _Pragma(arg)
6770-# else
6771-# define FMT_GCC_PRAGMA(arg)
6772-# endif
6773-#endif
6774-
6775-#ifdef __ICL
6776-# define FMT_ICC_VERSION __ICL
6777-#elif defined(__INTEL_COMPILER)
6778-# define FMT_ICC_VERSION __INTEL_COMPILER
6779-#else
6780-# define FMT_ICC_VERSION 0
6781-#endif
6782-
6783-#ifdef _MSC_VER
6784-# define FMT_MSC_VERSION _MSC_VER
6785-# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
6786-#else
6787-# define FMT_MSC_VERSION 0
6788-# define FMT_MSC_WARNING(...)
6789-#endif
6790-
6791-#ifdef _MSVC_LANG
6792-# define FMT_CPLUSPLUS _MSVC_LANG
6793-#else
6794-# define FMT_CPLUSPLUS __cplusplus
6795-#endif
6796-
6797-#ifdef __has_feature
6798-# define FMT_HAS_FEATURE(x) __has_feature(x)
6799-#else
6800-# define FMT_HAS_FEATURE(x) 0
6801-#endif
6802-
6803-#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900
6804-# define FMT_HAS_INCLUDE(x) __has_include(x)
6805-#else
6806-# define FMT_HAS_INCLUDE(x) 0
6807-#endif
6808-
6809-#ifdef __has_cpp_attribute
6810-# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
6811-#else
6812-# define FMT_HAS_CPP_ATTRIBUTE(x) 0
6813-#endif
6814-
6815-#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
6816- (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
6817-
6818-#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
6819- (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
6820-
6821-// Check if relaxed C++14 constexpr is supported.
6822-// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
6823-#ifndef FMT_USE_CONSTEXPR
6824-# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
6825- (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \
6826- !FMT_ICC_VERSION && !defined(__NVCC__)
6827-# define FMT_USE_CONSTEXPR 1
6828-# else
6829-# define FMT_USE_CONSTEXPR 0
6830-# endif
6831-#endif
6832-#if FMT_USE_CONSTEXPR
6833-# define FMT_CONSTEXPR constexpr
6834-#else
6835-# define FMT_CONSTEXPR
6836-#endif
6837-
6838-#if ((FMT_CPLUSPLUS >= 202002L) && \
6839- (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
6840- (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
6841-# define FMT_CONSTEXPR20 constexpr
6842-#else
6843-# define FMT_CONSTEXPR20
6844-#endif
6845-
6846-// Check if constexpr std::char_traits<>::{compare,length} are supported.
6847-#if defined(__GLIBCXX__)
6848-# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \
6849- _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
6850-# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
6851-# endif
6852-#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \
6853- _LIBCPP_VERSION >= 4000
6854-# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
6855-#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L
6856-# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
6857-#endif
6858-#ifndef FMT_CONSTEXPR_CHAR_TRAITS
6859-# define FMT_CONSTEXPR_CHAR_TRAITS
6860-#endif
6861-
6862-// Check if exceptions are disabled.
6863-#ifndef FMT_EXCEPTIONS
6864-# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
6865- (FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
6866-# define FMT_EXCEPTIONS 0
6867-# else
6868-# define FMT_EXCEPTIONS 1
6869-# endif
6870-#endif
6871-
6872-// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings.
6873-#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \
6874- !defined(__NVCC__)
6875-# define FMT_NORETURN [[noreturn]]
6876-#else
6877-# define FMT_NORETURN
6878-#endif
6879-
6880-#ifndef FMT_NODISCARD
6881-# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
6882-# define FMT_NODISCARD [[nodiscard]]
6883-# else
6884-# define FMT_NODISCARD
6885-# endif
6886-#endif
6887-
6888-#ifndef FMT_INLINE
6889-# if FMT_GCC_VERSION || FMT_CLANG_VERSION
6890-# define FMT_INLINE inline __attribute__((always_inline))
6891-# else
6892-# define FMT_INLINE inline
6893-# endif
6894-#endif
6895-
6896-#ifdef _MSC_VER
6897-# define FMT_UNCHECKED_ITERATOR(It) \
6898- using _Unchecked_type = It // Mark iterator as checked.
6899-#else
6900-# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
6901-#endif
6902-
6903-#ifndef FMT_BEGIN_NAMESPACE
6904-# define FMT_BEGIN_NAMESPACE \
6905- namespace fmt { \
6906- inline namespace v10 {
6907-# define FMT_END_NAMESPACE \
6908- } \
6909- }
6910-#endif
6911-
6912-#ifndef FMT_EXPORT
6913-# define FMT_EXPORT
6914-# define FMT_BEGIN_EXPORT
6915-# define FMT_END_EXPORT
6916-#endif
6917-
6918-#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
6919-# ifdef FMT_LIB_EXPORT
6920-# define FMT_API __declspec(dllexport)
6921-# elif defined(FMT_SHARED)
6922-# define FMT_API __declspec(dllimport)
6923-# endif
6924-#else
6925-# if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
6926-# if defined(__GNUC__) || defined(__clang__)
6927-# define FMT_API __attribute__((visibility("default")))
6928-# endif
6929-# endif
6930-#endif
6931-#ifndef FMT_API
6932-# define FMT_API
6933-#endif
6934-
6935-// libc++ supports string_view in pre-c++17.
6936-#if FMT_HAS_INCLUDE(<string_view>) && \
6937- (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
6938-# include <string_view>
6939-# define FMT_USE_STRING_VIEW
6940-#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L
6941-# include <experimental/string_view>
6942-# define FMT_USE_EXPERIMENTAL_STRING_VIEW
6943-#endif
6944-
6945-#ifndef FMT_UNICODE
6946-# define FMT_UNICODE !FMT_MSC_VERSION
6947-#endif
6948-
6949-#ifndef FMT_CONSTEVAL
6950-# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
6951- (!defined(__apple_build_version__) || \
6952- __apple_build_version__ >= 14000029L) && \
6953- FMT_CPLUSPLUS >= 202002L) || \
6954- (defined(__cpp_consteval) && \
6955- (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
6956-// consteval is broken in MSVC before VS2022 and Apple clang before 14.
6957-# define FMT_CONSTEVAL consteval
6958-# define FMT_HAS_CONSTEVAL
6959-# else
6960-# define FMT_CONSTEVAL
6961-# endif
6962-#endif
6963-
6964-#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
6965-# if defined(__cpp_nontype_template_args) && \
6966- ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
6967- __cpp_nontype_template_args >= 201911L) && \
6968- !defined(__NVCOMPILER) && !defined(__LCC__)
6969-# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
6970-# else
6971-# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
6972-# endif
6973-#endif
6974-
6975-// Enable minimal optimizations for more compact code in debug mode.
6976-FMT_GCC_PRAGMA("GCC push_options")
6977-#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \
6978- !defined(__CUDACC__)
6979-FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
6980-#endif
6981-
6982-FMT_BEGIN_NAMESPACE
6983-
6984-// Implementations of enable_if_t and other metafunctions for older systems.
6985-template <bool B, typename T = void>
6986-using enable_if_t = typename std::enable_if<B, T>::type;
6987-template <bool B, typename T, typename F>
6988-using conditional_t = typename std::conditional<B, T, F>::type;
6989-template <bool B> using bool_constant = std::integral_constant<bool, B>;
6990-template <typename T>
6991-using remove_reference_t = typename std::remove_reference<T>::type;
6992-template <typename T>
6993-using remove_const_t = typename std::remove_const<T>::type;
6994-template <typename T>
6995-using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
6996-template <typename T> struct type_identity { using type = T; };
6997-template <typename T> using type_identity_t = typename type_identity<T>::type;
6998-template <typename T>
6999-using underlying_t = typename std::underlying_type<T>::type;
7000-
7001-// Checks whether T is a container with contiguous storage.
7002-template <typename T> struct is_contiguous : std::false_type {};
7003-template <typename Char>
7004-struct is_contiguous<std::basic_string<Char>> : std::true_type {};
7005-
7006-struct monostate {
7007- constexpr monostate() {}
7008-};
7009-
7010-// An enable_if helper to be used in template parameters which results in much
7011-// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
7012-// to workaround a bug in MSVC 2019 (see #1140 and #1186).
7013-#ifdef FMT_DOC
7014-# define FMT_ENABLE_IF(...)
7015-#else
7016-# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
7017-#endif
7018-
7019-// This is defined in core.h instead of format.h to avoid injecting in std.
7020-#ifdef __cpp_lib_byte
7021-inline auto format_as(std::byte b) -> unsigned char {
7022- return static_cast<unsigned char>(b);
7023-}
7024-#endif
7025-
7026-namespace detail {
7027-// Suppresses "unused variable" warnings with the method described in
7028-// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
7029-// (void)var does not work on many Intel compilers.
7030-template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
7031-
7032-constexpr FMT_INLINE auto is_constant_evaluated(
7033- bool default_value = false) noexcept -> bool {
7034-// Workaround for incompatibility between libstdc++ consteval-based
7035-// std::is_constant_evaluated() implementation and clang-14.
7036-// https://github.com/fmtlib/fmt/issues/3247
7037-#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \
7038- _GLIBCXX_RELEASE >= 12 && \
7039- (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
7040- ignore_unused(default_value);
7041- return __builtin_is_constant_evaluated();
7042-#elif defined(__cpp_lib_is_constant_evaluated)
7043- ignore_unused(default_value);
7044- return std::is_constant_evaluated();
7045-#else
7046- return default_value;
7047-#endif
7048-}
7049-
7050-// Suppresses "conditional expression is constant" warnings.
7051-template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
7052- return value;
7053-}
7054-
7055-FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
7056- const char* message);
7057-
7058-#ifndef FMT_ASSERT
7059-# ifdef NDEBUG
7060-// FMT_ASSERT is not empty to avoid -Wempty-body.
7061-# define FMT_ASSERT(condition, message) \
7062- fmt::detail::ignore_unused((condition), (message))
7063-# else
7064-# define FMT_ASSERT(condition, message) \
7065- ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
7066- ? (void)0 \
7067- : fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
7068-# endif
7069-#endif
7070-
7071-#if defined(FMT_USE_STRING_VIEW)
7072-template <typename Char> using std_string_view = std::basic_string_view<Char>;
7073-#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
7074-template <typename Char>
7075-using std_string_view = std::experimental::basic_string_view<Char>;
7076-#else
7077-template <typename T> struct std_string_view {};
7078-#endif
7079-
7080-#ifdef FMT_USE_INT128
7081-// Do nothing.
7082-#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
7083- !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
7084-# define FMT_USE_INT128 1
7085-using int128_opt = __int128_t; // An optional native 128-bit integer.
7086-using uint128_opt = __uint128_t;
7087-template <typename T> inline auto convert_for_visit(T value) -> T {
7088- return value;
7089-}
7090-#else
7091-# define FMT_USE_INT128 0
7092-#endif
7093-#if !FMT_USE_INT128
7094-enum class int128_opt {};
7095-enum class uint128_opt {};
7096-// Reduce template instantiations.
7097-template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
7098-#endif
7099-
7100-// Casts a nonnegative integer to unsigned.
7101-template <typename Int>
7102-FMT_CONSTEXPR auto to_unsigned(Int value) ->
7103- typename std::make_unsigned<Int>::type {
7104- FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
7105- return static_cast<typename std::make_unsigned<Int>::type>(value);
7106-}
7107-
7108-FMT_CONSTEXPR inline auto is_utf8() -> bool {
7109- FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7";
7110-
7111- // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
7112- using uchar = unsigned char;
7113- return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 &&
7114- uchar(section[1]) == 0xA7);
7115-}
7116-} // namespace detail
7117-
7118-/**
7119- An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
7120- subset of the API. ``fmt::basic_string_view`` is used for format strings even
7121- if ``std::string_view`` is available to prevent issues when a library is
7122- compiled with a different ``-std`` option than the client code (which is not
7123- recommended).
7124- */
7125-FMT_EXPORT
7126-template <typename Char> class basic_string_view {
7127- private:
7128- const Char* data_;
7129- size_t size_;
7130-
7131- public:
7132- using value_type = Char;
7133- using iterator = const Char*;
7134-
7135- constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
7136-
7137- /** Constructs a string reference object from a C string and a size. */
7138- constexpr basic_string_view(const Char* s, size_t count) noexcept
7139- : data_(s), size_(count) {}
7140-
7141- /**
7142- \rst
7143- Constructs a string reference object from a C string computing
7144- the size with ``std::char_traits<Char>::length``.
7145- \endrst
7146- */
7147- FMT_CONSTEXPR_CHAR_TRAITS
7148- FMT_INLINE
7149- basic_string_view(const Char* s)
7150- : data_(s),
7151- size_(detail::const_check(std::is_same<Char, char>::value &&
7152- !detail::is_constant_evaluated(true))
7153- ? std::strlen(reinterpret_cast<const char*>(s))
7154- : std::char_traits<Char>::length(s)) {}
7155-
7156- /** Constructs a string reference from a ``std::basic_string`` object. */
7157- template <typename Traits, typename Alloc>
7158- FMT_CONSTEXPR basic_string_view(
7159- const std::basic_string<Char, Traits, Alloc>& s) noexcept
7160- : data_(s.data()), size_(s.size()) {}
7161-
7162- template <typename S, FMT_ENABLE_IF(std::is_same<
7163- S, detail::std_string_view<Char>>::value)>
7164- FMT_CONSTEXPR basic_string_view(S s) noexcept
7165- : data_(s.data()), size_(s.size()) {}
7166-
7167- /** Returns a pointer to the string data. */
7168- constexpr auto data() const noexcept -> const Char* { return data_; }
7169-
7170- /** Returns the string size. */
7171- constexpr auto size() const noexcept -> size_t { return size_; }
7172-
7173- constexpr auto begin() const noexcept -> iterator { return data_; }
7174- constexpr auto end() const noexcept -> iterator { return data_ + size_; }
7175-
7176- constexpr auto operator[](size_t pos) const noexcept -> const Char& {
7177- return data_[pos];
7178- }
7179-
7180- FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
7181- data_ += n;
7182- size_ -= n;
7183- }
7184-
7185- FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(
7186- basic_string_view<Char> sv) const noexcept {
7187- return size_ >= sv.size_ &&
7188- std::char_traits<Char>::compare(data_, sv.data_, sv.size_) == 0;
7189- }
7190- FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept {
7191- return size_ >= 1 && std::char_traits<Char>::eq(*data_, c);
7192- }
7193- FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const {
7194- return starts_with(basic_string_view<Char>(s));
7195- }
7196-
7197- // Lexicographically compare this string reference to other.
7198- FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
7199- size_t str_size = size_ < other.size_ ? size_ : other.size_;
7200- int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
7201- if (result == 0)
7202- result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
7203- return result;
7204- }
7205-
7206- FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs,
7207- basic_string_view rhs)
7208- -> bool {
7209- return lhs.compare(rhs) == 0;
7210- }
7211- friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
7212- return lhs.compare(rhs) != 0;
7213- }
7214- friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
7215- return lhs.compare(rhs) < 0;
7216- }
7217- friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
7218- return lhs.compare(rhs) <= 0;
7219- }
7220- friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
7221- return lhs.compare(rhs) > 0;
7222- }
7223- friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
7224- return lhs.compare(rhs) >= 0;
7225- }
7226-};
7227-
7228-FMT_EXPORT
7229-using string_view = basic_string_view<char>;
7230-
7231-/** Specifies if ``T`` is a character type. Can be specialized by users. */
7232-FMT_EXPORT
7233-template <typename T> struct is_char : std::false_type {};
7234-template <> struct is_char<char> : std::true_type {};
7235-
7236-namespace detail {
7237-
7238-// A base class for compile-time strings.
7239-struct compile_string {};
7240-
7241-template <typename S>
7242-struct is_compile_string : std::is_base_of<compile_string, S> {};
7243-
7244-template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
7245-FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
7246- return s;
7247-}
7248-template <typename Char, typename Traits, typename Alloc>
7249-inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
7250- -> basic_string_view<Char> {
7251- return s;
7252-}
7253-template <typename Char>
7254-constexpr auto to_string_view(basic_string_view<Char> s)
7255- -> basic_string_view<Char> {
7256- return s;
7257-}
7258-template <typename Char,
7259- FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
7260-inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> {
7261- return s;
7262-}
7263-template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
7264-constexpr auto to_string_view(const S& s)
7265- -> basic_string_view<typename S::char_type> {
7266- return basic_string_view<typename S::char_type>(s);
7267-}
7268-void to_string_view(...);
7269-
7270-// Specifies whether S is a string type convertible to fmt::basic_string_view.
7271-// It should be a constexpr function but MSVC 2017 fails to compile it in
7272-// enable_if and MSVC 2015 fails to compile it as an alias template.
7273-// ADL is intentionally disabled as to_string_view is not an extension point.
7274-template <typename S>
7275-struct is_string
7276- : std::is_class<decltype(detail::to_string_view(std::declval<S>()))> {};
7277-
7278-template <typename S, typename = void> struct char_t_impl {};
7279-template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
7280- using result = decltype(to_string_view(std::declval<S>()));
7281- using type = typename result::value_type;
7282-};
7283-
7284-enum class type {
7285- none_type,
7286- // Integer types should go first,
7287- int_type,
7288- uint_type,
7289- long_long_type,
7290- ulong_long_type,
7291- int128_type,
7292- uint128_type,
7293- bool_type,
7294- char_type,
7295- last_integer_type = char_type,
7296- // followed by floating-point types.
7297- float_type,
7298- double_type,
7299- long_double_type,
7300- last_numeric_type = long_double_type,
7301- cstring_type,
7302- string_type,
7303- pointer_type,
7304- custom_type
7305-};
7306-
7307-// Maps core type T to the corresponding type enum constant.
7308-template <typename T, typename Char>
7309-struct type_constant : std::integral_constant<type, type::custom_type> {};
7310-
7311-#define FMT_TYPE_CONSTANT(Type, constant) \
7312- template <typename Char> \
7313- struct type_constant<Type, Char> \
7314- : std::integral_constant<type, type::constant> {}
7315-
7316-FMT_TYPE_CONSTANT(int, int_type);
7317-FMT_TYPE_CONSTANT(unsigned, uint_type);
7318-FMT_TYPE_CONSTANT(long long, long_long_type);
7319-FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
7320-FMT_TYPE_CONSTANT(int128_opt, int128_type);
7321-FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
7322-FMT_TYPE_CONSTANT(bool, bool_type);
7323-FMT_TYPE_CONSTANT(Char, char_type);
7324-FMT_TYPE_CONSTANT(float, float_type);
7325-FMT_TYPE_CONSTANT(double, double_type);
7326-FMT_TYPE_CONSTANT(long double, long_double_type);
7327-FMT_TYPE_CONSTANT(const Char*, cstring_type);
7328-FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
7329-FMT_TYPE_CONSTANT(const void*, pointer_type);
7330-
7331-constexpr bool is_integral_type(type t) {
7332- return t > type::none_type && t <= type::last_integer_type;
7333-}
7334-constexpr bool is_arithmetic_type(type t) {
7335- return t > type::none_type && t <= type::last_numeric_type;
7336-}
7337-
7338-constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
7339-constexpr auto in(type t, int set) -> bool {
7340- return ((set >> static_cast<int>(t)) & 1) != 0;
7341-}
7342-
7343-// Bitsets of types.
7344-enum {
7345- sint_set =
7346- set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
7347- uint_set = set(type::uint_type) | set(type::ulong_long_type) |
7348- set(type::uint128_type),
7349- bool_set = set(type::bool_type),
7350- char_set = set(type::char_type),
7351- float_set = set(type::float_type) | set(type::double_type) |
7352- set(type::long_double_type),
7353- string_set = set(type::string_type),
7354- cstring_set = set(type::cstring_type),
7355- pointer_set = set(type::pointer_type)
7356-};
7357-
7358-FMT_NORETURN FMT_API void throw_format_error(const char* message);
7359-
7360-struct error_handler {
7361- constexpr error_handler() = default;
7362-
7363- // This function is intentionally not constexpr to give a compile-time error.
7364- FMT_NORETURN void on_error(const char* message) {
7365- throw_format_error(message);
7366- }
7367-};
7368-} // namespace detail
7369-
7370-/** String's character type. */
7371-template <typename S> using char_t = typename detail::char_t_impl<S>::type;
7372-
7373-/**
7374- \rst
7375- Parsing context consisting of a format string range being parsed and an
7376- argument counter for automatic indexing.
7377- You can use the ``format_parse_context`` type alias for ``char`` instead.
7378- \endrst
7379- */
7380-FMT_EXPORT
7381-template <typename Char> class basic_format_parse_context {
7382- private:
7383- basic_string_view<Char> format_str_;
7384- int next_arg_id_;
7385-
7386- FMT_CONSTEXPR void do_check_arg_id(int id);
7387-
7388- public:
7389- using char_type = Char;
7390- using iterator = const Char*;
7391-
7392- explicit constexpr basic_format_parse_context(
7393- basic_string_view<Char> format_str, int next_arg_id = 0)
7394- : format_str_(format_str), next_arg_id_(next_arg_id) {}
7395-
7396- /**
7397- Returns an iterator to the beginning of the format string range being
7398- parsed.
7399- */
7400- constexpr auto begin() const noexcept -> iterator {
7401- return format_str_.begin();
7402- }
7403-
7404- /**
7405- Returns an iterator past the end of the format string range being parsed.
7406- */
7407- constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
7408-
7409- /** Advances the begin iterator to ``it``. */
7410- FMT_CONSTEXPR void advance_to(iterator it) {
7411- format_str_.remove_prefix(detail::to_unsigned(it - begin()));
7412- }
7413-
7414- /**
7415- Reports an error if using the manual argument indexing; otherwise returns
7416- the next argument index and switches to the automatic indexing.
7417- */
7418- FMT_CONSTEXPR auto next_arg_id() -> int {
7419- if (next_arg_id_ < 0) {
7420- detail::throw_format_error(
7421- "cannot switch from manual to automatic argument indexing");
7422- return 0;
7423- }
7424- int id = next_arg_id_++;
7425- do_check_arg_id(id);
7426- return id;
7427- }
7428-
7429- /**
7430- Reports an error if using the automatic argument indexing; otherwise
7431- switches to the manual indexing.
7432- */
7433- FMT_CONSTEXPR void check_arg_id(int id) {
7434- if (next_arg_id_ > 0) {
7435- detail::throw_format_error(
7436- "cannot switch from automatic to manual argument indexing");
7437- return;
7438- }
7439- next_arg_id_ = -1;
7440- do_check_arg_id(id);
7441- }
7442- FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
7443- FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
7444-};
7445-
7446-FMT_EXPORT
7447-using format_parse_context = basic_format_parse_context<char>;
7448-
7449-namespace detail {
7450-// A parse context with extra data used only in compile-time checks.
7451-template <typename Char>
7452-class compile_parse_context : public basic_format_parse_context<Char> {
7453- private:
7454- int num_args_;
7455- const type* types_;
7456- using base = basic_format_parse_context<Char>;
7457-
7458- public:
7459- explicit FMT_CONSTEXPR compile_parse_context(
7460- basic_string_view<Char> format_str, int num_args, const type* types,
7461- int next_arg_id = 0)
7462- : base(format_str, next_arg_id), num_args_(num_args), types_(types) {}
7463-
7464- constexpr auto num_args() const -> int { return num_args_; }
7465- constexpr auto arg_type(int id) const -> type { return types_[id]; }
7466-
7467- FMT_CONSTEXPR auto next_arg_id() -> int {
7468- int id = base::next_arg_id();
7469- if (id >= num_args_) throw_format_error("argument not found");
7470- return id;
7471- }
7472-
7473- FMT_CONSTEXPR void check_arg_id(int id) {
7474- base::check_arg_id(id);
7475- if (id >= num_args_) throw_format_error("argument not found");
7476- }
7477- using base::check_arg_id;
7478-
7479- FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
7480- detail::ignore_unused(arg_id);
7481-#if !defined(__LCC__)
7482- if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
7483- throw_format_error("width/precision is not integer");
7484-#endif
7485- }
7486-};
7487-
7488-// Extracts a reference to the container from back_insert_iterator.
7489-template <typename Container>
7490-inline auto get_container(std::back_insert_iterator<Container> it)
7491- -> Container& {
7492- using base = std::back_insert_iterator<Container>;
7493- struct accessor : base {
7494- accessor(base b) : base(b) {}
7495- using base::container;
7496- };
7497- return *accessor(it).container;
7498-}
7499-
7500-template <typename Char, typename InputIt, typename OutputIt>
7501-FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out)
7502- -> OutputIt {
7503- while (begin != end) *out++ = static_cast<Char>(*begin++);
7504- return out;
7505-}
7506-
7507-template <typename Char, typename T, typename U,
7508- FMT_ENABLE_IF(
7509- std::is_same<remove_const_t<T>, U>::value&& is_char<U>::value)>
7510-FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
7511- if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
7512- auto size = to_unsigned(end - begin);
7513- if (size > 0) memcpy(out, begin, size * sizeof(U));
7514- return out + size;
7515-}
7516-
7517-/**
7518- \rst
7519- A contiguous memory buffer with an optional growing ability. It is an internal
7520- class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
7521- \endrst
7522- */
7523-template <typename T> class buffer {
7524- private:
7525- T* ptr_;
7526- size_t size_;
7527- size_t capacity_;
7528-
7529- protected:
7530- // Don't initialize ptr_ since it is not accessed to save a few cycles.
7531- FMT_MSC_WARNING(suppress : 26495)
7532- buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
7533-
7534- FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
7535- : ptr_(p), size_(sz), capacity_(cap) {}
7536-
7537- FMT_CONSTEXPR20 ~buffer() = default;
7538- buffer(buffer&&) = default;
7539-
7540- /** Sets the buffer data and capacity. */
7541- FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
7542- ptr_ = buf_data;
7543- capacity_ = buf_capacity;
7544- }
7545-
7546- /** Increases the buffer capacity to hold at least *capacity* elements. */
7547- virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
7548-
7549- public:
7550- using value_type = T;
7551- using const_reference = const T&;
7552-
7553- buffer(const buffer&) = delete;
7554- void operator=(const buffer&) = delete;
7555-
7556- FMT_INLINE auto begin() noexcept -> T* { return ptr_; }
7557- FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; }
7558-
7559- FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; }
7560- FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; }
7561-
7562- /** Returns the size of this buffer. */
7563- constexpr auto size() const noexcept -> size_t { return size_; }
7564-
7565- /** Returns the capacity of this buffer. */
7566- constexpr auto capacity() const noexcept -> size_t { return capacity_; }
7567-
7568- /** Returns a pointer to the buffer data. */
7569- FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
7570-
7571- /** Returns a pointer to the buffer data. */
7572- FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
7573-
7574- /** Clears this buffer. */
7575- void clear() { size_ = 0; }
7576-
7577- // Tries resizing the buffer to contain *count* elements. If T is a POD type
7578- // the new elements may not be initialized.
7579- FMT_CONSTEXPR20 void try_resize(size_t count) {
7580- try_reserve(count);
7581- size_ = count <= capacity_ ? count : capacity_;
7582- }
7583-
7584- // Tries increasing the buffer capacity to *new_capacity*. It can increase the
7585- // capacity by a smaller amount than requested but guarantees there is space
7586- // for at least one additional element either by increasing the capacity or by
7587- // flushing the buffer if it is full.
7588- FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
7589- if (new_capacity > capacity_) grow(new_capacity);
7590- }
7591-
7592- FMT_CONSTEXPR20 void push_back(const T& value) {
7593- try_reserve(size_ + 1);
7594- ptr_[size_++] = value;
7595- }
7596-
7597- /** Appends data to the end of the buffer. */
7598- template <typename U> void append(const U* begin, const U* end);
7599-
7600- template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
7601- return ptr_[index];
7602- }
7603- template <typename Idx>
7604- FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
7605- return ptr_[index];
7606- }
7607-};
7608-
7609-struct buffer_traits {
7610- explicit buffer_traits(size_t) {}
7611- auto count() const -> size_t { return 0; }
7612- auto limit(size_t size) -> size_t { return size; }
7613-};
7614-
7615-class fixed_buffer_traits {
7616- private:
7617- size_t count_ = 0;
7618- size_t limit_;
7619-
7620- public:
7621- explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
7622- auto count() const -> size_t { return count_; }
7623- auto limit(size_t size) -> size_t {
7624- size_t n = limit_ > count_ ? limit_ - count_ : 0;
7625- count_ += size;
7626- return size < n ? size : n;
7627- }
7628-};
7629-
7630-// A buffer that writes to an output iterator when flushed.
7631-template <typename OutputIt, typename T, typename Traits = buffer_traits>
7632-class iterator_buffer final : public Traits, public buffer<T> {
7633- private:
7634- OutputIt out_;
7635- enum { buffer_size = 256 };
7636- T data_[buffer_size];
7637-
7638- protected:
7639- FMT_CONSTEXPR20 void grow(size_t) override {
7640- if (this->size() == buffer_size) flush();
7641- }
7642-
7643- void flush() {
7644- auto size = this->size();
7645- this->clear();
7646- out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
7647- }
7648-
7649- public:
7650- explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
7651- : Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {}
7652- iterator_buffer(iterator_buffer&& other)
7653- : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
7654- ~iterator_buffer() { flush(); }
7655-
7656- auto out() -> OutputIt {
7657- flush();
7658- return out_;
7659- }
7660- auto count() const -> size_t { return Traits::count() + this->size(); }
7661-};
7662-
7663-template <typename T>
7664-class iterator_buffer<T*, T, fixed_buffer_traits> final
7665- : public fixed_buffer_traits,
7666- public buffer<T> {
7667- private:
7668- T* out_;
7669- enum { buffer_size = 256 };
7670- T data_[buffer_size];
7671-
7672- protected:
7673- FMT_CONSTEXPR20 void grow(size_t) override {
7674- if (this->size() == this->capacity()) flush();
7675- }
7676-
7677- void flush() {
7678- size_t n = this->limit(this->size());
7679- if (this->data() == out_) {
7680- out_ += n;
7681- this->set(data_, buffer_size);
7682- }
7683- this->clear();
7684- }
7685-
7686- public:
7687- explicit iterator_buffer(T* out, size_t n = buffer_size)
7688- : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {}
7689- iterator_buffer(iterator_buffer&& other)
7690- : fixed_buffer_traits(other),
7691- buffer<T>(std::move(other)),
7692- out_(other.out_) {
7693- if (this->data() != out_) {
7694- this->set(data_, buffer_size);
7695- this->clear();
7696- }
7697- }
7698- ~iterator_buffer() { flush(); }
7699-
7700- auto out() -> T* {
7701- flush();
7702- return out_;
7703- }
7704- auto count() const -> size_t {
7705- return fixed_buffer_traits::count() + this->size();
7706- }
7707-};
7708-
7709-template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
7710- protected:
7711- FMT_CONSTEXPR20 void grow(size_t) override {}
7712-
7713- public:
7714- explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
7715-
7716- auto out() -> T* { return &*this->end(); }
7717-};
7718-
7719-// A buffer that writes to a container with the contiguous storage.
7720-template <typename Container>
7721-class iterator_buffer<std::back_insert_iterator<Container>,
7722- enable_if_t<is_contiguous<Container>::value,
7723- typename Container::value_type>>
7724- final : public buffer<typename Container::value_type> {
7725- private:
7726- Container& container_;
7727-
7728- protected:
7729- FMT_CONSTEXPR20 void grow(size_t capacity) override {
7730- container_.resize(capacity);
7731- this->set(&container_[0], capacity);
7732- }
7733-
7734- public:
7735- explicit iterator_buffer(Container& c)
7736- : buffer<typename Container::value_type>(c.size()), container_(c) {}
7737- explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
7738- : iterator_buffer(get_container(out)) {}
7739-
7740- auto out() -> std::back_insert_iterator<Container> {
7741- return std::back_inserter(container_);
7742- }
7743-};
7744-
7745-// A buffer that counts the number of code units written discarding the output.
7746-template <typename T = char> class counting_buffer final : public buffer<T> {
7747- private:
7748- enum { buffer_size = 256 };
7749- T data_[buffer_size];
7750- size_t count_ = 0;
7751-
7752- protected:
7753- FMT_CONSTEXPR20 void grow(size_t) override {
7754- if (this->size() != buffer_size) return;
7755- count_ += this->size();
7756- this->clear();
7757- }
7758-
7759- public:
7760- counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
7761-
7762- auto count() -> size_t { return count_ + this->size(); }
7763-};
7764-} // namespace detail
7765-
7766-template <typename Char>
7767-FMT_CONSTEXPR void basic_format_parse_context<Char>::do_check_arg_id(int id) {
7768- // Argument id is only checked at compile-time during parsing because
7769- // formatting has its own validation.
7770- if (detail::is_constant_evaluated() &&
7771- (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
7772- using context = detail::compile_parse_context<Char>;
7773- if (id >= static_cast<context*>(this)->num_args())
7774- detail::throw_format_error("argument not found");
7775- }
7776-}
7777-
7778-template <typename Char>
7779-FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
7780- int arg_id) {
7781- if (detail::is_constant_evaluated() &&
7782- (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
7783- using context = detail::compile_parse_context<Char>;
7784- static_cast<context*>(this)->check_dynamic_spec(arg_id);
7785- }
7786-}
7787-
7788-FMT_EXPORT template <typename Context> class basic_format_arg;
7789-FMT_EXPORT template <typename Context> class basic_format_args;
7790-FMT_EXPORT template <typename Context> class dynamic_format_arg_store;
7791-
7792-// A formatter for objects of type T.
7793-FMT_EXPORT
7794-template <typename T, typename Char = char, typename Enable = void>
7795-struct formatter {
7796- // A deleted default constructor indicates a disabled formatter.
7797- formatter() = delete;
7798-};
7799-
7800-// Specifies if T has an enabled formatter specialization. A type can be
7801-// formattable even if it doesn't have a formatter e.g. via a conversion.
7802-template <typename T, typename Context>
7803-using has_formatter =
7804- std::is_constructible<typename Context::template formatter_type<T>>;
7805-
7806-// An output iterator that appends to a buffer.
7807-// It is used to reduce symbol sizes for the common case.
7808-class appender : public std::back_insert_iterator<detail::buffer<char>> {
7809- using base = std::back_insert_iterator<detail::buffer<char>>;
7810-
7811- public:
7812- using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
7813- appender(base it) noexcept : base(it) {}
7814- FMT_UNCHECKED_ITERATOR(appender);
7815-
7816- auto operator++() noexcept -> appender& { return *this; }
7817- auto operator++(int) noexcept -> appender { return *this; }
7818-};
7819-
7820-namespace detail {
7821-
7822-template <typename Context, typename T>
7823-constexpr auto has_const_formatter_impl(T*)
7824- -> decltype(typename Context::template formatter_type<T>().format(
7825- std::declval<const T&>(), std::declval<Context&>()),
7826- true) {
7827- return true;
7828-}
7829-template <typename Context>
7830-constexpr auto has_const_formatter_impl(...) -> bool {
7831- return false;
7832-}
7833-template <typename T, typename Context>
7834-constexpr auto has_const_formatter() -> bool {
7835- return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
7836-}
7837-
7838-template <typename T>
7839-using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
7840- std::back_insert_iterator<buffer<T>>>;
7841-
7842-// Maps an output iterator to a buffer.
7843-template <typename T, typename OutputIt>
7844-auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
7845- return iterator_buffer<OutputIt, T>(out);
7846-}
7847-template <typename T, typename Buf,
7848- FMT_ENABLE_IF(std::is_base_of<buffer<char>, Buf>::value)>
7849-auto get_buffer(std::back_insert_iterator<Buf> out) -> buffer<char>& {
7850- return get_container(out);
7851-}
7852-
7853-template <typename Buf, typename OutputIt>
7854-FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
7855- return buf.out();
7856-}
7857-template <typename T, typename OutputIt>
7858-auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
7859- return out;
7860-}
7861-
7862-struct view {};
7863-
7864-template <typename Char, typename T> struct named_arg : view {
7865- const Char* name;
7866- const T& value;
7867- named_arg(const Char* n, const T& v) : name(n), value(v) {}
7868-};
7869-
7870-template <typename Char> struct named_arg_info {
7871- const Char* name;
7872- int id;
7873-};
7874-
7875-template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
7876-struct arg_data {
7877- // args_[0].named_args points to named_args_ to avoid bloating format_args.
7878- // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
7879- T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
7880- named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
7881-
7882- template <typename... U>
7883- arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
7884- arg_data(const arg_data& other) = delete;
7885- auto args() const -> const T* { return args_ + 1; }
7886- auto named_args() -> named_arg_info<Char>* { return named_args_; }
7887-};
7888-
7889-template <typename T, typename Char, size_t NUM_ARGS>
7890-struct arg_data<T, Char, NUM_ARGS, 0> {
7891- // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
7892- T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
7893-
7894- template <typename... U>
7895- FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
7896- FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; }
7897- FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t {
7898- return nullptr;
7899- }
7900-};
7901-
7902-template <typename Char>
7903-inline void init_named_args(named_arg_info<Char>*, int, int) {}
7904-
7905-template <typename T> struct is_named_arg : std::false_type {};
7906-template <typename T> struct is_statically_named_arg : std::false_type {};
7907-
7908-template <typename T, typename Char>
7909-struct is_named_arg<named_arg<Char, T>> : std::true_type {};
7910-
7911-template <typename Char, typename T, typename... Tail,
7912- FMT_ENABLE_IF(!is_named_arg<T>::value)>
7913-void init_named_args(named_arg_info<Char>* named_args, int arg_count,
7914- int named_arg_count, const T&, const Tail&... args) {
7915- init_named_args(named_args, arg_count + 1, named_arg_count, args...);
7916-}
7917-
7918-template <typename Char, typename T, typename... Tail,
7919- FMT_ENABLE_IF(is_named_arg<T>::value)>
7920-void init_named_args(named_arg_info<Char>* named_args, int arg_count,
7921- int named_arg_count, const T& arg, const Tail&... args) {
7922- named_args[named_arg_count++] = {arg.name, arg_count};
7923- init_named_args(named_args, arg_count + 1, named_arg_count, args...);
7924-}
7925-
7926-template <typename... Args>
7927-FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int,
7928- const Args&...) {}
7929-
7930-template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
7931-template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
7932- return (B1 ? 1 : 0) + count<B2, Tail...>();
7933-}
7934-
7935-template <typename... Args> constexpr auto count_named_args() -> size_t {
7936- return count<is_named_arg<Args>::value...>();
7937-}
7938-
7939-template <typename... Args>
7940-constexpr auto count_statically_named_args() -> size_t {
7941- return count<is_statically_named_arg<Args>::value...>();
7942-}
7943-
7944-struct unformattable {};
7945-struct unformattable_char : unformattable {};
7946-struct unformattable_pointer : unformattable {};
7947-
7948-template <typename Char> struct string_value {
7949- const Char* data;
7950- size_t size;
7951-};
7952-
7953-template <typename Char> struct named_arg_value {
7954- const named_arg_info<Char>* data;
7955- size_t size;
7956-};
7957-
7958-template <typename Context> struct custom_value {
7959- using parse_context = typename Context::parse_context_type;
7960- void* value;
7961- void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
7962-};
7963-
7964-// A formatting argument value.
7965-template <typename Context> class value {
7966- public:
7967- using char_type = typename Context::char_type;
7968-
7969- union {
7970- monostate no_value;
7971- int int_value;
7972- unsigned uint_value;
7973- long long long_long_value;
7974- unsigned long long ulong_long_value;
7975- int128_opt int128_value;
7976- uint128_opt uint128_value;
7977- bool bool_value;
7978- char_type char_value;
7979- float float_value;
7980- double double_value;
7981- long double long_double_value;
7982- const void* pointer;
7983- string_value<char_type> string;
7984- custom_value<Context> custom;
7985- named_arg_value<char_type> named_args;
7986- };
7987-
7988- constexpr FMT_INLINE value() : no_value() {}
7989- constexpr FMT_INLINE value(int val) : int_value(val) {}
7990- constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
7991- constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
7992- constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
7993- FMT_INLINE value(int128_opt val) : int128_value(val) {}
7994- FMT_INLINE value(uint128_opt val) : uint128_value(val) {}
7995- constexpr FMT_INLINE value(float val) : float_value(val) {}
7996- constexpr FMT_INLINE value(double val) : double_value(val) {}
7997- FMT_INLINE value(long double val) : long_double_value(val) {}
7998- constexpr FMT_INLINE value(bool val) : bool_value(val) {}
7999- constexpr FMT_INLINE value(char_type val) : char_value(val) {}
8000- FMT_CONSTEXPR FMT_INLINE value(const char_type* val) {
8001- string.data = val;
8002- if (is_constant_evaluated()) string.size = {};
8003- }
8004- FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val) {
8005- string.data = val.data();
8006- string.size = val.size();
8007- }
8008- FMT_INLINE value(const void* val) : pointer(val) {}
8009- FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
8010- : named_args{args, size} {}
8011-
8012- template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
8013- using value_type = remove_const_t<T>;
8014- custom.value = const_cast<value_type*>(&val);
8015- // Get the formatter type through the context to allow different contexts
8016- // have different extension points, e.g. `formatter<T>` for `format` and
8017- // `printf_formatter<T>` for `printf`.
8018- custom.format = format_custom_arg<
8019- value_type, typename Context::template formatter_type<value_type>>;
8020- }
8021- value(unformattable);
8022- value(unformattable_char);
8023- value(unformattable_pointer);
8024-
8025- private:
8026- // Formats an argument of a custom type, such as a user-defined class.
8027- template <typename T, typename Formatter>
8028- static void format_custom_arg(void* arg,
8029- typename Context::parse_context_type& parse_ctx,
8030- Context& ctx) {
8031- auto f = Formatter();
8032- parse_ctx.advance_to(f.parse(parse_ctx));
8033- using qualified_type =
8034- conditional_t<has_const_formatter<T, Context>(), const T, T>;
8035- ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
8036- }
8037-};
8038-
8039-// To minimize the number of types we need to deal with, long is translated
8040-// either to int or to long long depending on its size.
8041-enum { long_short = sizeof(long) == sizeof(int) };
8042-using long_type = conditional_t<long_short, int, long long>;
8043-using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
8044-
8045-template <typename T> struct format_as_result {
8046- template <typename U,
8047- FMT_ENABLE_IF(std::is_enum<U>::value || std::is_class<U>::value)>
8048- static auto map(U*) -> decltype(format_as(std::declval<U>()));
8049- static auto map(...) -> void;
8050-
8051- using type = decltype(map(static_cast<T*>(nullptr)));
8052-};
8053-template <typename T> using format_as_t = typename format_as_result<T>::type;
8054-
8055-template <typename T>
8056-struct has_format_as
8057- : bool_constant<!std::is_same<format_as_t<T>, void>::value> {};
8058-
8059-// Maps formatting arguments to core types.
8060-// arg_mapper reports errors by returning unformattable instead of using
8061-// static_assert because it's used in the is_formattable trait.
8062-template <typename Context> struct arg_mapper {
8063- using char_type = typename Context::char_type;
8064-
8065- FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
8066- FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned {
8067- return val;
8068- }
8069- FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
8070- FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned {
8071- return val;
8072- }
8073- FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
8074- FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
8075- FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
8076- FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type {
8077- return val;
8078- }
8079- FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
8080- FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val)
8081- -> unsigned long long {
8082- return val;
8083- }
8084- FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt {
8085- return val;
8086- }
8087- FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt {
8088- return val;
8089- }
8090- FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
8091-
8092- template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
8093- std::is_same<T, char_type>::value)>
8094- FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
8095- return val;
8096- }
8097- template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value ||
8098-#ifdef __cpp_char8_t
8099- std::is_same<T, char8_t>::value ||
8100-#endif
8101- std::is_same<T, char16_t>::value ||
8102- std::is_same<T, char32_t>::value) &&
8103- !std::is_same<T, char_type>::value,
8104- int> = 0>
8105- FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char {
8106- return {};
8107- }
8108-
8109- FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
8110- FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
8111- FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double {
8112- return val;
8113- }
8114-
8115- FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* {
8116- return val;
8117- }
8118- FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
8119- return val;
8120- }
8121- template <typename T,
8122- FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
8123- std::is_same<char_type, char_t<T>>::value)>
8124- FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
8125- -> basic_string_view<char_type> {
8126- return to_string_view(val);
8127- }
8128- template <typename T,
8129- FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
8130- !std::is_same<char_type, char_t<T>>::value)>
8131- FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
8132- return {};
8133- }
8134-
8135- FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
8136- FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
8137- return val;
8138- }
8139- FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* {
8140- return val;
8141- }
8142-
8143- // Use SFINAE instead of a const T* parameter to avoid a conflict with the
8144- // array overload.
8145- template <
8146- typename T,
8147- FMT_ENABLE_IF(
8148- std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
8149- std::is_function<typename std::remove_pointer<T>::type>::value ||
8150- (std::is_convertible<const T&, const void*>::value &&
8151- !std::is_convertible<const T&, const char_type*>::value &&
8152- !has_formatter<T, Context>::value))>
8153- FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
8154- return {};
8155- }
8156-
8157- template <typename T, std::size_t N,
8158- FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
8159- FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
8160- return values;
8161- }
8162-
8163- // Only map owning types because mapping views can be unsafe.
8164- template <typename T, typename U = format_as_t<T>,
8165- FMT_ENABLE_IF(std::is_arithmetic<U>::value)>
8166- FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) {
8167- return map(format_as(val));
8168- }
8169-
8170- template <typename T, typename U = remove_const_t<T>>
8171- struct formattable : bool_constant<has_const_formatter<U, Context>() ||
8172- (has_formatter<U, Context>::value &&
8173- !std::is_const<T>::value)> {};
8174-
8175- template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
8176- FMT_CONSTEXPR FMT_INLINE auto do_map(T& val) -> T& {
8177- return val;
8178- }
8179- template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
8180- FMT_CONSTEXPR FMT_INLINE auto do_map(T&) -> unformattable {
8181- return {};
8182- }
8183-
8184- template <typename T, typename U = remove_const_t<T>,
8185- FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value ||
8186- std::is_union<U>::value) &&
8187- !is_string<U>::value && !is_char<U>::value &&
8188- !is_named_arg<U>::value &&
8189- !std::is_arithmetic<format_as_t<U>>::value)>
8190- FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) {
8191- return do_map(val);
8192- }
8193-
8194- template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
8195- FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
8196- -> decltype(this->map(named_arg.value)) {
8197- return map(named_arg.value);
8198- }
8199-
8200- auto map(...) -> unformattable { return {}; }
8201-};
8202-
8203-// A type constant after applying arg_mapper<Context>.
8204-template <typename T, typename Context>
8205-using mapped_type_constant =
8206- type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
8207- typename Context::char_type>;
8208-
8209-enum { packed_arg_bits = 4 };
8210-// Maximum number of arguments with packed types.
8211-enum { max_packed_args = 62 / packed_arg_bits };
8212-enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
8213-enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
8214-
8215-template <typename Char, typename InputIt>
8216-auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
8217- get_container(out).append(begin, end);
8218- return out;
8219-}
8220-
8221-template <typename Char, typename R, typename OutputIt>
8222-FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
8223- return detail::copy_str<Char>(rng.begin(), rng.end(), out);
8224-}
8225-
8226-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
8227-// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
8228-template <typename...> struct void_t_impl { using type = void; };
8229-template <typename... T> using void_t = typename void_t_impl<T...>::type;
8230-#else
8231-template <typename...> using void_t = void;
8232-#endif
8233-
8234-template <typename It, typename T, typename Enable = void>
8235-struct is_output_iterator : std::false_type {};
8236-
8237-template <typename It, typename T>
8238-struct is_output_iterator<
8239- It, T,
8240- void_t<typename std::iterator_traits<It>::iterator_category,
8241- decltype(*std::declval<It>() = std::declval<T>())>>
8242- : std::true_type {};
8243-
8244-template <typename It> struct is_back_insert_iterator : std::false_type {};
8245-template <typename Container>
8246-struct is_back_insert_iterator<std::back_insert_iterator<Container>>
8247- : std::true_type {};
8248-
8249-// A type-erased reference to an std::locale to avoid a heavy <locale> include.
8250-class locale_ref {
8251- private:
8252- const void* locale_; // A type-erased pointer to std::locale.
8253-
8254- public:
8255- constexpr FMT_INLINE locale_ref() : locale_(nullptr) {}
8256- template <typename Locale> explicit locale_ref(const Locale& loc);
8257-
8258- explicit operator bool() const noexcept { return locale_ != nullptr; }
8259-
8260- template <typename Locale> auto get() const -> Locale;
8261-};
8262-
8263-template <typename> constexpr auto encode_types() -> unsigned long long {
8264- return 0;
8265-}
8266-
8267-template <typename Context, typename Arg, typename... Args>
8268-constexpr auto encode_types() -> unsigned long long {
8269- return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
8270- (encode_types<Context, Args...>() << packed_arg_bits);
8271-}
8272-
8273-template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(PACKED)>
8274-FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value<Context> {
8275- using arg_type = remove_cvref_t<decltype(arg_mapper<Context>().map(val))>;
8276-
8277- constexpr bool formattable_char =
8278- !std::is_same<arg_type, unformattable_char>::value;
8279- static_assert(formattable_char, "Mixing character types is disallowed.");
8280-
8281- // Formatting of arbitrary pointers is disallowed. If you want to format a
8282- // pointer cast it to `void*` or `const void*`. In particular, this forbids
8283- // formatting of `[const] volatile char*` printed as bool by iostreams.
8284- constexpr bool formattable_pointer =
8285- !std::is_same<arg_type, unformattable_pointer>::value;
8286- static_assert(formattable_pointer,
8287- "Formatting of non-void pointers is disallowed.");
8288-
8289- constexpr bool formattable = !std::is_same<arg_type, unformattable>::value;
8290- static_assert(
8291- formattable,
8292- "Cannot format an argument. To make type T formattable provide a "
8293- "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
8294- return {arg_mapper<Context>().map(val)};
8295-}
8296-
8297-template <typename Context, typename T>
8298-FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg<Context> {
8299- auto arg = basic_format_arg<Context>();
8300- arg.type_ = mapped_type_constant<T, Context>::value;
8301- arg.value_ = make_arg<true, Context>(val);
8302- return arg;
8303-}
8304-
8305-template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(!PACKED)>
8306-FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg<Context> {
8307- return make_arg<Context>(val);
8308-}
8309-} // namespace detail
8310-FMT_BEGIN_EXPORT
8311-
8312-// A formatting argument. It is a trivially copyable/constructible type to
8313-// allow storage in basic_memory_buffer.
8314-template <typename Context> class basic_format_arg {
8315- private:
8316- detail::value<Context> value_;
8317- detail::type type_;
8318-
8319- template <typename ContextType, typename T>
8320- friend FMT_CONSTEXPR auto detail::make_arg(T& value)
8321- -> basic_format_arg<ContextType>;
8322-
8323- template <typename Visitor, typename Ctx>
8324- friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
8325- const basic_format_arg<Ctx>& arg)
8326- -> decltype(vis(0));
8327-
8328- friend class basic_format_args<Context>;
8329- friend class dynamic_format_arg_store<Context>;
8330-
8331- using char_type = typename Context::char_type;
8332-
8333- template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
8334- friend struct detail::arg_data;
8335-
8336- basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
8337- : value_(args, size) {}
8338-
8339- public:
8340- class handle {
8341- public:
8342- explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
8343-
8344- void format(typename Context::parse_context_type& parse_ctx,
8345- Context& ctx) const {
8346- custom_.format(custom_.value, parse_ctx, ctx);
8347- }
8348-
8349- private:
8350- detail::custom_value<Context> custom_;
8351- };
8352-
8353- constexpr basic_format_arg() : type_(detail::type::none_type) {}
8354-
8355- constexpr explicit operator bool() const noexcept {
8356- return type_ != detail::type::none_type;
8357- }
8358-
8359- auto type() const -> detail::type { return type_; }
8360-
8361- auto is_integral() const -> bool { return detail::is_integral_type(type_); }
8362- auto is_arithmetic() const -> bool {
8363- return detail::is_arithmetic_type(type_);
8364- }
8365-};
8366-
8367-/**
8368- \rst
8369- Visits an argument dispatching to the appropriate visit method based on
8370- the argument type. For example, if the argument type is ``double`` then
8371- ``vis(value)`` will be called with the value of type ``double``.
8372- \endrst
8373- */
8374-FMT_EXPORT
8375-template <typename Visitor, typename Context>
8376-FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
8377- Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
8378- switch (arg.type_) {
8379- case detail::type::none_type:
8380- break;
8381- case detail::type::int_type:
8382- return vis(arg.value_.int_value);
8383- case detail::type::uint_type:
8384- return vis(arg.value_.uint_value);
8385- case detail::type::long_long_type:
8386- return vis(arg.value_.long_long_value);
8387- case detail::type::ulong_long_type:
8388- return vis(arg.value_.ulong_long_value);
8389- case detail::type::int128_type:
8390- return vis(detail::convert_for_visit(arg.value_.int128_value));
8391- case detail::type::uint128_type:
8392- return vis(detail::convert_for_visit(arg.value_.uint128_value));
8393- case detail::type::bool_type:
8394- return vis(arg.value_.bool_value);
8395- case detail::type::char_type:
8396- return vis(arg.value_.char_value);
8397- case detail::type::float_type:
8398- return vis(arg.value_.float_value);
8399- case detail::type::double_type:
8400- return vis(arg.value_.double_value);
8401- case detail::type::long_double_type:
8402- return vis(arg.value_.long_double_value);
8403- case detail::type::cstring_type:
8404- return vis(arg.value_.string.data);
8405- case detail::type::string_type:
8406- using sv = basic_string_view<typename Context::char_type>;
8407- return vis(sv(arg.value_.string.data, arg.value_.string.size));
8408- case detail::type::pointer_type:
8409- return vis(arg.value_.pointer);
8410- case detail::type::custom_type:
8411- return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
8412- }
8413- return vis(monostate());
8414-}
8415-
8416-// Formatting context.
8417-template <typename OutputIt, typename Char> class basic_format_context {
8418- private:
8419- OutputIt out_;
8420- basic_format_args<basic_format_context> args_;
8421- detail::locale_ref loc_;
8422-
8423- public:
8424- using iterator = OutputIt;
8425- using format_arg = basic_format_arg<basic_format_context>;
8426- using format_args = basic_format_args<basic_format_context>;
8427- using parse_context_type = basic_format_parse_context<Char>;
8428- template <typename T> using formatter_type = formatter<T, Char>;
8429-
8430- /** The character type for the output. */
8431- using char_type = Char;
8432-
8433- basic_format_context(basic_format_context&&) = default;
8434- basic_format_context(const basic_format_context&) = delete;
8435- void operator=(const basic_format_context&) = delete;
8436- /**
8437- Constructs a ``basic_format_context`` object. References to the arguments
8438- are stored in the object so make sure they have appropriate lifetimes.
8439- */
8440- constexpr basic_format_context(OutputIt out, format_args ctx_args,
8441- detail::locale_ref loc = {})
8442- : out_(out), args_(ctx_args), loc_(loc) {}
8443-
8444- constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
8445- FMT_CONSTEXPR auto arg(basic_string_view<Char> name) -> format_arg {
8446- return args_.get(name);
8447- }
8448- FMT_CONSTEXPR auto arg_id(basic_string_view<Char> name) -> int {
8449- return args_.get_id(name);
8450- }
8451- auto args() const -> const format_args& { return args_; }
8452-
8453- FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
8454- void on_error(const char* message) { error_handler().on_error(message); }
8455-
8456- // Returns an iterator to the beginning of the output range.
8457- FMT_CONSTEXPR auto out() -> iterator { return out_; }
8458-
8459- // Advances the begin iterator to ``it``.
8460- void advance_to(iterator it) {
8461- if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
8462- }
8463-
8464- FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
8465-};
8466-
8467-template <typename Char>
8468-using buffer_context =
8469- basic_format_context<detail::buffer_appender<Char>, Char>;
8470-using format_context = buffer_context<char>;
8471-
8472-template <typename T, typename Char = char>
8473-using is_formattable = bool_constant<!std::is_base_of<
8474- detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>()
8475- .map(std::declval<T&>()))>::value>;
8476-
8477-/**
8478- \rst
8479- An array of references to arguments. It can be implicitly converted into
8480- `~fmt::basic_format_args` for passing into type-erased formatting functions
8481- such as `~fmt::vformat`.
8482- \endrst
8483- */
8484-template <typename Context, typename... Args>
8485-class format_arg_store
8486-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
8487- // Workaround a GCC template argument substitution bug.
8488- : public basic_format_args<Context>
8489-#endif
8490-{
8491- private:
8492- static const size_t num_args = sizeof...(Args);
8493- static constexpr size_t num_named_args = detail::count_named_args<Args...>();
8494- static const bool is_packed = num_args <= detail::max_packed_args;
8495-
8496- using value_type = conditional_t<is_packed, detail::value<Context>,
8497- basic_format_arg<Context>>;
8498-
8499- detail::arg_data<value_type, typename Context::char_type, num_args,
8500- num_named_args>
8501- data_;
8502-
8503- friend class basic_format_args<Context>;
8504-
8505- static constexpr unsigned long long desc =
8506- (is_packed ? detail::encode_types<Context, Args...>()
8507- : detail::is_unpacked_bit | num_args) |
8508- (num_named_args != 0
8509- ? static_cast<unsigned long long>(detail::has_named_args_bit)
8510- : 0);
8511-
8512- public:
8513- template <typename... T>
8514- FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args)
8515- :
8516-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
8517- basic_format_args<Context>(*this),
8518-#endif
8519- data_{detail::make_arg<is_packed, Context>(args)...} {
8520- if (num_named_args != 0)
8521- detail::init_named_args(data_.named_args(), 0, 0, args...);
8522- }
8523-};
8524-
8525-/**
8526- \rst
8527- Constructs a `~fmt::format_arg_store` object that contains references to
8528- arguments and can be implicitly converted to `~fmt::format_args`. `Context`
8529- can be omitted in which case it defaults to `~fmt::format_context`.
8530- See `~fmt::arg` for lifetime considerations.
8531- \endrst
8532- */
8533-// Arguments are taken by lvalue references to avoid some lifetime issues.
8534-template <typename Context = format_context, typename... T>
8535-constexpr auto make_format_args(T&... args)
8536- -> format_arg_store<Context, remove_cvref_t<T>...> {
8537- return {args...};
8538-}
8539-
8540-/**
8541- \rst
8542- Returns a named argument to be used in a formatting function.
8543- It should only be used in a call to a formatting function or
8544- `dynamic_format_arg_store::push_back`.
8545-
8546- **Example**::
8547-
8548- fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
8549- \endrst
8550- */
8551-template <typename Char, typename T>
8552-inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
8553- static_assert(!detail::is_named_arg<T>(), "nested named arguments");
8554- return {name, arg};
8555-}
8556-FMT_END_EXPORT
8557-
8558-/**
8559- \rst
8560- A view of a collection of formatting arguments. To avoid lifetime issues it
8561- should only be used as a parameter type in type-erased functions such as
8562- ``vformat``::
8563-
8564- void vlog(string_view format_str, format_args args); // OK
8565- format_args args = make_format_args(); // Error: dangling reference
8566- \endrst
8567- */
8568-template <typename Context> class basic_format_args {
8569- public:
8570- using size_type = int;
8571- using format_arg = basic_format_arg<Context>;
8572-
8573- private:
8574- // A descriptor that contains information about formatting arguments.
8575- // If the number of arguments is less or equal to max_packed_args then
8576- // argument types are passed in the descriptor. This reduces binary code size
8577- // per formatting function call.
8578- unsigned long long desc_;
8579- union {
8580- // If is_packed() returns true then argument values are stored in values_;
8581- // otherwise they are stored in args_. This is done to improve cache
8582- // locality and reduce compiled code size since storing larger objects
8583- // may require more code (at least on x86-64) even if the same amount of
8584- // data is actually copied to stack. It saves ~10% on the bloat test.
8585- const detail::value<Context>* values_;
8586- const format_arg* args_;
8587- };
8588-
8589- constexpr auto is_packed() const -> bool {
8590- return (desc_ & detail::is_unpacked_bit) == 0;
8591- }
8592- auto has_named_args() const -> bool {
8593- return (desc_ & detail::has_named_args_bit) != 0;
8594- }
8595-
8596- FMT_CONSTEXPR auto type(int index) const -> detail::type {
8597- int shift = index * detail::packed_arg_bits;
8598- unsigned int mask = (1 << detail::packed_arg_bits) - 1;
8599- return static_cast<detail::type>((desc_ >> shift) & mask);
8600- }
8601-
8602- constexpr FMT_INLINE basic_format_args(unsigned long long desc,
8603- const detail::value<Context>* values)
8604- : desc_(desc), values_(values) {}
8605- constexpr basic_format_args(unsigned long long desc, const format_arg* args)
8606- : desc_(desc), args_(args) {}
8607-
8608- public:
8609- constexpr basic_format_args() : desc_(0), args_(nullptr) {}
8610-
8611- /**
8612- \rst
8613- Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
8614- \endrst
8615- */
8616- template <typename... Args>
8617- constexpr FMT_INLINE basic_format_args(
8618- const format_arg_store<Context, Args...>& store)
8619- : basic_format_args(format_arg_store<Context, Args...>::desc,
8620- store.data_.args()) {}
8621-
8622- /**
8623- \rst
8624- Constructs a `basic_format_args` object from
8625- `~fmt::dynamic_format_arg_store`.
8626- \endrst
8627- */
8628- constexpr FMT_INLINE basic_format_args(
8629- const dynamic_format_arg_store<Context>& store)
8630- : basic_format_args(store.get_types(), store.data()) {}
8631-
8632- /**
8633- \rst
8634- Constructs a `basic_format_args` object from a dynamic set of arguments.
8635- \endrst
8636- */
8637- constexpr basic_format_args(const format_arg* args, int count)
8638- : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
8639- args) {}
8640-
8641- /** Returns the argument with the specified id. */
8642- FMT_CONSTEXPR auto get(int id) const -> format_arg {
8643- format_arg arg;
8644- if (!is_packed()) {
8645- if (id < max_size()) arg = args_[id];
8646- return arg;
8647- }
8648- if (id >= detail::max_packed_args) return arg;
8649- arg.type_ = type(id);
8650- if (arg.type_ == detail::type::none_type) return arg;
8651- arg.value_ = values_[id];
8652- return arg;
8653- }
8654-
8655- template <typename Char>
8656- auto get(basic_string_view<Char> name) const -> format_arg {
8657- int id = get_id(name);
8658- return id >= 0 ? get(id) : format_arg();
8659- }
8660-
8661- template <typename Char>
8662- auto get_id(basic_string_view<Char> name) const -> int {
8663- if (!has_named_args()) return -1;
8664- const auto& named_args =
8665- (is_packed() ? values_[-1] : args_[-1].value_).named_args;
8666- for (size_t i = 0; i < named_args.size; ++i) {
8667- if (named_args.data[i].name == name) return named_args.data[i].id;
8668- }
8669- return -1;
8670- }
8671-
8672- auto max_size() const -> int {
8673- unsigned long long max_packed = detail::max_packed_args;
8674- return static_cast<int>(is_packed() ? max_packed
8675- : desc_ & ~detail::is_unpacked_bit);
8676- }
8677-};
8678-
8679-/** An alias to ``basic_format_args<format_context>``. */
8680-// A separate type would result in shorter symbols but break ABI compatibility
8681-// between clang and gcc on ARM (#1919).
8682-FMT_EXPORT using format_args = basic_format_args<format_context>;
8683-
8684-// We cannot use enum classes as bit fields because of a gcc bug, so we put them
8685-// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
8686-// Additionally, if an underlying type is specified, older gcc incorrectly warns
8687-// that the type is too small. Both bugs are fixed in gcc 9.3.
8688-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
8689-# define FMT_ENUM_UNDERLYING_TYPE(type)
8690-#else
8691-# define FMT_ENUM_UNDERLYING_TYPE(type) : type
8692-#endif
8693-namespace align {
8694-enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center,
8695- numeric};
8696-}
8697-using align_t = align::type;
8698-namespace sign {
8699-enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
8700-}
8701-using sign_t = sign::type;
8702-
8703-namespace detail {
8704-
8705-// Workaround an array initialization issue in gcc 4.8.
8706-template <typename Char> struct fill_t {
8707- private:
8708- enum { max_size = 4 };
8709- Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
8710- unsigned char size_ = 1;
8711-
8712- public:
8713- FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
8714- auto size = s.size();
8715- FMT_ASSERT(size <= max_size, "invalid fill");
8716- for (size_t i = 0; i < size; ++i) data_[i] = s[i];
8717- size_ = static_cast<unsigned char>(size);
8718- }
8719-
8720- constexpr auto size() const -> size_t { return size_; }
8721- constexpr auto data() const -> const Char* { return data_; }
8722-
8723- FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; }
8724- FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& {
8725- return data_[index];
8726- }
8727-};
8728-} // namespace detail
8729-
8730-enum class presentation_type : unsigned char {
8731- none,
8732- dec, // 'd'
8733- oct, // 'o'
8734- hex_lower, // 'x'
8735- hex_upper, // 'X'
8736- bin_lower, // 'b'
8737- bin_upper, // 'B'
8738- hexfloat_lower, // 'a'
8739- hexfloat_upper, // 'A'
8740- exp_lower, // 'e'
8741- exp_upper, // 'E'
8742- fixed_lower, // 'f'
8743- fixed_upper, // 'F'
8744- general_lower, // 'g'
8745- general_upper, // 'G'
8746- chr, // 'c'
8747- string, // 's'
8748- pointer, // 'p'
8749- debug // '?'
8750-};
8751-
8752-// Format specifiers for built-in and string types.
8753-template <typename Char = char> struct format_specs {
8754- int width;
8755- int precision;
8756- presentation_type type;
8757- align_t align : 4;
8758- sign_t sign : 3;
8759- bool alt : 1; // Alternate form ('#').
8760- bool localized : 1;
8761- detail::fill_t<Char> fill;
8762-
8763- constexpr format_specs()
8764- : width(0),
8765- precision(-1),
8766- type(presentation_type::none),
8767- align(align::none),
8768- sign(sign::none),
8769- alt(false),
8770- localized(false) {}
8771-};
8772-
8773-namespace detail {
8774-
8775-enum class arg_id_kind { none, index, name };
8776-
8777-// An argument reference.
8778-template <typename Char> struct arg_ref {
8779- FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
8780-
8781- FMT_CONSTEXPR explicit arg_ref(int index)
8782- : kind(arg_id_kind::index), val(index) {}
8783- FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
8784- : kind(arg_id_kind::name), val(name) {}
8785-
8786- FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
8787- kind = arg_id_kind::index;
8788- val.index = idx;
8789- return *this;
8790- }
8791-
8792- arg_id_kind kind;
8793- union value {
8794- FMT_CONSTEXPR value(int idx = 0) : index(idx) {}
8795- FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
8796-
8797- int index;
8798- basic_string_view<Char> name;
8799- } val;
8800-};
8801-
8802-// Format specifiers with width and precision resolved at formatting rather
8803-// than parsing time to allow reusing the same parsed specifiers with
8804-// different sets of arguments (precompilation of format strings).
8805-template <typename Char = char>
8806-struct dynamic_format_specs : format_specs<Char> {
8807- arg_ref<Char> width_ref;
8808- arg_ref<Char> precision_ref;
8809-};
8810-
8811-// Converts a character to ASCII. Returns '\0' on conversion failure.
8812-template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
8813-constexpr auto to_ascii(Char c) -> char {
8814- return c <= 0xff ? static_cast<char>(c) : '\0';
8815-}
8816-template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
8817-constexpr auto to_ascii(Char c) -> char {
8818- return c <= 0xff ? static_cast<char>(c) : '\0';
8819-}
8820-
8821-// Returns the number of code units in a code point or 1 on error.
8822-template <typename Char>
8823-FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
8824- if (const_check(sizeof(Char) != 1)) return 1;
8825- auto c = static_cast<unsigned char>(*begin);
8826- return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1;
8827-}
8828-
8829-// Return the result via the out param to workaround gcc bug 77539.
8830-template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
8831-FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
8832- for (out = first; out != last; ++out) {
8833- if (*out == value) return true;
8834- }
8835- return false;
8836-}
8837-
8838-template <>
8839-inline auto find<false, char>(const char* first, const char* last, char value,
8840- const char*& out) -> bool {
8841- out = static_cast<const char*>(
8842- std::memchr(first, value, to_unsigned(last - first)));
8843- return out != nullptr;
8844-}
8845-
8846-// Parses the range [begin, end) as an unsigned integer. This function assumes
8847-// that the range is non-empty and the first character is a digit.
8848-template <typename Char>
8849-FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
8850- int error_value) noexcept -> int {
8851- FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
8852- unsigned value = 0, prev = 0;
8853- auto p = begin;
8854- do {
8855- prev = value;
8856- value = value * 10 + unsigned(*p - '0');
8857- ++p;
8858- } while (p != end && '0' <= *p && *p <= '9');
8859- auto num_digits = p - begin;
8860- begin = p;
8861- if (num_digits <= std::numeric_limits<int>::digits10)
8862- return static_cast<int>(value);
8863- // Check for overflow.
8864- const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
8865- return num_digits == std::numeric_limits<int>::digits10 + 1 &&
8866- prev * 10ull + unsigned(p[-1] - '0') <= max
8867- ? static_cast<int>(value)
8868- : error_value;
8869-}
8870-
8871-FMT_CONSTEXPR inline auto parse_align(char c) -> align_t {
8872- switch (c) {
8873- case '<':
8874- return align::left;
8875- case '>':
8876- return align::right;
8877- case '^':
8878- return align::center;
8879- }
8880- return align::none;
8881-}
8882-
8883-template <typename Char> constexpr auto is_name_start(Char c) -> bool {
8884- return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
8885-}
8886-
8887-template <typename Char, typename Handler>
8888-FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
8889- Handler&& handler) -> const Char* {
8890- Char c = *begin;
8891- if (c >= '0' && c <= '9') {
8892- int index = 0;
8893- constexpr int max = (std::numeric_limits<int>::max)();
8894- if (c != '0')
8895- index = parse_nonnegative_int(begin, end, max);
8896- else
8897- ++begin;
8898- if (begin == end || (*begin != '}' && *begin != ':'))
8899- throw_format_error("invalid format string");
8900- else
8901- handler.on_index(index);
8902- return begin;
8903- }
8904- if (!is_name_start(c)) {
8905- throw_format_error("invalid format string");
8906- return begin;
8907- }
8908- auto it = begin;
8909- do {
8910- ++it;
8911- } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
8912- handler.on_name({begin, to_unsigned(it - begin)});
8913- return it;
8914-}
8915-
8916-template <typename Char, typename Handler>
8917-FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
8918- Handler&& handler) -> const Char* {
8919- FMT_ASSERT(begin != end, "");
8920- Char c = *begin;
8921- if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
8922- handler.on_auto();
8923- return begin;
8924-}
8925-
8926-template <typename Char> struct dynamic_spec_id_handler {
8927- basic_format_parse_context<Char>& ctx;
8928- arg_ref<Char>& ref;
8929-
8930- FMT_CONSTEXPR void on_auto() {
8931- int id = ctx.next_arg_id();
8932- ref = arg_ref<Char>(id);
8933- ctx.check_dynamic_spec(id);
8934- }
8935- FMT_CONSTEXPR void on_index(int id) {
8936- ref = arg_ref<Char>(id);
8937- ctx.check_arg_id(id);
8938- ctx.check_dynamic_spec(id);
8939- }
8940- FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
8941- ref = arg_ref<Char>(id);
8942- ctx.check_arg_id(id);
8943- }
8944-};
8945-
8946-// Parses [integer | "{" [arg_id] "}"].
8947-template <typename Char>
8948-FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
8949- int& value, arg_ref<Char>& ref,
8950- basic_format_parse_context<Char>& ctx)
8951- -> const Char* {
8952- FMT_ASSERT(begin != end, "");
8953- if ('0' <= *begin && *begin <= '9') {
8954- int val = parse_nonnegative_int(begin, end, -1);
8955- if (val != -1)
8956- value = val;
8957- else
8958- throw_format_error("number is too big");
8959- } else if (*begin == '{') {
8960- ++begin;
8961- auto handler = dynamic_spec_id_handler<Char>{ctx, ref};
8962- if (begin != end) begin = parse_arg_id(begin, end, handler);
8963- if (begin != end && *begin == '}') return ++begin;
8964- throw_format_error("invalid format string");
8965- }
8966- return begin;
8967-}
8968-
8969-template <typename Char>
8970-FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
8971- int& value, arg_ref<Char>& ref,
8972- basic_format_parse_context<Char>& ctx)
8973- -> const Char* {
8974- ++begin;
8975- if (begin == end || *begin == '}') {
8976- throw_format_error("invalid precision");
8977- return begin;
8978- }
8979- return parse_dynamic_spec(begin, end, value, ref, ctx);
8980-}
8981-
8982-enum class state { start, align, sign, hash, zero, width, precision, locale };
8983-
8984-// Parses standard format specifiers.
8985-template <typename Char>
8986-FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
8987- const Char* begin, const Char* end, dynamic_format_specs<Char>& specs,
8988- basic_format_parse_context<Char>& ctx, type arg_type) -> const Char* {
8989- auto c = '\0';
8990- if (end - begin > 1) {
8991- auto next = to_ascii(begin[1]);
8992- c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
8993- } else {
8994- if (begin == end) return begin;
8995- c = to_ascii(*begin);
8996- }
8997-
8998- struct {
8999- state current_state = state::start;
9000- FMT_CONSTEXPR void operator()(state s, bool valid = true) {
9001- if (current_state >= s || !valid)
9002- throw_format_error("invalid format specifier");
9003- current_state = s;
9004- }
9005- } enter_state;
9006-
9007- using pres = presentation_type;
9008- constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
9009- struct {
9010- const Char*& begin;
9011- dynamic_format_specs<Char>& specs;
9012- type arg_type;
9013-
9014- FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* {
9015- if (!in(arg_type, set)) throw_format_error("invalid format specifier");
9016- specs.type = type;
9017- return begin + 1;
9018- }
9019- } parse_presentation_type{begin, specs, arg_type};
9020-
9021- for (;;) {
9022- switch (c) {
9023- case '<':
9024- case '>':
9025- case '^':
9026- enter_state(state::align);
9027- specs.align = parse_align(c);
9028- ++begin;
9029- break;
9030- case '+':
9031- case '-':
9032- case ' ':
9033- enter_state(state::sign, in(arg_type, sint_set | float_set));
9034- switch (c) {
9035- case '+':
9036- specs.sign = sign::plus;
9037- break;
9038- case '-':
9039- specs.sign = sign::minus;
9040- break;
9041- case ' ':
9042- specs.sign = sign::space;
9043- break;
9044- }
9045- ++begin;
9046- break;
9047- case '#':
9048- enter_state(state::hash, is_arithmetic_type(arg_type));
9049- specs.alt = true;
9050- ++begin;
9051- break;
9052- case '0':
9053- enter_state(state::zero);
9054- if (!is_arithmetic_type(arg_type))
9055- throw_format_error("format specifier requires numeric argument");
9056- if (specs.align == align::none) {
9057- // Ignore 0 if align is specified for compatibility with std::format.
9058- specs.align = align::numeric;
9059- specs.fill[0] = Char('0');
9060- }
9061- ++begin;
9062- break;
9063- case '1':
9064- case '2':
9065- case '3':
9066- case '4':
9067- case '5':
9068- case '6':
9069- case '7':
9070- case '8':
9071- case '9':
9072- case '{':
9073- enter_state(state::width);
9074- begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
9075- break;
9076- case '.':
9077- enter_state(state::precision,
9078- in(arg_type, float_set | string_set | cstring_set));
9079- begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
9080- ctx);
9081- break;
9082- case 'L':
9083- enter_state(state::locale, is_arithmetic_type(arg_type));
9084- specs.localized = true;
9085- ++begin;
9086- break;
9087- case 'd':
9088- return parse_presentation_type(pres::dec, integral_set);
9089- case 'o':
9090- return parse_presentation_type(pres::oct, integral_set);
9091- case 'x':
9092- return parse_presentation_type(pres::hex_lower, integral_set);
9093- case 'X':
9094- return parse_presentation_type(pres::hex_upper, integral_set);
9095- case 'b':
9096- return parse_presentation_type(pres::bin_lower, integral_set);
9097- case 'B':
9098- return parse_presentation_type(pres::bin_upper, integral_set);
9099- case 'a':
9100- return parse_presentation_type(pres::hexfloat_lower, float_set);
9101- case 'A':
9102- return parse_presentation_type(pres::hexfloat_upper, float_set);
9103- case 'e':
9104- return parse_presentation_type(pres::exp_lower, float_set);
9105- case 'E':
9106- return parse_presentation_type(pres::exp_upper, float_set);
9107- case 'f':
9108- return parse_presentation_type(pres::fixed_lower, float_set);
9109- case 'F':
9110- return parse_presentation_type(pres::fixed_upper, float_set);
9111- case 'g':
9112- return parse_presentation_type(pres::general_lower, float_set);
9113- case 'G':
9114- return parse_presentation_type(pres::general_upper, float_set);
9115- case 'c':
9116- return parse_presentation_type(pres::chr, integral_set);
9117- case 's':
9118- return parse_presentation_type(pres::string,
9119- bool_set | string_set | cstring_set);
9120- case 'p':
9121- return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
9122- case '?':
9123- return parse_presentation_type(pres::debug,
9124- char_set | string_set | cstring_set);
9125- case '}':
9126- return begin;
9127- default: {
9128- if (*begin == '}') return begin;
9129- // Parse fill and alignment.
9130- auto fill_end = begin + code_point_length(begin);
9131- if (end - fill_end <= 0) {
9132- throw_format_error("invalid format specifier");
9133- return begin;
9134- }
9135- if (*begin == '{') {
9136- throw_format_error("invalid fill character '{'");
9137- return begin;
9138- }
9139- auto align = parse_align(to_ascii(*fill_end));
9140- enter_state(state::align, align != align::none);
9141- specs.fill = {begin, to_unsigned(fill_end - begin)};
9142- specs.align = align;
9143- begin = fill_end + 1;
9144- }
9145- }
9146- if (begin == end) return begin;
9147- c = to_ascii(*begin);
9148- }
9149-}
9150-
9151-template <typename Char, typename Handler>
9152-FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
9153- Handler&& handler) -> const Char* {
9154- struct id_adapter {
9155- Handler& handler;
9156- int arg_id;
9157-
9158- FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); }
9159- FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
9160- FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
9161- arg_id = handler.on_arg_id(id);
9162- }
9163- };
9164-
9165- ++begin;
9166- if (begin == end) return handler.on_error("invalid format string"), end;
9167- if (*begin == '}') {
9168- handler.on_replacement_field(handler.on_arg_id(), begin);
9169- } else if (*begin == '{') {
9170- handler.on_text(begin, begin + 1);
9171- } else {
9172- auto adapter = id_adapter{handler, 0};
9173- begin = parse_arg_id(begin, end, adapter);
9174- Char c = begin != end ? *begin : Char();
9175- if (c == '}') {
9176- handler.on_replacement_field(adapter.arg_id, begin);
9177- } else if (c == ':') {
9178- begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
9179- if (begin == end || *begin != '}')
9180- return handler.on_error("unknown format specifier"), end;
9181- } else {
9182- return handler.on_error("missing '}' in format string"), end;
9183- }
9184- }
9185- return begin + 1;
9186-}
9187-
9188-template <bool IS_CONSTEXPR, typename Char, typename Handler>
9189-FMT_CONSTEXPR FMT_INLINE void parse_format_string(
9190- basic_string_view<Char> format_str, Handler&& handler) {
9191- auto begin = format_str.data();
9192- auto end = begin + format_str.size();
9193- if (end - begin < 32) {
9194- // Use a simple loop instead of memchr for small strings.
9195- const Char* p = begin;
9196- while (p != end) {
9197- auto c = *p++;
9198- if (c == '{') {
9199- handler.on_text(begin, p - 1);
9200- begin = p = parse_replacement_field(p - 1, end, handler);
9201- } else if (c == '}') {
9202- if (p == end || *p != '}')
9203- return handler.on_error("unmatched '}' in format string");
9204- handler.on_text(begin, p);
9205- begin = ++p;
9206- }
9207- }
9208- handler.on_text(begin, end);
9209- return;
9210- }
9211- struct writer {
9212- FMT_CONSTEXPR void operator()(const Char* from, const Char* to) {
9213- if (from == to) return;
9214- for (;;) {
9215- const Char* p = nullptr;
9216- if (!find<IS_CONSTEXPR>(from, to, Char('}'), p))
9217- return handler_.on_text(from, to);
9218- ++p;
9219- if (p == to || *p != '}')
9220- return handler_.on_error("unmatched '}' in format string");
9221- handler_.on_text(from, p);
9222- from = p + 1;
9223- }
9224- }
9225- Handler& handler_;
9226- } write = {handler};
9227- while (begin != end) {
9228- // Doing two passes with memchr (one for '{' and another for '}') is up to
9229- // 2.5x faster than the naive one-pass implementation on big format strings.
9230- const Char* p = begin;
9231- if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
9232- return write(begin, end);
9233- write(begin, p);
9234- begin = parse_replacement_field(p, end, handler);
9235- }
9236-}
9237-
9238-template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg {
9239- using type = T;
9240-};
9241-template <typename T> struct strip_named_arg<T, true> {
9242- using type = remove_cvref_t<decltype(T::value)>;
9243-};
9244-
9245-template <typename T, typename ParseContext>
9246-FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
9247- -> decltype(ctx.begin()) {
9248- using char_type = typename ParseContext::char_type;
9249- using context = buffer_context<char_type>;
9250- using mapped_type = conditional_t<
9251- mapped_type_constant<T, context>::value != type::custom_type,
9252- decltype(arg_mapper<context>().map(std::declval<const T&>())),
9253- typename strip_named_arg<T>::type>;
9254- return formatter<mapped_type, char_type>().parse(ctx);
9255-}
9256-
9257-// Checks char specs and returns true iff the presentation type is char-like.
9258-template <typename Char>
9259-FMT_CONSTEXPR auto check_char_specs(const format_specs<Char>& specs) -> bool {
9260- if (specs.type != presentation_type::none &&
9261- specs.type != presentation_type::chr &&
9262- specs.type != presentation_type::debug) {
9263- return false;
9264- }
9265- if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
9266- throw_format_error("invalid format specifier for char");
9267- return true;
9268-}
9269-
9270-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
9271-template <int N, typename T, typename... Args, typename Char>
9272-constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
9273- if constexpr (is_statically_named_arg<T>()) {
9274- if (name == T::name) return N;
9275- }
9276- if constexpr (sizeof...(Args) > 0)
9277- return get_arg_index_by_name<N + 1, Args...>(name);
9278- (void)name; // Workaround an MSVC bug about "unused" parameter.
9279- return -1;
9280-}
9281-#endif
9282-
9283-template <typename... Args, typename Char>
9284-FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
9285-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
9286- if constexpr (sizeof...(Args) > 0)
9287- return get_arg_index_by_name<0, Args...>(name);
9288-#endif
9289- (void)name;
9290- return -1;
9291-}
9292-
9293-template <typename Char, typename... Args> class format_string_checker {
9294- private:
9295- using parse_context_type = compile_parse_context<Char>;
9296- static constexpr int num_args = sizeof...(Args);
9297-
9298- // Format specifier parsing function.
9299- // In the future basic_format_parse_context will replace compile_parse_context
9300- // here and will use is_constant_evaluated and downcasting to access the data
9301- // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
9302- using parse_func = const Char* (*)(parse_context_type&);
9303-
9304- parse_context_type context_;
9305- parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
9306- type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
9307-
9308- public:
9309- explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt)
9310- : context_(fmt, num_args, types_),
9311- parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
9312- types_{mapped_type_constant<Args, buffer_context<Char>>::value...} {}
9313-
9314- FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
9315-
9316- FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
9317- FMT_CONSTEXPR auto on_arg_id(int id) -> int {
9318- return context_.check_arg_id(id), id;
9319- }
9320- FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
9321-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
9322- auto index = get_arg_index_by_name<Args...>(id);
9323- if (index < 0) on_error("named argument is not found");
9324- return index;
9325-#else
9326- (void)id;
9327- on_error("compile-time checks for named arguments require C++20 support");
9328- return 0;
9329-#endif
9330- }
9331-
9332- FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
9333-
9334- FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
9335- -> const Char* {
9336- context_.advance_to(begin);
9337- // id >= 0 check is a workaround for gcc 10 bug (#2065).
9338- return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
9339- }
9340-
9341- FMT_CONSTEXPR void on_error(const char* message) {
9342- throw_format_error(message);
9343- }
9344-};
9345-
9346-// Reports a compile-time error if S is not a valid format string.
9347-template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
9348-FMT_INLINE void check_format_string(const S&) {
9349-#ifdef FMT_ENFORCE_COMPILE_STRING
9350- static_assert(is_compile_string<S>::value,
9351- "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
9352- "FMT_STRING.");
9353-#endif
9354-}
9355-template <typename... Args, typename S,
9356- FMT_ENABLE_IF(is_compile_string<S>::value)>
9357-void check_format_string(S format_str) {
9358- using char_t = typename S::char_type;
9359- FMT_CONSTEXPR auto s = basic_string_view<char_t>(format_str);
9360- using checker = format_string_checker<char_t, remove_cvref_t<Args>...>;
9361- FMT_CONSTEXPR bool error = (parse_format_string<true>(s, checker(s)), true);
9362- ignore_unused(error);
9363-}
9364-
9365-template <typename Char = char> struct vformat_args {
9366- using type = basic_format_args<
9367- basic_format_context<std::back_insert_iterator<buffer<Char>>, Char>>;
9368-};
9369-template <> struct vformat_args<char> { using type = format_args; };
9370-
9371-// Use vformat_args and avoid type_identity to keep symbols short.
9372-template <typename Char>
9373-void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
9374- typename vformat_args<Char>::type args, locale_ref loc = {});
9375-
9376-FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
9377-#ifndef _WIN32
9378-inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
9379-#endif
9380-} // namespace detail
9381-
9382-FMT_BEGIN_EXPORT
9383-
9384-// A formatter specialization for natively supported types.
9385-template <typename T, typename Char>
9386-struct formatter<T, Char,
9387- enable_if_t<detail::type_constant<T, Char>::value !=
9388- detail::type::custom_type>> {
9389- private:
9390- detail::dynamic_format_specs<Char> specs_;
9391-
9392- public:
9393- template <typename ParseContext>
9394- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
9395- auto type = detail::type_constant<T, Char>::value;
9396- auto end =
9397- detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type);
9398- if (type == detail::type::char_type) detail::check_char_specs(specs_);
9399- return end;
9400- }
9401-
9402- template <detail::type U = detail::type_constant<T, Char>::value,
9403- FMT_ENABLE_IF(U == detail::type::string_type ||
9404- U == detail::type::cstring_type ||
9405- U == detail::type::char_type)>
9406- FMT_CONSTEXPR void set_debug_format(bool set = true) {
9407- specs_.type = set ? presentation_type::debug : presentation_type::none;
9408- }
9409-
9410- template <typename FormatContext>
9411- FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
9412- -> decltype(ctx.out());
9413-};
9414-
9415-#define FMT_FORMAT_AS(Type, Base) \
9416- template <typename Char> \
9417- struct formatter<Type, Char> : formatter<Base, Char> {}
9418-
9419-FMT_FORMAT_AS(signed char, int);
9420-FMT_FORMAT_AS(unsigned char, unsigned);
9421-FMT_FORMAT_AS(short, int);
9422-FMT_FORMAT_AS(unsigned short, unsigned);
9423-FMT_FORMAT_AS(long, detail::long_type);
9424-FMT_FORMAT_AS(unsigned long, detail::ulong_type);
9425-FMT_FORMAT_AS(Char*, const Char*);
9426-FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
9427-FMT_FORMAT_AS(std::nullptr_t, const void*);
9428-FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
9429-
9430-template <typename Char = char> struct runtime_format_string {
9431- basic_string_view<Char> str;
9432-};
9433-
9434-/** A compile-time format string. */
9435-template <typename Char, typename... Args> class basic_format_string {
9436- private:
9437- basic_string_view<Char> str_;
9438-
9439- public:
9440- template <typename S,
9441- FMT_ENABLE_IF(
9442- std::is_convertible<const S&, basic_string_view<Char>>::value)>
9443- FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) {
9444- static_assert(
9445- detail::count<
9446- (std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
9447- std::is_reference<Args>::value)...>() == 0,
9448- "passing views as lvalues is disallowed");
9449-#ifdef FMT_HAS_CONSTEVAL
9450- if constexpr (detail::count_named_args<Args...>() ==
9451- detail::count_statically_named_args<Args...>()) {
9452- using checker =
9453- detail::format_string_checker<Char, remove_cvref_t<Args>...>;
9454- detail::parse_format_string<true>(str_, checker(s));
9455- }
9456-#else
9457- detail::check_format_string<Args...>(s);
9458-#endif
9459- }
9460- basic_format_string(runtime_format_string<Char> fmt) : str_(fmt.str) {}
9461-
9462- FMT_INLINE operator basic_string_view<Char>() const { return str_; }
9463- FMT_INLINE auto get() const -> basic_string_view<Char> { return str_; }
9464-};
9465-
9466-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
9467-// Workaround broken conversion on older gcc.
9468-template <typename...> using format_string = string_view;
9469-inline auto runtime(string_view s) -> string_view { return s; }
9470-#else
9471-template <typename... Args>
9472-using format_string = basic_format_string<char, type_identity_t<Args>...>;
9473-/**
9474- \rst
9475- Creates a runtime format string.
9476-
9477- **Example**::
9478-
9479- // Check format string at runtime instead of compile-time.
9480- fmt::print(fmt::runtime("{:d}"), "I am not a number");
9481- \endrst
9482- */
9483-inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
9484-#endif
9485-
9486-FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
9487-
9488-/**
9489- \rst
9490- Formats ``args`` according to specifications in ``fmt`` and returns the result
9491- as a string.
9492-
9493- **Example**::
9494-
9495- #include <fmt/core.h>
9496- std::string message = fmt::format("The answer is {}.", 42);
9497- \endrst
9498-*/
9499-template <typename... T>
9500-FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
9501- -> std::string {
9502- return vformat(fmt, fmt::make_format_args(args...));
9503-}
9504-
9505-/** Formats a string and writes the output to ``out``. */
9506-template <typename OutputIt,
9507- FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
9508-auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
9509- auto&& buf = detail::get_buffer<char>(out);
9510- detail::vformat_to(buf, fmt, args, {});
9511- return detail::get_iterator(buf, out);
9512-}
9513-
9514-/**
9515- \rst
9516- Formats ``args`` according to specifications in ``fmt``, writes the result to
9517- the output iterator ``out`` and returns the iterator past the end of the output
9518- range. `format_to` does not append a terminating null character.
9519-
9520- **Example**::
9521-
9522- auto out = std::vector<char>();
9523- fmt::format_to(std::back_inserter(out), "{}", 42);
9524- \endrst
9525- */
9526-template <typename OutputIt, typename... T,
9527- FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
9528-FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
9529- -> OutputIt {
9530- return vformat_to(out, fmt, fmt::make_format_args(args...));
9531-}
9532-
9533-template <typename OutputIt> struct format_to_n_result {
9534- /** Iterator past the end of the output range. */
9535- OutputIt out;
9536- /** Total (not truncated) output size. */
9537- size_t size;
9538-};
9539-
9540-template <typename OutputIt, typename... T,
9541- FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
9542-auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
9543- -> format_to_n_result<OutputIt> {
9544- using traits = detail::fixed_buffer_traits;
9545- auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
9546- detail::vformat_to(buf, fmt, args, {});
9547- return {buf.out(), buf.count()};
9548-}
9549-
9550-/**
9551- \rst
9552- Formats ``args`` according to specifications in ``fmt``, writes up to ``n``
9553- characters of the result to the output iterator ``out`` and returns the total
9554- (not truncated) output size and the iterator past the end of the output range.
9555- `format_to_n` does not append a terminating null character.
9556- \endrst
9557- */
9558-template <typename OutputIt, typename... T,
9559- FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
9560-FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
9561- T&&... args) -> format_to_n_result<OutputIt> {
9562- return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
9563-}
9564-
9565-/** Returns the number of chars in the output of ``format(fmt, args...)``. */
9566-template <typename... T>
9567-FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
9568- T&&... args) -> size_t {
9569- auto buf = detail::counting_buffer<>();
9570- detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...), {});
9571- return buf.count();
9572-}
9573-
9574-FMT_API void vprint(string_view fmt, format_args args);
9575-FMT_API void vprint(std::FILE* f, string_view fmt, format_args args);
9576-
9577-/**
9578- \rst
9579- Formats ``args`` according to specifications in ``fmt`` and writes the output
9580- to ``stdout``.
9581-
9582- **Example**::
9583-
9584- fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
9585- \endrst
9586- */
9587-template <typename... T>
9588-FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
9589- const auto& vargs = fmt::make_format_args(args...);
9590- return detail::is_utf8() ? vprint(fmt, vargs)
9591- : detail::vprint_mojibake(stdout, fmt, vargs);
9592-}
9593-
9594-/**
9595- \rst
9596- Formats ``args`` according to specifications in ``fmt`` and writes the
9597- output to the file ``f``.
9598-
9599- **Example**::
9600-
9601- fmt::print(stderr, "Don't {}!", "panic");
9602- \endrst
9603- */
9604-template <typename... T>
9605-FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
9606- const auto& vargs = fmt::make_format_args(args...);
9607- return detail::is_utf8() ? vprint(f, fmt, vargs)
9608- : detail::vprint_mojibake(f, fmt, vargs);
9609-}
9610-
9611-/**
9612- Formats ``args`` according to specifications in ``fmt`` and writes the
9613- output to the file ``f`` followed by a newline.
9614- */
9615-template <typename... T>
9616-FMT_INLINE void println(std::FILE* f, format_string<T...> fmt, T&&... args) {
9617- return fmt::print(f, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
9618-}
9619-
9620-/**
9621- Formats ``args`` according to specifications in ``fmt`` and writes the output
9622- to ``stdout`` followed by a newline.
9623- */
9624-template <typename... T>
9625-FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
9626- return fmt::println(stdout, fmt, std::forward<T>(args)...);
9627-}
9628-
9629-FMT_END_EXPORT
9630-FMT_GCC_PRAGMA("GCC pop_options")
9631-FMT_END_NAMESPACE
9632-
9633-#ifdef FMT_HEADER_ONLY
9634-# include "format.h"
9635-#endif
9636-#endif // FMT_CORE_H_
9637+#include "format.h"
9638diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h
9639index dac2d43..66ee747 100644
9640--- a/include/fmt/format-inl.h
9641+++ b/include/fmt/format-inl.h
9642@@ -8,36 +8,36 @@
9643 #ifndef FMT_FORMAT_INL_H_
9644 #define FMT_FORMAT_INL_H_
9645
9646-#include <algorithm>
9647-#include <cerrno> // errno
9648-#include <climits>
9649-#include <cmath>
9650-#include <exception>
9651-
9652-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
9653-# include <locale>
9654+#ifndef FMT_MODULE
9655+# include <algorithm>
9656+# include <cerrno> // errno
9657+# include <climits>
9658+# include <cmath>
9659+# include <exception>
9660 #endif
9661
9662-#ifdef _WIN32
9663+#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
9664 # include <io.h> // _isatty
9665 #endif
9666
9667 #include "format.h"
9668
9669+#if FMT_USE_LOCALE
9670+# include <locale>
9671+#endif
9672+
9673+#ifndef FMT_FUNC
9674+# define FMT_FUNC
9675+#endif
9676+
9677 FMT_BEGIN_NAMESPACE
9678 namespace detail {
9679
9680 FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
9681 // Use unchecked std::fprintf to avoid triggering another assertion when
9682- // writing to stderr fails
9683- std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
9684- // Chosen instead of std::abort to satisfy Clang in CUDA mode during device
9685- // code pass.
9686- std::terminate();
9687-}
9688-
9689-FMT_FUNC void throw_format_error(const char* message) {
9690- FMT_THROW(format_error(message));
9691+ // writing to stderr fails.
9692+ fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
9693+ abort();
9694 }
9695
9696 FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
9697@@ -56,112 +56,129 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
9698 ++error_code_size;
9699 }
9700 error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
9701- auto it = buffer_appender<char>(out);
9702+ auto it = appender(out);
9703 if (message.size() <= inline_buffer_size - error_code_size)
9704- format_to(it, FMT_STRING("{}{}"), message, SEP);
9705- format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
9706+ fmt::format_to(it, FMT_STRING("{}{}"), message, SEP);
9707+ fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
9708 FMT_ASSERT(out.size() <= inline_buffer_size, "");
9709 }
9710
9711-FMT_FUNC void report_error(format_func func, int error_code,
9712- const char* message) noexcept {
9713+FMT_FUNC void do_report_error(format_func func, int error_code,
9714+ const char* message) noexcept {
9715 memory_buffer full_message;
9716 func(full_message, error_code, message);
9717- // Don't use fwrite_fully because the latter may throw.
9718+ // Don't use fwrite_all because the latter may throw.
9719 if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
9720 std::fputc('\n', stderr);
9721 }
9722
9723 // A wrapper around fwrite that throws on error.
9724-inline void fwrite_fully(const void* ptr, size_t size, size_t count,
9725- FILE* stream) {
9726- size_t written = std::fwrite(ptr, size, count, stream);
9727+inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
9728+ size_t written = std::fwrite(ptr, 1, count, stream);
9729 if (written < count)
9730 FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
9731 }
9732
9733-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
9734+#if FMT_USE_LOCALE
9735+using std::locale;
9736+using std::numpunct;
9737+using std::use_facet;
9738+
9739 template <typename Locale>
9740 locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
9741- static_assert(std::is_same<Locale, std::locale>::value, "");
9742+ static_assert(std::is_same<Locale, locale>::value, "");
9743 }
9744+#else
9745+struct locale {};
9746+template <typename Char> struct numpunct {
9747+ auto grouping() const -> std::string { return "\03"; }
9748+ auto thousands_sep() const -> Char { return ','; }
9749+ auto decimal_point() const -> Char { return '.'; }
9750+};
9751+template <typename Facet> Facet use_facet(locale) { return {}; }
9752+#endif // FMT_USE_LOCALE
9753
9754-template <typename Locale> Locale locale_ref::get() const {
9755- static_assert(std::is_same<Locale, std::locale>::value, "");
9756- return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
9757+template <typename Locale> auto locale_ref::get() const -> Locale {
9758+ static_assert(std::is_same<Locale, locale>::value, "");
9759+#if FMT_USE_LOCALE
9760+ if (locale_) return *static_cast<const locale*>(locale_);
9761+#endif
9762+ return locale();
9763 }
9764
9765 template <typename Char>
9766 FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
9767- auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
9768+ auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
9769 auto grouping = facet.grouping();
9770 auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
9771 return {std::move(grouping), thousands_sep};
9772 }
9773-template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
9774- return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
9775- .decimal_point();
9776-}
9777-#else
9778 template <typename Char>
9779-FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
9780- return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
9781+FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char {
9782+ return use_facet<numpunct<Char>>(loc.get<locale>()).decimal_point();
9783 }
9784-template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
9785- return '.';
9786-}
9787-#endif
9788
9789+#if FMT_USE_LOCALE
9790 FMT_FUNC auto write_loc(appender out, loc_value value,
9791- const format_specs<>& specs, locale_ref loc) -> bool {
9792-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
9793+ const format_specs& specs, locale_ref loc) -> bool {
9794 auto locale = loc.get<std::locale>();
9795 // We cannot use the num_put<char> facet because it may produce output in
9796 // a wrong encoding.
9797 using facet = format_facet<std::locale>;
9798 if (std::has_facet<facet>(locale))
9799- return std::use_facet<facet>(locale).put(out, value, specs);
9800+ return use_facet<facet>(locale).put(out, value, specs);
9801 return facet(locale).put(out, value, specs);
9802-#endif
9803- return false;
9804 }
9805+#endif
9806 } // namespace detail
9807
9808+FMT_FUNC void report_error(const char* message) {
9809+#if FMT_USE_EXCEPTIONS
9810+ // Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
9811+ // from MSVC.
9812+ FMT_THROW(format_error(message));
9813+#else
9814+ fputs(message, stderr);
9815+ abort();
9816+#endif
9817+}
9818+
9819 template <typename Locale> typename Locale::id format_facet<Locale>::id;
9820
9821-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
9822 template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
9823- auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
9824- grouping_ = numpunct.grouping();
9825- if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
9826+ auto& np = detail::use_facet<detail::numpunct<char>>(loc);
9827+ grouping_ = np.grouping();
9828+ if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep());
9829 }
9830
9831+#if FMT_USE_LOCALE
9832 template <>
9833 FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
9834- appender out, loc_value val, const format_specs<>& specs) const -> bool {
9835+ appender out, loc_value val, const format_specs& specs) const -> bool {
9836 return val.visit(
9837 detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
9838 }
9839 #endif
9840
9841-FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt,
9842- format_args args) {
9843+FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args)
9844+ -> std::system_error {
9845 auto ec = std::error_code(error_code, std::generic_category());
9846 return std::system_error(ec, vformat(fmt, args));
9847 }
9848
9849 namespace detail {
9850
9851-template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) {
9852+template <typename F>
9853+inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
9854 return x.f == y.f && x.e == y.e;
9855 }
9856
9857 // Compilers should be able to optimize this into the ror instruction.
9858-FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept {
9859+FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
9860 r &= 31;
9861 return (n >> r) | (n << (32 - r));
9862 }
9863-FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
9864+FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
9865 r &= 63;
9866 return (n >> r) | (n << (64 - r));
9867 }
9868@@ -170,14 +187,14 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
9869 namespace dragonbox {
9870 // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
9871 // 64-bit unsigned integer.
9872-inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
9873+inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t {
9874 return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
9875 }
9876
9877 // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
9878 // 128-bit unsigned integer.
9879-inline uint128_fallback umul192_lower128(uint64_t x,
9880- uint128_fallback y) noexcept {
9881+inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept
9882+ -> uint128_fallback {
9883 uint64_t high = x * y.high();
9884 uint128_fallback high_low = umul128(x, y.low());
9885 return {high + high_low.high(), high_low.low()};
9886@@ -185,17 +202,17 @@ inline uint128_fallback umul192_lower128(uint64_t x,
9887
9888 // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
9889 // 64-bit unsigned integer.
9890-inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
9891+inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t {
9892 return x * y;
9893 }
9894
9895 // Various fast log computations.
9896-inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
9897+inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int {
9898 FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
9899 return (e * 631305 - 261663) >> 21;
9900 }
9901
9902-FMT_INLINE_VARIABLE constexpr struct {
9903+FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct {
9904 uint32_t divisor;
9905 int shift_amount;
9906 } div_small_pow10_infos[] = {{10, 16}, {100, 16}};
9907@@ -204,7 +221,7 @@ FMT_INLINE_VARIABLE constexpr struct {
9908 // divisible by pow(10, N).
9909 // Precondition: n <= pow(10, N + 1).
9910 template <int N>
9911-bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
9912+auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool {
9913 // The numbers below are chosen such that:
9914 // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
9915 // 2. nm mod 2^k < m if and only if n is divisible by d,
9916@@ -229,7 +246,7 @@ bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
9917
9918 // Computes floor(n / pow(10, N)) for small n and N.
9919 // Precondition: n <= pow(10, N + 1).
9920-template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
9921+template <int N> auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t {
9922 constexpr auto info = div_small_pow10_infos[N - 1];
9923 FMT_ASSERT(n <= info.divisor * 10, "n is too large");
9924 constexpr uint32_t magic_number =
9925@@ -238,12 +255,12 @@ template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
9926 }
9927
9928 // Computes floor(n / 10^(kappa + 1)) (float)
9929-inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept {
9930+inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t {
9931 // 1374389535 = ceil(2^37/100)
9932 return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
9933 }
9934 // Computes floor(n / 10^(kappa + 1)) (double)
9935-inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
9936+inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t {
9937 // 2361183241434822607 = ceil(2^(64+7)/1000)
9938 return umul128_upper64(n, 2361183241434822607ull) >> 7;
9939 }
9940@@ -255,7 +272,7 @@ template <> struct cache_accessor<float> {
9941 using carrier_uint = float_info<float>::carrier_uint;
9942 using cache_entry_type = uint64_t;
9943
9944- static uint64_t get_cached_power(int k) noexcept {
9945+ static auto get_cached_power(int k) noexcept -> uint64_t {
9946 FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
9947 "k is out of range");
9948 static constexpr const uint64_t pow10_significands[] = {
9949@@ -297,20 +314,23 @@ template <> struct cache_accessor<float> {
9950 bool is_integer;
9951 };
9952
9953- static compute_mul_result compute_mul(
9954- carrier_uint u, const cache_entry_type& cache) noexcept {
9955+ static auto compute_mul(carrier_uint u,
9956+ const cache_entry_type& cache) noexcept
9957+ -> compute_mul_result {
9958 auto r = umul96_upper64(u, cache);
9959 return {static_cast<carrier_uint>(r >> 32),
9960 static_cast<carrier_uint>(r) == 0};
9961 }
9962
9963- static uint32_t compute_delta(const cache_entry_type& cache,
9964- int beta) noexcept {
9965+ static auto compute_delta(const cache_entry_type& cache, int beta) noexcept
9966+ -> uint32_t {
9967 return static_cast<uint32_t>(cache >> (64 - 1 - beta));
9968 }
9969
9970- static compute_mul_parity_result compute_mul_parity(
9971- carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
9972+ static auto compute_mul_parity(carrier_uint two_f,
9973+ const cache_entry_type& cache,
9974+ int beta) noexcept
9975+ -> compute_mul_parity_result {
9976 FMT_ASSERT(beta >= 1, "");
9977 FMT_ASSERT(beta < 64, "");
9978
9979@@ -319,22 +339,22 @@ template <> struct cache_accessor<float> {
9980 static_cast<uint32_t>(r >> (32 - beta)) == 0};
9981 }
9982
9983- static carrier_uint compute_left_endpoint_for_shorter_interval_case(
9984- const cache_entry_type& cache, int beta) noexcept {
9985+ static auto compute_left_endpoint_for_shorter_interval_case(
9986+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
9987 return static_cast<carrier_uint>(
9988 (cache - (cache >> (num_significand_bits<float>() + 2))) >>
9989 (64 - num_significand_bits<float>() - 1 - beta));
9990 }
9991
9992- static carrier_uint compute_right_endpoint_for_shorter_interval_case(
9993- const cache_entry_type& cache, int beta) noexcept {
9994+ static auto compute_right_endpoint_for_shorter_interval_case(
9995+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
9996 return static_cast<carrier_uint>(
9997 (cache + (cache >> (num_significand_bits<float>() + 1))) >>
9998 (64 - num_significand_bits<float>() - 1 - beta));
9999 }
10000
10001- static carrier_uint compute_round_up_for_shorter_interval_case(
10002- const cache_entry_type& cache, int beta) noexcept {
10003+ static auto compute_round_up_for_shorter_interval_case(
10004+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
10005 return (static_cast<carrier_uint>(
10006 cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
10007 1) /
10008@@ -346,7 +366,7 @@ template <> struct cache_accessor<double> {
10009 using carrier_uint = float_info<double>::carrier_uint;
10010 using cache_entry_type = uint128_fallback;
10011
10012- static uint128_fallback get_cached_power(int k) noexcept {
10013+ static auto get_cached_power(int k) noexcept -> uint128_fallback {
10014 FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
10015 "k is out of range");
10016
10017@@ -985,8 +1005,7 @@ template <> struct cache_accessor<double> {
10018 {0xe0accfa875af45a7, 0x93eb1b80a33b8606},
10019 {0x8c6c01c9498d8b88, 0xbc72f130660533c4},
10020 {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
10021- { 0xdb68c2ca82ed2a05,
10022- 0xa67398db9f6820e2 }
10023+ {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2},
10024 #else
10025 {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
10026 {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
10027@@ -1071,19 +1090,22 @@ template <> struct cache_accessor<double> {
10028 bool is_integer;
10029 };
10030
10031- static compute_mul_result compute_mul(
10032- carrier_uint u, const cache_entry_type& cache) noexcept {
10033+ static auto compute_mul(carrier_uint u,
10034+ const cache_entry_type& cache) noexcept
10035+ -> compute_mul_result {
10036 auto r = umul192_upper128(u, cache);
10037 return {r.high(), r.low() == 0};
10038 }
10039
10040- static uint32_t compute_delta(cache_entry_type const& cache,
10041- int beta) noexcept {
10042+ static auto compute_delta(cache_entry_type const& cache, int beta) noexcept
10043+ -> uint32_t {
10044 return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
10045 }
10046
10047- static compute_mul_parity_result compute_mul_parity(
10048- carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
10049+ static auto compute_mul_parity(carrier_uint two_f,
10050+ const cache_entry_type& cache,
10051+ int beta) noexcept
10052+ -> compute_mul_parity_result {
10053 FMT_ASSERT(beta >= 1, "");
10054 FMT_ASSERT(beta < 64, "");
10055
10056@@ -1092,35 +1114,35 @@ template <> struct cache_accessor<double> {
10057 ((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
10058 }
10059
10060- static carrier_uint compute_left_endpoint_for_shorter_interval_case(
10061- const cache_entry_type& cache, int beta) noexcept {
10062+ static auto compute_left_endpoint_for_shorter_interval_case(
10063+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
10064 return (cache.high() -
10065 (cache.high() >> (num_significand_bits<double>() + 2))) >>
10066 (64 - num_significand_bits<double>() - 1 - beta);
10067 }
10068
10069- static carrier_uint compute_right_endpoint_for_shorter_interval_case(
10070- const cache_entry_type& cache, int beta) noexcept {
10071+ static auto compute_right_endpoint_for_shorter_interval_case(
10072+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
10073 return (cache.high() +
10074 (cache.high() >> (num_significand_bits<double>() + 1))) >>
10075 (64 - num_significand_bits<double>() - 1 - beta);
10076 }
10077
10078- static carrier_uint compute_round_up_for_shorter_interval_case(
10079- const cache_entry_type& cache, int beta) noexcept {
10080+ static auto compute_round_up_for_shorter_interval_case(
10081+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
10082 return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
10083 1) /
10084 2;
10085 }
10086 };
10087
10088-FMT_FUNC uint128_fallback get_cached_power(int k) noexcept {
10089+FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback {
10090 return cache_accessor<double>::get_cached_power(k);
10091 }
10092
10093 // Various integer checks
10094 template <typename T>
10095-bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
10096+auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
10097 const int case_shorter_interval_left_endpoint_lower_threshold = 2;
10098 const int case_shorter_interval_left_endpoint_upper_threshold = 3;
10099 return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
10100@@ -1132,7 +1154,7 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
10101 FMT_ASSERT(n != 0, "");
10102 // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
10103 constexpr uint32_t mod_inv_5 = 0xcccccccd;
10104- constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5
10105+ constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5
10106
10107 while (true) {
10108 auto q = rotr(n * mod_inv_25, 2);
10109@@ -1168,7 +1190,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
10110
10111 // If n is not divisible by 10^8, work with n itself.
10112 constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd;
10113- constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // = mod_inv_5 * mod_inv_5
10114+ constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5
10115
10116 int s = 0;
10117 while (true) {
10118@@ -1234,7 +1256,7 @@ FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
10119 return ret_value;
10120 }
10121
10122-template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
10123+template <typename T> auto to_decimal(T x) noexcept -> decimal_fp<T> {
10124 // Step 1: integer promotion & Schubfach multiplier calculation.
10125
10126 using carrier_uint = typename float_info<T>::carrier_uint;
10127@@ -1373,15 +1395,15 @@ template <> struct formatter<detail::bigint> {
10128 for (auto i = n.bigits_.size(); i > 0; --i) {
10129 auto value = n.bigits_[i - 1u];
10130 if (first) {
10131- out = format_to(out, FMT_STRING("{:x}"), value);
10132+ out = fmt::format_to(out, FMT_STRING("{:x}"), value);
10133 first = false;
10134 continue;
10135 }
10136- out = format_to(out, FMT_STRING("{:08x}"), value);
10137+ out = fmt::format_to(out, FMT_STRING("{:08x}"), value);
10138 }
10139 if (n.exp_ > 0)
10140- out = format_to(out, FMT_STRING("p{}"),
10141- n.exp_ * detail::bigint::bigit_bits);
10142+ out = fmt::format_to(out, FMT_STRING("p{}"),
10143+ n.exp_ * detail::bigint::bigit_bits);
10144 return out;
10145 }
10146 };
10147@@ -1405,7 +1427,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
10148 const char* message) noexcept {
10149 FMT_TRY {
10150 auto ec = std::error_code(error_code, std::generic_category());
10151- write(std::back_inserter(out), std::system_error(ec, message).what());
10152+ detail::write(appender(out), std::system_error(ec, message).what());
10153 return;
10154 }
10155 FMT_CATCH(...) {}
10156@@ -1414,10 +1436,10 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
10157
10158 FMT_FUNC void report_system_error(int error_code,
10159 const char* message) noexcept {
10160- report_error(format_system_error, error_code, message);
10161+ do_report_error(format_system_error, error_code, message);
10162 }
10163
10164-FMT_FUNC std::string vformat(string_view fmt, format_args args) {
10165+FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
10166 // Don't optimize the "{}" case to keep the binary size small and because it
10167 // can be better optimized in fmt::format anyway.
10168 auto buffer = memory_buffer();
10169@@ -1426,39 +1448,304 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
10170 }
10171
10172 namespace detail {
10173-#ifndef _WIN32
10174-FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
10175+
10176+FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
10177+ locale_ref loc) {
10178+ auto out = appender(buf);
10179+ if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
10180+ return args.get(0).visit(default_arg_formatter<char>{out});
10181+ parse_format_string(
10182+ fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
10183+}
10184+
10185+template <typename T> struct span {
10186+ T* data;
10187+ size_t size;
10188+};
10189+
10190+template <typename F> auto flockfile(F* f) -> decltype(_lock_file(f)) {
10191+ _lock_file(f);
10192+}
10193+template <typename F> auto funlockfile(F* f) -> decltype(_unlock_file(f)) {
10194+ _unlock_file(f);
10195+}
10196+
10197+#ifndef getc_unlocked
10198+template <typename F> auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) {
10199+ return _fgetc_nolock(f);
10200+}
10201+#endif
10202+
10203+template <typename F = FILE, typename Enable = void>
10204+struct has_flockfile : std::false_type {};
10205+
10206+template <typename F>
10207+struct has_flockfile<F, void_t<decltype(flockfile(&std::declval<F&>()))>>
10208+ : std::true_type {};
10209+
10210+// A FILE wrapper. F is FILE defined as a template parameter to make system API
10211+// detection work.
10212+template <typename F> class file_base {
10213+ public:
10214+ F* file_;
10215+
10216+ public:
10217+ file_base(F* file) : file_(file) {}
10218+ operator F*() const { return file_; }
10219+
10220+ // Reads a code unit from the stream.
10221+ auto get() -> int {
10222+ int result = getc_unlocked(file_);
10223+ if (result == EOF && ferror(file_) != 0)
10224+ FMT_THROW(system_error(errno, FMT_STRING("getc failed")));
10225+ return result;
10226+ }
10227+
10228+ // Puts the code unit back into the stream buffer.
10229+ void unget(char c) {
10230+ if (ungetc(c, file_) == EOF)
10231+ FMT_THROW(system_error(errno, FMT_STRING("ungetc failed")));
10232+ }
10233+
10234+ void flush() { fflush(this->file_); }
10235+};
10236+
10237+// A FILE wrapper for glibc.
10238+template <typename F> class glibc_file : public file_base<F> {
10239+ private:
10240+ enum {
10241+ line_buffered = 0x200, // _IO_LINE_BUF
10242+ unbuffered = 2 // _IO_UNBUFFERED
10243+ };
10244+
10245+ public:
10246+ using file_base<F>::file_base;
10247+
10248+ auto is_buffered() const -> bool {
10249+ return (this->file_->_flags & unbuffered) == 0;
10250+ }
10251+
10252+ void init_buffer() {
10253+ if (this->file_->_IO_write_ptr) return;
10254+ // Force buffer initialization by placing and removing a char in a buffer.
10255+ assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end);
10256+ putc_unlocked(0, this->file_);
10257+ --this->file_->_IO_write_ptr;
10258+ }
10259+
10260+ // Returns the file's read buffer.
10261+ auto get_read_buffer() const -> span<const char> {
10262+ auto ptr = this->file_->_IO_read_ptr;
10263+ return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)};
10264+ }
10265+
10266+ // Returns the file's write buffer.
10267+ auto get_write_buffer() const -> span<char> {
10268+ auto ptr = this->file_->_IO_write_ptr;
10269+ return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)};
10270+ }
10271+
10272+ void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
10273+
10274+ bool needs_flush() const {
10275+ if ((this->file_->_flags & line_buffered) == 0) return false;
10276+ char* end = this->file_->_IO_write_end;
10277+ return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
10278+ }
10279+
10280+ void flush() { fflush_unlocked(this->file_); }
10281+};
10282+
10283+// A FILE wrapper for Apple's libc.
10284+template <typename F> class apple_file : public file_base<F> {
10285+ private:
10286+ enum {
10287+ line_buffered = 1, // __SNBF
10288+ unbuffered = 2 // __SLBF
10289+ };
10290+
10291+ public:
10292+ using file_base<F>::file_base;
10293+
10294+ auto is_buffered() const -> bool {
10295+ return (this->file_->_flags & unbuffered) == 0;
10296+ }
10297+
10298+ void init_buffer() {
10299+ if (this->file_->_p) return;
10300+ // Force buffer initialization by placing and removing a char in a buffer.
10301+ putc_unlocked(0, this->file_);
10302+ --this->file_->_p;
10303+ ++this->file_->_w;
10304+ }
10305+
10306+ auto get_read_buffer() const -> span<const char> {
10307+ return {reinterpret_cast<char*>(this->file_->_p),
10308+ to_unsigned(this->file_->_r)};
10309+ }
10310+
10311+ auto get_write_buffer() const -> span<char> {
10312+ return {reinterpret_cast<char*>(this->file_->_p),
10313+ to_unsigned(this->file_->_bf._base + this->file_->_bf._size -
10314+ this->file_->_p)};
10315+ }
10316+
10317+ void advance_write_buffer(size_t size) {
10318+ this->file_->_p += size;
10319+ this->file_->_w -= size;
10320+ }
10321+
10322+ bool needs_flush() const {
10323+ if ((this->file_->_flags & line_buffered) == 0) return false;
10324+ return memchr(this->file_->_p + this->file_->_w, '\n',
10325+ to_unsigned(-this->file_->_w));
10326+ }
10327+};
10328+
10329+// A fallback FILE wrapper.
10330+template <typename F> class fallback_file : public file_base<F> {
10331+ private:
10332+ char next_; // The next unconsumed character in the buffer.
10333+ bool has_next_ = false;
10334+
10335+ public:
10336+ using file_base<F>::file_base;
10337+
10338+ auto is_buffered() const -> bool { return false; }
10339+ auto needs_flush() const -> bool { return false; }
10340+ void init_buffer() {}
10341+
10342+ auto get_read_buffer() const -> span<const char> {
10343+ return {&next_, has_next_ ? 1u : 0u};
10344+ }
10345+
10346+ auto get_write_buffer() const -> span<char> { return {nullptr, 0}; }
10347+
10348+ void advance_write_buffer(size_t) {}
10349+
10350+ auto get() -> int {
10351+ has_next_ = false;
10352+ return file_base<F>::get();
10353+ }
10354+
10355+ void unget(char c) {
10356+ file_base<F>::unget(c);
10357+ next_ = c;
10358+ has_next_ = true;
10359+ }
10360+};
10361+
10362+#ifndef FMT_USE_FALLBACK_FILE
10363+# define FMT_USE_FALLBACK_FILE 0
10364+#endif
10365+
10366+template <typename F,
10367+ FMT_ENABLE_IF(sizeof(F::_p) != 0 && !FMT_USE_FALLBACK_FILE)>
10368+auto get_file(F* f, int) -> apple_file<F> {
10369+ return f;
10370+}
10371+template <typename F,
10372+ FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0 && !FMT_USE_FALLBACK_FILE)>
10373+inline auto get_file(F* f, int) -> glibc_file<F> {
10374+ return f;
10375+}
10376+
10377+inline auto get_file(FILE* f, ...) -> fallback_file<FILE> { return f; }
10378+
10379+using file_ref = decltype(get_file(static_cast<FILE*>(nullptr), 0));
10380+
10381+template <typename F = FILE, typename Enable = void>
10382+class file_print_buffer : public buffer<char> {
10383+ public:
10384+ explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {}
10385+};
10386+
10387+template <typename F>
10388+class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
10389+ : public buffer<char> {
10390+ private:
10391+ file_ref file_;
10392+
10393+ static void grow(buffer<char>& base, size_t) {
10394+ auto& self = static_cast<file_print_buffer&>(base);
10395+ self.file_.advance_write_buffer(self.size());
10396+ if (self.file_.get_write_buffer().size == 0) self.file_.flush();
10397+ auto buf = self.file_.get_write_buffer();
10398+ FMT_ASSERT(buf.size > 0, "");
10399+ self.set(buf.data, buf.size);
10400+ self.clear();
10401+ }
10402+
10403+ public:
10404+ explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) {
10405+ flockfile(f);
10406+ file_.init_buffer();
10407+ auto buf = file_.get_write_buffer();
10408+ set(buf.data, buf.size);
10409+ }
10410+ ~file_print_buffer() {
10411+ file_.advance_write_buffer(size());
10412+ bool flush = file_.needs_flush();
10413+ F* f = file_; // Make funlockfile depend on the template parameter F
10414+ funlockfile(f); // for the system API detection to work.
10415+ if (flush) fflush(file_);
10416+ }
10417+};
10418+
10419+#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE)
10420+FMT_FUNC auto write_console(int, string_view) -> bool { return false; }
10421 #else
10422 using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
10423 extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
10424 void*, const void*, dword, dword*, void*);
10425
10426-FMT_FUNC bool write_console(std::FILE* f, string_view text) {
10427- auto fd = _fileno(f);
10428- if (!_isatty(fd)) return false;
10429+FMT_FUNC bool write_console(int fd, string_view text) {
10430 auto u16 = utf8_to_utf16(text);
10431- auto written = dword();
10432 return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
10433- static_cast<uint32_t>(u16.size()), &written, nullptr) != 0;
10434+ static_cast<dword>(u16.size()), nullptr, nullptr) != 0;
10435 }
10436+#endif
10437
10438+#ifdef _WIN32
10439 // Print assuming legacy (non-Unicode) encoding.
10440-FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
10441+FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args,
10442+ bool newline) {
10443 auto buffer = memory_buffer();
10444- detail::vformat_to(buffer, fmt,
10445- basic_format_args<buffer_context<char>>(args));
10446- fwrite_fully(buffer.data(), 1, buffer.size(), f);
10447+ detail::vformat_to(buffer, fmt, args);
10448+ if (newline) buffer.push_back('\n');
10449+ fwrite_all(buffer.data(), buffer.size(), f);
10450 }
10451 #endif
10452
10453 FMT_FUNC void print(std::FILE* f, string_view text) {
10454- if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
10455+#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
10456+ int fd = _fileno(f);
10457+ if (_isatty(fd)) {
10458+ std::fflush(f);
10459+ if (write_console(fd, text)) return;
10460+ }
10461+#endif
10462+ fwrite_all(text.data(), text.size(), f);
10463 }
10464 } // namespace detail
10465
10466+FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) {
10467+ auto buffer = memory_buffer();
10468+ detail::vformat_to(buffer, fmt, args);
10469+ detail::print(f, {buffer.data(), buffer.size()});
10470+}
10471+
10472 FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
10473+ if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>())
10474+ return vprint_buffered(f, fmt, args);
10475+ auto&& buffer = detail::file_print_buffer<>(f);
10476+ return detail::vformat_to(buffer, fmt, args);
10477+}
10478+
10479+FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) {
10480 auto buffer = memory_buffer();
10481 detail::vformat_to(buffer, fmt, args);
10482+ buffer.push_back('\n');
10483 detail::print(f, {buffer.data(), buffer.size()});
10484 }
10485
10486diff --git a/include/fmt/format.h b/include/fmt/format.h
10487index a65d376..287e716 100644
10488--- a/include/fmt/format.h
10489+++ b/include/fmt/format.h
10490@@ -33,24 +33,58 @@
10491 #ifndef FMT_FORMAT_H_
10492 #define FMT_FORMAT_H_
10493
10494-#include <cmath> // std::signbit
10495-#include <cstdint> // uint32_t
10496-#include <cstring> // std::memcpy
10497-#include <initializer_list> // std::initializer_list
10498-#include <limits> // std::numeric_limits
10499-#include <memory> // std::uninitialized_copy
10500-#include <stdexcept> // std::runtime_error
10501-#include <system_error> // std::system_error
10502-
10503-#ifdef __cpp_lib_bit_cast
10504-# include <bit> // std::bitcast
10505+#ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES
10506+# define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES
10507+# define FMT_REMOVE_TRANSITIVE_INCLUDES
10508 #endif
10509
10510-#include "core.h"
10511+#include "base.h"
10512+
10513+#ifndef FMT_MODULE
10514+# include <cmath> // std::signbit
10515+# include <cstddef> // std::byte
10516+# include <cstdint> // uint32_t
10517+# include <cstring> // std::memcpy
10518+# include <limits> // std::numeric_limits
10519+# include <new> // std::bad_alloc
10520+# if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)
10521+// Workaround for pre gcc 5 libstdc++.
10522+# include <memory> // std::allocator_traits
10523+# endif
10524+# include <stdexcept> // std::runtime_error
10525+# include <string> // std::string
10526+# include <system_error> // std::system_error
10527+
10528+// Check FMT_CPLUSPLUS to avoid a warning in MSVC.
10529+# if FMT_HAS_INCLUDE(<bit>) && FMT_CPLUSPLUS > 201703L
10530+# include <bit> // std::bit_cast
10531+# endif
10532+
10533+// libc++ supports string_view in pre-c++17.
10534+# if FMT_HAS_INCLUDE(<string_view>) && \
10535+ (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
10536+# include <string_view>
10537+# define FMT_USE_STRING_VIEW
10538+# endif
10539
10540-#ifndef FMT_BEGIN_DETAIL_NAMESPACE
10541-# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
10542-# define FMT_END_DETAIL_NAMESPACE }
10543+# if FMT_MSC_VERSION
10544+# include <intrin.h> // _BitScanReverse[64], _umul128
10545+# endif
10546+#endif // FMT_MODULE
10547+
10548+#if defined(FMT_USE_NONTYPE_TEMPLATE_ARGS)
10549+// Use the provided definition.
10550+#elif defined(__NVCOMPILER)
10551+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
10552+#elif FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L
10553+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
10554+#elif defined(__cpp_nontype_template_args) && \
10555+ __cpp_nontype_template_args >= 201911L
10556+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
10557+#elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L
10558+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
10559+#else
10560+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
10561 #endif
10562
10563 #if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L
10564@@ -59,41 +93,22 @@
10565 # define FMT_INLINE_VARIABLE
10566 #endif
10567
10568-#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
10569-# define FMT_FALLTHROUGH [[fallthrough]]
10570-#elif defined(__clang__)
10571-# define FMT_FALLTHROUGH [[clang::fallthrough]]
10572-#elif FMT_GCC_VERSION >= 700 && \
10573- (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
10574-# define FMT_FALLTHROUGH [[gnu::fallthrough]]
10575-#else
10576-# define FMT_FALLTHROUGH
10577-#endif
10578-
10579-#ifndef FMT_DEPRECATED
10580-# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
10581-# define FMT_DEPRECATED [[deprecated]]
10582-# else
10583-# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
10584-# define FMT_DEPRECATED __attribute__((deprecated))
10585-# elif FMT_MSC_VERSION
10586-# define FMT_DEPRECATED __declspec(deprecated)
10587-# else
10588-# define FMT_DEPRECATED /* deprecated */
10589-# endif
10590-# endif
10591-#endif
10592-
10593-#if FMT_GCC_VERSION || defined(__clang__)
10594-# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
10595+// Check if RTTI is disabled.
10596+#ifdef FMT_USE_RTTI
10597+// Use the provided definition.
10598+#elif defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \
10599+ defined(__INTEL_RTTI__) || defined(__RTTI)
10600+// __RTTI is for EDG compilers. _CPPRTTI is for MSVC.
10601+# define FMT_USE_RTTI 1
10602 #else
10603-# define FMT_VISIBILITY(value)
10604+# define FMT_USE_RTTI 0
10605 #endif
10606
10607-#ifdef __has_builtin
10608-# define FMT_HAS_BUILTIN(x) __has_builtin(x)
10609+// Visibility when compiled as a shared library/object.
10610+#if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
10611+# define FMT_SO_VISIBILITY(value) FMT_VISIBILITY(value)
10612 #else
10613-# define FMT_HAS_BUILTIN(x) 0
10614+# define FMT_SO_VISIBILITY(value)
10615 #endif
10616
10617 #if FMT_GCC_VERSION || FMT_CLANG_VERSION
10618@@ -102,8 +117,19 @@
10619 # define FMT_NOINLINE
10620 #endif
10621
10622+namespace std {
10623+template <typename T> struct iterator_traits<fmt::basic_appender<T>> {
10624+ using iterator_category = output_iterator_tag;
10625+ using value_type = T;
10626+ using difference_type =
10627+ decltype(static_cast<int*>(nullptr) - static_cast<int*>(nullptr));
10628+ using pointer = void;
10629+ using reference = void;
10630+};
10631+} // namespace std
10632+
10633 #ifndef FMT_THROW
10634-# if FMT_EXCEPTIONS
10635+# if FMT_USE_EXCEPTIONS
10636 # if FMT_MSC_VERSION || defined(__NVCC__)
10637 FMT_BEGIN_NAMESPACE
10638 namespace detail {
10639@@ -122,35 +148,8 @@ FMT_END_NAMESPACE
10640 # else
10641 # define FMT_THROW(x) \
10642 ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what())
10643-# endif
10644-#endif
10645-
10646-#if FMT_EXCEPTIONS
10647-# define FMT_TRY try
10648-# define FMT_CATCH(x) catch (x)
10649-#else
10650-# define FMT_TRY if (true)
10651-# define FMT_CATCH(x) if (false)
10652-#endif
10653-
10654-#ifndef FMT_MAYBE_UNUSED
10655-# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
10656-# define FMT_MAYBE_UNUSED [[maybe_unused]]
10657-# else
10658-# define FMT_MAYBE_UNUSED
10659-# endif
10660-#endif
10661-
10662-#ifndef FMT_USE_USER_DEFINED_LITERALS
10663-// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
10664-# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
10665- FMT_MSC_VERSION >= 1900) && \
10666- (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
10667-# define FMT_USE_USER_DEFINED_LITERALS 1
10668-# else
10669-# define FMT_USE_USER_DEFINED_LITERALS 0
10670-# endif
10671-#endif
10672+# endif // FMT_USE_EXCEPTIONS
10673+#endif // FMT_THROW
10674
10675 // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
10676 // integer formatter template instantiations to just one by only using the
10677@@ -160,7 +159,15 @@ FMT_END_NAMESPACE
10678 # define FMT_REDUCE_INT_INSTANTIATIONS 0
10679 #endif
10680
10681-// __builtin_clz is broken in clang with Microsoft CodeGen:
10682+FMT_BEGIN_NAMESPACE
10683+
10684+template <typename Char, typename Traits, typename Allocator>
10685+struct is_contiguous<std::basic_string<Char, Traits, Allocator>>
10686+ : std::true_type {};
10687+
10688+namespace detail {
10689+
10690+// __builtin_clz is broken in clang with Microsoft codegen:
10691 // https://github.com/fmtlib/fmt/issues/519.
10692 #if !FMT_MSC_VERSION
10693 # if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION
10694@@ -171,53 +178,30 @@ FMT_END_NAMESPACE
10695 # endif
10696 #endif
10697
10698-// __builtin_ctz is broken in Intel Compiler Classic on Windows:
10699-// https://github.com/fmtlib/fmt/issues/2510.
10700-#ifndef __ICL
10701-# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \
10702- defined(__NVCOMPILER)
10703-# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
10704-# endif
10705-# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \
10706- FMT_ICC_VERSION || defined(__NVCOMPILER)
10707-# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
10708-# endif
10709-#endif
10710-
10711-#if FMT_MSC_VERSION
10712-# include <intrin.h> // _BitScanReverse[64], _BitScanForward[64], _umul128
10713-#endif
10714-
10715-// Some compilers masquerade as both MSVC and GCC-likes or otherwise support
10716+// Some compilers masquerade as both MSVC and GCC but otherwise support
10717 // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the
10718 // MSVC intrinsics if the clz and clzll builtins are not available.
10719-#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \
10720- !defined(FMT_BUILTIN_CTZLL)
10721-FMT_BEGIN_NAMESPACE
10722-namespace detail {
10723+#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL)
10724 // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
10725-# if !defined(__clang__)
10726-# pragma intrinsic(_BitScanForward)
10727+# ifndef __clang__
10728 # pragma intrinsic(_BitScanReverse)
10729-# if defined(_WIN64)
10730-# pragma intrinsic(_BitScanForward64)
10731+# ifdef _WIN64
10732 # pragma intrinsic(_BitScanReverse64)
10733 # endif
10734 # endif
10735
10736 inline auto clz(uint32_t x) -> int {
10737+ FMT_ASSERT(x != 0, "");
10738+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
10739 unsigned long r = 0;
10740 _BitScanReverse(&r, x);
10741- FMT_ASSERT(x != 0, "");
10742- // Static analysis complains about using uninitialized data
10743- // "r", but the only way that can happen is if "x" is 0,
10744- // which the callers guarantee to not happen.
10745- FMT_MSC_WARNING(suppress : 6102)
10746 return 31 ^ static_cast<int>(r);
10747 }
10748 # define FMT_BUILTIN_CLZ(n) detail::clz(n)
10749
10750 inline auto clzll(uint64_t x) -> int {
10751+ FMT_ASSERT(x != 0, "");
10752+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
10753 unsigned long r = 0;
10754 # ifdef _WIN64
10755 _BitScanReverse64(&r, x);
10756@@ -228,56 +212,10 @@ inline auto clzll(uint64_t x) -> int {
10757 // Scan the low 32 bits.
10758 _BitScanReverse(&r, static_cast<uint32_t>(x));
10759 # endif
10760- FMT_ASSERT(x != 0, "");
10761- FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
10762 return 63 ^ static_cast<int>(r);
10763 }
10764 # define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
10765-
10766-inline auto ctz(uint32_t x) -> int {
10767- unsigned long r = 0;
10768- _BitScanForward(&r, x);
10769- FMT_ASSERT(x != 0, "");
10770- FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
10771- return static_cast<int>(r);
10772-}
10773-# define FMT_BUILTIN_CTZ(n) detail::ctz(n)
10774-
10775-inline auto ctzll(uint64_t x) -> int {
10776- unsigned long r = 0;
10777- FMT_ASSERT(x != 0, "");
10778- FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
10779-# ifdef _WIN64
10780- _BitScanForward64(&r, x);
10781-# else
10782- // Scan the low 32 bits.
10783- if (_BitScanForward(&r, static_cast<uint32_t>(x))) return static_cast<int>(r);
10784- // Scan the high 32 bits.
10785- _BitScanForward(&r, static_cast<uint32_t>(x >> 32));
10786- r += 32;
10787-# endif
10788- return static_cast<int>(r);
10789-}
10790-# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n)
10791-} // namespace detail
10792-FMT_END_NAMESPACE
10793-#endif
10794-
10795-FMT_BEGIN_NAMESPACE
10796-
10797-template <typename...> struct disjunction : std::false_type {};
10798-template <typename P> struct disjunction<P> : P {};
10799-template <typename P1, typename... Pn>
10800-struct disjunction<P1, Pn...>
10801- : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
10802-
10803-template <typename...> struct conjunction : std::true_type {};
10804-template <typename P> struct conjunction<P> : P {};
10805-template <typename P1, typename... Pn>
10806-struct conjunction<P1, Pn...>
10807- : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
10808-
10809-namespace detail {
10810+#endif // FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL)
10811
10812 FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
10813 ignore_unused(condition);
10814@@ -286,48 +224,24 @@ FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
10815 #endif
10816 }
10817
10818-template <typename CharT, CharT... C> struct string_literal {
10819- static constexpr CharT value[sizeof...(C)] = {C...};
10820- constexpr operator basic_string_view<CharT>() const {
10821- return {value, sizeof...(C)};
10822- }
10823+#if defined(FMT_USE_STRING_VIEW)
10824+template <typename Char> using std_string_view = std::basic_string_view<Char>;
10825+#else
10826+template <typename Char> struct std_string_view {
10827+ operator basic_string_view<Char>() const;
10828 };
10829-
10830-#if FMT_CPLUSPLUS < 201703L
10831-template <typename CharT, CharT... C>
10832-constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)];
10833 #endif
10834
10835-template <typename Streambuf> class formatbuf : public Streambuf {
10836- private:
10837- using char_type = typename Streambuf::char_type;
10838- using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
10839- using int_type = typename Streambuf::int_type;
10840- using traits_type = typename Streambuf::traits_type;
10841-
10842- buffer<char_type>& buffer_;
10843-
10844- public:
10845- explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
10846-
10847- protected:
10848- // The put area is always empty. This makes the implementation simpler and has
10849- // the advantage that the streambuf and the buffer are always in sync and
10850- // sputc never writes into uninitialized memory. A disadvantage is that each
10851- // call to sputc always results in a (virtual) call to overflow. There is no
10852- // disadvantage here for sputn since this always results in a call to xsputn.
10853-
10854- auto overflow(int_type ch) -> int_type override {
10855- if (!traits_type::eq_int_type(ch, traits_type::eof()))
10856- buffer_.push_back(static_cast<char_type>(ch));
10857- return ch;
10858- }
10859-
10860- auto xsputn(const char_type* s, streamsize count) -> streamsize override {
10861- buffer_.append(s, s + count);
10862- return count;
10863+template <typename Char, Char... C> struct string_literal {
10864+ static constexpr Char value[sizeof...(C)] = {C...};
10865+ constexpr operator basic_string_view<Char>() const {
10866+ return {value, sizeof...(C)};
10867 }
10868 };
10869+#if FMT_CPLUSPLUS < 201703L
10870+template <typename Char, Char... C>
10871+constexpr Char string_literal<Char, C...>::value[sizeof...(C)];
10872+#endif
10873
10874 // Implementation of std::bit_cast for pre-C++20.
10875 template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
10876@@ -360,14 +274,12 @@ class uint128_fallback {
10877 private:
10878 uint64_t lo_, hi_;
10879
10880- friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept;
10881-
10882 public:
10883 constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
10884 constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
10885
10886- constexpr uint64_t high() const noexcept { return hi_; }
10887- constexpr uint64_t low() const noexcept { return lo_; }
10888+ constexpr auto high() const noexcept -> uint64_t { return hi_; }
10889+ constexpr auto low() const noexcept -> uint64_t { return lo_; }
10890
10891 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
10892 constexpr explicit operator T() const {
10893@@ -400,13 +312,14 @@ class uint128_fallback {
10894 -> uint128_fallback {
10895 return {~n.hi_, ~n.lo_};
10896 }
10897- friend auto operator+(const uint128_fallback& lhs,
10898- const uint128_fallback& rhs) -> uint128_fallback {
10899+ friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs,
10900+ const uint128_fallback& rhs)
10901+ -> uint128_fallback {
10902 auto result = uint128_fallback(lhs);
10903 result += rhs;
10904 return result;
10905 }
10906- friend auto operator*(const uint128_fallback& lhs, uint32_t rhs)
10907+ friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs)
10908 -> uint128_fallback {
10909 FMT_ASSERT(lhs.hi_ == 0, "");
10910 uint64_t hi = (lhs.lo_ >> 32) * rhs;
10911@@ -414,7 +327,7 @@ class uint128_fallback {
10912 uint64_t new_lo = (hi << 32) + lo;
10913 return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
10914 }
10915- friend auto operator-(const uint128_fallback& lhs, uint64_t rhs)
10916+ friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs)
10917 -> uint128_fallback {
10918 return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
10919 }
10920@@ -443,7 +356,7 @@ class uint128_fallback {
10921 hi_ &= n.hi_;
10922 }
10923
10924- FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept {
10925+ FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& {
10926 if (is_constant_evaluated()) {
10927 lo_ += n;
10928 hi_ += (lo_ < n ? 1 : 0);
10929@@ -487,23 +400,24 @@ template <typename T> constexpr auto num_bits() -> int {
10930 }
10931 // std::numeric_limits<T>::digits may return 0 for 128-bit ints.
10932 template <> constexpr auto num_bits<int128_opt>() -> int { return 128; }
10933-template <> constexpr auto num_bits<uint128_t>() -> int { return 128; }
10934+template <> constexpr auto num_bits<uint128_opt>() -> int { return 128; }
10935+template <> constexpr auto num_bits<uint128_fallback>() -> int { return 128; }
10936
10937 // A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
10938 // and 128-bit pointers to uint128_fallback.
10939 template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
10940 inline auto bit_cast(const From& from) -> To {
10941- constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
10942+ constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned short));
10943 struct data_t {
10944- unsigned value[static_cast<unsigned>(size)];
10945+ unsigned short value[static_cast<unsigned>(size)];
10946 } data = bit_cast<data_t>(from);
10947 auto result = To();
10948 if (const_check(is_big_endian())) {
10949 for (int i = 0; i < size; ++i)
10950- result = (result << num_bits<unsigned>()) | data.value[i];
10951+ result = (result << num_bits<unsigned short>()) | data.value[i];
10952 } else {
10953 for (int i = size - 1; i >= 0; --i)
10954- result = (result << num_bits<unsigned>()) | data.value[i];
10955+ result = (result << num_bits<unsigned short>()) | data.value[i];
10956 }
10957 return result;
10958 }
10959@@ -534,55 +448,30 @@ FMT_INLINE void assume(bool condition) {
10960 (void)condition;
10961 #if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
10962 __builtin_assume(condition);
10963+#elif FMT_GCC_VERSION
10964+ if (!condition) __builtin_unreachable();
10965 #endif
10966 }
10967
10968-// An approximation of iterator_t for pre-C++20 systems.
10969-template <typename T>
10970-using iterator_t = decltype(std::begin(std::declval<T&>()));
10971-template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
10972-
10973-// A workaround for std::string not having mutable data() until C++17.
10974-template <typename Char>
10975-inline auto get_data(std::basic_string<Char>& s) -> Char* {
10976- return &s[0];
10977-}
10978-template <typename Container>
10979-inline auto get_data(Container& c) -> typename Container::value_type* {
10980- return c.data();
10981-}
10982-
10983-#if defined(_SECURE_SCL) && _SECURE_SCL
10984-// Make a checked iterator to avoid MSVC warnings.
10985-template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
10986-template <typename T>
10987-constexpr auto make_checked(T* p, size_t size) -> checked_ptr<T> {
10988- return {p, size};
10989-}
10990-#else
10991-template <typename T> using checked_ptr = T*;
10992-template <typename T> constexpr auto make_checked(T* p, size_t) -> T* {
10993- return p;
10994-}
10995-#endif
10996-
10997 // Attempts to reserve space for n extra characters in the output range.
10998 // Returns a pointer to the reserved range or a reference to it.
10999-template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
11000+template <typename OutputIt,
11001+ FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
11002+ is_contiguous<typename OutputIt::container>::value)>
11003 #if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
11004 __attribute__((no_sanitize("undefined")))
11005 #endif
11006-inline auto
11007-reserve(std::back_insert_iterator<Container> it, size_t n)
11008- -> checked_ptr<typename Container::value_type> {
11009- Container& c = get_container(it);
11010+FMT_CONSTEXPR20 inline auto
11011+reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* {
11012+ auto& c = get_container(it);
11013 size_t size = c.size();
11014 c.resize(size + n);
11015- return make_checked(get_data(c) + size, n);
11016+ return &c[size];
11017 }
11018
11019 template <typename T>
11020-inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T> {
11021+FMT_CONSTEXPR20 inline auto reserve(basic_appender<T> it, size_t n)
11022+ -> basic_appender<T> {
11023 buffer<T>& buf = get_container(it);
11024 buf.try_reserve(buf.size() + n);
11025 return it;
11026@@ -601,18 +490,22 @@ template <typename T, typename OutputIt>
11027 constexpr auto to_pointer(OutputIt, size_t) -> T* {
11028 return nullptr;
11029 }
11030-template <typename T> auto to_pointer(buffer_appender<T> it, size_t n) -> T* {
11031+template <typename T>
11032+FMT_CONSTEXPR20 auto to_pointer(basic_appender<T> it, size_t n) -> T* {
11033 buffer<T>& buf = get_container(it);
11034+ buf.try_reserve(buf.size() + n);
11035 auto size = buf.size();
11036 if (buf.capacity() < size + n) return nullptr;
11037 buf.try_resize(size + n);
11038 return buf.data() + size;
11039 }
11040
11041-template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
11042-inline auto base_iterator(std::back_insert_iterator<Container>& it,
11043- checked_ptr<typename Container::value_type>)
11044- -> std::back_insert_iterator<Container> {
11045+template <typename OutputIt,
11046+ FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
11047+ is_contiguous<typename OutputIt::container>::value)>
11048+inline auto base_iterator(OutputIt it,
11049+ typename OutputIt::container_type::value_type*)
11050+ -> OutputIt {
11051 return it;
11052 }
11053
11054@@ -631,23 +524,15 @@ FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value)
11055 }
11056 template <typename T, typename Size>
11057 FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
11058- if (is_constant_evaluated()) {
11059- return fill_n<T*, Size, T>(out, count, value);
11060- }
11061+ if (is_constant_evaluated()) return fill_n<T*, Size, T>(out, count, value);
11062 std::memset(out, value, to_unsigned(count));
11063 return out + count;
11064 }
11065
11066-#ifdef __cpp_char8_t
11067-using char8_type = char8_t;
11068-#else
11069-enum char8_type : unsigned char {};
11070-#endif
11071-
11072 template <typename OutChar, typename InputIt, typename OutputIt>
11073-FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end,
11074- OutputIt out) -> OutputIt {
11075- return copy_str<OutChar>(begin, end, out);
11076+FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end,
11077+ OutputIt out) -> OutputIt {
11078+ return copy<OutChar>(begin, end, out);
11079 }
11080
11081 // A public domain branchless UTF-8 decoder by Christopher Wellons:
11082@@ -718,6 +603,7 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
11083 string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
11084 return result ? (error ? buf_ptr + 1 : end) : nullptr;
11085 };
11086+
11087 auto p = s.data();
11088 const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
11089 if (s.size() >= block_size) {
11090@@ -726,17 +612,20 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
11091 if (!p) return;
11092 }
11093 }
11094- if (auto num_chars_left = s.data() + s.size() - p) {
11095- char buf[2 * block_size - 1] = {};
11096- copy_str<char>(p, p + num_chars_left, buf);
11097- const char* buf_ptr = buf;
11098- do {
11099- auto end = decode(buf_ptr, p);
11100- if (!end) return;
11101- p += end - buf_ptr;
11102- buf_ptr = end;
11103- } while (buf_ptr - buf < num_chars_left);
11104- }
11105+ auto num_chars_left = to_unsigned(s.data() + s.size() - p);
11106+ if (num_chars_left == 0) return;
11107+
11108+ // Suppress bogus -Wstringop-overflow.
11109+ if (FMT_GCC_VERSION) num_chars_left &= 3;
11110+ char buf[2 * block_size - 1] = {};
11111+ copy<char>(p, p + num_chars_left, buf);
11112+ const char* buf_ptr = buf;
11113+ do {
11114+ auto end = decode(buf_ptr, p);
11115+ if (!end) return;
11116+ p += end - buf_ptr;
11117+ buf_ptr = end;
11118+ } while (buf_ptr < buf + num_chars_left);
11119 }
11120
11121 template <typename Char>
11122@@ -745,13 +634,13 @@ inline auto compute_width(basic_string_view<Char> s) -> size_t {
11123 }
11124
11125 // Computes approximate display width of a UTF-8 string.
11126-FMT_CONSTEXPR inline size_t compute_width(string_view s) {
11127+FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t {
11128 size_t num_code_points = 0;
11129 // It is not a lambda for compatibility with C++14.
11130 struct count_code_points {
11131 size_t* count;
11132 FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool {
11133- *count += detail::to_unsigned(
11134+ *count += to_unsigned(
11135 1 +
11136 (cp >= 0x1100 &&
11137 (cp <= 0x115f || // Hangul Jamo init. consonants
11138@@ -779,31 +668,24 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) {
11139 return num_code_points;
11140 }
11141
11142-inline auto compute_width(basic_string_view<char8_type> s) -> size_t {
11143- return compute_width(
11144- string_view(reinterpret_cast<const char*>(s.data()), s.size()));
11145-}
11146-
11147 template <typename Char>
11148 inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t {
11149- size_t size = s.size();
11150- return n < size ? n : size;
11151+ return min_of(n, s.size());
11152 }
11153
11154 // Calculates the index of the nth code point in a UTF-8 string.
11155 inline auto code_point_index(string_view s, size_t n) -> size_t {
11156- const char* data = s.data();
11157- size_t num_code_points = 0;
11158- for (size_t i = 0, size = s.size(); i != size; ++i) {
11159- if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i;
11160- }
11161- return s.size();
11162-}
11163-
11164-inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
11165- -> size_t {
11166- return code_point_index(
11167- string_view(reinterpret_cast<const char*>(s.data()), s.size()), n);
11168+ size_t result = s.size();
11169+ const char* begin = s.begin();
11170+ for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) {
11171+ if (n != 0) {
11172+ --n;
11173+ return true;
11174+ }
11175+ result = to_unsigned(sv.begin() - begin);
11176+ return false;
11177+ });
11178+ return result;
11179 }
11180
11181 template <typename T> struct is_integral : std::is_integral<T> {};
11182@@ -821,38 +703,22 @@ using is_integer =
11183 !std::is_same<T, char>::value &&
11184 !std::is_same<T, wchar_t>::value>;
11185
11186-#ifndef FMT_USE_FLOAT
11187-# define FMT_USE_FLOAT 1
11188-#endif
11189-#ifndef FMT_USE_DOUBLE
11190-# define FMT_USE_DOUBLE 1
11191-#endif
11192-#ifndef FMT_USE_LONG_DOUBLE
11193-# define FMT_USE_LONG_DOUBLE 1
11194-#endif
11195-
11196-#ifndef FMT_USE_FLOAT128
11197-# ifdef __clang__
11198-// Clang emulates GCC, so it has to appear early.
11199-# if FMT_HAS_INCLUDE(<quadmath.h>)
11200-# define FMT_USE_FLOAT128 1
11201-# endif
11202-# elif defined(__GNUC__)
11203-// GNU C++:
11204-# if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__)
11205-# define FMT_USE_FLOAT128 1
11206-# endif
11207-# endif
11208-# ifndef FMT_USE_FLOAT128
11209-# define FMT_USE_FLOAT128 0
11210-# endif
11211+#if defined(FMT_USE_FLOAT128)
11212+// Use the provided definition.
11213+#elif FMT_CLANG_VERSION && FMT_HAS_INCLUDE(<quadmath.h>)
11214+# define FMT_USE_FLOAT128 1
11215+#elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \
11216+ !defined(__STRICT_ANSI__)
11217+# define FMT_USE_FLOAT128 1
11218+#else
11219+# define FMT_USE_FLOAT128 0
11220 #endif
11221-
11222 #if FMT_USE_FLOAT128
11223 using float128 = __float128;
11224 #else
11225-using float128 = void;
11226+struct float128 {};
11227 #endif
11228+
11229 template <typename T> using is_float128 = std::is_same<T, float128>;
11230
11231 template <typename T>
11232@@ -871,24 +737,21 @@ using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
11233 # define FMT_USE_FULL_CACHE_DRAGONBOX 0
11234 #endif
11235
11236-template <typename T>
11237-template <typename U>
11238-void buffer<T>::append(const U* begin, const U* end) {
11239- while (begin != end) {
11240- auto count = to_unsigned(end - begin);
11241- try_reserve(size_ + count);
11242- auto free_cap = capacity_ - size_;
11243- if (free_cap < count) count = free_cap;
11244- std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count));
11245- size_ += count;
11246- begin += count;
11247+// An allocator that uses malloc/free to allow removing dependency on the C++
11248+// standard libary runtime.
11249+template <typename T> struct allocator {
11250+ using value_type = T;
11251+
11252+ T* allocate(size_t n) {
11253+ FMT_ASSERT(n <= max_value<size_t>() / sizeof(T), "");
11254+ T* p = static_cast<T*>(malloc(n * sizeof(T)));
11255+ if (!p) FMT_THROW(std::bad_alloc());
11256+ return p;
11257 }
11258-}
11259
11260-template <typename T, typename Enable = void>
11261-struct is_locale : std::false_type {};
11262-template <typename T>
11263-struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
11264+ void deallocate(T* p, size_t) { free(p); }
11265+};
11266+
11267 } // namespace detail
11268
11269 FMT_BEGIN_EXPORT
11270@@ -898,34 +761,26 @@ FMT_BEGIN_EXPORT
11271 enum { inline_buffer_size = 500 };
11272
11273 /**
11274- \rst
11275- A dynamically growing memory buffer for trivially copyable/constructible types
11276- with the first ``SIZE`` elements stored in the object itself.
11277-
11278- You can use the ``memory_buffer`` type alias for ``char`` instead.
11279-
11280- **Example**::
11281-
11282- auto out = fmt::memory_buffer();
11283- format_to(std::back_inserter(out), "The answer is {}.", 42);
11284-
11285- This will append the following output to the ``out`` object:
11286-
11287- .. code-block:: none
11288-
11289- The answer is 42.
11290-
11291- The output can be converted to an ``std::string`` with ``to_string(out)``.
11292- \endrst
11293+ * A dynamically growing memory buffer for trivially copyable/constructible
11294+ * types with the first `SIZE` elements stored in the object itself. Most
11295+ * commonly used via the `memory_buffer` alias for `char`.
11296+ *
11297+ * **Example**:
11298+ *
11299+ * auto out = fmt::memory_buffer();
11300+ * fmt::format_to(std::back_inserter(out), "The answer is {}.", 42);
11301+ *
11302+ * This will append "The answer is 42." to `out`. The buffer content can be
11303+ * converted to `std::string` with `to_string(out)`.
11304 */
11305 template <typename T, size_t SIZE = inline_buffer_size,
11306- typename Allocator = std::allocator<T>>
11307-class basic_memory_buffer final : public detail::buffer<T> {
11308+ typename Allocator = detail::allocator<T>>
11309+class basic_memory_buffer : public detail::buffer<T> {
11310 private:
11311 T store_[SIZE];
11312
11313- // Don't inherit from Allocator avoid generating type_info for it.
11314- Allocator alloc_;
11315+ // Don't inherit from Allocator to avoid generating type_info for it.
11316+ FMT_NO_UNIQUE_ADDRESS Allocator alloc_;
11317
11318 // Deallocate memory allocated by the buffer.
11319 FMT_CONSTEXPR20 void deallocate() {
11320@@ -933,36 +788,37 @@ class basic_memory_buffer final : public detail::buffer<T> {
11321 if (data != store_) alloc_.deallocate(data, this->capacity());
11322 }
11323
11324- protected:
11325- FMT_CONSTEXPR20 void grow(size_t size) override {
11326+ static FMT_CONSTEXPR20 void grow(detail::buffer<T>& buf, size_t size) {
11327 detail::abort_fuzzing_if(size > 5000);
11328- const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_);
11329- size_t old_capacity = this->capacity();
11330+ auto& self = static_cast<basic_memory_buffer&>(buf);
11331+ const size_t max_size =
11332+ std::allocator_traits<Allocator>::max_size(self.alloc_);
11333+ size_t old_capacity = buf.capacity();
11334 size_t new_capacity = old_capacity + old_capacity / 2;
11335 if (size > new_capacity)
11336 new_capacity = size;
11337 else if (new_capacity > max_size)
11338- new_capacity = size > max_size ? size : max_size;
11339- T* old_data = this->data();
11340- T* new_data =
11341- std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
11342+ new_capacity = max_of(size, max_size);
11343+ T* old_data = buf.data();
11344+ T* new_data = self.alloc_.allocate(new_capacity);
11345+ // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481).
11346+ detail::assume(buf.size() <= new_capacity);
11347 // The following code doesn't throw, so the raw pointer above doesn't leak.
11348- std::uninitialized_copy(old_data, old_data + this->size(),
11349- detail::make_checked(new_data, new_capacity));
11350- this->set(new_data, new_capacity);
11351+ memcpy(new_data, old_data, buf.size() * sizeof(T));
11352+ self.set(new_data, new_capacity);
11353 // deallocate must not throw according to the standard, but even if it does,
11354 // the buffer already uses the new storage and will deallocate it in
11355 // destructor.
11356- if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
11357+ if (old_data != self.store_) self.alloc_.deallocate(old_data, old_capacity);
11358 }
11359
11360 public:
11361 using value_type = T;
11362 using const_reference = const T&;
11363
11364- FMT_CONSTEXPR20 explicit basic_memory_buffer(
11365+ FMT_CONSTEXPR explicit basic_memory_buffer(
11366 const Allocator& alloc = Allocator())
11367- : alloc_(alloc) {
11368+ : detail::buffer<T>(grow), alloc_(alloc) {
11369 this->set(store_, SIZE);
11370 if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T());
11371 }
11372@@ -976,8 +832,7 @@ class basic_memory_buffer final : public detail::buffer<T> {
11373 size_t size = other.size(), capacity = other.capacity();
11374 if (data == other.store_) {
11375 this->set(store_, capacity);
11376- detail::copy_str<T>(other.store_, other.store_ + size,
11377- detail::make_checked(store_, capacity));
11378+ detail::copy<T>(other.store_, other.store_ + size, store_);
11379 } else {
11380 this->set(data, capacity);
11381 // Set pointer to the inline array so that delete is not called
11382@@ -989,21 +844,14 @@ class basic_memory_buffer final : public detail::buffer<T> {
11383 }
11384
11385 public:
11386- /**
11387- \rst
11388- Constructs a :class:`fmt::basic_memory_buffer` object moving the content
11389- of the other object to it.
11390- \endrst
11391- */
11392- FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept {
11393+ /// Constructs a `basic_memory_buffer` object moving the content of the other
11394+ /// object to it.
11395+ FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept
11396+ : detail::buffer<T>(grow) {
11397 move(other);
11398 }
11399
11400- /**
11401- \rst
11402- Moves the content of the other ``basic_memory_buffer`` object to this one.
11403- \endrst
11404- */
11405+ /// Moves the content of the other `basic_memory_buffer` object to this one.
11406 auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& {
11407 FMT_ASSERT(this != &other, "");
11408 deallocate();
11409@@ -1014,119 +862,108 @@ class basic_memory_buffer final : public detail::buffer<T> {
11410 // Returns a copy of the allocator associated with this buffer.
11411 auto get_allocator() const -> Allocator { return alloc_; }
11412
11413- /**
11414- Resizes the buffer to contain *count* elements. If T is a POD type new
11415- elements may not be initialized.
11416- */
11417- FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); }
11418+ /// Resizes the buffer to contain `count` elements. If T is a POD type new
11419+ /// elements may not be initialized.
11420+ FMT_CONSTEXPR void resize(size_t count) { this->try_resize(count); }
11421
11422- /** Increases the buffer capacity to *new_capacity*. */
11423+ /// Increases the buffer capacity to `new_capacity`.
11424 void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
11425
11426- // Directly append data into the buffer
11427 using detail::buffer<T>::append;
11428 template <typename ContiguousRange>
11429- void append(const ContiguousRange& range) {
11430+ FMT_CONSTEXPR20 void append(const ContiguousRange& range) {
11431 append(range.data(), range.data() + range.size());
11432 }
11433 };
11434
11435 using memory_buffer = basic_memory_buffer<char>;
11436
11437+template <size_t SIZE>
11438+FMT_NODISCARD auto to_string(const basic_memory_buffer<char, SIZE>& buf)
11439+ -> std::string {
11440+ auto size = buf.size();
11441+ detail::assume(size < std::string().max_size());
11442+ return {buf.data(), size};
11443+}
11444+
11445+// A writer to a buffered stream. It doesn't own the underlying stream.
11446+class writer {
11447+ private:
11448+ detail::buffer<char>* buf_;
11449+
11450+ // We cannot create a file buffer in advance because any write to a FILE may
11451+ // invalidate it.
11452+ FILE* file_;
11453+
11454+ public:
11455+ inline writer(FILE* f) : buf_(nullptr), file_(f) {}
11456+ inline writer(detail::buffer<char>& buf) : buf_(&buf) {}
11457+
11458+ /// Formats `args` according to specifications in `fmt` and writes the
11459+ /// output to the file.
11460+ template <typename... T> void print(format_string<T...> fmt, T&&... args) {
11461+ if (buf_)
11462+ fmt::format_to(appender(*buf_), fmt, std::forward<T>(args)...);
11463+ else
11464+ fmt::print(file_, fmt, std::forward<T>(args)...);
11465+ }
11466+};
11467+
11468+class string_buffer {
11469+ private:
11470+ std::string str_;
11471+ detail::container_buffer<std::string> buf_;
11472+
11473+ public:
11474+ inline string_buffer() : buf_(str_) {}
11475+
11476+ inline operator writer() { return buf_; }
11477+ inline std::string& str() { return str_; }
11478+};
11479+
11480 template <typename T, size_t SIZE, typename Allocator>
11481 struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
11482 };
11483
11484-FMT_END_EXPORT
11485-namespace detail {
11486-FMT_API bool write_console(std::FILE* f, string_view text);
11487-FMT_API void print(std::FILE*, string_view);
11488-} // namespace detail
11489-FMT_BEGIN_EXPORT
11490-
11491 // Suppress a misleading warning in older versions of clang.
11492-#if FMT_CLANG_VERSION
11493-# pragma clang diagnostic ignored "-Wweak-vtables"
11494-#endif
11495+FMT_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables")
11496
11497-/** An error reported from a formatting function. */
11498-class FMT_VISIBILITY("default") format_error : public std::runtime_error {
11499+/// An error reported from a formatting function.
11500+class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error {
11501 public:
11502 using std::runtime_error::runtime_error;
11503 };
11504
11505-namespace detail_exported {
11506-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
11507+class loc_value;
11508+
11509+FMT_END_EXPORT
11510+namespace detail {
11511+FMT_API auto write_console(int fd, string_view text) -> bool;
11512+FMT_API void print(FILE*, string_view);
11513+} // namespace detail
11514+
11515+namespace detail {
11516 template <typename Char, size_t N> struct fixed_string {
11517- constexpr fixed_string(const Char (&str)[N]) {
11518- detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str),
11519- str + N, data);
11520+ FMT_CONSTEXPR20 fixed_string(const Char (&s)[N]) {
11521+ detail::copy<Char, const Char*, Char*>(static_cast<const Char*>(s), s + N,
11522+ data);
11523 }
11524 Char data[N] = {};
11525 };
11526-#endif
11527
11528 // Converts a compile-time string to basic_string_view.
11529-template <typename Char, size_t N>
11530+FMT_EXPORT template <typename Char, size_t N>
11531 constexpr auto compile_string_to_view(const Char (&s)[N])
11532 -> basic_string_view<Char> {
11533 // Remove trailing NUL character if needed. Won't be present if this is used
11534 // with a raw character array (i.e. not defined as a string).
11535 return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
11536 }
11537-template <typename Char>
11538-constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
11539+FMT_EXPORT template <typename Char>
11540+constexpr auto compile_string_to_view(basic_string_view<Char> s)
11541 -> basic_string_view<Char> {
11542- return {s.data(), s.size()};
11543+ return s;
11544 }
11545-} // namespace detail_exported
11546-
11547-class loc_value {
11548- private:
11549- basic_format_arg<format_context> value_;
11550-
11551- public:
11552- template <typename T, FMT_ENABLE_IF(!detail::is_float128<T>::value)>
11553- loc_value(T value) : value_(detail::make_arg<format_context>(value)) {}
11554-
11555- template <typename T, FMT_ENABLE_IF(detail::is_float128<T>::value)>
11556- loc_value(T) {}
11557-
11558- template <typename Visitor> auto visit(Visitor&& vis) -> decltype(vis(0)) {
11559- return visit_format_arg(vis, value_);
11560- }
11561-};
11562-
11563-// A locale facet that formats values in UTF-8.
11564-// It is parameterized on the locale to avoid the heavy <locale> include.
11565-template <typename Locale> class format_facet : public Locale::facet {
11566- private:
11567- std::string separator_;
11568- std::string grouping_;
11569- std::string decimal_point_;
11570-
11571- protected:
11572- virtual auto do_put(appender out, loc_value val,
11573- const format_specs<>& specs) const -> bool;
11574-
11575- public:
11576- static FMT_API typename Locale::id id;
11577-
11578- explicit format_facet(Locale& loc);
11579- explicit format_facet(string_view sep = "",
11580- std::initializer_list<unsigned char> g = {3},
11581- std::string decimal_point = ".")
11582- : separator_(sep.data(), sep.size()),
11583- grouping_(g.begin(), g.end()),
11584- decimal_point_(decimal_point) {}
11585-
11586- auto put(appender out, loc_value val, const format_specs<>& specs) const
11587- -> bool {
11588- return do_put(out, val, specs);
11589- }
11590-};
11591-
11592-FMT_BEGIN_DETAIL_NAMESPACE
11593
11594 // Returns true if value is negative, false otherwise.
11595 // Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
11596@@ -1139,14 +976,6 @@ constexpr auto is_negative(T) -> bool {
11597 return false;
11598 }
11599
11600-template <typename T>
11601-FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool {
11602- if (std::is_same<T, float>()) return FMT_USE_FLOAT;
11603- if (std::is_same<T, double>()) return FMT_USE_DOUBLE;
11604- if (std::is_same<T, long double>()) return FMT_USE_LONG_DOUBLE;
11605- return true;
11606-}
11607-
11608 // Smallest of uint32_t, uint64_t, uint128_t that is large enough to
11609 // represent all values of an integral type T.
11610 template <typename T>
11611@@ -1157,27 +986,28 @@ using uint32_or_64_or_128_t =
11612 template <typename T>
11613 using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
11614
11615-#define FMT_POWERS_OF_10(factor) \
11616- factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
11617- (factor)*1000000, (factor)*10000000, (factor)*100000000, \
11618- (factor)*1000000000
11619+#define FMT_POWERS_OF_10(factor) \
11620+ factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \
11621+ (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \
11622+ (factor) * 100000000, (factor) * 1000000000
11623
11624 // Converts value in the range [0, 100) to a string.
11625-constexpr const char* digits2(size_t value) {
11626- // GCC generates slightly better code when value is pointer-size.
11627- return &"0001020304050607080910111213141516171819"
11628- "2021222324252627282930313233343536373839"
11629- "4041424344454647484950515253545556575859"
11630- "6061626364656667686970717273747576777879"
11631- "8081828384858687888990919293949596979899"[value * 2];
11632-}
11633-
11634-// Sign is a template parameter to workaround a bug in gcc 4.8.
11635-template <typename Char, typename Sign> constexpr Char sign(Sign s) {
11636-#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604
11637- static_assert(std::is_same<Sign, sign_t>::value, "");
11638-#endif
11639- return static_cast<Char>("\0-+ "[s]);
11640+// GCC generates slightly better code when value is pointer-size.
11641+inline auto digits2(size_t value) -> const char* {
11642+ // Align data since unaligned access may be slower when crossing a
11643+ // hardware-specific boundary.
11644+ alignas(2) static const char data[] =
11645+ "0001020304050607080910111213141516171819"
11646+ "2021222324252627282930313233343536373839"
11647+ "4041424344454647484950515253545556575859"
11648+ "6061626364656667686970717273747576777879"
11649+ "8081828384858687888990919293949596979899";
11650+ return &data[value * 2];
11651+}
11652+
11653+template <typename Char> constexpr auto getsign(sign s) -> Char {
11654+ return static_cast<char>(((' ' << 24) | ('+' << 16) | ('-' << 8)) >>
11655+ (static_cast<int>(s) * 8));
11656 }
11657
11658 template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
11659@@ -1225,9 +1055,7 @@ inline auto do_count_digits(uint64_t n) -> int {
11660 // except for n == 0 in which case count_digits returns 1.
11661 FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
11662 #ifdef FMT_BUILTIN_CLZLL
11663- if (!is_constant_evaluated()) {
11664- return do_count_digits(n);
11665- }
11666+ if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n);
11667 #endif
11668 return count_digits_fallback(n);
11669 }
11670@@ -1255,7 +1083,7 @@ FMT_CONSTEXPR auto count_digits(UInt n) -> int {
11671 FMT_INLINE auto do_count_digits(uint32_t n) -> int {
11672 // An optimization by Kendall Willets from https://bit.ly/3uOIQrB.
11673 // This increments the upper 32 bits (log10(T) - 1) when >= T is added.
11674-# define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T)
11675+# define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T)
11676 static constexpr uint64_t table[] = {
11677 FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8
11678 FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64
11679@@ -1277,9 +1105,7 @@ FMT_INLINE auto do_count_digits(uint32_t n) -> int {
11680 // Optional version of count_digits for better performance on 32-bit platforms.
11681 FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
11682 #ifdef FMT_BUILTIN_CLZ
11683- if (!is_constant_evaluated()) {
11684- return do_count_digits(n);
11685- }
11686+ if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n);
11687 #endif
11688 return count_digits_fallback(n);
11689 }
11690@@ -1316,91 +1142,118 @@ template <> inline auto decimal_point(locale_ref loc) -> wchar_t {
11691 return decimal_point_impl<wchar_t>(loc);
11692 }
11693
11694-// Compares two characters for equality.
11695-template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
11696- return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
11697-}
11698-inline auto equal2(const char* lhs, const char* rhs) -> bool {
11699+#ifndef FMT_HEADER_ONLY
11700+FMT_BEGIN_EXPORT
11701+extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
11702+ -> thousands_sep_result<char>;
11703+extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
11704+ -> thousands_sep_result<wchar_t>;
11705+extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
11706+extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
11707+FMT_END_EXPORT
11708+#endif // FMT_HEADER_ONLY
11709+
11710+// Compares two characters for equality.
11711+template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
11712+ return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
11713+}
11714+inline auto equal2(const char* lhs, const char* rhs) -> bool {
11715 return memcmp(lhs, rhs, 2) == 0;
11716 }
11717
11718-// Copies two characters from src to dst.
11719+// Writes a two-digit value to out.
11720 template <typename Char>
11721-FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) {
11722- if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) {
11723- memcpy(dst, src, 2);
11724+FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) {
11725+ if (!is_constant_evaluated() && std::is_same<Char, char>::value &&
11726+ !FMT_OPTIMIZE_SIZE) {
11727+ memcpy(out, digits2(value), 2);
11728 return;
11729 }
11730- *dst++ = static_cast<Char>(*src++);
11731- *dst = static_cast<Char>(*src);
11732+ *out++ = static_cast<Char>('0' + value / 10);
11733+ *out = static_cast<Char>('0' + value % 10);
11734 }
11735
11736-template <typename Iterator> struct format_decimal_result {
11737- Iterator begin;
11738- Iterator end;
11739-};
11740-
11741-// Formats a decimal unsigned integer value writing into out pointing to a
11742-// buffer of specified size. The caller must ensure that the buffer is large
11743-// enough.
11744+// Formats a decimal unsigned integer value writing to out pointing to a buffer
11745+// of specified size. The caller must ensure that the buffer is large enough.
11746 template <typename Char, typename UInt>
11747-FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size)
11748- -> format_decimal_result<Char*> {
11749+FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size)
11750+ -> Char* {
11751 FMT_ASSERT(size >= count_digits(value), "invalid digit count");
11752- out += size;
11753- Char* end = out;
11754+ unsigned n = to_unsigned(size);
11755 while (value >= 100) {
11756 // Integer division is slow so do it for a group of two digits instead
11757 // of for every digit. The idea comes from the talk by Alexandrescu
11758 // "Three Optimization Tips for C++". See speed-test for a comparison.
11759- out -= 2;
11760- copy2(out, digits2(static_cast<size_t>(value % 100)));
11761+ n -= 2;
11762+ write2digits(out + n, static_cast<unsigned>(value % 100));
11763 value /= 100;
11764 }
11765- if (value < 10) {
11766- *--out = static_cast<Char>('0' + value);
11767- return {out, end};
11768+ if (value >= 10) {
11769+ n -= 2;
11770+ write2digits(out + n, static_cast<unsigned>(value));
11771+ } else {
11772+ out[--n] = static_cast<Char>('0' + value);
11773 }
11774- out -= 2;
11775- copy2(out, digits2(static_cast<size_t>(value)));
11776- return {out, end};
11777+ return out + n;
11778+}
11779+
11780+template <typename Char, typename UInt>
11781+FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value,
11782+ int num_digits) -> Char* {
11783+ do_format_decimal(out, value, num_digits);
11784+ return out + num_digits;
11785 }
11786
11787-template <typename Char, typename UInt, typename Iterator,
11788- FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
11789-FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size)
11790- -> format_decimal_result<Iterator> {
11791+template <typename Char, typename UInt, typename OutputIt,
11792+ FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
11793+FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits)
11794+ -> OutputIt {
11795+ if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
11796+ do_format_decimal(ptr, value, num_digits);
11797+ return out;
11798+ }
11799 // Buffer is large enough to hold all digits (digits10 + 1).
11800- Char buffer[digits10<UInt>() + 1] = {};
11801- auto end = format_decimal(buffer, value, size).end;
11802- return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
11803+ char buffer[digits10<UInt>() + 1];
11804+ if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0');
11805+ do_format_decimal(buffer, value, num_digits);
11806+ return copy_noinline<Char>(buffer, buffer + num_digits, out);
11807 }
11808
11809-template <unsigned BASE_BITS, typename Char, typename UInt>
11810-FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits,
11811- bool upper = false) -> Char* {
11812- buffer += num_digits;
11813- Char* end = buffer;
11814+template <typename Char, typename UInt>
11815+FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value,
11816+ int size, bool upper = false) -> Char* {
11817+ out += size;
11818 do {
11819 const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
11820- unsigned digit = static_cast<unsigned>(value & ((1 << BASE_BITS) - 1));
11821- *--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
11822- : digits[digit]);
11823- } while ((value >>= BASE_BITS) != 0);
11824- return end;
11825+ unsigned digit = static_cast<unsigned>(value & ((1 << base_bits) - 1));
11826+ *--out = static_cast<Char>(base_bits < 4 ? static_cast<char>('0' + digit)
11827+ : digits[digit]);
11828+ } while ((value >>= base_bits) != 0);
11829+ return out;
11830+}
11831+
11832+// Formats an unsigned integer in the power of two base (binary, octal, hex).
11833+template <typename Char, typename UInt>
11834+FMT_CONSTEXPR auto format_base2e(int base_bits, Char* out, UInt value,
11835+ int num_digits, bool upper = false) -> Char* {
11836+ do_format_base2e(base_bits, out, value, num_digits, upper);
11837+ return out + num_digits;
11838 }
11839
11840-template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
11841-inline auto format_uint(It out, UInt value, int num_digits, bool upper = false)
11842- -> It {
11843+template <typename Char, typename OutputIt, typename UInt,
11844+ FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value)>
11845+FMT_CONSTEXPR inline auto format_base2e(int base_bits, OutputIt out, UInt value,
11846+ int num_digits, bool upper = false)
11847+ -> OutputIt {
11848 if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
11849- format_uint<BASE_BITS>(ptr, value, num_digits, upper);
11850+ format_base2e(base_bits, ptr, value, num_digits, upper);
11851 return out;
11852 }
11853- // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
11854- char buffer[num_bits<UInt>() / BASE_BITS + 1];
11855- format_uint<BASE_BITS>(buffer, value, num_digits, upper);
11856- return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out);
11857+ // Make buffer large enough for any base.
11858+ char buffer[num_bits<UInt>()];
11859+ if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0');
11860+ format_base2e(base_bits, buffer, value, num_digits, upper);
11861+ return detail::copy_noinline<Char>(buffer, buffer + num_digits, out);
11862 }
11863
11864 // A converter from UTF-8 to UTF-16.
11865@@ -1410,12 +1263,16 @@ class utf8_to_utf16 {
11866
11867 public:
11868 FMT_API explicit utf8_to_utf16(string_view s);
11869- operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
11870- auto size() const -> size_t { return buffer_.size() - 1; }
11871- auto c_str() const -> const wchar_t* { return &buffer_[0]; }
11872- auto str() const -> std::wstring { return {&buffer_[0], size()}; }
11873+ inline operator basic_string_view<wchar_t>() const {
11874+ return {&buffer_[0], size()};
11875+ }
11876+ inline auto size() const -> size_t { return buffer_.size() - 1; }
11877+ inline auto c_str() const -> const wchar_t* { return &buffer_[0]; }
11878+ inline auto str() const -> std::wstring { return {&buffer_[0], size()}; }
11879 };
11880
11881+enum class to_utf8_error_policy { abort, replace };
11882+
11883 // A converter from UTF-16/UTF-32 (host endian) to UTF-8.
11884 template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
11885 private:
11886@@ -1423,37 +1280,45 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
11887
11888 public:
11889 to_utf8() {}
11890- explicit to_utf8(basic_string_view<WChar> s) {
11891+ explicit to_utf8(basic_string_view<WChar> s,
11892+ to_utf8_error_policy policy = to_utf8_error_policy::abort) {
11893 static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4,
11894 "Expect utf16 or utf32");
11895-
11896- if (!convert(s))
11897+ if (!convert(s, policy))
11898 FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16"
11899 : "invalid utf32"));
11900 }
11901 operator string_view() const { return string_view(&buffer_[0], size()); }
11902- size_t size() const { return buffer_.size() - 1; }
11903- const char* c_str() const { return &buffer_[0]; }
11904- std::string str() const { return std::string(&buffer_[0], size()); }
11905+ auto size() const -> size_t { return buffer_.size() - 1; }
11906+ auto c_str() const -> const char* { return &buffer_[0]; }
11907+ auto str() const -> std::string { return std::string(&buffer_[0], size()); }
11908
11909 // Performs conversion returning a bool instead of throwing exception on
11910 // conversion error. This method may still throw in case of memory allocation
11911 // error.
11912- bool convert(basic_string_view<WChar> s) {
11913- if (!convert(buffer_, s)) return false;
11914+ auto convert(basic_string_view<WChar> s,
11915+ to_utf8_error_policy policy = to_utf8_error_policy::abort)
11916+ -> bool {
11917+ if (!convert(buffer_, s, policy)) return false;
11918 buffer_.push_back(0);
11919 return true;
11920 }
11921- static bool convert(Buffer& buf, basic_string_view<WChar> s) {
11922+ static auto convert(Buffer& buf, basic_string_view<WChar> s,
11923+ to_utf8_error_policy policy = to_utf8_error_policy::abort)
11924+ -> bool {
11925 for (auto p = s.begin(); p != s.end(); ++p) {
11926 uint32_t c = static_cast<uint32_t>(*p);
11927 if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) {
11928- // surrogate pair
11929+ // Handle a surrogate pair.
11930 ++p;
11931 if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
11932- return false;
11933+ if (policy == to_utf8_error_policy::abort) return false;
11934+ buf.append(string_view("\xEF\xBF\xBD"));
11935+ --p;
11936+ continue;
11937+ } else {
11938+ c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
11939 }
11940- c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
11941 }
11942 if (c < 0x80) {
11943 buf.push_back(static_cast<char>(c));
11944@@ -1478,14 +1343,14 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
11945 };
11946
11947 // Computes 128-bit result of multiplication of two 64-bit unsigned integers.
11948-inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
11949+inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback {
11950 #if FMT_USE_INT128
11951 auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
11952 return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
11953 #elif defined(_MSC_VER) && defined(_M_X64)
11954- auto result = uint128_fallback();
11955- result.lo_ = _umul128(x, y, &result.hi_);
11956- return result;
11957+ auto hi = uint64_t();
11958+ auto lo = _umul128(x, y, &hi);
11959+ return {hi, lo};
11960 #else
11961 const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
11962
11963@@ -1509,19 +1374,19 @@ inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
11964 namespace dragonbox {
11965 // Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
11966 // https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
11967-inline int floor_log10_pow2(int e) noexcept {
11968+inline auto floor_log10_pow2(int e) noexcept -> int {
11969 FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
11970 static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
11971 return (e * 315653) >> 20;
11972 }
11973
11974-inline int floor_log2_pow10(int e) noexcept {
11975+inline auto floor_log2_pow10(int e) noexcept -> int {
11976 FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
11977 return (e * 1741647) >> 19;
11978 }
11979
11980 // Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
11981-inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
11982+inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t {
11983 #if FMT_USE_INT128
11984 auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
11985 return static_cast<uint64_t>(p >> 64);
11986@@ -1534,14 +1399,14 @@ inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
11987
11988 // Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
11989 // 128-bit unsigned integer.
11990-inline uint128_fallback umul192_upper128(uint64_t x,
11991- uint128_fallback y) noexcept {
11992+inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept
11993+ -> uint128_fallback {
11994 uint128_fallback r = umul128(x, y.high());
11995 r += umul128_upper64(x, y.low());
11996 return r;
11997 }
11998
11999-FMT_API uint128_fallback get_cached_power(int k) noexcept;
12000+FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback;
12001
12002 // Type-specific information that Dragonbox uses.
12003 template <typename T, typename Enable = void> struct float_info;
12004@@ -1595,14 +1460,14 @@ template <typename T> FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
12005 } // namespace dragonbox
12006
12007 // Returns true iff Float has the implicit bit which is not stored.
12008-template <typename Float> constexpr bool has_implicit_bit() {
12009+template <typename Float> constexpr auto has_implicit_bit() -> bool {
12010 // An 80-bit FP number has a 64-bit significand an no implicit bit.
12011 return std::numeric_limits<Float>::digits != 64;
12012 }
12013
12014 // Returns the number of significand bits stored in Float. The implicit bit is
12015 // not counted since it is not stored.
12016-template <typename Float> constexpr int num_significand_bits() {
12017+template <typename Float> constexpr auto num_significand_bits() -> int {
12018 // std::numeric_limits may not support __float128.
12019 return is_float128<Float>() ? 112
12020 : (std::numeric_limits<Float>::digits -
12021@@ -1623,25 +1488,30 @@ template <typename Float> constexpr auto exponent_bias() -> int {
12022 }
12023
12024 // Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
12025-template <typename Char, typename It>
12026-FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It {
12027+template <typename Char, typename OutputIt>
12028+FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt {
12029 FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
12030 if (exp < 0) {
12031- *it++ = static_cast<Char>('-');
12032+ *out++ = static_cast<Char>('-');
12033 exp = -exp;
12034 } else {
12035- *it++ = static_cast<Char>('+');
12036+ *out++ = static_cast<Char>('+');
12037 }
12038- if (exp >= 100) {
12039- const char* top = digits2(to_unsigned(exp / 100));
12040- if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
12041- *it++ = static_cast<Char>(top[1]);
12042- exp %= 100;
12043- }
12044- const char* d = digits2(to_unsigned(exp));
12045- *it++ = static_cast<Char>(d[0]);
12046- *it++ = static_cast<Char>(d[1]);
12047- return it;
12048+ auto uexp = static_cast<uint32_t>(exp);
12049+ if (is_constant_evaluated()) {
12050+ if (uexp < 10) *out++ = '0';
12051+ return format_decimal<Char>(out, uexp, count_digits(uexp));
12052+ }
12053+ if (uexp >= 100u) {
12054+ const char* top = digits2(uexp / 100);
12055+ if (uexp >= 1000u) *out++ = static_cast<Char>(top[0]);
12056+ *out++ = static_cast<Char>(top[1]);
12057+ uexp %= 100;
12058+ }
12059+ const char* d = digits2(uexp);
12060+ *out++ = static_cast<Char>(d[0]);
12061+ *out++ = static_cast<Char>(d[1]);
12062+ return out;
12063 }
12064
12065 // A floating-point number f * pow(2, e) where F is an unsigned type.
12066@@ -1695,7 +1565,7 @@ using fp = basic_fp<unsigned long long>;
12067
12068 // Normalizes the value converted from double and multiplied by (1 << SHIFT).
12069 template <int SHIFT = 0, typename F>
12070-FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
12071+FMT_CONSTEXPR auto normalize(basic_fp<F> value) -> basic_fp<F> {
12072 // Handle subnormals.
12073 const auto implicit_bit = F(1) << num_significand_bits<double>();
12074 const auto shifted_implicit_bit = implicit_bit << SHIFT;
12075@@ -1712,7 +1582,7 @@ FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
12076 }
12077
12078 // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
12079-FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
12080+FMT_CONSTEXPR inline auto multiply(uint64_t lhs, uint64_t rhs) -> uint64_t {
12081 #if FMT_USE_INT128
12082 auto product = static_cast<__uint128_t>(lhs) * rhs;
12083 auto f = static_cast<uint64_t>(product >> 64);
12084@@ -1729,191 +1599,82 @@ FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
12085 #endif
12086 }
12087
12088-FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
12089+FMT_CONSTEXPR inline auto operator*(fp x, fp y) -> fp {
12090 return {multiply(x.f, y.f), x.e + y.e + 64};
12091 }
12092
12093-template <typename T = void> struct basic_data {
12094- // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
12095- // These are generated by support/compute-powers.py.
12096- static constexpr uint64_t pow10_significands[87] = {
12097- 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
12098- 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
12099- 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
12100- 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
12101- 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
12102- 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
12103- 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
12104- 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
12105- 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
12106- 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
12107- 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
12108- 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
12109- 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
12110- 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
12111- 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
12112- 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
12113- 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
12114- 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
12115- 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
12116- 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
12117- 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
12118- 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
12119- 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
12120- 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
12121- 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
12122- 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
12123- 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
12124- 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
12125- 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
12126- };
12127-
12128-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
12129-# pragma GCC diagnostic push
12130-# pragma GCC diagnostic ignored "-Wnarrowing"
12131-#endif
12132- // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
12133- // to significands above.
12134- static constexpr int16_t pow10_exponents[87] = {
12135- -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
12136- -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
12137- -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
12138- -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
12139- -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
12140- 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
12141- 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
12142- 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
12143-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
12144-# pragma GCC diagnostic pop
12145-#endif
12146-
12147- static constexpr uint64_t power_of_10_64[20] = {
12148- 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
12149- 10000000000000000000ULL};
12150-
12151- // For checking rounding thresholds.
12152- // The kth entry is chosen to be the smallest integer such that the
12153- // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
12154- static constexpr uint32_t fractional_part_rounding_thresholds[8] = {
12155- 2576980378, // ceil(2^31 + 2^32/10^1)
12156- 2190433321, // ceil(2^31 + 2^32/10^2)
12157- 2151778616, // ceil(2^31 + 2^32/10^3)
12158- 2147913145, // ceil(2^31 + 2^32/10^4)
12159- 2147526598, // ceil(2^31 + 2^32/10^5)
12160- 2147487943, // ceil(2^31 + 2^32/10^6)
12161- 2147484078, // ceil(2^31 + 2^32/10^7)
12162- 2147483691 // ceil(2^31 + 2^32/10^8)
12163- };
12164-};
12165-
12166-#if FMT_CPLUSPLUS < 201703L
12167-template <typename T> constexpr uint64_t basic_data<T>::pow10_significands[];
12168-template <typename T> constexpr int16_t basic_data<T>::pow10_exponents[];
12169-template <typename T> constexpr uint64_t basic_data<T>::power_of_10_64[];
12170-template <typename T>
12171-constexpr uint32_t basic_data<T>::fractional_part_rounding_thresholds[];
12172-#endif
12173-
12174-// This is a struct rather than an alias to avoid shadowing warnings in gcc.
12175-struct data : basic_data<> {};
12176-
12177-// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
12178-// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
12179-FMT_CONSTEXPR inline fp get_cached_power(int min_exponent,
12180- int& pow10_exponent) {
12181- const int shift = 32;
12182- // log10(2) = 0x0.4d104d427de7fbcc...
12183- const int64_t significand = 0x4d104d427de7fbcc;
12184- int index = static_cast<int>(
12185- ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
12186- ((int64_t(1) << shift) - 1)) // ceil
12187- >> 32 // arithmetic shift
12188- );
12189- // Decimal exponent of the first (smallest) cached power of 10.
12190- const int first_dec_exp = -348;
12191- // Difference between 2 consecutive decimal exponents in cached powers of 10.
12192- const int dec_exp_step = 8;
12193- index = (index - first_dec_exp - 1) / dec_exp_step + 1;
12194- pow10_exponent = first_dec_exp + index * dec_exp_step;
12195- // Using *(x + index) instead of x[index] avoids an issue with some compilers
12196- // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
12197- return {*(data::pow10_significands + index),
12198- *(data::pow10_exponents + index)};
12199-}
12200-
12201-template <typename T>
12202+template <typename T, bool doublish = num_bits<T>() == num_bits<double>()>
12203 using convert_float_result =
12204- conditional_t<std::is_same<T, float>::value ||
12205- std::numeric_limits<T>::digits ==
12206- std::numeric_limits<double>::digits,
12207- double, T>;
12208+ conditional_t<std::is_same<T, float>::value || doublish, double, T>;
12209
12210 template <typename T>
12211 constexpr auto convert_float(T value) -> convert_float_result<T> {
12212 return static_cast<convert_float_result<T>>(value);
12213 }
12214
12215-template <typename OutputIt, typename Char>
12216+template <typename Char, typename OutputIt>
12217 FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
12218- const fill_t<Char>& fill) -> OutputIt {
12219- auto fill_size = fill.size();
12220- if (fill_size == 1) return detail::fill_n(it, n, fill[0]);
12221- auto data = fill.data();
12222- for (size_t i = 0; i < n; ++i)
12223- it = copy_str<Char>(data, data + fill_size, it);
12224+ const basic_specs& specs) -> OutputIt {
12225+ auto fill_size = specs.fill_size();
12226+ if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit<Char>());
12227+ if (const Char* data = specs.fill<Char>()) {
12228+ for (size_t i = 0; i < n; ++i) it = copy<Char>(data, data + fill_size, it);
12229+ }
12230 return it;
12231 }
12232
12233 // Writes the output of f, padded according to format specifications in specs.
12234 // size: output size in code units.
12235 // width: output display width in (terminal) column positions.
12236-template <align::type align = align::left, typename OutputIt, typename Char,
12237+template <typename Char, align default_align = align::left, typename OutputIt,
12238 typename F>
12239-FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs<Char>& specs,
12240+FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs,
12241 size_t size, size_t width, F&& f) -> OutputIt {
12242- static_assert(align == align::left || align == align::right, "");
12243+ static_assert(default_align == align::left || default_align == align::right,
12244+ "");
12245 unsigned spec_width = to_unsigned(specs.width);
12246 size_t padding = spec_width > width ? spec_width - width : 0;
12247 // Shifts are encoded as string literals because static constexpr is not
12248 // supported in constexpr functions.
12249- auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
12250- size_t left_padding = padding >> shifts[specs.align];
12251+ auto* shifts =
12252+ default_align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
12253+ size_t left_padding = padding >> shifts[static_cast<int>(specs.align())];
12254 size_t right_padding = padding - left_padding;
12255- auto it = reserve(out, size + padding * specs.fill.size());
12256- if (left_padding != 0) it = fill(it, left_padding, specs.fill);
12257+ auto it = reserve(out, size + padding * specs.fill_size());
12258+ if (left_padding != 0) it = fill<Char>(it, left_padding, specs);
12259 it = f(it);
12260- if (right_padding != 0) it = fill(it, right_padding, specs.fill);
12261+ if (right_padding != 0) it = fill<Char>(it, right_padding, specs);
12262 return base_iterator(out, it);
12263 }
12264
12265-template <align::type align = align::left, typename OutputIt, typename Char,
12266+template <typename Char, align default_align = align::left, typename OutputIt,
12267 typename F>
12268-constexpr auto write_padded(OutputIt out, const format_specs<Char>& specs,
12269+constexpr auto write_padded(OutputIt out, const format_specs& specs,
12270 size_t size, F&& f) -> OutputIt {
12271- return write_padded<align>(out, specs, size, size, f);
12272+ return write_padded<Char, default_align>(out, specs, size, size, f);
12273 }
12274
12275-template <align::type align = align::left, typename Char, typename OutputIt>
12276+template <typename Char, align default_align = align::left, typename OutputIt>
12277 FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
12278- const format_specs<Char>& specs) -> OutputIt {
12279- return write_padded<align>(
12280+ const format_specs& specs = {}) -> OutputIt {
12281+ return write_padded<Char, default_align>(
12282 out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
12283 const char* data = bytes.data();
12284- return copy_str<Char>(data, data + bytes.size(), it);
12285+ return copy<Char>(data, data + bytes.size(), it);
12286 });
12287 }
12288
12289 template <typename Char, typename OutputIt, typename UIntPtr>
12290-auto write_ptr(OutputIt out, UIntPtr value, const format_specs<Char>* specs)
12291+auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs)
12292 -> OutputIt {
12293 int num_digits = count_digits<4>(value);
12294 auto size = to_unsigned(num_digits) + size_t(2);
12295 auto write = [=](reserve_iterator<OutputIt> it) {
12296 *it++ = static_cast<Char>('0');
12297 *it++ = static_cast<Char>('x');
12298- return format_uint<4, Char>(it, value, num_digits);
12299+ return format_base2e<Char>(4, it, value, num_digits);
12300 };
12301- return specs ? write_padded<align::right>(out, *specs, size, write)
12302+ return specs ? write_padded<Char, align::right>(out, *specs, size, write)
12303 : base_iterator(out, write(reserve(out, size)));
12304 }
12305
12306@@ -1921,8 +1682,9 @@ auto write_ptr(OutputIt out, UIntPtr value, const format_specs<Char>* specs)
12307 FMT_API auto is_printable(uint32_t cp) -> bool;
12308
12309 inline auto needs_escape(uint32_t cp) -> bool {
12310- return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
12311- !is_printable(cp);
12312+ if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true;
12313+ if (const_check(FMT_OPTIMIZE_SIZE > 1)) return false;
12314+ return !is_printable(cp);
12315 }
12316
12317 template <typename Char> struct find_escape_result {
12318@@ -1931,17 +1693,11 @@ template <typename Char> struct find_escape_result {
12319 uint32_t cp;
12320 };
12321
12322-template <typename Char>
12323-using make_unsigned_char =
12324- typename conditional_t<std::is_integral<Char>::value,
12325- std::make_unsigned<Char>,
12326- type_identity<uint32_t>>::type;
12327-
12328 template <typename Char>
12329 auto find_escape(const Char* begin, const Char* end)
12330 -> find_escape_result<Char> {
12331 for (; begin != end; ++begin) {
12332- uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin);
12333+ uint32_t cp = static_cast<unsigned_char<Char>>(*begin);
12334 if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
12335 if (needs_escape(cp)) return {begin, begin + 1, cp};
12336 }
12337@@ -1950,7 +1706,7 @@ auto find_escape(const Char* begin, const Char* end)
12338
12339 inline auto find_escape(const char* begin, const char* end)
12340 -> find_escape_result<char> {
12341- if (!is_utf8()) return find_escape<char>(begin, end);
12342+ if (const_check(!use_utf8)) return find_escape<char>(begin, end);
12343 auto result = find_escape_result<char>{end, nullptr, 0};
12344 for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
12345 [&](uint32_t cp, string_view sv) {
12346@@ -1963,40 +1719,14 @@ inline auto find_escape(const char* begin, const char* end)
12347 return result;
12348 }
12349
12350-#define FMT_STRING_IMPL(s, base, explicit) \
12351- [] { \
12352- /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
12353- /* Use a macro-like name to avoid shadowing warnings. */ \
12354- struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \
12355- using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
12356- FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \
12357- operator fmt::basic_string_view<char_type>() const { \
12358- return fmt::detail_exported::compile_string_to_view<char_type>(s); \
12359- } \
12360- }; \
12361- return FMT_COMPILE_STRING(); \
12362- }()
12363-
12364-/**
12365- \rst
12366- Constructs a compile-time format string from a string literal *s*.
12367-
12368- **Example**::
12369-
12370- // A compile-time error because 'd' is an invalid specifier for strings.
12371- std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
12372- \endrst
12373- */
12374-#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, )
12375-
12376 template <size_t width, typename Char, typename OutputIt>
12377 auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {
12378 *out++ = static_cast<Char>('\\');
12379 *out++ = static_cast<Char>(prefix);
12380 Char buf[width];
12381 fill_n(buf, width, static_cast<Char>('0'));
12382- format_uint<4>(buf, cp, width);
12383- return copy_str<Char>(buf, buf + width, out);
12384+ format_base2e(4, buf, cp, width);
12385+ return copy<Char>(buf, buf + width, out);
12386 }
12387
12388 template <typename OutputIt, typename Char>
12389@@ -2016,23 +1746,15 @@ auto write_escaped_cp(OutputIt out, const find_escape_result<Char>& escape)
12390 *out++ = static_cast<Char>('\\');
12391 c = static_cast<Char>('t');
12392 break;
12393- case '"':
12394- FMT_FALLTHROUGH;
12395- case '\'':
12396- FMT_FALLTHROUGH;
12397- case '\\':
12398- *out++ = static_cast<Char>('\\');
12399- break;
12400+ case '"': FMT_FALLTHROUGH;
12401+ case '\'': FMT_FALLTHROUGH;
12402+ case '\\': *out++ = static_cast<Char>('\\'); break;
12403 default:
12404- if (escape.cp < 0x100) {
12405- return write_codepoint<2, Char>(out, 'x', escape.cp);
12406- }
12407- if (escape.cp < 0x10000) {
12408+ if (escape.cp < 0x100) return write_codepoint<2, Char>(out, 'x', escape.cp);
12409+ if (escape.cp < 0x10000)
12410 return write_codepoint<4, Char>(out, 'u', escape.cp);
12411- }
12412- if (escape.cp < 0x110000) {
12413+ if (escape.cp < 0x110000)
12414 return write_codepoint<8, Char>(out, 'U', escape.cp);
12415- }
12416 for (Char escape_char : basic_string_view<Char>(
12417 escape.begin, to_unsigned(escape.end - escape.begin))) {
12418 out = write_codepoint<2, Char>(out, 'x',
12419@@ -2051,7 +1773,7 @@ auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
12420 auto begin = str.begin(), end = str.end();
12421 do {
12422 auto escape = find_escape(begin, end);
12423- out = copy_str<Char>(begin, escape.begin, out);
12424+ out = copy<Char>(begin, escape.begin, out);
12425 begin = escape.end;
12426 if (!begin) break;
12427 out = write_escaped_cp<OutputIt, Char>(out, escape);
12428@@ -2062,11 +1784,13 @@ auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
12429
12430 template <typename Char, typename OutputIt>
12431 auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
12432+ Char v_array[1] = {v};
12433 *out++ = static_cast<Char>('\'');
12434 if ((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) ||
12435 v == static_cast<Char>('\'')) {
12436- out = write_escaped_cp(
12437- out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
12438+ out = write_escaped_cp(out,
12439+ find_escape_result<Char>{v_array, v_array + 1,
12440+ static_cast<uint32_t>(v)});
12441 } else {
12442 *out++ = v;
12443 }
12444@@ -2076,74 +1800,23 @@ auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
12445
12446 template <typename Char, typename OutputIt>
12447 FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
12448- const format_specs<Char>& specs) -> OutputIt {
12449- bool is_debug = specs.type == presentation_type::debug;
12450- return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
12451+ const format_specs& specs) -> OutputIt {
12452+ bool is_debug = specs.type() == presentation_type::debug;
12453+ return write_padded<Char>(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
12454 if (is_debug) return write_escaped_char(it, value);
12455 *it++ = value;
12456 return it;
12457 });
12458 }
12459 template <typename Char, typename OutputIt>
12460-FMT_CONSTEXPR auto write(OutputIt out, Char value,
12461- const format_specs<Char>& specs, locale_ref loc = {})
12462- -> OutputIt {
12463+FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs,
12464+ locale_ref loc = {}) -> OutputIt {
12465 // char is formatted as unsigned char for consistency across platforms.
12466 using unsigned_type =
12467 conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>;
12468 return check_char_specs(specs)
12469- ? write_char(out, value, specs)
12470- : write(out, static_cast<unsigned_type>(value), specs, loc);
12471-}
12472-
12473-// Data for write_int that doesn't depend on output iterator type. It is used to
12474-// avoid template code bloat.
12475-template <typename Char> struct write_int_data {
12476- size_t size;
12477- size_t padding;
12478-
12479- FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
12480- const format_specs<Char>& specs)
12481- : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
12482- if (specs.align == align::numeric) {
12483- auto width = to_unsigned(specs.width);
12484- if (width > size) {
12485- padding = width - size;
12486- size = width;
12487- }
12488- } else if (specs.precision > num_digits) {
12489- size = (prefix >> 24) + to_unsigned(specs.precision);
12490- padding = to_unsigned(specs.precision - num_digits);
12491- }
12492- }
12493-};
12494-
12495-// Writes an integer in the format
12496-// <left-padding><prefix><numeric-padding><digits><right-padding>
12497-// where <digits> are written by write_digits(it).
12498-// prefix contains chars in three lower bytes and the size in the fourth byte.
12499-template <typename OutputIt, typename Char, typename W>
12500-FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
12501- unsigned prefix,
12502- const format_specs<Char>& specs,
12503- W write_digits) -> OutputIt {
12504- // Slightly faster check for specs.width == 0 && specs.precision == -1.
12505- if ((specs.width | (specs.precision + 1)) == 0) {
12506- auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
12507- if (prefix != 0) {
12508- for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
12509- *it++ = static_cast<Char>(p & 0xff);
12510- }
12511- return base_iterator(out, write_digits(it));
12512- }
12513- auto data = write_int_data<Char>(num_digits, prefix, specs);
12514- return write_padded<align::right>(
12515- out, specs, data.size, [=](reserve_iterator<OutputIt> it) {
12516- for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
12517- *it++ = static_cast<Char>(p & 0xff);
12518- it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
12519- return write_digits(it);
12520- });
12521+ ? write_char<Char>(out, value, specs)
12522+ : write<Char>(out, static_cast<unsigned_type>(value), specs, loc);
12523 }
12524
12525 template <typename Char> class digit_grouping {
12526@@ -2155,10 +1828,10 @@ template <typename Char> class digit_grouping {
12527 std::string::const_iterator group;
12528 int pos;
12529 };
12530- next_state initial_state() const { return {grouping_.begin(), 0}; }
12531+ auto initial_state() const -> next_state { return {grouping_.begin(), 0}; }
12532
12533 // Returns the next digit group separator position.
12534- int next(next_state& state) const {
12535+ auto next(next_state& state) const -> int {
12536 if (thousands_sep_.empty()) return max_value<int>();
12537 if (state.group == grouping_.end()) return state.pos += grouping_.back();
12538 if (*state.group <= 0 || *state.group == max_value<char>())
12539@@ -2168,7 +1841,9 @@ template <typename Char> class digit_grouping {
12540 }
12541
12542 public:
12543- explicit digit_grouping(locale_ref loc, bool localized = true) {
12544+ template <typename Locale,
12545+ FMT_ENABLE_IF(std::is_same<Locale, locale_ref>::value)>
12546+ explicit digit_grouping(Locale loc, bool localized = true) {
12547 if (!localized) return;
12548 auto sep = thousands_sep<Char>(loc);
12549 grouping_ = sep.grouping;
12550@@ -2177,9 +1852,9 @@ template <typename Char> class digit_grouping {
12551 digit_grouping(std::string grouping, std::basic_string<Char> sep)
12552 : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {}
12553
12554- bool has_separator() const { return !thousands_sep_.empty(); }
12555+ auto has_separator() const -> bool { return !thousands_sep_.empty(); }
12556
12557- int count_separators(int num_digits) const {
12558+ auto count_separators(int num_digits) const -> int {
12559 int count = 0;
12560 auto state = initial_state();
12561 while (num_digits > next(state)) ++count;
12562@@ -2188,7 +1863,7 @@ template <typename Char> class digit_grouping {
12563
12564 // Applies grouping to digits and write the output to out.
12565 template <typename Out, typename C>
12566- Out apply(Out out, basic_string_view<C> digits) const {
12567+ auto apply(Out out, basic_string_view<C> digits) const -> Out {
12568 auto num_digits = static_cast<int>(digits.size());
12569 auto separators = basic_memory_buffer<int>();
12570 separators.push_back(0);
12571@@ -2200,9 +1875,8 @@ template <typename Char> class digit_grouping {
12572 for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
12573 i < num_digits; ++i) {
12574 if (num_digits - i == separators[sep_index]) {
12575- out =
12576- copy_str<Char>(thousands_sep_.data(),
12577- thousands_sep_.data() + thousands_sep_.size(), out);
12578+ out = copy<Char>(thousands_sep_.data(),
12579+ thousands_sep_.data() + thousands_sep_.size(), out);
12580 --sep_index;
12581 }
12582 *out++ = static_cast<Char>(digits[to_unsigned(i)]);
12583@@ -2211,48 +1885,78 @@ template <typename Char> class digit_grouping {
12584 }
12585 };
12586
12587+FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
12588+ prefix |= prefix != 0 ? value << 8 : value;
12589+ prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
12590+}
12591+
12592 // Writes a decimal integer with digit grouping.
12593 template <typename OutputIt, typename UInt, typename Char>
12594 auto write_int(OutputIt out, UInt value, unsigned prefix,
12595- const format_specs<Char>& specs,
12596- const digit_grouping<Char>& grouping) -> OutputIt {
12597+ const format_specs& specs, const digit_grouping<Char>& grouping)
12598+ -> OutputIt {
12599 static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
12600- int num_digits = count_digits(value);
12601- char digits[40];
12602- format_decimal(digits, value, num_digits);
12603- unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits +
12604- grouping.count_separators(num_digits));
12605- return write_padded<align::right>(
12606+ int num_digits = 0;
12607+ auto buffer = memory_buffer();
12608+ switch (specs.type()) {
12609+ default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH;
12610+ case presentation_type::none:
12611+ case presentation_type::dec:
12612+ num_digits = count_digits(value);
12613+ format_decimal<char>(appender(buffer), value, num_digits);
12614+ break;
12615+ case presentation_type::hex:
12616+ if (specs.alt())
12617+ prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0');
12618+ num_digits = count_digits<4>(value);
12619+ format_base2e<char>(4, appender(buffer), value, num_digits, specs.upper());
12620+ break;
12621+ case presentation_type::oct:
12622+ num_digits = count_digits<3>(value);
12623+ // Octal prefix '0' is counted as a digit, so only add it if precision
12624+ // is not greater than the number of digits.
12625+ if (specs.alt() && specs.precision <= num_digits && value != 0)
12626+ prefix_append(prefix, '0');
12627+ format_base2e<char>(3, appender(buffer), value, num_digits);
12628+ break;
12629+ case presentation_type::bin:
12630+ if (specs.alt())
12631+ prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
12632+ num_digits = count_digits<1>(value);
12633+ format_base2e<char>(1, appender(buffer), value, num_digits);
12634+ break;
12635+ case presentation_type::chr:
12636+ return write_char<Char>(out, static_cast<Char>(value), specs);
12637+ }
12638+
12639+ unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) +
12640+ to_unsigned(grouping.count_separators(num_digits));
12641+ return write_padded<Char, align::right>(
12642 out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
12643- if (prefix != 0) {
12644- char sign = static_cast<char>(prefix);
12645- *it++ = static_cast<Char>(sign);
12646- }
12647- return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
12648+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
12649+ *it++ = static_cast<Char>(p & 0xff);
12650+ return grouping.apply(it, string_view(buffer.data(), buffer.size()));
12651 });
12652 }
12653
12654+#if FMT_USE_LOCALE
12655 // Writes a localized value.
12656-FMT_API auto write_loc(appender out, loc_value value,
12657- const format_specs<>& specs, locale_ref loc) -> bool;
12658-template <typename OutputIt, typename Char>
12659-inline auto write_loc(OutputIt, loc_value, const format_specs<Char>&,
12660+FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs,
12661+ locale_ref loc) -> bool;
12662+#endif
12663+template <typename OutputIt>
12664+inline auto write_loc(OutputIt, const loc_value&, const format_specs&,
12665 locale_ref) -> bool {
12666 return false;
12667 }
12668
12669-FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
12670- prefix |= prefix != 0 ? value << 8 : value;
12671- prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
12672-}
12673-
12674 template <typename UInt> struct write_int_arg {
12675 UInt abs_value;
12676 unsigned prefix;
12677 };
12678
12679 template <typename T>
12680-FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
12681+FMT_CONSTEXPR auto make_write_int_arg(T value, sign s)
12682 -> write_int_arg<uint32_or_64_or_128_t<T>> {
12683 auto prefix = 0u;
12684 auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
12685@@ -2262,21 +1966,21 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
12686 } else {
12687 constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
12688 0x1000000u | ' '};
12689- prefix = prefixes[sign];
12690+ prefix = prefixes[static_cast<int>(s)];
12691 }
12692 return {abs_value, prefix};
12693 }
12694
12695 template <typename Char = char> struct loc_writer {
12696- buffer_appender<Char> out;
12697- const format_specs<Char>& specs;
12698+ basic_appender<Char> out;
12699+ const format_specs& specs;
12700 std::basic_string<Char> sep;
12701 std::string grouping;
12702 std::basic_string<Char> decimal_point;
12703
12704 template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
12705 auto operator()(T value) -> bool {
12706- auto arg = make_write_int_arg(value, specs.sign);
12707+ auto arg = make_write_int_arg(value, specs.sign());
12708 write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
12709 specs, digit_grouping<Char>(grouping, sep));
12710 return true;
12711@@ -2288,166 +1992,162 @@ template <typename Char = char> struct loc_writer {
12712 }
12713 };
12714
12715+// Size and padding computation separate from write_int to avoid template bloat.
12716+struct size_padding {
12717+ unsigned size;
12718+ unsigned padding;
12719+
12720+ FMT_CONSTEXPR size_padding(int num_digits, unsigned prefix,
12721+ const format_specs& specs)
12722+ : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
12723+ if (specs.align() == align::numeric) {
12724+ auto width = to_unsigned(specs.width);
12725+ if (width > size) {
12726+ padding = width - size;
12727+ size = width;
12728+ }
12729+ } else if (specs.precision > num_digits) {
12730+ size = (prefix >> 24) + to_unsigned(specs.precision);
12731+ padding = to_unsigned(specs.precision - num_digits);
12732+ }
12733+ }
12734+};
12735+
12736 template <typename Char, typename OutputIt, typename T>
12737 FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
12738- const format_specs<Char>& specs,
12739- locale_ref) -> OutputIt {
12740+ const format_specs& specs) -> OutputIt {
12741 static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
12742+
12743+ constexpr int buffer_size = num_bits<T>();
12744+ char buffer[buffer_size];
12745+ if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0');
12746+ const char* begin = nullptr;
12747+ const char* end = buffer + buffer_size;
12748+
12749 auto abs_value = arg.abs_value;
12750 auto prefix = arg.prefix;
12751- switch (specs.type) {
12752+ switch (specs.type()) {
12753+ default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH;
12754 case presentation_type::none:
12755- case presentation_type::dec: {
12756- auto num_digits = count_digits(abs_value);
12757- return write_int(
12758- out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
12759- return format_decimal<Char>(it, abs_value, num_digits).end;
12760- });
12761- }
12762- case presentation_type::hex_lower:
12763- case presentation_type::hex_upper: {
12764- bool upper = specs.type == presentation_type::hex_upper;
12765- if (specs.alt)
12766- prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
12767- int num_digits = count_digits<4>(abs_value);
12768- return write_int(
12769- out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
12770- return format_uint<4, Char>(it, abs_value, num_digits, upper);
12771- });
12772- }
12773- case presentation_type::bin_lower:
12774- case presentation_type::bin_upper: {
12775- bool upper = specs.type == presentation_type::bin_upper;
12776- if (specs.alt)
12777- prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
12778- int num_digits = count_digits<1>(abs_value);
12779- return write_int(out, num_digits, prefix, specs,
12780- [=](reserve_iterator<OutputIt> it) {
12781- return format_uint<1, Char>(it, abs_value, num_digits);
12782- });
12783- }
12784+ case presentation_type::dec:
12785+ begin = do_format_decimal(buffer, abs_value, buffer_size);
12786+ break;
12787+ case presentation_type::hex:
12788+ begin = do_format_base2e(4, buffer, abs_value, buffer_size, specs.upper());
12789+ if (specs.alt())
12790+ prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0');
12791+ break;
12792 case presentation_type::oct: {
12793- int num_digits = count_digits<3>(abs_value);
12794+ begin = do_format_base2e(3, buffer, abs_value, buffer_size);
12795 // Octal prefix '0' is counted as a digit, so only add it if precision
12796 // is not greater than the number of digits.
12797- if (specs.alt && specs.precision <= num_digits && abs_value != 0)
12798+ auto num_digits = end - begin;
12799+ if (specs.alt() && specs.precision <= num_digits && abs_value != 0)
12800 prefix_append(prefix, '0');
12801- return write_int(out, num_digits, prefix, specs,
12802- [=](reserve_iterator<OutputIt> it) {
12803- return format_uint<3, Char>(it, abs_value, num_digits);
12804- });
12805+ break;
12806 }
12807+ case presentation_type::bin:
12808+ begin = do_format_base2e(1, buffer, abs_value, buffer_size);
12809+ if (specs.alt())
12810+ prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
12811+ break;
12812 case presentation_type::chr:
12813- return write_char(out, static_cast<Char>(abs_value), specs);
12814- default:
12815- throw_format_error("invalid format specifier");
12816+ return write_char<Char>(out, static_cast<Char>(abs_value), specs);
12817 }
12818- return out;
12819+
12820+ // Write an integer in the format
12821+ // <left-padding><prefix><numeric-padding><digits><right-padding>
12822+ // prefix contains chars in three lower bytes and the size in the fourth byte.
12823+ int num_digits = static_cast<int>(end - begin);
12824+ // Slightly faster check for specs.width == 0 && specs.precision == -1.
12825+ if ((specs.width | (specs.precision + 1)) == 0) {
12826+ auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
12827+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
12828+ *it++ = static_cast<Char>(p & 0xff);
12829+ return base_iterator(out, copy<Char>(begin, end, it));
12830+ }
12831+ auto sp = size_padding(num_digits, prefix, specs);
12832+ unsigned padding = sp.padding;
12833+ return write_padded<Char, align::right>(
12834+ out, specs, sp.size, [=](reserve_iterator<OutputIt> it) {
12835+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
12836+ *it++ = static_cast<Char>(p & 0xff);
12837+ it = detail::fill_n(it, padding, static_cast<Char>('0'));
12838+ return copy<Char>(begin, end, it);
12839+ });
12840 }
12841+
12842 template <typename Char, typename OutputIt, typename T>
12843-FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(
12844- OutputIt out, write_int_arg<T> arg, const format_specs<Char>& specs,
12845- locale_ref loc) -> OutputIt {
12846- return write_int(out, arg, specs, loc);
12847+FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out,
12848+ write_int_arg<T> arg,
12849+ const format_specs& specs)
12850+ -> OutputIt {
12851+ return write_int<Char>(out, arg, specs);
12852 }
12853-template <typename Char, typename OutputIt, typename T,
12854+
12855+template <typename Char, typename T,
12856 FMT_ENABLE_IF(is_integral<T>::value &&
12857 !std::is_same<T, bool>::value &&
12858- std::is_same<OutputIt, buffer_appender<Char>>::value)>
12859-FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
12860- const format_specs<Char>& specs,
12861- locale_ref loc) -> OutputIt {
12862- if (specs.localized && write_loc(out, value, specs, loc)) return out;
12863- return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
12864- loc);
12865+ !std::is_same<T, Char>::value)>
12866+FMT_CONSTEXPR FMT_INLINE auto write(basic_appender<Char> out, T value,
12867+ const format_specs& specs, locale_ref loc)
12868+ -> basic_appender<Char> {
12869+ if (specs.localized() && write_loc(out, value, specs, loc)) return out;
12870+ return write_int_noinline<Char>(out, make_write_int_arg(value, specs.sign()),
12871+ specs);
12872 }
12873+
12874 // An inlined version of write used in format string compilation.
12875 template <typename Char, typename OutputIt, typename T,
12876 FMT_ENABLE_IF(is_integral<T>::value &&
12877 !std::is_same<T, bool>::value &&
12878- !std::is_same<OutputIt, buffer_appender<Char>>::value)>
12879+ !std::is_same<T, Char>::value &&
12880+ !std::is_same<OutputIt, basic_appender<Char>>::value)>
12881 FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
12882- const format_specs<Char>& specs,
12883- locale_ref loc) -> OutputIt {
12884- if (specs.localized && write_loc(out, value, specs, loc)) return out;
12885- return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
12886+ const format_specs& specs, locale_ref loc)
12887+ -> OutputIt {
12888+ if (specs.localized() && write_loc(out, value, specs, loc)) return out;
12889+ return write_int<Char>(out, make_write_int_arg(value, specs.sign()), specs);
12890 }
12891
12892-// An output iterator that counts the number of objects written to it and
12893-// discards them.
12894-class counting_iterator {
12895- private:
12896- size_t count_;
12897-
12898- public:
12899- using iterator_category = std::output_iterator_tag;
12900- using difference_type = std::ptrdiff_t;
12901- using pointer = void;
12902- using reference = void;
12903- FMT_UNCHECKED_ITERATOR(counting_iterator);
12904-
12905- struct value_type {
12906- template <typename T> FMT_CONSTEXPR void operator=(const T&) {}
12907- };
12908-
12909- FMT_CONSTEXPR counting_iterator() : count_(0) {}
12910-
12911- FMT_CONSTEXPR size_t count() const { return count_; }
12912-
12913- FMT_CONSTEXPR counting_iterator& operator++() {
12914- ++count_;
12915- return *this;
12916- }
12917- FMT_CONSTEXPR counting_iterator operator++(int) {
12918- auto it = *this;
12919- ++*this;
12920- return it;
12921- }
12922-
12923- FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it,
12924- difference_type n) {
12925- it.count_ += static_cast<size_t>(n);
12926- return it;
12927- }
12928-
12929- FMT_CONSTEXPR value_type operator*() const { return {}; }
12930-};
12931-
12932 template <typename Char, typename OutputIt>
12933 FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
12934- const format_specs<Char>& specs) -> OutputIt {
12935+ const format_specs& specs) -> OutputIt {
12936 auto data = s.data();
12937 auto size = s.size();
12938 if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
12939 size = code_point_index(s, to_unsigned(specs.precision));
12940- bool is_debug = specs.type == presentation_type::debug;
12941+
12942+ bool is_debug = specs.type() == presentation_type::debug;
12943+ if (is_debug) {
12944+ auto buf = counting_buffer<Char>();
12945+ write_escaped_string(basic_appender<Char>(buf), s);
12946+ size = buf.count();
12947+ }
12948+
12949 size_t width = 0;
12950 if (specs.width != 0) {
12951- if (is_debug)
12952- width = write_escaped_string(counting_iterator{}, s).count();
12953- else
12954- width = compute_width(basic_string_view<Char>(data, size));
12955+ width =
12956+ is_debug ? size : compute_width(basic_string_view<Char>(data, size));
12957 }
12958- return write_padded(out, specs, size, width,
12959- [=](reserve_iterator<OutputIt> it) {
12960- if (is_debug) return write_escaped_string(it, s);
12961- return copy_str<Char>(data, data + size, it);
12962- });
12963+ return write_padded<Char>(
12964+ out, specs, size, width, [=](reserve_iterator<OutputIt> it) {
12965+ return is_debug ? write_escaped_string(it, s)
12966+ : copy<Char>(data, data + size, it);
12967+ });
12968 }
12969 template <typename Char, typename OutputIt>
12970-FMT_CONSTEXPR auto write(OutputIt out,
12971- basic_string_view<type_identity_t<Char>> s,
12972- const format_specs<Char>& specs, locale_ref)
12973- -> OutputIt {
12974- return write(out, s, specs);
12975+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
12976+ const format_specs& specs, locale_ref) -> OutputIt {
12977+ return write<Char>(out, s, specs);
12978 }
12979 template <typename Char, typename OutputIt>
12980-FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
12981- const format_specs<Char>& specs, locale_ref)
12982- -> OutputIt {
12983- return specs.type != presentation_type::pointer
12984- ? write(out, basic_string_view<Char>(s), specs, {})
12985- : write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
12986+FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs,
12987+ locale_ref) -> OutputIt {
12988+ if (specs.type() == presentation_type::pointer)
12989+ return write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
12990+ if (!s) report_error("string pointer is null");
12991+ return write<Char>(out, basic_string_view<Char>(s), specs, {});
12992 }
12993
12994 template <typename Char, typename OutputIt, typename T,
12995@@ -2461,96 +2161,68 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
12996 if (negative) abs_value = ~abs_value + 1;
12997 int num_digits = count_digits(abs_value);
12998 auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
12999- auto it = reserve(out, size);
13000- if (auto ptr = to_pointer<Char>(it, size)) {
13001+ if (auto ptr = to_pointer<Char>(out, size)) {
13002 if (negative) *ptr++ = static_cast<Char>('-');
13003 format_decimal<Char>(ptr, abs_value, num_digits);
13004 return out;
13005 }
13006- if (negative) *it++ = static_cast<Char>('-');
13007- it = format_decimal<Char>(it, abs_value, num_digits).end;
13008- return base_iterator(out, it);
13009+ if (negative) *out++ = static_cast<Char>('-');
13010+ return format_decimal<Char>(out, abs_value, num_digits);
13011 }
13012
13013-// A floating-point presentation format.
13014-enum class float_format : unsigned char {
13015- general, // General: exponent notation or fixed point based on magnitude.
13016- exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
13017- fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
13018- hex
13019-};
13020-
13021-struct float_specs {
13022- int precision;
13023- float_format format : 8;
13024- sign_t sign : 8;
13025- bool upper : 1;
13026- bool locale : 1;
13027- bool binary32 : 1;
13028- bool showpoint : 1;
13029-};
13030-
13031-template <typename ErrorHandler = error_handler, typename Char>
13032-FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs,
13033- ErrorHandler&& eh = {})
13034- -> float_specs {
13035- auto result = float_specs();
13036- result.showpoint = specs.alt;
13037- result.locale = specs.localized;
13038- switch (specs.type) {
13039- case presentation_type::none:
13040- result.format = float_format::general;
13041- break;
13042- case presentation_type::general_upper:
13043- result.upper = true;
13044- FMT_FALLTHROUGH;
13045- case presentation_type::general_lower:
13046- result.format = float_format::general;
13047- break;
13048- case presentation_type::exp_upper:
13049- result.upper = true;
13050- FMT_FALLTHROUGH;
13051- case presentation_type::exp_lower:
13052- result.format = float_format::exp;
13053- result.showpoint |= specs.precision != 0;
13054- break;
13055- case presentation_type::fixed_upper:
13056- result.upper = true;
13057- FMT_FALLTHROUGH;
13058- case presentation_type::fixed_lower:
13059- result.format = float_format::fixed;
13060- result.showpoint |= specs.precision != 0;
13061- break;
13062- case presentation_type::hexfloat_upper:
13063- result.upper = true;
13064- FMT_FALLTHROUGH;
13065- case presentation_type::hexfloat_lower:
13066- result.format = float_format::hex;
13067- break;
13068- default:
13069- eh.on_error("invalid format specifier");
13070- break;
13071+template <typename Char>
13072+FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
13073+ format_specs& specs) -> const Char* {
13074+ FMT_ASSERT(begin != end, "");
13075+ auto alignment = align::none;
13076+ auto p = begin + code_point_length(begin);
13077+ if (end - p <= 0) p = begin;
13078+ for (;;) {
13079+ switch (to_ascii(*p)) {
13080+ case '<': alignment = align::left; break;
13081+ case '>': alignment = align::right; break;
13082+ case '^': alignment = align::center; break;
13083+ }
13084+ if (alignment != align::none) {
13085+ if (p != begin) {
13086+ auto c = *begin;
13087+ if (c == '}') return begin;
13088+ if (c == '{') {
13089+ report_error("invalid fill character '{'");
13090+ return begin;
13091+ }
13092+ specs.set_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
13093+ begin = p + 1;
13094+ } else {
13095+ ++begin;
13096+ }
13097+ break;
13098+ } else if (p == begin) {
13099+ break;
13100+ }
13101+ p = begin;
13102 }
13103- return result;
13104+ specs.set_align(alignment);
13105+ return begin;
13106 }
13107
13108 template <typename Char, typename OutputIt>
13109 FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
13110- format_specs<Char> specs,
13111- const float_specs& fspecs) -> OutputIt {
13112+ format_specs specs, sign s) -> OutputIt {
13113 auto str =
13114- isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
13115+ isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf");
13116 constexpr size_t str_size = 3;
13117- auto sign = fspecs.sign;
13118- auto size = str_size + (sign ? 1 : 0);
13119+ auto size = str_size + (s != sign::none ? 1 : 0);
13120 // Replace '0'-padding with space for non-finite values.
13121 const bool is_zero_fill =
13122- specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0');
13123- if (is_zero_fill) specs.fill[0] = static_cast<Char>(' ');
13124- return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) {
13125- if (sign) *it++ = detail::sign<Char>(sign);
13126- return copy_str<Char>(str, str + str_size, it);
13127- });
13128+ specs.fill_size() == 1 && specs.fill_unit<Char>() == '0';
13129+ if (is_zero_fill) specs.set_fill(' ');
13130+ return write_padded<Char>(out, specs, size,
13131+ [=](reserve_iterator<OutputIt> it) {
13132+ if (s != sign::none)
13133+ *it++ = detail::getsign<Char>(s);
13134+ return copy<Char>(str, str + str_size, it);
13135+ });
13136 }
13137
13138 // A decimal floating-point number significand * pow(10, exp).
13139@@ -2571,12 +2243,12 @@ inline auto get_significand_size(const dragonbox::decimal_fp<T>& f) -> int {
13140 template <typename Char, typename OutputIt>
13141 constexpr auto write_significand(OutputIt out, const char* significand,
13142 int significand_size) -> OutputIt {
13143- return copy_str<Char>(significand, significand + significand_size, out);
13144+ return copy<Char>(significand, significand + significand_size, out);
13145 }
13146 template <typename Char, typename OutputIt, typename UInt>
13147 inline auto write_significand(OutputIt out, UInt significand,
13148 int significand_size) -> OutputIt {
13149- return format_decimal<Char>(out, significand, significand_size).end;
13150+ return format_decimal<Char>(out, significand, significand_size);
13151 }
13152 template <typename Char, typename OutputIt, typename T, typename Grouping>
13153 FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
13154@@ -2596,14 +2268,13 @@ template <typename Char, typename UInt,
13155 FMT_ENABLE_IF(std::is_integral<UInt>::value)>
13156 inline auto write_significand(Char* out, UInt significand, int significand_size,
13157 int integral_size, Char decimal_point) -> Char* {
13158- if (!decimal_point)
13159- return format_decimal(out, significand, significand_size).end;
13160+ if (!decimal_point) return format_decimal(out, significand, significand_size);
13161 out += significand_size + 1;
13162 Char* end = out;
13163 int floating_size = significand_size - integral_size;
13164 for (int i = floating_size / 2; i > 0; --i) {
13165 out -= 2;
13166- copy2(out, digits2(static_cast<std::size_t>(significand % 100)));
13167+ write2digits(out, static_cast<std::size_t>(significand % 100));
13168 significand /= 100;
13169 }
13170 if (floating_size % 2 != 0) {
13171@@ -2624,19 +2295,19 @@ inline auto write_significand(OutputIt out, UInt significand,
13172 Char buffer[digits10<UInt>() + 2];
13173 auto end = write_significand(buffer, significand, significand_size,
13174 integral_size, decimal_point);
13175- return detail::copy_str_noinline<Char>(buffer, end, out);
13176+ return detail::copy_noinline<Char>(buffer, end, out);
13177 }
13178
13179 template <typename OutputIt, typename Char>
13180 FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand,
13181 int significand_size, int integral_size,
13182 Char decimal_point) -> OutputIt {
13183- out = detail::copy_str_noinline<Char>(significand,
13184- significand + integral_size, out);
13185+ out = detail::copy_noinline<Char>(significand, significand + integral_size,
13186+ out);
13187 if (!decimal_point) return out;
13188 *out++ = decimal_point;
13189- return detail::copy_str_noinline<Char>(significand + integral_size,
13190- significand + significand_size, out);
13191+ return detail::copy_noinline<Char>(significand + integral_size,
13192+ significand + significand_size, out);
13193 }
13194
13195 template <typename OutputIt, typename Char, typename T, typename Grouping>
13196@@ -2649,44 +2320,42 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
13197 decimal_point);
13198 }
13199 auto buffer = basic_memory_buffer<Char>();
13200- write_significand(buffer_appender<Char>(buffer), significand,
13201- significand_size, integral_size, decimal_point);
13202+ write_significand(basic_appender<Char>(buffer), significand, significand_size,
13203+ integral_size, decimal_point);
13204 grouping.apply(
13205 out, basic_string_view<Char>(buffer.data(), to_unsigned(integral_size)));
13206- return detail::copy_str_noinline<Char>(buffer.data() + integral_size,
13207- buffer.end(), out);
13208+ return detail::copy_noinline<Char>(buffer.data() + integral_size,
13209+ buffer.end(), out);
13210 }
13211
13212-template <typename OutputIt, typename DecimalFP, typename Char,
13213+template <typename Char, typename OutputIt, typename DecimalFP,
13214 typename Grouping = digit_grouping<Char>>
13215 FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
13216- const format_specs<Char>& specs,
13217- float_specs fspecs, locale_ref loc)
13218- -> OutputIt {
13219+ const format_specs& specs, sign s,
13220+ int exp_upper, locale_ref loc) -> OutputIt {
13221 auto significand = f.significand;
13222 int significand_size = get_significand_size(f);
13223 const Char zero = static_cast<Char>('0');
13224- auto sign = fspecs.sign;
13225- size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
13226+ size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0);
13227 using iterator = reserve_iterator<OutputIt>;
13228
13229- Char decimal_point =
13230- fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
13231+ Char decimal_point = specs.localized() ? detail::decimal_point<Char>(loc)
13232+ : static_cast<Char>('.');
13233
13234 int output_exp = f.exponent + significand_size - 1;
13235 auto use_exp_format = [=]() {
13236- if (fspecs.format == float_format::exp) return true;
13237- if (fspecs.format != float_format::general) return false;
13238+ if (specs.type() == presentation_type::exp) return true;
13239+ if (specs.type() == presentation_type::fixed) return false;
13240 // Use the fixed notation if the exponent is in [exp_lower, exp_upper),
13241 // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
13242- const int exp_lower = -4, exp_upper = 16;
13243+ const int exp_lower = -4;
13244 return output_exp < exp_lower ||
13245- output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper);
13246+ output_exp >= (specs.precision > 0 ? specs.precision : exp_upper);
13247 };
13248 if (use_exp_format()) {
13249 int num_zeros = 0;
13250- if (fspecs.showpoint) {
13251- num_zeros = fspecs.precision - significand_size;
13252+ if (specs.alt()) {
13253+ num_zeros = specs.precision - significand_size;
13254 if (num_zeros < 0) num_zeros = 0;
13255 size += to_unsigned(num_zeros);
13256 } else if (significand_size == 1) {
13257@@ -2697,9 +2366,9 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
13258 if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
13259
13260 size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
13261- char exp_char = fspecs.upper ? 'E' : 'e';
13262+ char exp_char = specs.upper() ? 'E' : 'e';
13263 auto write = [=](iterator it) {
13264- if (sign) *it++ = detail::sign<Char>(sign);
13265+ if (s != sign::none) *it++ = detail::getsign<Char>(s);
13266 // Insert a decimal point after the first digit and add an exponent.
13267 it = write_significand(it, significand, significand_size, 1,
13268 decimal_point);
13269@@ -2707,39 +2376,41 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
13270 *it++ = static_cast<Char>(exp_char);
13271 return write_exponent<Char>(output_exp, it);
13272 };
13273- return specs.width > 0 ? write_padded<align::right>(out, specs, size, write)
13274- : base_iterator(out, write(reserve(out, size)));
13275+ return specs.width > 0
13276+ ? write_padded<Char, align::right>(out, specs, size, write)
13277+ : base_iterator(out, write(reserve(out, size)));
13278 }
13279
13280 int exp = f.exponent + significand_size;
13281 if (f.exponent >= 0) {
13282 // 1234e5 -> 123400000[.0+]
13283 size += to_unsigned(f.exponent);
13284- int num_zeros = fspecs.precision - exp;
13285+ int num_zeros = specs.precision - exp;
13286 abort_fuzzing_if(num_zeros > 5000);
13287- if (fspecs.showpoint) {
13288+ if (specs.alt()) {
13289 ++size;
13290- if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0;
13291+ if (num_zeros <= 0 && specs.type() != presentation_type::fixed)
13292+ num_zeros = 0;
13293 if (num_zeros > 0) size += to_unsigned(num_zeros);
13294 }
13295- auto grouping = Grouping(loc, fspecs.locale);
13296+ auto grouping = Grouping(loc, specs.localized());
13297 size += to_unsigned(grouping.count_separators(exp));
13298- return write_padded<align::right>(out, specs, size, [&](iterator it) {
13299- if (sign) *it++ = detail::sign<Char>(sign);
13300+ return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
13301+ if (s != sign::none) *it++ = detail::getsign<Char>(s);
13302 it = write_significand<Char>(it, significand, significand_size,
13303 f.exponent, grouping);
13304- if (!fspecs.showpoint) return it;
13305+ if (!specs.alt()) return it;
13306 *it++ = decimal_point;
13307 return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
13308 });
13309 } else if (exp > 0) {
13310 // 1234e-2 -> 12.34[0+]
13311- int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
13312- size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
13313- auto grouping = Grouping(loc, fspecs.locale);
13314+ int num_zeros = specs.alt() ? specs.precision - significand_size : 0;
13315+ size += 1 + static_cast<unsigned>(max_of(num_zeros, 0));
13316+ auto grouping = Grouping(loc, specs.localized());
13317 size += to_unsigned(grouping.count_separators(exp));
13318- return write_padded<align::right>(out, specs, size, [&](iterator it) {
13319- if (sign) *it++ = detail::sign<Char>(sign);
13320+ return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
13321+ if (s != sign::none) *it++ = detail::getsign<Char>(s);
13322 it = write_significand(it, significand, significand_size, exp,
13323 decimal_point, grouping);
13324 return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
13325@@ -2747,14 +2418,14 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
13326 }
13327 // 1234e-6 -> 0.001234
13328 int num_zeros = -exp;
13329- if (significand_size == 0 && fspecs.precision >= 0 &&
13330- fspecs.precision < num_zeros) {
13331- num_zeros = fspecs.precision;
13332+ if (significand_size == 0 && specs.precision >= 0 &&
13333+ specs.precision < num_zeros) {
13334+ num_zeros = specs.precision;
13335 }
13336- bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint;
13337+ bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
13338 size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
13339- return write_padded<align::right>(out, specs, size, [&](iterator it) {
13340- if (sign) *it++ = detail::sign<Char>(sign);
13341+ return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
13342+ if (s != sign::none) *it++ = detail::getsign<Char>(s);
13343 *it++ = zero;
13344 if (!pointy) return it;
13345 *it++ = decimal_point;
13346@@ -2767,32 +2438,31 @@ template <typename Char> class fallback_digit_grouping {
13347 public:
13348 constexpr fallback_digit_grouping(locale_ref, bool) {}
13349
13350- constexpr bool has_separator() const { return false; }
13351+ constexpr auto has_separator() const -> bool { return false; }
13352
13353- constexpr int count_separators(int) const { return 0; }
13354+ constexpr auto count_separators(int) const -> int { return 0; }
13355
13356 template <typename Out, typename C>
13357- constexpr Out apply(Out out, basic_string_view<C>) const {
13358+ constexpr auto apply(Out out, basic_string_view<C>) const -> Out {
13359 return out;
13360 }
13361 };
13362
13363-template <typename OutputIt, typename DecimalFP, typename Char>
13364+template <typename Char, typename OutputIt, typename DecimalFP>
13365 FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
13366- const format_specs<Char>& specs,
13367- float_specs fspecs, locale_ref loc)
13368- -> OutputIt {
13369+ const format_specs& specs, sign s,
13370+ int exp_upper, locale_ref loc) -> OutputIt {
13371 if (is_constant_evaluated()) {
13372- return do_write_float<OutputIt, DecimalFP, Char,
13373- fallback_digit_grouping<Char>>(out, f, specs, fspecs,
13374- loc);
13375+ return do_write_float<Char, OutputIt, DecimalFP,
13376+ fallback_digit_grouping<Char>>(out, f, specs, s,
13377+ exp_upper, loc);
13378 } else {
13379- return do_write_float(out, f, specs, fspecs, loc);
13380+ return do_write_float<Char>(out, f, specs, s, exp_upper, loc);
13381 }
13382 }
13383
13384-template <typename T> constexpr bool isnan(T value) {
13385- return !(value >= value); // std::isnan doesn't support __float128.
13386+template <typename T> constexpr auto isnan(T value) -> bool {
13387+ return value != value; // std::isnan doesn't support __float128.
13388 }
13389
13390 template <typename T, typename Enable = void>
13391@@ -2804,14 +2474,14 @@ struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
13392
13393 template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&&
13394 has_isfinite<T>::value)>
13395-FMT_CONSTEXPR20 bool isfinite(T value) {
13396+FMT_CONSTEXPR20 auto isfinite(T value) -> bool {
13397 constexpr T inf = T(std::numeric_limits<double>::infinity());
13398 if (is_constant_evaluated())
13399 return !detail::isnan(value) && value < inf && value > -inf;
13400 return std::isfinite(value);
13401 }
13402 template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)>
13403-FMT_CONSTEXPR bool isfinite(T value) {
13404+FMT_CONSTEXPR auto isfinite(T value) -> bool {
13405 T inf = T(std::numeric_limits<double>::infinity());
13406 // std::isfinite doesn't support __float128.
13407 return !detail::isnan(value) && value < inf && value > -inf;
13408@@ -2830,78 +2500,6 @@ FMT_INLINE FMT_CONSTEXPR bool signbit(T value) {
13409 return std::signbit(static_cast<double>(value));
13410 }
13411
13412-enum class round_direction { unknown, up, down };
13413-
13414-// Given the divisor (normally a power of 10), the remainder = v % divisor for
13415-// some number v and the error, returns whether v should be rounded up, down, or
13416-// whether the rounding direction can't be determined due to error.
13417-// error should be less than divisor / 2.
13418-FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
13419- uint64_t remainder,
13420- uint64_t error) {
13421- FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
13422- FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
13423- FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
13424- // Round down if (remainder + error) * 2 <= divisor.
13425- if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
13426- return round_direction::down;
13427- // Round up if (remainder - error) * 2 >= divisor.
13428- if (remainder >= error &&
13429- remainder - error >= divisor - (remainder - error)) {
13430- return round_direction::up;
13431- }
13432- return round_direction::unknown;
13433-}
13434-
13435-namespace digits {
13436-enum result {
13437- more, // Generate more digits.
13438- done, // Done generating digits.
13439- error // Digit generation cancelled due to an error.
13440-};
13441-}
13442-
13443-struct gen_digits_handler {
13444- char* buf;
13445- int size;
13446- int precision;
13447- int exp10;
13448- bool fixed;
13449-
13450- FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
13451- uint64_t remainder, uint64_t error,
13452- bool integral) {
13453- FMT_ASSERT(remainder < divisor, "");
13454- buf[size++] = digit;
13455- if (!integral && error >= remainder) return digits::error;
13456- if (size < precision) return digits::more;
13457- if (!integral) {
13458- // Check if error * 2 < divisor with overflow prevention.
13459- // The check is not needed for the integral part because error = 1
13460- // and divisor > (1 << 32) there.
13461- if (error >= divisor || error >= divisor - error) return digits::error;
13462- } else {
13463- FMT_ASSERT(error == 1 && divisor > 2, "");
13464- }
13465- auto dir = get_round_direction(divisor, remainder, error);
13466- if (dir != round_direction::up)
13467- return dir == round_direction::down ? digits::done : digits::error;
13468- ++buf[size - 1];
13469- for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
13470- buf[i] = '0';
13471- ++buf[i - 1];
13472- }
13473- if (buf[0] > '9') {
13474- buf[0] = '1';
13475- if (fixed)
13476- buf[size++] = '0';
13477- else
13478- ++exp10;
13479- }
13480- return digits::done;
13481- }
13482-};
13483-
13484 inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
13485 // Adjust fixed precision by exponent because it is relative to decimal
13486 // point.
13487@@ -2910,149 +2508,50 @@ inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
13488 precision += exp10;
13489 }
13490
13491-// Generates output using the Grisu digit-gen algorithm.
13492-// error: the size of the region (lower, upper) outside of which numbers
13493-// definitely do not round to value (Delta in Grisu3).
13494-FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error,
13495- int& exp,
13496- gen_digits_handler& handler)
13497- -> digits::result {
13498- const fp one(1ULL << -value.e, value.e);
13499- // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
13500- // zero because it contains a product of two 64-bit numbers with MSB set (due
13501- // to normalization) - 1, shifted right by at most 60 bits.
13502- auto integral = static_cast<uint32_t>(value.f >> -one.e);
13503- FMT_ASSERT(integral != 0, "");
13504- FMT_ASSERT(integral == value.f >> -one.e, "");
13505- // The fractional part of scaled value (p2 in Grisu) c = value % one.
13506- uint64_t fractional = value.f & (one.f - 1);
13507- exp = count_digits(integral); // kappa in Grisu.
13508- // Non-fixed formats require at least one digit and no precision adjustment.
13509- if (handler.fixed) {
13510- adjust_precision(handler.precision, exp + handler.exp10);
13511- // Check if precision is satisfied just by leading zeros, e.g.
13512- // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
13513- if (handler.precision <= 0) {
13514- if (handler.precision < 0) return digits::done;
13515- // Divide by 10 to prevent overflow.
13516- uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e;
13517- auto dir = get_round_direction(divisor, value.f / 10, error * 10);
13518- if (dir == round_direction::unknown) return digits::error;
13519- handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
13520- return digits::done;
13521- }
13522- }
13523- // Generate digits for the integral part. This can produce up to 10 digits.
13524- do {
13525- uint32_t digit = 0;
13526- auto divmod_integral = [&](uint32_t divisor) {
13527- digit = integral / divisor;
13528- integral %= divisor;
13529- };
13530- // This optimization by Milo Yip reduces the number of integer divisions by
13531- // one per iteration.
13532- switch (exp) {
13533- case 10:
13534- divmod_integral(1000000000);
13535- break;
13536- case 9:
13537- divmod_integral(100000000);
13538- break;
13539- case 8:
13540- divmod_integral(10000000);
13541- break;
13542- case 7:
13543- divmod_integral(1000000);
13544- break;
13545- case 6:
13546- divmod_integral(100000);
13547- break;
13548- case 5:
13549- divmod_integral(10000);
13550- break;
13551- case 4:
13552- divmod_integral(1000);
13553- break;
13554- case 3:
13555- divmod_integral(100);
13556- break;
13557- case 2:
13558- divmod_integral(10);
13559- break;
13560- case 1:
13561- digit = integral;
13562- integral = 0;
13563- break;
13564- default:
13565- FMT_ASSERT(false, "invalid number of digits");
13566- }
13567- --exp;
13568- auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
13569- auto result = handler.on_digit(static_cast<char>('0' + digit),
13570- data::power_of_10_64[exp] << -one.e,
13571- remainder, error, true);
13572- if (result != digits::more) return result;
13573- } while (exp > 0);
13574- // Generate digits for the fractional part.
13575- for (;;) {
13576- fractional *= 10;
13577- error *= 10;
13578- char digit = static_cast<char>('0' + (fractional >> -one.e));
13579- fractional &= one.f - 1;
13580- --exp;
13581- auto result = handler.on_digit(digit, one.f, fractional, error, false);
13582- if (result != digits::more) return result;
13583- }
13584-}
13585-
13586 class bigint {
13587 private:
13588- // A bigint is stored as an array of bigits (big digits), with bigit at index
13589- // 0 being the least significant one.
13590- using bigit = uint32_t;
13591+ // A bigint is a number in the form bigit_[N - 1] ... bigit_[0] * 32^exp_.
13592+ using bigit = uint32_t; // A big digit.
13593 using double_bigit = uint64_t;
13594+ enum { bigit_bits = num_bits<bigit>() };
13595 enum { bigits_capacity = 32 };
13596 basic_memory_buffer<bigit, bigits_capacity> bigits_;
13597 int exp_;
13598
13599- FMT_CONSTEXPR20 bigit operator[](int index) const {
13600- return bigits_[to_unsigned(index)];
13601- }
13602- FMT_CONSTEXPR20 bigit& operator[](int index) {
13603- return bigits_[to_unsigned(index)];
13604- }
13605-
13606- static constexpr const int bigit_bits = num_bits<bigit>();
13607-
13608 friend struct formatter<bigint>;
13609
13610- FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
13611- auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
13612- (*this)[index] = static_cast<bigit>(result);
13613+ FMT_CONSTEXPR auto get_bigit(int i) const -> bigit {
13614+ return i >= exp_ && i < num_bigits() ? bigits_[i - exp_] : 0;
13615+ }
13616+
13617+ FMT_CONSTEXPR void subtract_bigits(int index, bigit other, bigit& borrow) {
13618+ auto result = double_bigit(bigits_[index]) - other - borrow;
13619+ bigits_[index] = static_cast<bigit>(result);
13620 borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
13621 }
13622
13623- FMT_CONSTEXPR20 void remove_leading_zeros() {
13624+ FMT_CONSTEXPR void remove_leading_zeros() {
13625 int num_bigits = static_cast<int>(bigits_.size()) - 1;
13626- while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
13627+ while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
13628 bigits_.resize(to_unsigned(num_bigits + 1));
13629 }
13630
13631 // Computes *this -= other assuming aligned bigints and *this >= other.
13632- FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
13633+ FMT_CONSTEXPR void subtract_aligned(const bigint& other) {
13634 FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
13635 FMT_ASSERT(compare(*this, other) >= 0, "");
13636 bigit borrow = 0;
13637 int i = other.exp_ - exp_;
13638 for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
13639 subtract_bigits(i, other.bigits_[j], borrow);
13640- while (borrow > 0) subtract_bigits(i, 0, borrow);
13641+ if (borrow != 0) subtract_bigits(i, 0, borrow);
13642+ FMT_ASSERT(borrow == 0, "");
13643 remove_leading_zeros();
13644 }
13645
13646- FMT_CONSTEXPR20 void multiply(uint32_t value) {
13647- const double_bigit wide_value = value;
13648+ FMT_CONSTEXPR void multiply(uint32_t value) {
13649 bigit carry = 0;
13650+ const double_bigit wide_value = value;
13651 for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
13652 double_bigit result = bigits_[i] * wide_value + carry;
13653 bigits_[i] = static_cast<bigit>(result);
13654@@ -3063,7 +2562,7 @@ class bigint {
13655
13656 template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
13657 std::is_same<UInt, uint128_t>::value)>
13658- FMT_CONSTEXPR20 void multiply(UInt value) {
13659+ FMT_CONSTEXPR void multiply(UInt value) {
13660 using half_uint =
13661 conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>;
13662 const int shift = num_bits<half_uint>() - bigit_bits;
13663@@ -3084,7 +2583,7 @@ class bigint {
13664
13665 template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
13666 std::is_same<UInt, uint128_t>::value)>
13667- FMT_CONSTEXPR20 void assign(UInt n) {
13668+ FMT_CONSTEXPR void assign(UInt n) {
13669 size_t num_bigits = 0;
13670 do {
13671 bigits_[num_bigits++] = static_cast<bigit>(n);
13672@@ -3095,30 +2594,30 @@ class bigint {
13673 }
13674
13675 public:
13676- FMT_CONSTEXPR20 bigint() : exp_(0) {}
13677+ FMT_CONSTEXPR bigint() : exp_(0) {}
13678 explicit bigint(uint64_t n) { assign(n); }
13679
13680 bigint(const bigint&) = delete;
13681 void operator=(const bigint&) = delete;
13682
13683- FMT_CONSTEXPR20 void assign(const bigint& other) {
13684+ FMT_CONSTEXPR void assign(const bigint& other) {
13685 auto size = other.bigits_.size();
13686 bigits_.resize(size);
13687 auto data = other.bigits_.data();
13688- std::copy(data, data + size, make_checked(bigits_.data(), size));
13689+ copy<bigit>(data, data + size, bigits_.data());
13690 exp_ = other.exp_;
13691 }
13692
13693- template <typename Int> FMT_CONSTEXPR20 void operator=(Int n) {
13694+ template <typename Int> FMT_CONSTEXPR void operator=(Int n) {
13695 FMT_ASSERT(n > 0, "");
13696 assign(uint64_or_128_t<Int>(n));
13697 }
13698
13699- FMT_CONSTEXPR20 int num_bigits() const {
13700+ FMT_CONSTEXPR auto num_bigits() const -> int {
13701 return static_cast<int>(bigits_.size()) + exp_;
13702 }
13703
13704- FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
13705+ FMT_CONSTEXPR auto operator<<=(int shift) -> bigint& {
13706 FMT_ASSERT(shift >= 0, "");
13707 exp_ += shift / bigit_bits;
13708 shift %= bigit_bits;
13709@@ -3133,46 +2632,39 @@ class bigint {
13710 return *this;
13711 }
13712
13713- template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
13714+ template <typename Int> FMT_CONSTEXPR auto operator*=(Int value) -> bigint& {
13715 FMT_ASSERT(value > 0, "");
13716 multiply(uint32_or_64_or_128_t<Int>(value));
13717 return *this;
13718 }
13719
13720- friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
13721- int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
13722- if (num_lhs_bigits != num_rhs_bigits)
13723- return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
13724- int i = static_cast<int>(lhs.bigits_.size()) - 1;
13725- int j = static_cast<int>(rhs.bigits_.size()) - 1;
13726+ friend FMT_CONSTEXPR auto compare(const bigint& b1, const bigint& b2) -> int {
13727+ int num_bigits1 = b1.num_bigits(), num_bigits2 = b2.num_bigits();
13728+ if (num_bigits1 != num_bigits2) return num_bigits1 > num_bigits2 ? 1 : -1;
13729+ int i = static_cast<int>(b1.bigits_.size()) - 1;
13730+ int j = static_cast<int>(b2.bigits_.size()) - 1;
13731 int end = i - j;
13732 if (end < 0) end = 0;
13733 for (; i >= end; --i, --j) {
13734- bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
13735- if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
13736+ bigit b1_bigit = b1.bigits_[i], b2_bigit = b2.bigits_[j];
13737+ if (b1_bigit != b2_bigit) return b1_bigit > b2_bigit ? 1 : -1;
13738 }
13739 if (i != j) return i > j ? 1 : -1;
13740 return 0;
13741 }
13742
13743 // Returns compare(lhs1 + lhs2, rhs).
13744- friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
13745- const bigint& rhs) {
13746- auto minimum = [](int a, int b) { return a < b ? a : b; };
13747- auto maximum = [](int a, int b) { return a > b ? a : b; };
13748- int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
13749+ friend FMT_CONSTEXPR auto add_compare(const bigint& lhs1, const bigint& lhs2,
13750+ const bigint& rhs) -> int {
13751+ int max_lhs_bigits = max_of(lhs1.num_bigits(), lhs2.num_bigits());
13752 int num_rhs_bigits = rhs.num_bigits();
13753 if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
13754 if (max_lhs_bigits > num_rhs_bigits) return 1;
13755- auto get_bigit = [](const bigint& n, int i) -> bigit {
13756- return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
13757- };
13758 double_bigit borrow = 0;
13759- int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_);
13760+ int min_exp = min_of(min_of(lhs1.exp_, lhs2.exp_), rhs.exp_);
13761 for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
13762- double_bigit sum =
13763- static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
13764- bigit rhs_bigit = get_bigit(rhs, i);
13765+ double_bigit sum = double_bigit(lhs1.get_bigit(i)) + lhs2.get_bigit(i);
13766+ bigit rhs_bigit = rhs.get_bigit(i);
13767 if (sum > rhs_bigit + borrow) return 1;
13768 borrow = rhs_bigit + borrow - sum;
13769 if (borrow > 1) return -1;
13770@@ -3185,10 +2677,8 @@ class bigint {
13771 FMT_CONSTEXPR20 void assign_pow10(int exp) {
13772 FMT_ASSERT(exp >= 0, "");
13773 if (exp == 0) return *this = 1;
13774- // Find the top bit.
13775- int bitmask = 1;
13776- while (exp >= bitmask) bitmask <<= 1;
13777- bitmask >>= 1;
13778+ int bitmask = 1 << (num_bits<unsigned>() -
13779+ countl_zero(static_cast<uint32_t>(exp)) - 1);
13780 // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
13781 // repeated squaring and multiplication.
13782 *this = 5;
13783@@ -3212,17 +2702,17 @@ class bigint {
13784 // cross-product terms n[i] * n[j] such that i + j == bigit_index.
13785 for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
13786 // Most terms are multiplied twice which can be optimized in the future.
13787- sum += static_cast<double_bigit>(n[i]) * n[j];
13788+ sum += double_bigit(n[i]) * n[j];
13789 }
13790- (*this)[bigit_index] = static_cast<bigit>(sum);
13791+ bigits_[bigit_index] = static_cast<bigit>(sum);
13792 sum >>= num_bits<bigit>(); // Compute the carry.
13793 }
13794 // Do the same for the top half.
13795 for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
13796 ++bigit_index) {
13797 for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
13798- sum += static_cast<double_bigit>(n[i++]) * n[j--];
13799- (*this)[bigit_index] = static_cast<bigit>(sum);
13800+ sum += double_bigit(n[i++]) * n[j--];
13801+ bigits_[bigit_index] = static_cast<bigit>(sum);
13802 sum >>= num_bits<bigit>();
13803 }
13804 remove_leading_zeros();
13805@@ -3231,20 +2721,20 @@ class bigint {
13806
13807 // If this bigint has a bigger exponent than other, adds trailing zero to make
13808 // exponents equal. This simplifies some operations such as subtraction.
13809- FMT_CONSTEXPR20 void align(const bigint& other) {
13810+ FMT_CONSTEXPR void align(const bigint& other) {
13811 int exp_difference = exp_ - other.exp_;
13812 if (exp_difference <= 0) return;
13813 int num_bigits = static_cast<int>(bigits_.size());
13814 bigits_.resize(to_unsigned(num_bigits + exp_difference));
13815 for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
13816 bigits_[j] = bigits_[i];
13817- std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
13818+ memset(bigits_.data(), 0, to_unsigned(exp_difference) * sizeof(bigit));
13819 exp_ -= exp_difference;
13820 }
13821
13822 // Divides this bignum by divisor, assigning the remainder to this and
13823 // returning the quotient.
13824- FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
13825+ FMT_CONSTEXPR auto divmod_assign(const bigint& divisor) -> int {
13826 FMT_ASSERT(this != &divisor, "");
13827 if (compare(*this, divisor) < 0) return 0;
13828 FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
13829@@ -3319,6 +2809,7 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
13830 }
13831 int even = static_cast<int>((value.f & 1) == 0);
13832 if (!upper) upper = &lower;
13833+ bool shortest = num_digits < 0;
13834 if ((flags & dragon::fixup) != 0) {
13835 if (add_compare(numerator, *upper, denominator) + even <= 0) {
13836 --exp10;
13837@@ -3331,7 +2822,7 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
13838 if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1);
13839 }
13840 // Invariant: value == (numerator / denominator) * pow(10, exp10).
13841- if (num_digits < 0) {
13842+ if (shortest) {
13843 // Generate the shortest representation.
13844 num_digits = 0;
13845 char* data = buf.data();
13846@@ -3361,9 +2852,12 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
13847 }
13848 // Generate the given number of digits.
13849 exp10 -= num_digits - 1;
13850- if (num_digits == 0) {
13851- denominator *= 10;
13852- auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
13853+ if (num_digits <= 0) {
13854+ auto digit = '0';
13855+ if (num_digits == 0) {
13856+ denominator *= 10;
13857+ digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
13858+ }
13859 buf.push_back(digit);
13860 return;
13861 }
13862@@ -3386,7 +2880,10 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
13863 }
13864 if (buf[0] == overflow) {
13865 buf[0] = '1';
13866- ++exp10;
13867+ if ((flags & dragon::fixed) != 0)
13868+ buf.push_back('0');
13869+ else
13870+ ++exp10;
13871 }
13872 return;
13873 }
13874@@ -3397,8 +2894,8 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
13875
13876 // Formats a floating-point number using the hexfloat format.
13877 template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
13878-FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
13879- float_specs specs, buffer<char>& buf) {
13880+FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs,
13881+ buffer<char>& buf) {
13882 // float is passed as double to reduce the number of instantiations and to
13883 // simplify implementation.
13884 static_assert(!std::is_same<Float, float>::value, "");
13885@@ -3408,26 +2905,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
13886 // Assume Float is in the format [sign][exponent][significand].
13887 using carrier_uint = typename info::carrier_uint;
13888
13889- constexpr auto num_float_significand_bits =
13890- detail::num_significand_bits<Float>();
13891+ const auto num_float_significand_bits = detail::num_significand_bits<Float>();
13892
13893 basic_fp<carrier_uint> f(value);
13894 f.e += num_float_significand_bits;
13895 if (!has_implicit_bit<Float>()) --f.e;
13896
13897- constexpr auto num_fraction_bits =
13898+ const auto num_fraction_bits =
13899 num_float_significand_bits + (has_implicit_bit<Float>() ? 1 : 0);
13900- constexpr auto num_xdigits = (num_fraction_bits + 3) / 4;
13901+ const auto num_xdigits = (num_fraction_bits + 3) / 4;
13902
13903- constexpr auto leading_shift = ((num_xdigits - 1) * 4);
13904+ const auto leading_shift = ((num_xdigits - 1) * 4);
13905 const auto leading_mask = carrier_uint(0xF) << leading_shift;
13906 const auto leading_xdigit =
13907 static_cast<uint32_t>((f.f & leading_mask) >> leading_shift);
13908 if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1);
13909
13910 int print_xdigits = num_xdigits - 1;
13911- if (precision >= 0 && print_xdigits > precision) {
13912- const int shift = ((print_xdigits - precision - 1) * 4);
13913+ if (specs.precision >= 0 && print_xdigits > specs.precision) {
13914+ const int shift = ((print_xdigits - specs.precision - 1) * 4);
13915 const auto mask = carrier_uint(0xF) << shift;
13916 const auto v = static_cast<uint32_t>((f.f & mask) >> shift);
13917
13918@@ -3446,25 +2942,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
13919 }
13920 }
13921
13922- print_xdigits = precision;
13923+ print_xdigits = specs.precision;
13924 }
13925
13926 char xdigits[num_bits<carrier_uint>() / 4];
13927 detail::fill_n(xdigits, sizeof(xdigits), '0');
13928- format_uint<4>(xdigits, f.f, num_xdigits, specs.upper);
13929+ format_base2e(4, xdigits, f.f, num_xdigits, specs.upper());
13930
13931 // Remove zero tail
13932 while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits;
13933
13934 buf.push_back('0');
13935- buf.push_back(specs.upper ? 'X' : 'x');
13936+ buf.push_back(specs.upper() ? 'X' : 'x');
13937 buf.push_back(xdigits[0]);
13938- if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision)
13939+ if (specs.alt() || print_xdigits > 0 || print_xdigits < specs.precision)
13940 buf.push_back('.');
13941 buf.append(xdigits + 1, xdigits + 1 + print_xdigits);
13942- for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0');
13943+ for (; print_xdigits < specs.precision; ++print_xdigits) buf.push_back('0');
13944
13945- buf.push_back(specs.upper ? 'P' : 'p');
13946+ buf.push_back(specs.upper() ? 'P' : 'p');
13947
13948 uint32_t abs_e;
13949 if (f.e < 0) {
13950@@ -3478,21 +2974,32 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
13951 }
13952
13953 template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
13954-FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
13955- float_specs specs, buffer<char>& buf) {
13956- format_hexfloat(static_cast<double>(value), precision, specs, buf);
13957+FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs,
13958+ buffer<char>& buf) {
13959+ format_hexfloat(static_cast<double>(value), specs, buf);
13960+}
13961+
13962+constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t {
13963+ // For checking rounding thresholds.
13964+ // The kth entry is chosen to be the smallest integer such that the
13965+ // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
13966+ // It is equal to ceil(2^31 + 2^32/10^(k + 1)).
13967+ // These are stored in a string literal because we cannot have static arrays
13968+ // in constexpr functions and non-static ones are poorly optimized.
13969+ return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7"
13970+ U"\x800001ae\x8000002b"[index];
13971 }
13972
13973 template <typename Float>
13974-FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
13975+FMT_CONSTEXPR20 auto format_float(Float value, int precision,
13976+ const format_specs& specs, bool binary32,
13977 buffer<char>& buf) -> int {
13978 // float is passed as double to reduce the number of instantiations.
13979 static_assert(!std::is_same<Float, float>::value, "");
13980- FMT_ASSERT(value >= 0, "value is negative");
13981 auto converted_value = convert_float(value);
13982
13983- const bool fixed = specs.format == float_format::fixed;
13984- if (value <= 0) { // <= instead of == to silence a warning.
13985+ const bool fixed = specs.type() == presentation_type::fixed;
13986+ if (value == 0) {
13987 if (precision <= 0 || !fixed) {
13988 buf.push_back('0');
13989 return 0;
13990@@ -3505,7 +3012,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
13991 int exp = 0;
13992 bool use_dragon = true;
13993 unsigned dragon_flags = 0;
13994- if (!is_fast_float<Float>()) {
13995+ if (!is_fast_float<Float>() || is_constant_evaluated()) {
13996 const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10)
13997 using info = dragonbox::float_info<decltype(converted_value)>;
13998 const auto f = basic_fp<typename info::carrier_uint>(converted_value);
13999@@ -3513,38 +3020,10 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14000 // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
14001 // This is based on log10(value) == log2(value) / log2(10) and approximation
14002 // of log2(value) by e + num_fraction_bits idea from double-conversion.
14003- exp = static_cast<int>(
14004- std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10));
14005+ auto e = (f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10;
14006+ exp = static_cast<int>(e);
14007+ if (e > exp) ++exp; // Compute ceil.
14008 dragon_flags = dragon::fixup;
14009- } else if (!is_constant_evaluated() && precision < 0) {
14010- // Use Dragonbox for the shortest format.
14011- if (specs.binary32) {
14012- auto dec = dragonbox::to_decimal(static_cast<float>(value));
14013- write<char>(buffer_appender<char>(buf), dec.significand);
14014- return dec.exponent;
14015- }
14016- auto dec = dragonbox::to_decimal(static_cast<double>(value));
14017- write<char>(buffer_appender<char>(buf), dec.significand);
14018- return dec.exponent;
14019- } else if (is_constant_evaluated()) {
14020- // Use Grisu + Dragon4 for the given precision:
14021- // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
14022- const int min_exp = -60; // alpha in Grisu.
14023- int cached_exp10 = 0; // K in Grisu.
14024- fp normalized = normalize(fp(converted_value));
14025- const auto cached_pow = get_cached_power(
14026- min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
14027- normalized = normalized * cached_pow;
14028- gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
14029- if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
14030- !is_constant_evaluated()) {
14031- exp += handler.exp10;
14032- buf.try_resize(to_unsigned(handler.size));
14033- use_dragon = false;
14034- } else {
14035- exp += handler.size - cached_exp10 - 1;
14036- precision = handler.precision;
14037- }
14038 } else {
14039 // Extract significand bits and exponent bits.
14040 using info = dragonbox::float_info<double>;
14041@@ -3563,7 +3042,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14042 significand <<= 1;
14043 } else {
14044 // Normalize subnormal inputs.
14045- FMT_ASSERT(significand != 0, "zeros should not appear hear");
14046+ FMT_ASSERT(significand != 0, "zeros should not appear here");
14047 int shift = countl_zero(significand);
14048 FMT_ASSERT(shift >= num_bits<uint64_t>() - num_significand_bits<double>(),
14049 "");
14050@@ -3600,9 +3079,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14051 }
14052
14053 // Compute the actual number of decimal digits to print.
14054- if (fixed) {
14055- adjust_precision(precision, exp + digits_in_the_first_segment);
14056- }
14057+ if (fixed) adjust_precision(precision, exp + digits_in_the_first_segment);
14058
14059 // Use Dragon4 only when there might be not enough digits in the first
14060 // segment.
14061@@ -3645,7 +3122,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14062 uint64_t prod;
14063 uint32_t digits;
14064 bool should_round_up;
14065- int number_of_digits_to_print = precision > 9 ? 9 : precision;
14066+ int number_of_digits_to_print = min_of(precision, 9);
14067
14068 // Print a 9-digits subsegment, either the first or the second.
14069 auto print_subsegment = [&](uint32_t subsegment, char* buffer) {
14070@@ -3673,7 +3150,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14071 // for details.
14072 prod = ((subsegment * static_cast<uint64_t>(450359963)) >> 20) + 1;
14073 digits = static_cast<uint32_t>(prod >> 32);
14074- copy2(buffer, digits2(digits));
14075+ write2digits(buffer, digits);
14076 number_of_digits_printed += 2;
14077 }
14078
14079@@ -3681,7 +3158,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14080 while (number_of_digits_printed < number_of_digits_to_print) {
14081 prod = static_cast<uint32_t>(prod) * static_cast<uint64_t>(100);
14082 digits = static_cast<uint32_t>(prod >> 32);
14083- copy2(buffer + number_of_digits_printed, digits2(digits));
14084+ write2digits(buffer + number_of_digits_printed, digits);
14085 number_of_digits_printed += 2;
14086 }
14087 };
14088@@ -3707,12 +3184,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14089 // fractional part is strictly larger than 1/2.
14090 if (precision < 9) {
14091 uint32_t fractional_part = static_cast<uint32_t>(prod);
14092- should_round_up = fractional_part >=
14093- data::fractional_part_rounding_thresholds
14094- [8 - number_of_digits_to_print] ||
14095- ((fractional_part >> 31) &
14096- ((digits & 1) | (second_third_subsegments != 0) |
14097- has_more_segments)) != 0;
14098+ should_round_up =
14099+ fractional_part >= fractional_part_rounding_thresholds(
14100+ 8 - number_of_digits_to_print) ||
14101+ ((fractional_part >> 31) &
14102+ ((digits & 1) | (second_third_subsegments != 0) |
14103+ has_more_segments)) != 0;
14104 }
14105 // Rounding at the subsegment boundary.
14106 // In this case, the fractional part is at least 1/2 if and only if
14107@@ -3747,12 +3224,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14108 // of 19 digits, so in this case the third segment should be
14109 // consisting of a genuine digit from the input.
14110 uint32_t fractional_part = static_cast<uint32_t>(prod);
14111- should_round_up = fractional_part >=
14112- data::fractional_part_rounding_thresholds
14113- [8 - number_of_digits_to_print] ||
14114- ((fractional_part >> 31) &
14115- ((digits & 1) | (third_subsegment != 0) |
14116- has_more_segments)) != 0;
14117+ should_round_up =
14118+ fractional_part >= fractional_part_rounding_thresholds(
14119+ 8 - number_of_digits_to_print) ||
14120+ ((fractional_part >> 31) &
14121+ ((digits & 1) | (third_subsegment != 0) |
14122+ has_more_segments)) != 0;
14123 }
14124 // Rounding at the subsegment boundary.
14125 else {
14126@@ -3790,9 +3267,8 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14127 }
14128 if (use_dragon) {
14129 auto f = basic_fp<uint128_t>();
14130- bool is_predecessor_closer = specs.binary32
14131- ? f.assign(static_cast<float>(value))
14132- : f.assign(converted_value);
14133+ bool is_predecessor_closer = binary32 ? f.assign(static_cast<float>(value))
14134+ : f.assign(converted_value);
14135 if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer;
14136 if (fixed) dragon_flags |= dragon::fixed;
14137 // Limit precision to the maximum possible number of significant digits in
14138@@ -3801,7 +3277,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14139 if (precision > max_double_digits) precision = max_double_digits;
14140 format_dragon(f, dragon_flags, precision, buf, exp);
14141 }
14142- if (!fixed && !specs.showpoint) {
14143+ if (!fixed && !specs.alt()) {
14144 // Remove trailing zeros.
14145 auto num_digits = buf.size();
14146 while (num_digits > 0 && buf[num_digits - 1] == '0') {
14147@@ -3812,97 +3288,106 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
14148 }
14149 return exp;
14150 }
14151+
14152+// Numbers with exponents greater or equal to the returned value will use
14153+// the exponential notation.
14154+template <typename T> constexpr auto exp_upper() -> int {
14155+ return std::numeric_limits<T>::digits10 != 0
14156+ ? min_of(16, std::numeric_limits<T>::digits10 + 1)
14157+ : 16;
14158+}
14159+
14160 template <typename Char, typename OutputIt, typename T>
14161-FMT_CONSTEXPR20 auto write_float(OutputIt out, T value,
14162- format_specs<Char> specs, locale_ref loc)
14163- -> OutputIt {
14164- float_specs fspecs = parse_float_type_spec(specs);
14165- fspecs.sign = specs.sign;
14166- if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit.
14167- fspecs.sign = sign::minus;
14168- value = -value;
14169- } else if (fspecs.sign == sign::minus) {
14170- fspecs.sign = sign::none;
14171- }
14172+FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
14173+ locale_ref loc) -> OutputIt {
14174+ // Use signbit because value < 0 is false for NaN.
14175+ sign s = detail::signbit(value) ? sign::minus : specs.sign();
14176
14177 if (!detail::isfinite(value))
14178- return write_nonfinite(out, detail::isnan(value), specs, fspecs);
14179+ return write_nonfinite<Char>(out, detail::isnan(value), specs, s);
14180
14181- if (specs.align == align::numeric && fspecs.sign) {
14182- auto it = reserve(out, 1);
14183- *it++ = detail::sign<Char>(fspecs.sign);
14184- out = base_iterator(out, it);
14185- fspecs.sign = sign::none;
14186+ if (specs.align() == align::numeric && s != sign::none) {
14187+ *out++ = detail::getsign<Char>(s);
14188+ s = sign::none;
14189 if (specs.width != 0) --specs.width;
14190 }
14191
14192+ constexpr int exp_upper = detail::exp_upper<T>();
14193+ int precision = specs.precision;
14194+ if (precision < 0) {
14195+ if (specs.type() != presentation_type::none) {
14196+ precision = 6;
14197+ } else if (is_fast_float<T>::value && !is_constant_evaluated()) {
14198+ // Use Dragonbox for the shortest format.
14199+ using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>;
14200+ auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
14201+ return write_float<Char>(out, dec, specs, s, exp_upper, loc);
14202+ }
14203+ }
14204+
14205 memory_buffer buffer;
14206- if (fspecs.format == float_format::hex) {
14207- if (fspecs.sign) buffer.push_back(detail::sign<char>(fspecs.sign));
14208- format_hexfloat(convert_float(value), specs.precision, fspecs, buffer);
14209- return write_bytes<align::right>(out, {buffer.data(), buffer.size()},
14210- specs);
14211- }
14212- int precision = specs.precision >= 0 || specs.type == presentation_type::none
14213- ? specs.precision
14214- : 6;
14215- if (fspecs.format == float_format::exp) {
14216+ if (specs.type() == presentation_type::hexfloat) {
14217+ if (s != sign::none) buffer.push_back(detail::getsign<char>(s));
14218+ format_hexfloat(convert_float(value), specs, buffer);
14219+ return write_bytes<Char, align::right>(out, {buffer.data(), buffer.size()},
14220+ specs);
14221+ }
14222+
14223+ if (specs.type() == presentation_type::exp) {
14224 if (precision == max_value<int>())
14225- throw_format_error("number is too big");
14226+ report_error("number is too big");
14227 else
14228 ++precision;
14229- } else if (fspecs.format != float_format::fixed && precision == 0) {
14230+ if (specs.precision != 0) specs.set_alt();
14231+ } else if (specs.type() == presentation_type::fixed) {
14232+ if (specs.precision != 0) specs.set_alt();
14233+ } else if (precision == 0) {
14234 precision = 1;
14235 }
14236- if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
14237- int exp = format_float(convert_float(value), precision, fspecs, buffer);
14238- fspecs.precision = precision;
14239+ int exp = format_float(convert_float(value), precision, specs,
14240+ std::is_same<T, float>(), buffer);
14241+
14242+ specs.precision = precision;
14243 auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
14244- return write_float(out, f, specs, fspecs, loc);
14245+ return write_float<Char>(out, f, specs, s, exp_upper, loc);
14246 }
14247
14248 template <typename Char, typename OutputIt, typename T,
14249 FMT_ENABLE_IF(is_floating_point<T>::value)>
14250-FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs<Char> specs,
14251+FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs,
14252 locale_ref loc = {}) -> OutputIt {
14253- if (const_check(!is_supported_floating_point(value))) return out;
14254- return specs.localized && write_loc(out, value, specs, loc)
14255+ return specs.localized() && write_loc(out, value, specs, loc)
14256 ? out
14257- : write_float(out, value, specs, loc);
14258+ : write_float<Char>(out, value, specs, loc);
14259 }
14260
14261 template <typename Char, typename OutputIt, typename T,
14262 FMT_ENABLE_IF(is_fast_float<T>::value)>
14263 FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
14264- if (is_constant_evaluated()) return write(out, value, format_specs<Char>());
14265- if (const_check(!is_supported_floating_point(value))) return out;
14266+ if (is_constant_evaluated()) return write<Char>(out, value, format_specs());
14267
14268- auto fspecs = float_specs();
14269- if (detail::signbit(value)) {
14270- fspecs.sign = sign::minus;
14271- value = -value;
14272- }
14273+ auto s = detail::signbit(value) ? sign::minus : sign::none;
14274
14275- constexpr auto specs = format_specs<Char>();
14276- using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
14277+ constexpr auto specs = format_specs();
14278+ using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>;
14279 using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint;
14280 floaty_uint mask = exponent_mask<floaty>();
14281 if ((bit_cast<floaty_uint>(value) & mask) == mask)
14282- return write_nonfinite(out, std::isnan(value), specs, fspecs);
14283+ return write_nonfinite<Char>(out, std::isnan(value), specs, s);
14284
14285 auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
14286- return write_float(out, dec, specs, fspecs, {});
14287+ return write_float<Char>(out, dec, specs, s, exp_upper<T>(), {});
14288 }
14289
14290 template <typename Char, typename OutputIt, typename T,
14291 FMT_ENABLE_IF(is_floating_point<T>::value &&
14292 !is_fast_float<T>::value)>
14293 inline auto write(OutputIt out, T value) -> OutputIt {
14294- return write(out, value, format_specs<Char>());
14295+ return write<Char>(out, value, format_specs());
14296 }
14297
14298 template <typename Char, typename OutputIt>
14299-auto write(OutputIt out, monostate, format_specs<Char> = {}, locale_ref = {})
14300+auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {})
14301 -> OutputIt {
14302 FMT_ASSERT(false, "");
14303 return out;
14304@@ -3911,13 +3396,11 @@ auto write(OutputIt out, monostate, format_specs<Char> = {}, locale_ref = {})
14305 template <typename Char, typename OutputIt>
14306 FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value)
14307 -> OutputIt {
14308- auto it = reserve(out, value.size());
14309- it = copy_str_noinline<Char>(value.begin(), value.end(), it);
14310- return base_iterator(out, it);
14311+ return copy_noinline<Char>(value.begin(), value.end(), out);
14312 }
14313
14314 template <typename Char, typename OutputIt, typename T,
14315- FMT_ENABLE_IF(is_string<T>::value)>
14316+ FMT_ENABLE_IF(has_to_string_view<T>::value)>
14317 constexpr auto write(OutputIt out, const T& value) -> OutputIt {
14318 return write<Char>(out, to_string_view(value));
14319 }
14320@@ -3925,10 +3408,8 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt {
14321 // FMT_ENABLE_IF() condition separated to workaround an MSVC bug.
14322 template <
14323 typename Char, typename OutputIt, typename T,
14324- bool check =
14325- std::is_enum<T>::value && !std::is_same<T, Char>::value &&
14326- mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value !=
14327- type::custom_type,
14328+ bool check = std::is_enum<T>::value && !std::is_same<T, Char>::value &&
14329+ mapped_type_constant<T, Char>::value != type::custom_type,
14330 FMT_ENABLE_IF(check)>
14331 FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
14332 return write<Char>(out, static_cast<underlying_t<T>>(value));
14333@@ -3936,13 +3417,12 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
14334
14335 template <typename Char, typename OutputIt, typename T,
14336 FMT_ENABLE_IF(std::is_same<T, bool>::value)>
14337-FMT_CONSTEXPR auto write(OutputIt out, T value,
14338- const format_specs<Char>& specs = {}, locale_ref = {})
14339- -> OutputIt {
14340- return specs.type != presentation_type::none &&
14341- specs.type != presentation_type::string
14342- ? write(out, value ? 1 : 0, specs, {})
14343- : write_bytes(out, value ? "true" : "false", specs);
14344+FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {},
14345+ locale_ref = {}) -> OutputIt {
14346+ return specs.type() != presentation_type::none &&
14347+ specs.type() != presentation_type::string
14348+ ? write<Char>(out, value ? 1 : 0, specs, {})
14349+ : write_bytes<Char>(out, value ? "true" : "false", specs);
14350 }
14351
14352 template <typename Char, typename OutputIt>
14353@@ -3953,202 +3433,150 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt {
14354 }
14355
14356 template <typename Char, typename OutputIt>
14357-FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value)
14358- -> OutputIt {
14359+FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt {
14360 if (value) return write(out, basic_string_view<Char>(value));
14361- throw_format_error("string pointer is null");
14362+ report_error("string pointer is null");
14363 return out;
14364 }
14365
14366 template <typename Char, typename OutputIt, typename T,
14367 FMT_ENABLE_IF(std::is_same<T, void>::value)>
14368-auto write(OutputIt out, const T* value, const format_specs<Char>& specs = {},
14369+auto write(OutputIt out, const T* value, const format_specs& specs = {},
14370 locale_ref = {}) -> OutputIt {
14371 return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
14372 }
14373
14374-// A write overload that handles implicit conversions.
14375 template <typename Char, typename OutputIt, typename T,
14376- typename Context = basic_format_context<OutputIt, Char>>
14377-FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
14378- std::is_class<T>::value && !is_string<T>::value &&
14379- !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
14380- !std::is_same<T, remove_cvref_t<decltype(arg_mapper<Context>().map(
14381- value))>>::value,
14382- OutputIt> {
14383- return write<Char>(out, arg_mapper<Context>().map(value));
14384+ FMT_ENABLE_IF(mapped_type_constant<T, Char>::value ==
14385+ type::custom_type &&
14386+ !std::is_fundamental<T>::value)>
14387+FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> OutputIt {
14388+ auto f = formatter<T, Char>();
14389+ auto parse_ctx = parse_context<Char>({});
14390+ f.parse(parse_ctx);
14391+ auto ctx = basic_format_context<OutputIt, Char>(out, {}, {});
14392+ return f.format(value, ctx);
14393 }
14394
14395-template <typename Char, typename OutputIt, typename T,
14396- typename Context = basic_format_context<OutputIt, Char>>
14397-FMT_CONSTEXPR auto write(OutputIt out, const T& value)
14398- -> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
14399- OutputIt> {
14400- auto ctx = Context(out, {}, {});
14401- return typename Context::template formatter_type<T>().format(value, ctx);
14402-}
14403+template <typename T>
14404+using is_builtin =
14405+ bool_constant<std::is_same<T, int>::value || FMT_BUILTIN_TYPES>;
14406
14407 // An argument visitor that formats the argument and writes it via the output
14408 // iterator. It's a class and not a generic lambda for compatibility with C++11.
14409 template <typename Char> struct default_arg_formatter {
14410- using iterator = buffer_appender<Char>;
14411- using context = buffer_context<Char>;
14412+ using context = buffered_context<Char>;
14413
14414- iterator out;
14415- basic_format_args<context> args;
14416- locale_ref loc;
14417+ basic_appender<Char> out;
14418
14419- template <typename T> auto operator()(T value) -> iterator {
14420- return write<Char>(out, value);
14421- }
14422- auto operator()(typename basic_format_arg<context>::handle h) -> iterator {
14423- basic_format_parse_context<Char> parse_ctx({});
14424- context format_ctx(out, args, loc);
14425- h.format(parse_ctx, format_ctx);
14426- return format_ctx.out();
14427- }
14428-};
14429-
14430-template <typename Char> struct arg_formatter {
14431- using iterator = buffer_appender<Char>;
14432- using context = buffer_context<Char>;
14433+ void operator()(monostate) { report_error("argument not found"); }
14434
14435- iterator out;
14436- const format_specs<Char>& specs;
14437- locale_ref locale;
14438-
14439- template <typename T>
14440- FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
14441- return detail::write(out, value, specs, locale);
14442- }
14443- auto operator()(typename basic_format_arg<context>::handle) -> iterator {
14444- // User-defined types are handled separately because they require access
14445- // to the parse context.
14446- return out;
14447+ template <typename T, FMT_ENABLE_IF(is_builtin<T>::value)>
14448+ void operator()(T value) {
14449+ write<Char>(out, value);
14450 }
14451-};
14452
14453-template <typename Char> struct custom_formatter {
14454- basic_format_parse_context<Char>& parse_ctx;
14455- buffer_context<Char>& ctx;
14456+ template <typename T, FMT_ENABLE_IF(!is_builtin<T>::value)>
14457+ void operator()(T) {
14458+ FMT_ASSERT(false, "");
14459+ }
14460
14461- void operator()(
14462- typename basic_format_arg<buffer_context<Char>>::handle h) const {
14463- h.format(parse_ctx, ctx);
14464+ void operator()(typename basic_format_arg<context>::handle h) {
14465+ // Use a null locale since the default format must be unlocalized.
14466+ auto parse_ctx = parse_context<Char>({});
14467+ auto format_ctx = context(out, {}, {});
14468+ h.format(parse_ctx, format_ctx);
14469 }
14470- template <typename T> void operator()(T) const {}
14471 };
14472
14473-template <typename ErrorHandler> class width_checker {
14474- public:
14475- explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
14476+template <typename Char> struct arg_formatter {
14477+ basic_appender<Char> out;
14478+ const format_specs& specs;
14479+ FMT_NO_UNIQUE_ADDRESS locale_ref locale;
14480
14481- template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
14482- FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
14483- if (is_negative(value)) handler_.on_error("negative width");
14484- return static_cast<unsigned long long>(value);
14485+ template <typename T, FMT_ENABLE_IF(is_builtin<T>::value)>
14486+ FMT_CONSTEXPR FMT_INLINE void operator()(T value) {
14487+ detail::write<Char>(out, value, specs, locale);
14488 }
14489
14490- template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
14491- FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
14492- handler_.on_error("width is not integer");
14493- return 0;
14494+ template <typename T, FMT_ENABLE_IF(!is_builtin<T>::value)>
14495+ void operator()(T) {
14496+ FMT_ASSERT(false, "");
14497 }
14498
14499- private:
14500- ErrorHandler& handler_;
14501+ void operator()(typename basic_format_arg<buffered_context<Char>>::handle) {
14502+ // User-defined types are handled separately because they require access
14503+ // to the parse context.
14504+ }
14505 };
14506
14507-template <typename ErrorHandler> class precision_checker {
14508- public:
14509- explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
14510-
14511+struct dynamic_spec_getter {
14512 template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
14513 FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
14514- if (is_negative(value)) handler_.on_error("negative precision");
14515- return static_cast<unsigned long long>(value);
14516+ return is_negative(value) ? ~0ull : static_cast<unsigned long long>(value);
14517 }
14518
14519 template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
14520 FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
14521- handler_.on_error("precision is not integer");
14522+ report_error("width/precision is not integer");
14523 return 0;
14524 }
14525-
14526- private:
14527- ErrorHandler& handler_;
14528 };
14529
14530-template <template <typename> class Handler, typename FormatArg,
14531- typename ErrorHandler>
14532-FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int {
14533- unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
14534- if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
14535- return static_cast<int>(value);
14536-}
14537-
14538 template <typename Context, typename ID>
14539-FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) {
14540+FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg<Context> {
14541 auto arg = ctx.arg(id);
14542- if (!arg) ctx.on_error("argument not found");
14543+ if (!arg) report_error("argument not found");
14544 return arg;
14545 }
14546
14547-template <template <typename> class Handler, typename Context>
14548-FMT_CONSTEXPR void handle_dynamic_spec(int& value,
14549- arg_ref<typename Context::char_type> ref,
14550- Context& ctx) {
14551- switch (ref.kind) {
14552- case arg_id_kind::none:
14553- break;
14554- case arg_id_kind::index:
14555- value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index),
14556- ctx.error_handler());
14557- break;
14558- case arg_id_kind::name:
14559- value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name),
14560- ctx.error_handler());
14561- break;
14562- }
14563+template <typename Context>
14564+FMT_CONSTEXPR int get_dynamic_spec(
14565+ arg_id_kind kind, const arg_ref<typename Context::char_type>& ref,
14566+ Context& ctx) {
14567+ FMT_ASSERT(kind != arg_id_kind::none, "");
14568+ auto arg =
14569+ kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name);
14570+ if (!arg) report_error("argument not found");
14571+ unsigned long long value = arg.visit(dynamic_spec_getter());
14572+ if (value > to_unsigned(max_value<int>()))
14573+ report_error("width/precision is out of range");
14574+ return static_cast<int>(value);
14575 }
14576
14577-#if FMT_USE_USER_DEFINED_LITERALS
14578-template <typename Char> struct udl_formatter {
14579- basic_string_view<Char> str;
14580-
14581- template <typename... T>
14582- auto operator()(T&&... args) const -> std::basic_string<Char> {
14583- return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...));
14584- }
14585-};
14586+template <typename Context>
14587+FMT_CONSTEXPR void handle_dynamic_spec(
14588+ arg_id_kind kind, int& value,
14589+ const arg_ref<typename Context::char_type>& ref, Context& ctx) {
14590+ if (kind != arg_id_kind::none) value = get_dynamic_spec(kind, ref, ctx);
14591+}
14592
14593-# if FMT_USE_NONTYPE_TEMPLATE_ARGS
14594+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
14595 template <typename T, typename Char, size_t N,
14596- fmt::detail_exported::fixed_string<Char, N> Str>
14597-struct statically_named_arg : view {
14598+ fmt::detail::fixed_string<Char, N> Str>
14599+struct static_named_arg : view {
14600 static constexpr auto name = Str.data;
14601
14602 const T& value;
14603- statically_named_arg(const T& v) : value(v) {}
14604+ static_named_arg(const T& v) : value(v) {}
14605 };
14606
14607 template <typename T, typename Char, size_t N,
14608- fmt::detail_exported::fixed_string<Char, N> Str>
14609-struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {};
14610+ fmt::detail::fixed_string<Char, N> Str>
14611+struct is_named_arg<static_named_arg<T, Char, N, Str>> : std::true_type {};
14612
14613 template <typename T, typename Char, size_t N,
14614- fmt::detail_exported::fixed_string<Char, N> Str>
14615-struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>>
14616- : std::true_type {};
14617+ fmt::detail::fixed_string<Char, N> Str>
14618+struct is_static_named_arg<static_named_arg<T, Char, N, Str>> : std::true_type {
14619+};
14620
14621-template <typename Char, size_t N,
14622- fmt::detail_exported::fixed_string<Char, N> Str>
14623+template <typename Char, size_t N, fmt::detail::fixed_string<Char, N> Str>
14624 struct udl_arg {
14625 template <typename T> auto operator=(T&& value) const {
14626- return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
14627+ return static_named_arg<T, Char, N, Str>(std::forward<T>(value));
14628 }
14629 };
14630-# else
14631+#else
14632 template <typename Char> struct udl_arg {
14633 const Char* str;
14634
14635@@ -4156,195 +3584,258 @@ template <typename Char> struct udl_arg {
14636 return {str, std::forward<T>(value)};
14637 }
14638 };
14639-# endif
14640-#endif // FMT_USE_USER_DEFINED_LITERALS
14641+#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS
14642
14643-template <typename Locale, typename Char>
14644-auto vformat(const Locale& loc, basic_string_view<Char> fmt,
14645- basic_format_args<buffer_context<type_identity_t<Char>>> args)
14646- -> std::basic_string<Char> {
14647- auto buf = basic_memory_buffer<Char>();
14648- detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
14649- return {buf.data(), buf.size()};
14650-}
14651+template <typename Char> struct format_handler {
14652+ parse_context<Char> parse_ctx;
14653+ buffered_context<Char> ctx;
14654+
14655+ void on_text(const Char* begin, const Char* end) {
14656+ copy_noinline<Char>(begin, end, ctx.out());
14657+ }
14658+
14659+ FMT_CONSTEXPR auto on_arg_id() -> int { return parse_ctx.next_arg_id(); }
14660+ FMT_CONSTEXPR auto on_arg_id(int id) -> int {
14661+ parse_ctx.check_arg_id(id);
14662+ return id;
14663+ }
14664+ FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
14665+ parse_ctx.check_arg_id(id);
14666+ int arg_id = ctx.arg_id(id);
14667+ if (arg_id < 0) report_error("argument not found");
14668+ return arg_id;
14669+ }
14670+
14671+ FMT_INLINE void on_replacement_field(int id, const Char*) {
14672+ ctx.arg(id).visit(default_arg_formatter<Char>{ctx.out()});
14673+ }
14674+
14675+ auto on_format_specs(int id, const Char* begin, const Char* end)
14676+ -> const Char* {
14677+ auto arg = get_arg(ctx, id);
14678+ // Not using a visitor for custom types gives better codegen.
14679+ if (arg.format_custom(begin, parse_ctx, ctx)) return parse_ctx.begin();
14680+
14681+ auto specs = dynamic_format_specs<Char>();
14682+ begin = parse_format_specs(begin, end, specs, parse_ctx, arg.type());
14683+ if (specs.dynamic()) {
14684+ handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref,
14685+ ctx);
14686+ handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
14687+ specs.precision_ref, ctx);
14688+ }
14689+
14690+ arg.visit(arg_formatter<Char>{ctx.out(), specs, ctx.locale()});
14691+ return begin;
14692+ }
14693+
14694+ FMT_NORETURN void on_error(const char* message) { report_error(message); }
14695+};
14696
14697 using format_func = void (*)(detail::buffer<char>&, int, const char*);
14698+FMT_API void do_report_error(format_func func, int error_code,
14699+ const char* message) noexcept;
14700
14701 FMT_API void format_error_code(buffer<char>& out, int error_code,
14702 string_view message) noexcept;
14703
14704-FMT_API void report_error(format_func func, int error_code,
14705- const char* message) noexcept;
14706-FMT_END_DETAIL_NAMESPACE
14707+template <typename T, typename Char, type TYPE>
14708+template <typename FormatContext>
14709+FMT_CONSTEXPR auto native_formatter<T, Char, TYPE>::format(
14710+ const T& val, FormatContext& ctx) const -> decltype(ctx.out()) {
14711+ if (!specs_.dynamic())
14712+ return write<Char>(ctx.out(), val, specs_, ctx.locale());
14713+ auto specs = format_specs(specs_);
14714+ handle_dynamic_spec(specs.dynamic_width(), specs.width, specs_.width_ref,
14715+ ctx);
14716+ handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
14717+ specs_.precision_ref, ctx);
14718+ return write<Char>(ctx.out(), val, specs, ctx.locale());
14719+}
14720+
14721+// DEPRECATED! https://github.com/fmtlib/fmt/issues/4292.
14722+template <typename T, typename Enable = void>
14723+struct is_locale : std::false_type {};
14724+template <typename T>
14725+struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
14726
14727-FMT_API auto vsystem_error(int error_code, string_view format_str,
14728- format_args args) -> std::system_error;
14729+// DEPRECATED!
14730+template <typename Char = char> struct vformat_args {
14731+ using type = basic_format_args<buffered_context<Char>>;
14732+};
14733+template <> struct vformat_args<char> {
14734+ using type = format_args;
14735+};
14736
14737-/**
14738- \rst
14739- Constructs :class:`std::system_error` with a message formatted with
14740- ``fmt::format(fmt, args...)``.
14741- *error_code* is a system error code as given by ``errno``.
14742-
14743- **Example**::
14744-
14745- // This throws std::system_error with the description
14746- // cannot open file 'madeup': No such file or directory
14747- // or similar (system message may vary).
14748- const char* filename = "madeup";
14749- std::FILE* file = std::fopen(filename, "r");
14750- if (!file)
14751- throw fmt::system_error(errno, "cannot open file '{}'", filename);
14752- \endrst
14753- */
14754-template <typename... T>
14755-auto system_error(int error_code, format_string<T...> fmt, T&&... args)
14756- -> std::system_error {
14757- return vsystem_error(error_code, fmt, fmt::make_format_args(args...));
14758+template <typename Char>
14759+void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
14760+ typename vformat_args<Char>::type args, locale_ref loc = {}) {
14761+ auto out = basic_appender<Char>(buf);
14762+ parse_format_string(
14763+ fmt, format_handler<Char>{parse_context<Char>(fmt), {out, args, loc}});
14764 }
14765+} // namespace detail
14766
14767-/**
14768- \rst
14769- Formats an error message for an error returned by an operating system or a
14770- language runtime, for example a file opening error, and writes it to *out*.
14771- The format is the same as the one used by ``std::system_error(ec, message)``
14772- where ``ec`` is ``std::error_code(error_code, std::generic_category()})``.
14773- It is implementation-defined but normally looks like:
14774-
14775- .. parsed-literal::
14776- *<message>*: *<system-message>*
14777-
14778- where *<message>* is the passed message and *<system-message>* is the system
14779- message corresponding to the error code.
14780- *error_code* is a system error code as given by ``errno``.
14781- \endrst
14782- */
14783-FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
14784- const char* message) noexcept;
14785-
14786-// Reports a system error without throwing an exception.
14787-// Can be used to report errors from destructors.
14788-FMT_API void report_system_error(int error_code, const char* message) noexcept;
14789+FMT_BEGIN_EXPORT
14790
14791-/** Fast integer formatter. */
14792-class format_int {
14793+// A generic formatting context with custom output iterator and character
14794+// (code unit) support. Char is the format string code unit type which can be
14795+// different from OutputIt::value_type.
14796+template <typename OutputIt, typename Char> class generic_context {
14797 private:
14798- // Buffer should be large enough to hold all digits (digits10 + 1),
14799- // a sign and a null character.
14800- enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
14801- mutable char buffer_[buffer_size];
14802- char* str_;
14803-
14804- template <typename UInt> auto format_unsigned(UInt value) -> char* {
14805- auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
14806- return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
14807- }
14808-
14809- template <typename Int> auto format_signed(Int value) -> char* {
14810- auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
14811- bool negative = value < 0;
14812- if (negative) abs_value = 0 - abs_value;
14813- auto begin = format_unsigned(abs_value);
14814- if (negative) *--begin = '-';
14815- return begin;
14816- }
14817+ OutputIt out_;
14818+ basic_format_args<generic_context> args_;
14819+ detail::locale_ref loc_;
14820
14821 public:
14822- explicit format_int(int value) : str_(format_signed(value)) {}
14823- explicit format_int(long value) : str_(format_signed(value)) {}
14824- explicit format_int(long long value) : str_(format_signed(value)) {}
14825- explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
14826- explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
14827- explicit format_int(unsigned long long value)
14828- : str_(format_unsigned(value)) {}
14829+ using char_type = Char;
14830+ using iterator = OutputIt;
14831+ using parse_context_type FMT_DEPRECATED = parse_context<Char>;
14832+ template <typename T>
14833+ using formatter_type FMT_DEPRECATED = formatter<T, Char>;
14834+ enum { builtin_types = FMT_BUILTIN_TYPES };
14835
14836- /** Returns the number of characters written to the output buffer. */
14837- auto size() const -> size_t {
14838- return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
14839+ constexpr generic_context(OutputIt out,
14840+ basic_format_args<generic_context> args,
14841+ detail::locale_ref loc = {})
14842+ : out_(out), args_(args), loc_(loc) {}
14843+ generic_context(generic_context&&) = default;
14844+ generic_context(const generic_context&) = delete;
14845+ void operator=(const generic_context&) = delete;
14846+
14847+ constexpr auto arg(int id) const -> basic_format_arg<generic_context> {
14848+ return args_.get(id);
14849+ }
14850+ auto arg(basic_string_view<Char> name) const
14851+ -> basic_format_arg<generic_context> {
14852+ return args_.get(name);
14853+ }
14854+ constexpr auto arg_id(basic_string_view<Char> name) const -> int {
14855+ return args_.get_id(name);
14856 }
14857
14858- /**
14859- Returns a pointer to the output buffer content. No terminating null
14860- character is appended.
14861- */
14862- auto data() const -> const char* { return str_; }
14863+ constexpr auto out() const -> iterator { return out_; }
14864
14865- /**
14866- Returns a pointer to the output buffer content with terminating null
14867- character appended.
14868- */
14869- auto c_str() const -> const char* {
14870- buffer_[buffer_size - 1] = '\0';
14871- return str_;
14872+ void advance_to(iterator it) {
14873+ if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
14874 }
14875
14876- /**
14877- \rst
14878- Returns the content of the output buffer as an ``std::string``.
14879- \endrst
14880- */
14881- auto str() const -> std::string { return std::string(str_, size()); }
14882+ constexpr auto locale() const -> detail::locale_ref { return loc_; }
14883 };
14884
14885-template <typename T, typename Char>
14886-struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
14887- : private formatter<detail::format_as_t<T>> {
14888- using base = formatter<detail::format_as_t<T>>;
14889- using base::parse;
14890+class loc_value {
14891+ private:
14892+ basic_format_arg<context> value_;
14893
14894- template <typename FormatContext>
14895- auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
14896- return base::format(format_as(value), ctx);
14897+ public:
14898+ template <typename T, FMT_ENABLE_IF(!detail::is_float128<T>::value)>
14899+ loc_value(T value) : value_(value) {}
14900+
14901+ template <typename T, FMT_ENABLE_IF(detail::is_float128<T>::value)>
14902+ loc_value(T) {}
14903+
14904+ template <typename Visitor> auto visit(Visitor&& vis) -> decltype(vis(0)) {
14905+ return value_.visit(vis);
14906 }
14907 };
14908
14909-template <typename Char>
14910-struct formatter<void*, Char> : formatter<const void*, Char> {
14911- template <typename FormatContext>
14912- auto format(void* val, FormatContext& ctx) const -> decltype(ctx.out()) {
14913- return formatter<const void*, Char>::format(val, ctx);
14914+// A locale facet that formats values in UTF-8.
14915+// It is parameterized on the locale to avoid the heavy <locale> include.
14916+template <typename Locale> class format_facet : public Locale::facet {
14917+ private:
14918+ std::string separator_;
14919+ std::string grouping_;
14920+ std::string decimal_point_;
14921+
14922+ protected:
14923+ virtual auto do_put(appender out, loc_value val,
14924+ const format_specs& specs) const -> bool;
14925+
14926+ public:
14927+ static FMT_API typename Locale::id id;
14928+
14929+ explicit format_facet(Locale& loc);
14930+ explicit format_facet(string_view sep = "", std::string grouping = "\3",
14931+ std::string decimal_point = ".")
14932+ : separator_(sep.data(), sep.size()),
14933+ grouping_(grouping),
14934+ decimal_point_(decimal_point) {}
14935+
14936+ auto put(appender out, loc_value val, const format_specs& specs) const
14937+ -> bool {
14938+ return do_put(out, val, specs);
14939 }
14940 };
14941
14942+#define FMT_FORMAT_AS(Type, Base) \
14943+ template <typename Char> \
14944+ struct formatter<Type, Char> : formatter<Base, Char> { \
14945+ template <typename FormatContext> \
14946+ FMT_CONSTEXPR auto format(Type value, FormatContext& ctx) const \
14947+ -> decltype(ctx.out()) { \
14948+ return formatter<Base, Char>::format(value, ctx); \
14949+ } \
14950+ }
14951+
14952+FMT_FORMAT_AS(signed char, int);
14953+FMT_FORMAT_AS(unsigned char, unsigned);
14954+FMT_FORMAT_AS(short, int);
14955+FMT_FORMAT_AS(unsigned short, unsigned);
14956+FMT_FORMAT_AS(long, detail::long_type);
14957+FMT_FORMAT_AS(unsigned long, detail::ulong_type);
14958+FMT_FORMAT_AS(Char*, const Char*);
14959+FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
14960+FMT_FORMAT_AS(std::nullptr_t, const void*);
14961+FMT_FORMAT_AS(void*, const void*);
14962+
14963 template <typename Char, size_t N>
14964-struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
14965+struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {};
14966+
14967+template <typename Char, typename Traits, typename Allocator>
14968+class formatter<std::basic_string<Char, Traits, Allocator>, Char>
14969+ : public formatter<basic_string_view<Char>, Char> {};
14970+
14971+template <int N, typename Char>
14972+struct formatter<detail::bitint<N>, Char> : formatter<long long, Char> {};
14973+template <int N, typename Char>
14974+struct formatter<detail::ubitint<N>, Char>
14975+ : formatter<unsigned long long, Char> {};
14976+
14977+template <typename Char>
14978+struct formatter<detail::float128, Char>
14979+ : detail::native_formatter<detail::float128, Char,
14980+ detail::type::float_type> {};
14981+
14982+template <typename T, typename Char>
14983+struct formatter<T, Char, void_t<detail::format_as_result<T>>>
14984+ : formatter<detail::format_as_result<T>, Char> {
14985 template <typename FormatContext>
14986- FMT_CONSTEXPR auto format(const Char* val, FormatContext& ctx) const
14987+ FMT_CONSTEXPR auto format(const T& value, FormatContext& ctx) const
14988 -> decltype(ctx.out()) {
14989- return formatter<basic_string_view<Char>, Char>::format(val, ctx);
14990+ auto&& val = format_as(value); // Make an lvalue reference for format.
14991+ return formatter<detail::format_as_result<T>, Char>::format(val, ctx);
14992 }
14993 };
14994
14995 /**
14996- \rst
14997- Converts ``p`` to ``const void*`` for pointer formatting.
14998-
14999- **Example**::
15000-
15001- auto s = fmt::format("{}", fmt::ptr(p));
15002- \endrst
15003+ * Converts `p` to `const void*` for pointer formatting.
15004+ *
15005+ * **Example**:
15006+ *
15007+ * auto s = fmt::format("{}", fmt::ptr(p));
15008 */
15009 template <typename T> auto ptr(T p) -> const void* {
15010 static_assert(std::is_pointer<T>::value, "");
15011 return detail::bit_cast<const void*>(p);
15012 }
15013-template <typename T, typename Deleter>
15014-auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
15015- return p.get();
15016-}
15017-template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
15018- return p.get();
15019-}
15020
15021 /**
15022- \rst
15023- Converts ``e`` to the underlying type.
15024-
15025- **Example**::
15026-
15027- enum class color { red, green, blue };
15028- auto s = fmt::format("{}", fmt::underlying(color::red));
15029- \endrst
15030+ * Converts `e` to the underlying type.
15031+ *
15032+ * **Example**:
15033+ *
15034+ * enum class color { red, green, blue };
15035+ * auto s = fmt::format("{}", fmt::underlying(color::red)); // s == "0"
15036 */
15037 template <typename Enum>
15038 constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum> {
15039@@ -4358,13 +3849,22 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> {
15040 }
15041 } // namespace enums
15042
15043-class bytes {
15044- private:
15045- string_view data_;
15046- friend struct formatter<bytes>;
15047+#ifdef __cpp_lib_byte
15048+template <> struct formatter<std::byte> : formatter<unsigned> {
15049+ static auto format_as(std::byte b) -> unsigned char {
15050+ return static_cast<unsigned char>(b);
15051+ }
15052+ template <typename Context>
15053+ auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) {
15054+ return formatter<unsigned>::format(format_as(b), ctx);
15055+ }
15056+};
15057+#endif
15058
15059- public:
15060- explicit bytes(string_view data) : data_(data) {}
15061+struct bytes {
15062+ string_view data;
15063+
15064+ inline explicit bytes(string_view s) : data(s) {}
15065 };
15066
15067 template <> struct formatter<bytes> {
15068@@ -4372,35 +3872,35 @@ template <> struct formatter<bytes> {
15069 detail::dynamic_format_specs<> specs_;
15070
15071 public:
15072- template <typename ParseContext>
15073- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
15074+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
15075 return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
15076 detail::type::string_type);
15077 }
15078
15079 template <typename FormatContext>
15080- auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
15081- detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
15082- specs_.width_ref, ctx);
15083- detail::handle_dynamic_spec<detail::precision_checker>(
15084- specs_.precision, specs_.precision_ref, ctx);
15085- return detail::write_bytes(ctx.out(), b.data_, specs_);
15086+ auto format(bytes b, FormatContext& ctx) const -> decltype(ctx.out()) {
15087+ auto specs = specs_;
15088+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
15089+ specs.width_ref, ctx);
15090+ detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
15091+ specs.precision_ref, ctx);
15092+ return detail::write_bytes<char>(ctx.out(), b.data, specs);
15093 }
15094 };
15095
15096 // group_digits_view is not derived from view because it copies the argument.
15097-template <typename T> struct group_digits_view { T value; };
15098+template <typename T> struct group_digits_view {
15099+ T value;
15100+};
15101
15102 /**
15103- \rst
15104- Returns a view that formats an integer value using ',' as a locale-independent
15105- thousands separator.
15106-
15107- **Example**::
15108-
15109- fmt::print("{}", fmt::group_digits(12345));
15110- // Output: "12,345"
15111- \endrst
15112+ * Returns a view that formats an integer value using ',' as a
15113+ * locale-independent thousands separator.
15114+ *
15115+ * **Example**:
15116+ *
15117+ * fmt::print("{}", fmt::group_digits(12345));
15118+ * // Output: "12,345"
15119 */
15120 template <typename T> auto group_digits(T value) -> group_digits_view<T> {
15121 return {value};
15122@@ -4411,269 +3911,255 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
15123 detail::dynamic_format_specs<> specs_;
15124
15125 public:
15126- template <typename ParseContext>
15127- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
15128+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
15129 return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
15130 detail::type::int_type);
15131 }
15132
15133 template <typename FormatContext>
15134- auto format(group_digits_view<T> t, FormatContext& ctx)
15135+ auto format(group_digits_view<T> view, FormatContext& ctx) const
15136 -> decltype(ctx.out()) {
15137- detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
15138- specs_.width_ref, ctx);
15139- detail::handle_dynamic_spec<detail::precision_checker>(
15140- specs_.precision, specs_.precision_ref, ctx);
15141+ auto specs = specs_;
15142+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
15143+ specs.width_ref, ctx);
15144+ detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
15145+ specs.precision_ref, ctx);
15146+ auto arg = detail::make_write_int_arg(view.value, specs.sign());
15147 return detail::write_int(
15148- ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
15149- detail::digit_grouping<char>("\3", ","));
15150+ ctx.out(), static_cast<detail::uint64_or_128_t<T>>(arg.abs_value),
15151+ arg.prefix, specs, detail::digit_grouping<char>("\3", ","));
15152 }
15153 };
15154
15155-// DEPRECATED! join_view will be moved to ranges.h.
15156-template <typename It, typename Sentinel, typename Char = char>
15157-struct join_view : detail::view {
15158- It begin;
15159- Sentinel end;
15160- basic_string_view<Char> sep;
15161+template <typename T, typename Char> struct nested_view {
15162+ const formatter<T, Char>* fmt;
15163+ const T* value;
15164+};
15165
15166- join_view(It b, Sentinel e, basic_string_view<Char> s)
15167- : begin(b), end(e), sep(s) {}
15168+template <typename T, typename Char>
15169+struct formatter<nested_view<T, Char>, Char> {
15170+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
15171+ return ctx.begin();
15172+ }
15173+ template <typename FormatContext>
15174+ auto format(nested_view<T, Char> view, FormatContext& ctx) const
15175+ -> decltype(ctx.out()) {
15176+ return view.fmt->format(*view.value, ctx);
15177+ }
15178 };
15179
15180-template <typename It, typename Sentinel, typename Char>
15181-struct formatter<join_view<It, Sentinel, Char>, Char> {
15182+template <typename T, typename Char = char> struct nested_formatter {
15183 private:
15184- using value_type =
15185-#ifdef __cpp_lib_ranges
15186- std::iter_value_t<It>;
15187-#else
15188- typename std::iterator_traits<It>::value_type;
15189-#endif
15190- formatter<remove_cvref_t<value_type>, Char> value_formatter_;
15191+ basic_specs specs_;
15192+ int width_;
15193+ formatter<T, Char> formatter_;
15194
15195 public:
15196- template <typename ParseContext>
15197- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
15198- return value_formatter_.parse(ctx);
15199+ constexpr nested_formatter() : width_(0) {}
15200+
15201+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
15202+ auto it = ctx.begin(), end = ctx.end();
15203+ if (it == end) return it;
15204+ auto specs = format_specs();
15205+ it = detail::parse_align(it, end, specs);
15206+ specs_ = specs;
15207+ Char c = *it;
15208+ auto width_ref = detail::arg_ref<Char>();
15209+ if ((c >= '0' && c <= '9') || c == '{') {
15210+ it = detail::parse_width(it, end, specs, width_ref, ctx);
15211+ width_ = specs.width;
15212+ }
15213+ ctx.advance_to(it);
15214+ return formatter_.parse(ctx);
15215 }
15216
15217- template <typename FormatContext>
15218- auto format(const join_view<It, Sentinel, Char>& value,
15219- FormatContext& ctx) const -> decltype(ctx.out()) {
15220- auto it = value.begin;
15221- auto out = ctx.out();
15222- if (it != value.end) {
15223- out = value_formatter_.format(*it, ctx);
15224- ++it;
15225- while (it != value.end) {
15226- out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
15227- ctx.advance_to(out);
15228- out = value_formatter_.format(*it, ctx);
15229- ++it;
15230- }
15231- }
15232- return out;
15233+ template <typename FormatContext, typename F>
15234+ auto write_padded(FormatContext& ctx, F write) const -> decltype(ctx.out()) {
15235+ if (width_ == 0) return write(ctx.out());
15236+ auto buf = basic_memory_buffer<Char>();
15237+ write(basic_appender<Char>(buf));
15238+ auto specs = format_specs();
15239+ specs.width = width_;
15240+ specs.copy_fill_from(specs_);
15241+ specs.set_align(specs_.align());
15242+ return detail::write<Char>(
15243+ ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
15244+ }
15245+
15246+ auto nested(const T& value) const -> nested_view<T, Char> {
15247+ return nested_view<T, Char>{&formatter_, &value};
15248 }
15249 };
15250
15251-/**
15252- Returns a view that formats the iterator range `[begin, end)` with elements
15253- separated by `sep`.
15254- */
15255-template <typename It, typename Sentinel>
15256-auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
15257- return {begin, end, sep};
15258+inline namespace literals {
15259+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
15260+template <detail::fixed_string S> constexpr auto operator""_a() {
15261+ using char_t = remove_cvref_t<decltype(*S.data)>;
15262+ return detail::udl_arg<char_t, sizeof(S.data) / sizeof(char_t), S>();
15263 }
15264-
15265+#else
15266 /**
15267- \rst
15268- Returns a view that formats `range` with elements separated by `sep`.
15269-
15270- **Example**::
15271-
15272- std::vector<int> v = {1, 2, 3};
15273- fmt::print("{}", fmt::join(v, ", "));
15274- // Output: "1, 2, 3"
15275-
15276- ``fmt::join`` applies passed format specifiers to the range elements::
15277-
15278- fmt::print("{:02}", fmt::join(v, ", "));
15279- // Output: "01, 02, 03"
15280- \endrst
15281+ * User-defined literal equivalent of `fmt::arg`.
15282+ *
15283+ * **Example**:
15284+ *
15285+ * using namespace fmt::literals;
15286+ * fmt::print("The answer is {answer}.", "answer"_a=42);
15287 */
15288-template <typename Range>
15289-auto join(Range&& range, string_view sep)
15290- -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
15291- return join(std::begin(range), std::end(range), sep);
15292+constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg<char> {
15293+ return {s};
15294 }
15295+#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS
15296+} // namespace literals
15297
15298-/**
15299- \rst
15300- Converts *value* to ``std::string`` using the default format for type *T*.
15301-
15302- **Example**::
15303+/// A fast integer formatter.
15304+class format_int {
15305+ private:
15306+ // Buffer should be large enough to hold all digits (digits10 + 1),
15307+ // a sign and a null character.
15308+ enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
15309+ mutable char buffer_[buffer_size];
15310+ char* str_;
15311
15312- #include <fmt/format.h>
15313+ template <typename UInt>
15314+ FMT_CONSTEXPR20 auto format_unsigned(UInt value) -> char* {
15315+ auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
15316+ return detail::do_format_decimal(buffer_, n, buffer_size - 1);
15317+ }
15318
15319- std::string answer = fmt::to_string(42);
15320- \endrst
15321- */
15322-template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
15323-inline auto to_string(const T& value) -> std::string {
15324- auto buffer = memory_buffer();
15325- detail::write<char>(appender(buffer), value);
15326- return {buffer.data(), buffer.size()};
15327-}
15328+ template <typename Int>
15329+ FMT_CONSTEXPR20 auto format_signed(Int value) -> char* {
15330+ auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
15331+ bool negative = value < 0;
15332+ if (negative) abs_value = 0 - abs_value;
15333+ auto begin = format_unsigned(abs_value);
15334+ if (negative) *--begin = '-';
15335+ return begin;
15336+ }
15337
15338-template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
15339-FMT_NODISCARD inline auto to_string(T value) -> std::string {
15340- // The buffer should be large enough to store the number including the sign
15341- // or "false" for bool.
15342- constexpr int max_size = detail::digits10<T>() + 2;
15343- char buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
15344- char* begin = buffer;
15345- return std::string(begin, detail::write<char>(begin, value));
15346-}
15347+ public:
15348+ FMT_CONSTEXPR20 explicit format_int(int value) : str_(format_signed(value)) {}
15349+ FMT_CONSTEXPR20 explicit format_int(long value)
15350+ : str_(format_signed(value)) {}
15351+ FMT_CONSTEXPR20 explicit format_int(long long value)
15352+ : str_(format_signed(value)) {}
15353+ FMT_CONSTEXPR20 explicit format_int(unsigned value)
15354+ : str_(format_unsigned(value)) {}
15355+ FMT_CONSTEXPR20 explicit format_int(unsigned long value)
15356+ : str_(format_unsigned(value)) {}
15357+ FMT_CONSTEXPR20 explicit format_int(unsigned long long value)
15358+ : str_(format_unsigned(value)) {}
15359
15360-template <typename Char, size_t SIZE>
15361-FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE>& buf)
15362- -> std::basic_string<Char> {
15363- auto size = buf.size();
15364- detail::assume(size < std::basic_string<Char>().max_size());
15365- return std::basic_string<Char>(buf.data(), size);
15366-}
15367+ /// Returns the number of characters written to the output buffer.
15368+ FMT_CONSTEXPR20 auto size() const -> size_t {
15369+ return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
15370+ }
15371
15372-FMT_BEGIN_DETAIL_NAMESPACE
15373+ /// Returns a pointer to the output buffer content. No terminating null
15374+ /// character is appended.
15375+ FMT_CONSTEXPR20 auto data() const -> const char* { return str_; }
15376
15377-template <typename Char>
15378-void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
15379- typename vformat_args<Char>::type args, locale_ref loc) {
15380- auto out = buffer_appender<Char>(buf);
15381- if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
15382- auto arg = args.get(0);
15383- if (!arg) error_handler().on_error("argument not found");
15384- visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
15385- return;
15386+ /// Returns a pointer to the output buffer content with terminating null
15387+ /// character appended.
15388+ FMT_CONSTEXPR20 auto c_str() const -> const char* {
15389+ buffer_[buffer_size - 1] = '\0';
15390+ return str_;
15391 }
15392
15393- struct format_handler : error_handler {
15394- basic_format_parse_context<Char> parse_context;
15395- buffer_context<Char> context;
15396-
15397- format_handler(buffer_appender<Char> p_out, basic_string_view<Char> str,
15398- basic_format_args<buffer_context<Char>> p_args,
15399- locale_ref p_loc)
15400- : parse_context(str), context(p_out, p_args, p_loc) {}
15401+ /// Returns the content of the output buffer as an `std::string`.
15402+ inline auto str() const -> std::string { return {str_, size()}; }
15403+};
15404
15405- void on_text(const Char* begin, const Char* end) {
15406- auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
15407- context.advance_to(write<Char>(context.out(), text));
15408- }
15409+#define FMT_STRING_IMPL(s, base) \
15410+ [] { \
15411+ /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
15412+ /* Use a macro-like name to avoid shadowing warnings. */ \
15413+ struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \
15414+ using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
15415+ constexpr explicit operator fmt::basic_string_view<char_type>() const { \
15416+ return fmt::detail::compile_string_to_view<char_type>(s); \
15417+ } \
15418+ }; \
15419+ using FMT_STRING_VIEW = \
15420+ fmt::basic_string_view<typename FMT_COMPILE_STRING::char_type>; \
15421+ fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \
15422+ return FMT_COMPILE_STRING(); \
15423+ }()
15424
15425- FMT_CONSTEXPR auto on_arg_id() -> int {
15426- return parse_context.next_arg_id();
15427- }
15428- FMT_CONSTEXPR auto on_arg_id(int id) -> int {
15429- return parse_context.check_arg_id(id), id;
15430- }
15431- FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
15432- int arg_id = context.arg_id(id);
15433- if (arg_id < 0) on_error("argument not found");
15434- return arg_id;
15435- }
15436+/**
15437+ * Constructs a legacy compile-time format string from a string literal `s`.
15438+ *
15439+ * **Example**:
15440+ *
15441+ * // A compile-time error because 'd' is an invalid specifier for strings.
15442+ * std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
15443+ */
15444+#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string)
15445
15446- FMT_INLINE void on_replacement_field(int id, const Char*) {
15447- auto arg = get_arg(context, id);
15448- context.advance_to(visit_format_arg(
15449- default_arg_formatter<Char>{context.out(), context.args(),
15450- context.locale()},
15451- arg));
15452- }
15453+FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args)
15454+ -> std::system_error;
15455
15456- auto on_format_specs(int id, const Char* begin, const Char* end)
15457- -> const Char* {
15458- auto arg = get_arg(context, id);
15459- if (arg.type() == type::custom_type) {
15460- parse_context.advance_to(begin);
15461- visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
15462- return parse_context.begin();
15463- }
15464- auto specs = detail::dynamic_format_specs<Char>();
15465- begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
15466- detail::handle_dynamic_spec<detail::width_checker>(
15467- specs.width, specs.width_ref, context);
15468- detail::handle_dynamic_spec<detail::precision_checker>(
15469- specs.precision, specs.precision_ref, context);
15470- if (begin == end || *begin != '}')
15471- on_error("missing '}' in format string");
15472- auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
15473- context.advance_to(visit_format_arg(f, arg));
15474- return begin;
15475- }
15476- };
15477- detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
15478+/**
15479+ * Constructs `std::system_error` with a message formatted with
15480+ * `fmt::format(fmt, args...)`.
15481+ * `error_code` is a system error code as given by `errno`.
15482+ *
15483+ * **Example**:
15484+ *
15485+ * // This throws std::system_error with the description
15486+ * // cannot open file 'madeup': No such file or directory
15487+ * // or similar (system message may vary).
15488+ * const char* filename = "madeup";
15489+ * FILE* file = fopen(filename, "r");
15490+ * if (!file)
15491+ * throw fmt::system_error(errno, "cannot open file '{}'", filename);
15492+ */
15493+template <typename... T>
15494+auto system_error(int error_code, format_string<T...> fmt, T&&... args)
15495+ -> std::system_error {
15496+ return vsystem_error(error_code, fmt.str, vargs<T...>{{args...}});
15497 }
15498
15499-#ifndef FMT_HEADER_ONLY
15500-extern template FMT_API void vformat_to(buffer<char>&, string_view,
15501- typename vformat_args<>::type,
15502- locale_ref);
15503-extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
15504- -> thousands_sep_result<char>;
15505-extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
15506- -> thousands_sep_result<wchar_t>;
15507-extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
15508-extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
15509-#endif // FMT_HEADER_ONLY
15510-
15511-FMT_END_DETAIL_NAMESPACE
15512-
15513-#if FMT_USE_USER_DEFINED_LITERALS
15514-inline namespace literals {
15515 /**
15516- \rst
15517- User-defined literal equivalent of :func:`fmt::arg`.
15518-
15519- **Example**::
15520-
15521- using namespace fmt::literals;
15522- fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
15523- \endrst
15524+ * Formats an error message for an error returned by an operating system or a
15525+ * language runtime, for example a file opening error, and writes it to `out`.
15526+ * The format is the same as the one used by `std::system_error(ec, message)`
15527+ * where `ec` is `std::error_code(error_code, std::generic_category())`.
15528+ * It is implementation-defined but normally looks like:
15529+ *
15530+ * <message>: <system-message>
15531+ *
15532+ * where `<message>` is the passed message and `<system-message>` is the system
15533+ * message corresponding to the error code.
15534+ * `error_code` is a system error code as given by `errno`.
15535 */
15536-# if FMT_USE_NONTYPE_TEMPLATE_ARGS
15537-template <detail_exported::fixed_string Str> constexpr auto operator""_a() {
15538- using char_t = remove_cvref_t<decltype(Str.data[0])>;
15539- return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
15540-}
15541-# else
15542-constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> {
15543- return {s};
15544-}
15545-# endif
15546-} // namespace literals
15547-#endif // FMT_USE_USER_DEFINED_LITERALS
15548+FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
15549+ const char* message) noexcept;
15550+
15551+// Reports a system error without throwing an exception.
15552+// Can be used to report errors from destructors.
15553+FMT_API void report_system_error(int error_code, const char* message) noexcept;
15554
15555 template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
15556 inline auto vformat(const Locale& loc, string_view fmt, format_args args)
15557 -> std::string {
15558- return detail::vformat(loc, fmt, args);
15559+ auto buf = memory_buffer();
15560+ detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
15561+ return {buf.data(), buf.size()};
15562 }
15563
15564 template <typename Locale, typename... T,
15565 FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
15566-inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
15567+FMT_INLINE auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
15568 -> std::string {
15569- return fmt::vformat(loc, string_view(fmt), fmt::make_format_args(args...));
15570+ return vformat(loc, fmt.str, vargs<T...>{{args...}});
15571 }
15572
15573 template <typename OutputIt, typename Locale,
15574- FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
15575- detail::is_locale<Locale>::value)>
15576+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
15577 auto vformat_to(OutputIt out, const Locale& loc, string_view fmt,
15578 format_args args) -> OutputIt {
15579- using detail::get_buffer;
15580- auto&& buf = get_buffer<char>(out);
15581+ auto&& buf = detail::get_buffer<char>(out);
15582 detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
15583 return detail::get_iterator(buf, out);
15584 }
15585@@ -4683,7 +4169,7 @@ template <typename OutputIt, typename Locale, typename... T,
15586 detail::is_locale<Locale>::value)>
15587 FMT_INLINE auto format_to(OutputIt out, const Locale& loc,
15588 format_string<T...> fmt, T&&... args) -> OutputIt {
15589- return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
15590+ return fmt::vformat_to(out, loc, fmt.str, vargs<T...>{{args...}});
15591 }
15592
15593 template <typename Locale, typename... T,
15594@@ -4692,40 +4178,67 @@ FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc,
15595 format_string<T...> fmt,
15596 T&&... args) -> size_t {
15597 auto buf = detail::counting_buffer<>();
15598- detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...),
15599- detail::locale_ref(loc));
15600+ detail::vformat_to(buf, fmt.str, vargs<T...>{{args...}},
15601+ detail::locale_ref(loc));
15602 return buf.count();
15603 }
15604
15605-FMT_END_EXPORT
15606+FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
15607
15608-template <typename T, typename Char>
15609-template <typename FormatContext>
15610-FMT_CONSTEXPR FMT_INLINE auto
15611-formatter<T, Char,
15612- enable_if_t<detail::type_constant<T, Char>::value !=
15613- detail::type::custom_type>>::format(const T& val,
15614- FormatContext& ctx)
15615- const -> decltype(ctx.out()) {
15616- if (specs_.width_ref.kind != detail::arg_id_kind::none ||
15617- specs_.precision_ref.kind != detail::arg_id_kind::none) {
15618- auto specs = specs_;
15619- detail::handle_dynamic_spec<detail::width_checker>(specs.width,
15620- specs.width_ref, ctx);
15621- detail::handle_dynamic_spec<detail::precision_checker>(
15622- specs.precision, specs.precision_ref, ctx);
15623- return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
15624- }
15625- return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
15626+/**
15627+ * Formats `args` according to specifications in `fmt` and returns the result
15628+ * as a string.
15629+ *
15630+ * **Example**:
15631+ *
15632+ * #include <fmt/format.h>
15633+ * std::string message = fmt::format("The answer is {}.", 42);
15634+ */
15635+template <typename... T>
15636+FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
15637+ -> std::string {
15638+ return vformat(fmt.str, vargs<T...>{{args...}});
15639+}
15640+
15641+/**
15642+ * Converts `value` to `std::string` using the default format for type `T`.
15643+ *
15644+ * **Example**:
15645+ *
15646+ * std::string answer = fmt::to_string(42);
15647+ */
15648+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
15649+FMT_NODISCARD auto to_string(T value) -> std::string {
15650+ // The buffer should be large enough to store the number including the sign
15651+ // or "false" for bool.
15652+ char buffer[max_of(detail::digits10<T>() + 2, 5)];
15653+ return {buffer, detail::write<char>(buffer, value)};
15654+}
15655+
15656+template <typename T, FMT_ENABLE_IF(detail::use_format_as<T>::value)>
15657+FMT_NODISCARD auto to_string(const T& value) -> std::string {
15658+ return to_string(format_as(value));
15659+}
15660+
15661+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
15662+ !detail::use_format_as<T>::value)>
15663+FMT_NODISCARD auto to_string(const T& value) -> std::string {
15664+ auto buffer = memory_buffer();
15665+ detail::write<char>(appender(buffer), value);
15666+ return {buffer.data(), buffer.size()};
15667 }
15668
15669+FMT_END_EXPORT
15670 FMT_END_NAMESPACE
15671
15672 #ifdef FMT_HEADER_ONLY
15673 # define FMT_FUNC inline
15674 # include "format-inl.h"
15675-#else
15676-# define FMT_FUNC
15677+#endif
15678+
15679+// Restore _LIBCPP_REMOVE_TRANSITIVE_INCLUDES.
15680+#ifdef FMT_REMOVE_TRANSITIVE_INCLUDES
15681+# undef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES
15682 #endif
15683
15684 #endif // FMT_FORMAT_H_
15685diff --git a/include/fmt/os.h b/include/fmt/os.h
15686index ec29040..b2cc5e4 100644
15687--- a/include/fmt/os.h
15688+++ b/include/fmt/os.h
15689@@ -8,16 +8,18 @@
15690 #ifndef FMT_OS_H_
15691 #define FMT_OS_H_
15692
15693-#include <cerrno>
15694-#include <cstddef>
15695-#include <cstdio>
15696-#include <system_error> // std::system_error
15697+#include "format.h"
15698
15699-#if defined __APPLE__ || defined(__FreeBSD__)
15700-# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
15701-#endif
15702+#ifndef FMT_MODULE
15703+# include <cerrno>
15704+# include <cstddef>
15705+# include <cstdio>
15706+# include <system_error> // std::system_error
15707
15708-#include "format.h"
15709+# if FMT_HAS_INCLUDE(<xlocale.h>)
15710+# include <xlocale.h> // LC_NUMERIC_MASK on macOS
15711+# endif
15712+#endif // FMT_MODULE
15713
15714 #ifndef FMT_USE_FCNTL
15715 // UWP doesn't provide _pipe.
15716@@ -46,6 +48,7 @@
15717
15718 // Calls to system functions are wrapped in FMT_SYSTEM for testability.
15719 #ifdef FMT_SYSTEM
15720+# define FMT_HAS_SYSTEM
15721 # define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
15722 #else
15723 # define FMT_SYSTEM(call) ::call
15724@@ -74,47 +77,34 @@ FMT_BEGIN_NAMESPACE
15725 FMT_BEGIN_EXPORT
15726
15727 /**
15728- \rst
15729- A reference to a null-terminated string. It can be constructed from a C
15730- string or ``std::string``.
15731-
15732- You can use one of the following type aliases for common character types:
15733-
15734- +---------------+-----------------------------+
15735- | Type | Definition |
15736- +===============+=============================+
15737- | cstring_view | basic_cstring_view<char> |
15738- +---------------+-----------------------------+
15739- | wcstring_view | basic_cstring_view<wchar_t> |
15740- +---------------+-----------------------------+
15741-
15742- This class is most useful as a parameter type to allow passing
15743- different types of strings to a function, for example::
15744-
15745- template <typename... Args>
15746- std::string format(cstring_view format_str, const Args & ... args);
15747-
15748- format("{}", 42);
15749- format(std::string("{}"), 42);
15750- \endrst
15751+ * A reference to a null-terminated string. It can be constructed from a C
15752+ * string or `std::string`.
15753+ *
15754+ * You can use one of the following type aliases for common character types:
15755+ *
15756+ * +---------------+-----------------------------+
15757+ * | Type | Definition |
15758+ * +===============+=============================+
15759+ * | cstring_view | basic_cstring_view<char> |
15760+ * +---------------+-----------------------------+
15761+ * | wcstring_view | basic_cstring_view<wchar_t> |
15762+ * +---------------+-----------------------------+
15763+ *
15764+ * This class is most useful as a parameter type for functions that wrap C APIs.
15765 */
15766 template <typename Char> class basic_cstring_view {
15767 private:
15768 const Char* data_;
15769
15770 public:
15771- /** Constructs a string reference object from a C string. */
15772+ /// Constructs a string reference object from a C string.
15773 basic_cstring_view(const Char* s) : data_(s) {}
15774
15775- /**
15776- \rst
15777- Constructs a string reference from an ``std::string`` object.
15778- \endrst
15779- */
15780+ /// Constructs a string reference from an `std::string` object.
15781 basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
15782
15783- /** Returns the pointer to a C string. */
15784- const Char* c_str() const { return data_; }
15785+ /// Returns the pointer to a C string.
15786+ auto c_str() const -> const Char* { return data_; }
15787 };
15788
15789 using cstring_view = basic_cstring_view<char>;
15790@@ -123,53 +113,50 @@ using wcstring_view = basic_cstring_view<wchar_t>;
15791 #ifdef _WIN32
15792 FMT_API const std::error_category& system_category() noexcept;
15793
15794-FMT_BEGIN_DETAIL_NAMESPACE
15795+namespace detail {
15796 FMT_API void format_windows_error(buffer<char>& out, int error_code,
15797 const char* message) noexcept;
15798-FMT_END_DETAIL_NAMESPACE
15799+}
15800
15801-FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
15802+FMT_API std::system_error vwindows_error(int error_code, string_view fmt,
15803 format_args args);
15804
15805 /**
15806- \rst
15807- Constructs a :class:`std::system_error` object with the description
15808- of the form
15809-
15810- .. parsed-literal::
15811- *<message>*: *<system-message>*
15812-
15813- where *<message>* is the formatted message and *<system-message>* is the
15814- system message corresponding to the error code.
15815- *error_code* is a Windows error code as given by ``GetLastError``.
15816- If *error_code* is not a valid error code such as -1, the system message
15817- will look like "error -1".
15818-
15819- **Example**::
15820-
15821- // This throws a system_error with the description
15822- // cannot open file 'madeup': The system cannot find the file specified.
15823- // or similar (system message may vary).
15824- const char *filename = "madeup";
15825- LPOFSTRUCT of = LPOFSTRUCT();
15826- HFILE file = OpenFile(filename, &of, OF_READ);
15827- if (file == HFILE_ERROR) {
15828- throw fmt::windows_error(GetLastError(),
15829- "cannot open file '{}'", filename);
15830- }
15831- \endrst
15832-*/
15833-template <typename... Args>
15834-std::system_error windows_error(int error_code, string_view message,
15835- const Args&... args) {
15836- return vwindows_error(error_code, message, fmt::make_format_args(args...));
15837+ * Constructs a `std::system_error` object with the description of the form
15838+ *
15839+ * <message>: <system-message>
15840+ *
15841+ * where `<message>` is the formatted message and `<system-message>` is the
15842+ * system message corresponding to the error code.
15843+ * `error_code` is a Windows error code as given by `GetLastError`.
15844+ * If `error_code` is not a valid error code such as -1, the system message
15845+ * will look like "error -1".
15846+ *
15847+ * **Example**:
15848+ *
15849+ * // This throws a system_error with the description
15850+ * // cannot open file 'madeup': The system cannot find the file
15851+ * specified.
15852+ * // or similar (system message may vary).
15853+ * const char *filename = "madeup";
15854+ * LPOFSTRUCT of = LPOFSTRUCT();
15855+ * HFILE file = OpenFile(filename, &of, OF_READ);
15856+ * if (file == HFILE_ERROR) {
15857+ * throw fmt::windows_error(GetLastError(),
15858+ * "cannot open file '{}'", filename);
15859+ * }
15860+ */
15861+template <typename... T>
15862+auto windows_error(int error_code, string_view message, const T&... args)
15863+ -> std::system_error {
15864+ return vwindows_error(error_code, message, vargs<T...>{{args...}});
15865 }
15866
15867 // Reports a Windows error without throwing an exception.
15868 // Can be used to report errors from destructors.
15869 FMT_API void report_windows_error(int error_code, const char* message) noexcept;
15870 #else
15871-inline const std::error_category& system_category() noexcept {
15872+inline auto system_category() noexcept -> const std::error_category& {
15873 return std::system_category();
15874 }
15875 #endif // _WIN32
15876@@ -177,8 +164,8 @@ inline const std::error_category& system_category() noexcept {
15877 // std::system is not available on some platforms such as iOS (#2248).
15878 #ifdef __OSX__
15879 template <typename S, typename... Args, typename Char = char_t<S>>
15880-void say(const S& format_str, Args&&... args) {
15881- std::system(format("say \"{}\"", format(format_str, args...)).c_str());
15882+void say(const S& fmt, Args&&... args) {
15883+ std::system(format("say \"{}\"", format(fmt, args...)).c_str());
15884 }
15885 #endif
15886
15887@@ -189,24 +176,24 @@ class buffered_file {
15888
15889 friend class file;
15890
15891- explicit buffered_file(FILE* f) : file_(f) {}
15892+ inline explicit buffered_file(FILE* f) : file_(f) {}
15893
15894 public:
15895 buffered_file(const buffered_file&) = delete;
15896 void operator=(const buffered_file&) = delete;
15897
15898 // Constructs a buffered_file object which doesn't represent any file.
15899- buffered_file() noexcept : file_(nullptr) {}
15900+ inline buffered_file() noexcept : file_(nullptr) {}
15901
15902 // Destroys the object closing the file it represents if any.
15903 FMT_API ~buffered_file() noexcept;
15904
15905 public:
15906- buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
15907+ inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
15908 other.file_ = nullptr;
15909 }
15910
15911- buffered_file& operator=(buffered_file&& other) {
15912+ inline auto operator=(buffered_file&& other) -> buffered_file& {
15913 close();
15914 file_ = other.file_;
15915 other.file_ = nullptr;
15916@@ -220,21 +207,20 @@ class buffered_file {
15917 FMT_API void close();
15918
15919 // Returns the pointer to a FILE object representing this file.
15920- FILE* get() const noexcept { return file_; }
15921-
15922- FMT_API int descriptor() const;
15923+ inline auto get() const noexcept -> FILE* { return file_; }
15924
15925- void vprint(string_view format_str, format_args args) {
15926- fmt::vprint(file_, format_str, args);
15927- }
15928+ FMT_API auto descriptor() const -> int;
15929
15930- template <typename... Args>
15931- inline void print(string_view format_str, const Args&... args) {
15932- vprint(format_str, fmt::make_format_args(args...));
15933+ template <typename... T>
15934+ inline void print(string_view fmt, const T&... args) {
15935+ fmt::vargs<T...> vargs = {{args...}};
15936+ detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
15937+ : fmt::vprint(file_, fmt, vargs);
15938 }
15939 };
15940
15941 #if FMT_USE_FCNTL
15942+
15943 // A file. Closed file is represented by a file object with descriptor -1.
15944 // Methods that are not declared with noexcept may throw
15945 // fmt::system_error in case of failure. Note that some errors such as
15946@@ -248,6 +234,8 @@ class FMT_API file {
15947 // Constructs a file object with a given descriptor.
15948 explicit file(int fd) : fd_(fd) {}
15949
15950+ friend struct pipe;
15951+
15952 public:
15953 // Possible values for the oflag argument to the constructor.
15954 enum {
15955@@ -260,7 +248,7 @@ class FMT_API file {
15956 };
15957
15958 // Constructs a file object which doesn't represent any file.
15959- file() noexcept : fd_(-1) {}
15960+ inline file() noexcept : fd_(-1) {}
15961
15962 // Opens a file and constructs a file object representing this file.
15963 file(cstring_view path, int oflag);
15964@@ -269,10 +257,10 @@ class FMT_API file {
15965 file(const file&) = delete;
15966 void operator=(const file&) = delete;
15967
15968- file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
15969+ inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
15970
15971 // Move assignment is not noexcept because close may throw.
15972- file& operator=(file&& other) {
15973+ inline auto operator=(file&& other) -> file& {
15974 close();
15975 fd_ = other.fd_;
15976 other.fd_ = -1;
15977@@ -283,24 +271,24 @@ class FMT_API file {
15978 ~file() noexcept;
15979
15980 // Returns the file descriptor.
15981- int descriptor() const noexcept { return fd_; }
15982+ inline auto descriptor() const noexcept -> int { return fd_; }
15983
15984 // Closes the file.
15985 void close();
15986
15987 // Returns the file size. The size has signed type for consistency with
15988 // stat::st_size.
15989- long long size() const;
15990+ auto size() const -> long long;
15991
15992 // Attempts to read count bytes from the file into the specified buffer.
15993- size_t read(void* buffer, size_t count);
15994+ auto read(void* buffer, size_t count) -> size_t;
15995
15996 // Attempts to write count bytes from the specified buffer to the file.
15997- size_t write(const void* buffer, size_t count);
15998+ auto write(const void* buffer, size_t count) -> size_t;
15999
16000 // Duplicates a file descriptor with the dup function and returns
16001 // the duplicate as a file object.
16002- static file dup(int fd);
16003+ static auto dup(int fd) -> file;
16004
16005 // Makes fd be the copy of this file descriptor, closing fd first if
16006 // necessary.
16007@@ -310,13 +298,9 @@ class FMT_API file {
16008 // necessary.
16009 void dup2(int fd, std::error_code& ec) noexcept;
16010
16011- // Creates a pipe setting up read_end and write_end file objects for reading
16012- // and writing respectively.
16013- static void pipe(file& read_end, file& write_end);
16014-
16015 // Creates a buffered_file object associated with this file and detaches
16016 // this file object from the file.
16017- buffered_file fdopen(const char* mode);
16018+ auto fdopen(const char* mode) -> buffered_file;
16019
16020 # if defined(_WIN32) && !defined(__MINGW32__)
16021 // Opens a file and constructs a file object representing this file by
16022@@ -325,15 +309,24 @@ class FMT_API file {
16023 # endif
16024 };
16025
16026+struct FMT_API pipe {
16027+ file read_end;
16028+ file write_end;
16029+
16030+ // Creates a pipe setting up read_end and write_end file objects for reading
16031+ // and writing respectively.
16032+ pipe();
16033+};
16034+
16035 // Returns the memory page size.
16036-long getpagesize();
16037+auto getpagesize() -> long;
16038
16039-FMT_BEGIN_DETAIL_NAMESPACE
16040+namespace detail {
16041
16042 struct buffer_size {
16043- buffer_size() = default;
16044+ constexpr buffer_size() = default;
16045 size_t value = 0;
16046- buffer_size operator=(size_t val) const {
16047+ FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size {
16048 auto bs = buffer_size();
16049 bs.value = val;
16050 return bs;
16051@@ -344,7 +337,7 @@ struct ostream_params {
16052 int oflag = file::WRONLY | file::CREATE | file::TRUNC;
16053 size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
16054
16055- ostream_params() {}
16056+ constexpr ostream_params() {}
16057
16058 template <typename... T>
16059 ostream_params(T... params, int new_oflag) : ostream_params(params...) {
16060@@ -365,82 +358,65 @@ struct ostream_params {
16061 # endif
16062 };
16063
16064-class file_buffer final : public buffer<char> {
16065+} // namespace detail
16066+
16067+FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size();
16068+
16069+/// A fast buffered output stream for writing from a single thread. Writing from
16070+/// multiple threads without external synchronization may result in a data race.
16071+class FMT_API ostream : private detail::buffer<char> {
16072+ private:
16073 file file_;
16074
16075- FMT_API void grow(size_t) override;
16076+ ostream(cstring_view path, const detail::ostream_params& params);
16077+
16078+ static void grow(buffer<char>& buf, size_t);
16079
16080 public:
16081- FMT_API file_buffer(cstring_view path, const ostream_params& params);
16082- FMT_API file_buffer(file_buffer&& other);
16083- FMT_API ~file_buffer();
16084+ ostream(ostream&& other) noexcept;
16085+ ~ostream();
16086
16087- void flush() {
16088+ operator writer() {
16089+ detail::buffer<char>& buf = *this;
16090+ return buf;
16091+ }
16092+
16093+ inline void flush() {
16094 if (size() == 0) return;
16095 file_.write(data(), size() * sizeof(data()[0]));
16096 clear();
16097 }
16098
16099- void close() {
16100+ template <typename... T>
16101+ friend auto output_file(cstring_view path, T... params) -> ostream;
16102+
16103+ inline void close() {
16104 flush();
16105 file_.close();
16106 }
16107-};
16108-
16109-FMT_END_DETAIL_NAMESPACE
16110-
16111-// Added {} below to work around default constructor error known to
16112-// occur in Xcode versions 7.2.1 and 8.2.1.
16113-constexpr detail::buffer_size buffer_size{};
16114-
16115-/** A fast output stream which is not thread-safe. */
16116-class FMT_API ostream {
16117- private:
16118- FMT_MSC_WARNING(suppress : 4251)
16119- detail::file_buffer buffer_;
16120
16121- ostream(cstring_view path, const detail::ostream_params& params)
16122- : buffer_(path, params) {}
16123-
16124- public:
16125- ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
16126-
16127- ~ostream();
16128-
16129- void flush() { buffer_.flush(); }
16130-
16131- template <typename... T>
16132- friend ostream output_file(cstring_view path, T... params);
16133-
16134- void close() { buffer_.close(); }
16135-
16136- /**
16137- Formats ``args`` according to specifications in ``fmt`` and writes the
16138- output to the file.
16139- */
16140+ /// Formats `args` according to specifications in `fmt` and writes the
16141+ /// output to the file.
16142 template <typename... T> void print(format_string<T...> fmt, T&&... args) {
16143- vformat_to(detail::buffer_appender<char>(buffer_), fmt,
16144- fmt::make_format_args(args...));
16145+ vformat_to(appender(*this), fmt.str, vargs<T...>{{args...}});
16146 }
16147 };
16148
16149 /**
16150- \rst
16151- Opens a file for writing. Supported parameters passed in *params*:
16152-
16153- * ``<integer>``: Flags passed to `open
16154- <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
16155- (``file::WRONLY | file::CREATE | file::TRUNC`` by default)
16156- * ``buffer_size=<integer>``: Output buffer size
16157-
16158- **Example**::
16159-
16160- auto out = fmt::output_file("guide.txt");
16161- out.print("Don't {}", "Panic");
16162- \endrst
16163+ * Opens a file for writing. Supported parameters passed in `params`:
16164+ *
16165+ * - `<integer>`: Flags passed to [open](
16166+ * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
16167+ * (`file::WRONLY | file::CREATE | file::TRUNC` by default)
16168+ * - `buffer_size=<integer>`: Output buffer size
16169+ *
16170+ * **Example**:
16171+ *
16172+ * auto out = fmt::output_file("guide.txt");
16173+ * out.print("Don't {}", "Panic");
16174 */
16175 template <typename... T>
16176-inline ostream output_file(cstring_view path, T... params) {
16177+inline auto output_file(cstring_view path, T... params) -> ostream {
16178 return {path, detail::ostream_params(params...)};
16179 }
16180 #endif // FMT_USE_FCNTL
16181diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h
16182index a112fe7..71fd6c8 100644
16183--- a/include/fmt/ostream.h
16184+++ b/include/fmt/ostream.h
16185@@ -8,19 +8,29 @@
16186 #ifndef FMT_OSTREAM_H_
16187 #define FMT_OSTREAM_H_
16188
16189-#include <fstream> // std::filebuf
16190+#ifndef FMT_MODULE
16191+# include <fstream> // std::filebuf
16192+#endif
16193
16194-#if defined(_WIN32) && defined(__GLIBCXX__)
16195-# include <ext/stdio_filebuf.h>
16196-# include <ext/stdio_sync_filebuf.h>
16197-#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
16198-# include <__std_stream>
16199+#ifdef _WIN32
16200+# ifdef __GLIBCXX__
16201+# include <ext/stdio_filebuf.h>
16202+# include <ext/stdio_sync_filebuf.h>
16203+# endif
16204+# include <io.h>
16205 #endif
16206
16207-#include "format.h"
16208+#include "chrono.h" // formatbuf
16209
16210-FMT_BEGIN_NAMESPACE
16211+#ifdef _MSVC_STL_UPDATE
16212+# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE
16213+#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5
16214+# define FMT_MSVC_STL_UPDATE _MSVC_LANG
16215+#else
16216+# define FMT_MSVC_STL_UPDATE 0
16217+#endif
16218
16219+FMT_BEGIN_NAMESPACE
16220 namespace detail {
16221
16222 // Generate a unique explicit instantion in every translation unit using a tag
16223@@ -33,49 +43,18 @@ class file_access {
16224 friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
16225 };
16226
16227-#if FMT_MSC_VERSION
16228+#if FMT_MSVC_STL_UPDATE
16229 template class file_access<file_access_tag, std::filebuf,
16230 &std::filebuf::_Myfile>;
16231 auto get_file(std::filebuf&) -> FILE*;
16232-#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
16233-template class file_access<file_access_tag, std::__stdoutbuf<char>,
16234- &std::__stdoutbuf<char>::__file_>;
16235-auto get_file(std::__stdoutbuf<char>&) -> FILE*;
16236-#endif
16237-
16238-inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
16239-#if FMT_MSC_VERSION
16240- if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
16241- if (FILE* f = get_file(*buf)) return write_console(f, data);
16242-#elif defined(_WIN32) && defined(__GLIBCXX__)
16243- auto* rdbuf = os.rdbuf();
16244- FILE* c_file;
16245- if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
16246- c_file = sfbuf->file();
16247- else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
16248- c_file = fbuf->file();
16249- else
16250- return false;
16251- if (c_file) return write_console(c_file, data);
16252-#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
16253- if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
16254- if (FILE* f = get_file(*buf)) return write_console(f, data);
16255-#else
16256- ignore_unused(os, data);
16257 #endif
16258- return false;
16259-}
16260-inline bool write_ostream_unicode(std::wostream&,
16261- fmt::basic_string_view<wchar_t>) {
16262- return false;
16263-}
16264
16265 // Write the content of buf to os.
16266 // It is a separate function rather than a part of vprint to simplify testing.
16267 template <typename Char>
16268 void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
16269 const Char* buf_data = buf.data();
16270- using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
16271+ using unsigned_streamsize = make_unsigned_t<std::streamsize>;
16272 unsigned_streamsize size = buf.size();
16273 unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
16274 do {
16275@@ -86,20 +65,9 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
16276 } while (size != 0);
16277 }
16278
16279-template <typename Char, typename T>
16280-void format_value(buffer<Char>& buf, const T& value,
16281- locale_ref loc = locale_ref()) {
16282- auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
16283- auto&& output = std::basic_ostream<Char>(&format_buf);
16284-#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
16285- if (loc) output.imbue(loc.get<std::locale>());
16286-#endif
16287- output << value;
16288- output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
16289-}
16290-
16291-template <typename T> struct streamed_view { const T& value; };
16292-
16293+template <typename T> struct streamed_view {
16294+ const T& value;
16295+};
16296 } // namespace detail
16297
16298 // Formats an object of type T that has an overloaded ostream operator<<.
16299@@ -107,11 +75,14 @@ template <typename Char>
16300 struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
16301 void set_debug_format() = delete;
16302
16303- template <typename T, typename OutputIt>
16304- auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
16305- -> OutputIt {
16306+ template <typename T, typename Context>
16307+ auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
16308 auto buffer = basic_memory_buffer<Char>();
16309- detail::format_value(buffer, value, ctx.locale());
16310+ auto&& formatbuf = detail::formatbuf<std::basic_streambuf<Char>>(buffer);
16311+ auto&& output = std::basic_ostream<Char>(&formatbuf);
16312+ output.imbue(std::locale::classic()); // The default is always unlocalized.
16313+ output << value;
16314+ output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
16315 return formatter<basic_string_view<Char>, Char>::format(
16316 {buffer.data(), buffer.size()}, ctx);
16317 }
16318@@ -122,73 +93,67 @@ using ostream_formatter = basic_ostream_formatter<char>;
16319 template <typename T, typename Char>
16320 struct formatter<detail::streamed_view<T>, Char>
16321 : basic_ostream_formatter<Char> {
16322- template <typename OutputIt>
16323- auto format(detail::streamed_view<T> view,
16324- basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
16325+ template <typename Context>
16326+ auto format(detail::streamed_view<T> view, Context& ctx) const
16327+ -> decltype(ctx.out()) {
16328 return basic_ostream_formatter<Char>::format(view.value, ctx);
16329 }
16330 };
16331
16332 /**
16333- \rst
16334- Returns a view that formats `value` via an ostream ``operator<<``.
16335-
16336- **Example**::
16337-
16338- fmt::print("Current thread id: {}\n",
16339- fmt::streamed(std::this_thread::get_id()));
16340- \endrst
16341+ * Returns a view that formats `value` via an ostream `operator<<`.
16342+ *
16343+ * **Example**:
16344+ *
16345+ * fmt::print("Current thread id: {}\n",
16346+ * fmt::streamed(std::this_thread::get_id()));
16347 */
16348 template <typename T>
16349-auto streamed(const T& value) -> detail::streamed_view<T> {
16350+constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
16351 return {value};
16352 }
16353
16354-namespace detail {
16355-
16356-inline void vprint_directly(std::ostream& os, string_view format_str,
16357- format_args args) {
16358+inline void vprint(std::ostream& os, string_view fmt, format_args args) {
16359 auto buffer = memory_buffer();
16360- detail::vformat_to(buffer, format_str, args);
16361- detail::write_buffer(os, buffer);
16362-}
16363-
16364-} // namespace detail
16365-
16366-FMT_EXPORT template <typename Char>
16367-void vprint(std::basic_ostream<Char>& os,
16368- basic_string_view<type_identity_t<Char>> format_str,
16369- basic_format_args<buffer_context<type_identity_t<Char>>> args) {
16370- auto buffer = basic_memory_buffer<Char>();
16371- detail::vformat_to(buffer, format_str, args);
16372- if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
16373+ detail::vformat_to(buffer, fmt, args);
16374+ FILE* f = nullptr;
16375+#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI
16376+ if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
16377+ f = detail::get_file(*buf);
16378+#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
16379+ auto* rdbuf = os.rdbuf();
16380+ if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
16381+ f = sfbuf->file();
16382+ else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
16383+ f = fbuf->file();
16384+#endif
16385+#ifdef _WIN32
16386+ if (f) {
16387+ int fd = _fileno(f);
16388+ if (_isatty(fd)) {
16389+ os.flush();
16390+ if (detail::write_console(fd, {buffer.data(), buffer.size()})) return;
16391+ }
16392+ }
16393+#endif
16394+ detail::ignore_unused(f);
16395 detail::write_buffer(os, buffer);
16396 }
16397
16398 /**
16399- \rst
16400- Prints formatted data to the stream *os*.
16401-
16402- **Example**::
16403-
16404- fmt::print(cerr, "Don't {}!", "panic");
16405- \endrst
16406+ * Prints formatted data to the stream `os`.
16407+ *
16408+ * **Example**:
16409+ *
16410+ * fmt::print(cerr, "Don't {}!", "panic");
16411 */
16412 FMT_EXPORT template <typename... T>
16413 void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
16414- const auto& vargs = fmt::make_format_args(args...);
16415- if (detail::is_utf8())
16416- vprint(os, fmt, vargs);
16417- else
16418- detail::vprint_directly(os, fmt, vargs);
16419-}
16420-
16421-FMT_EXPORT
16422-template <typename... Args>
16423-void print(std::wostream& os,
16424- basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
16425- Args&&... args) {
16426- vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
16427+ fmt::vargs<T...> vargs = {{args...}};
16428+ if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs);
16429+ auto buffer = memory_buffer();
16430+ detail::vformat_to(buffer, fmt.str, vargs);
16431+ detail::write_buffer(os, buffer);
16432 }
16433
16434 FMT_EXPORT template <typename... T>
16435@@ -196,14 +161,6 @@ void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
16436 fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
16437 }
16438
16439-FMT_EXPORT
16440-template <typename... Args>
16441-void println(std::wostream& os,
16442- basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
16443- Args&&... args) {
16444- print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
16445-}
16446-
16447 FMT_END_NAMESPACE
16448
16449 #endif // FMT_OSTREAM_H_
16450diff --git a/include/fmt/printf.h b/include/fmt/printf.h
16451index 5d1aeb7..e726840 100644
16452--- a/include/fmt/printf.h
16453+++ b/include/fmt/printf.h
16454@@ -8,60 +8,78 @@
16455 #ifndef FMT_PRINTF_H_
16456 #define FMT_PRINTF_H_
16457
16458-#include <algorithm> // std::max
16459-#include <limits> // std::numeric_limits
16460+#ifndef FMT_MODULE
16461+# include <algorithm> // std::max
16462+# include <limits> // std::numeric_limits
16463+#endif
16464
16465 #include "format.h"
16466
16467 FMT_BEGIN_NAMESPACE
16468 FMT_BEGIN_EXPORT
16469
16470-template <typename T> struct printf_formatter { printf_formatter() = delete; };
16471+template <typename T> struct printf_formatter {
16472+ printf_formatter() = delete;
16473+};
16474
16475 template <typename Char> class basic_printf_context {
16476 private:
16477- detail::buffer_appender<Char> out_;
16478+ basic_appender<Char> out_;
16479 basic_format_args<basic_printf_context> args_;
16480
16481+ static_assert(std::is_same<Char, char>::value ||
16482+ std::is_same<Char, wchar_t>::value,
16483+ "Unsupported code unit type.");
16484+
16485 public:
16486 using char_type = Char;
16487- using parse_context_type = basic_format_parse_context<Char>;
16488+ using parse_context_type = parse_context<Char>;
16489 template <typename T> using formatter_type = printf_formatter<T>;
16490+ enum { builtin_types = 1 };
16491
16492- /**
16493- \rst
16494- Constructs a ``printf_context`` object. References to the arguments are
16495- stored in the context object so make sure they have appropriate lifetimes.
16496- \endrst
16497- */
16498- basic_printf_context(detail::buffer_appender<Char> out,
16499+ /// Constructs a `printf_context` object. References to the arguments are
16500+ /// stored in the context object so make sure they have appropriate lifetimes.
16501+ basic_printf_context(basic_appender<Char> out,
16502 basic_format_args<basic_printf_context> args)
16503 : out_(out), args_(args) {}
16504
16505- auto out() -> detail::buffer_appender<Char> { return out_; }
16506- void advance_to(detail::buffer_appender<Char>) {}
16507+ auto out() -> basic_appender<Char> { return out_; }
16508+ void advance_to(basic_appender<Char>) {}
16509
16510 auto locale() -> detail::locale_ref { return {}; }
16511
16512 auto arg(int id) const -> basic_format_arg<basic_printf_context> {
16513 return args_.get(id);
16514 }
16515+};
16516
16517- FMT_CONSTEXPR void on_error(const char* message) {
16518- detail::error_handler().on_error(message);
16519+namespace detail {
16520+
16521+// Return the result via the out param to workaround gcc bug 77539.
16522+template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
16523+FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
16524+ for (out = first; out != last; ++out) {
16525+ if (*out == value) return true;
16526 }
16527-};
16528+ return false;
16529+}
16530
16531-FMT_BEGIN_DETAIL_NAMESPACE
16532+template <>
16533+inline auto find<false, char>(const char* first, const char* last, char value,
16534+ const char*& out) -> bool {
16535+ out =
16536+ static_cast<const char*>(memchr(first, value, to_unsigned(last - first)));
16537+ return out != nullptr;
16538+}
16539
16540 // Checks if a value fits in int - used to avoid warnings about comparing
16541 // signed and unsigned integers.
16542 template <bool IsSigned> struct int_checker {
16543 template <typename T> static auto fits_in_int(T value) -> bool {
16544- unsigned max = max_value<int>();
16545+ unsigned max = to_unsigned(max_value<int>());
16546 return value <= max;
16547 }
16548- static auto fits_in_int(bool) -> bool { return true; }
16549+ inline static auto fits_in_int(bool) -> bool { return true; }
16550 };
16551
16552 template <> struct int_checker<true> {
16553@@ -69,20 +87,20 @@ template <> struct int_checker<true> {
16554 return value >= (std::numeric_limits<int>::min)() &&
16555 value <= max_value<int>();
16556 }
16557- static auto fits_in_int(int) -> bool { return true; }
16558+ inline static auto fits_in_int(int) -> bool { return true; }
16559 };
16560
16561 struct printf_precision_handler {
16562 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
16563 auto operator()(T value) -> int {
16564 if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
16565- throw_format_error("number is too big");
16566+ report_error("number is too big");
16567 return (std::max)(static_cast<int>(value), 0);
16568 }
16569
16570 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
16571 auto operator()(T) -> int {
16572- throw_format_error("precision is not integer");
16573+ report_error("precision is not integer");
16574 return 0;
16575 }
16576 };
16577@@ -102,7 +120,9 @@ struct is_zero_int {
16578
16579 template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
16580
16581-template <> struct make_unsigned_or_bool<bool> { using type = bool; };
16582+template <> struct make_unsigned_or_bool<bool> {
16583+ using type = bool;
16584+};
16585
16586 template <typename T, typename Context> class arg_converter {
16587 private:
16588@@ -125,25 +145,19 @@ template <typename T, typename Context> class arg_converter {
16589 using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
16590 if (const_check(sizeof(target_type) <= sizeof(int))) {
16591 // Extra casts are used to silence warnings.
16592- if (is_signed) {
16593- auto n = static_cast<int>(static_cast<target_type>(value));
16594- arg_ = detail::make_arg<Context>(n);
16595- } else {
16596- using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
16597- auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
16598- arg_ = detail::make_arg<Context>(n);
16599- }
16600+ using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
16601+ if (is_signed)
16602+ arg_ = static_cast<int>(static_cast<target_type>(value));
16603+ else
16604+ arg_ = static_cast<unsigned>(static_cast<unsigned_type>(value));
16605 } else {
16606- if (is_signed) {
16607- // glibc's printf doesn't sign extend arguments of smaller types:
16608- // std::printf("%lld", -42); // prints "4294967254"
16609- // but we don't have to do the same because it's a UB.
16610- auto n = static_cast<long long>(value);
16611- arg_ = detail::make_arg<Context>(n);
16612- } else {
16613- auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
16614- arg_ = detail::make_arg<Context>(n);
16615- }
16616+ // glibc's printf doesn't sign extend arguments of smaller types:
16617+ // std::printf("%lld", -42); // prints "4294967254"
16618+ // but we don't have to do the same because it's a UB.
16619+ if (is_signed)
16620+ arg_ = static_cast<long long>(value);
16621+ else
16622+ arg_ = static_cast<typename make_unsigned_or_bool<U>::type>(value);
16623 }
16624 }
16625
16626@@ -157,7 +171,7 @@ template <typename T, typename Context> class arg_converter {
16627 // unsigned).
16628 template <typename T, typename Context, typename Char>
16629 void convert_arg(basic_format_arg<Context>& arg, Char type) {
16630- visit_format_arg(arg_converter<T, Context>(arg, type), arg);
16631+ arg.visit(arg_converter<T, Context>(arg, type));
16632 }
16633
16634 // Converts an integer argument to char for printf.
16635@@ -170,8 +184,7 @@ template <typename Context> class char_converter {
16636
16637 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
16638 void operator()(T value) {
16639- auto c = static_cast<typename Context::char_type>(value);
16640- arg_ = detail::make_arg<Context>(c);
16641+ arg_ = static_cast<typename Context::char_type>(value);
16642 }
16643
16644 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
16645@@ -187,28 +200,28 @@ template <typename Char> struct get_cstring {
16646
16647 // Checks if an argument is a valid printf width specifier and sets
16648 // left alignment if it is negative.
16649-template <typename Char> class printf_width_handler {
16650+class printf_width_handler {
16651 private:
16652- format_specs<Char>& specs_;
16653+ format_specs& specs_;
16654
16655 public:
16656- explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
16657+ inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
16658
16659 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
16660 auto operator()(T value) -> unsigned {
16661 auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
16662 if (detail::is_negative(value)) {
16663- specs_.align = align::left;
16664+ specs_.set_align(align::left);
16665 width = 0 - width;
16666 }
16667- unsigned int_max = max_value<int>();
16668- if (width > int_max) throw_format_error("number is too big");
16669+ unsigned int_max = to_unsigned(max_value<int>());
16670+ if (width > int_max) report_error("number is too big");
16671 return static_cast<unsigned>(width);
16672 }
16673
16674 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
16675 auto operator()(T) -> unsigned {
16676- throw_format_error("width is not integer");
16677+ report_error("width is not integer");
16678 return 0;
16679 }
16680 };
16681@@ -216,12 +229,12 @@ template <typename Char> class printf_width_handler {
16682 // Workaround for a bug with the XL compiler when initializing
16683 // printf_arg_formatter's base class.
16684 template <typename Char>
16685-auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
16686+auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
16687 -> arg_formatter<Char> {
16688 return {iter, s, locale_ref()};
16689 }
16690
16691-// The ``printf`` argument formatter.
16692+// The `printf` argument formatter.
16693 template <typename Char>
16694 class printf_arg_formatter : public arg_formatter<Char> {
16695 private:
16696@@ -232,105 +245,96 @@ class printf_arg_formatter : public arg_formatter<Char> {
16697
16698 void write_null_pointer(bool is_string = false) {
16699 auto s = this->specs;
16700- s.type = presentation_type::none;
16701- write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
16702+ s.set_type(presentation_type::none);
16703+ write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
16704+ }
16705+
16706+ template <typename T> void write(T value) {
16707+ detail::write<Char>(this->out, value, this->specs, this->locale);
16708 }
16709
16710 public:
16711- printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
16712+ printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
16713 context_type& ctx)
16714 : base(make_arg_formatter(iter, s)), context_(ctx) {}
16715
16716- void operator()(monostate value) { base::operator()(value); }
16717+ void operator()(monostate value) { write(value); }
16718
16719 template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
16720 void operator()(T value) {
16721 // MSVC2013 fails to compile separate overloads for bool and Char so use
16722 // std::is_same instead.
16723 if (!std::is_same<T, Char>::value) {
16724- base::operator()(value);
16725+ write(value);
16726 return;
16727 }
16728- format_specs<Char> fmt_specs = this->specs;
16729- if (fmt_specs.type != presentation_type::none &&
16730- fmt_specs.type != presentation_type::chr) {
16731+ format_specs s = this->specs;
16732+ if (s.type() != presentation_type::none &&
16733+ s.type() != presentation_type::chr) {
16734 return (*this)(static_cast<int>(value));
16735 }
16736- fmt_specs.sign = sign::none;
16737- fmt_specs.alt = false;
16738- fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
16739+ s.set_sign(sign::none);
16740+ s.clear_alt();
16741+ s.set_fill(' '); // Ignore '0' flag for char types.
16742 // align::numeric needs to be overwritten here since the '0' flag is
16743 // ignored for non-numeric types
16744- if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
16745- fmt_specs.align = align::right;
16746- write<Char>(this->out, static_cast<Char>(value), fmt_specs);
16747+ if (s.align() == align::none || s.align() == align::numeric)
16748+ s.set_align(align::right);
16749+ detail::write<Char>(this->out, static_cast<Char>(value), s);
16750 }
16751
16752 template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
16753 void operator()(T value) {
16754- base::operator()(value);
16755+ write(value);
16756 }
16757
16758- /** Formats a null-terminated C string. */
16759 void operator()(const char* value) {
16760 if (value)
16761- base::operator()(value);
16762+ write(value);
16763 else
16764- write_null_pointer(this->specs.type != presentation_type::pointer);
16765+ write_null_pointer(this->specs.type() != presentation_type::pointer);
16766 }
16767
16768- /** Formats a null-terminated wide C string. */
16769 void operator()(const wchar_t* value) {
16770 if (value)
16771- base::operator()(value);
16772+ write(value);
16773 else
16774- write_null_pointer(this->specs.type != presentation_type::pointer);
16775+ write_null_pointer(this->specs.type() != presentation_type::pointer);
16776 }
16777
16778- void operator()(basic_string_view<Char> value) { base::operator()(value); }
16779+ void operator()(basic_string_view<Char> value) { write(value); }
16780
16781- /** Formats a pointer. */
16782 void operator()(const void* value) {
16783 if (value)
16784- base::operator()(value);
16785+ write(value);
16786 else
16787 write_null_pointer();
16788 }
16789
16790- /** Formats an argument of a custom (user-defined) type. */
16791 void operator()(typename basic_format_arg<context_type>::handle handle) {
16792- auto parse_ctx = basic_format_parse_context<Char>({});
16793+ auto parse_ctx = parse_context<Char>({});
16794 handle.format(parse_ctx, context_);
16795 }
16796 };
16797
16798 template <typename Char>
16799-void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
16800+void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
16801 for (; it != end; ++it) {
16802 switch (*it) {
16803- case '-':
16804- specs.align = align::left;
16805- break;
16806- case '+':
16807- specs.sign = sign::plus;
16808- break;
16809- case '0':
16810- specs.fill[0] = '0';
16811- break;
16812+ case '-': specs.set_align(align::left); break;
16813+ case '+': specs.set_sign(sign::plus); break;
16814+ case '0': specs.set_fill('0'); break;
16815 case ' ':
16816- if (specs.sign != sign::plus) specs.sign = sign::space;
16817- break;
16818- case '#':
16819- specs.alt = true;
16820+ if (specs.sign() != sign::plus) specs.set_sign(sign::space);
16821 break;
16822- default:
16823- return;
16824+ case '#': specs.set_alt(); break;
16825+ default: return;
16826 }
16827 }
16828 }
16829
16830 template <typename Char, typename GetArg>
16831-auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
16832+auto parse_header(const Char*& it, const Char* end, format_specs& specs,
16833 GetArg get_arg) -> int {
16834 int arg_index = -1;
16835 Char c = *it;
16836@@ -342,11 +346,11 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
16837 ++it;
16838 arg_index = value != -1 ? value : max_value<int>();
16839 } else {
16840- if (c == '0') specs.fill[0] = '0';
16841+ if (c == '0') specs.set_fill('0');
16842 if (value != 0) {
16843 // Nonzero value means that we parsed width and don't need to
16844 // parse it or flags again, so return now.
16845- if (value == -1) throw_format_error("number is too big");
16846+ if (value == -1) report_error("number is too big");
16847 specs.width = value;
16848 return arg_index;
16849 }
16850@@ -357,63 +361,47 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
16851 if (it != end) {
16852 if (*it >= '0' && *it <= '9') {
16853 specs.width = parse_nonnegative_int(it, end, -1);
16854- if (specs.width == -1) throw_format_error("number is too big");
16855+ if (specs.width == -1) report_error("number is too big");
16856 } else if (*it == '*') {
16857 ++it;
16858- specs.width = static_cast<int>(visit_format_arg(
16859- detail::printf_width_handler<Char>(specs), get_arg(-1)));
16860+ specs.width = static_cast<int>(
16861+ get_arg(-1).visit(detail::printf_width_handler(specs)));
16862 }
16863 }
16864 return arg_index;
16865 }
16866
16867-inline auto parse_printf_presentation_type(char c, type t)
16868+inline auto parse_printf_presentation_type(char c, type t, bool& upper)
16869 -> presentation_type {
16870 using pt = presentation_type;
16871 constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
16872 switch (c) {
16873- case 'd':
16874- return in(t, integral_set) ? pt::dec : pt::none;
16875- case 'o':
16876- return in(t, integral_set) ? pt::oct : pt::none;
16877- case 'x':
16878- return in(t, integral_set) ? pt::hex_lower : pt::none;
16879- case 'X':
16880- return in(t, integral_set) ? pt::hex_upper : pt::none;
16881- case 'a':
16882- return in(t, float_set) ? pt::hexfloat_lower : pt::none;
16883- case 'A':
16884- return in(t, float_set) ? pt::hexfloat_upper : pt::none;
16885- case 'e':
16886- return in(t, float_set) ? pt::exp_lower : pt::none;
16887- case 'E':
16888- return in(t, float_set) ? pt::exp_upper : pt::none;
16889- case 'f':
16890- return in(t, float_set) ? pt::fixed_lower : pt::none;
16891- case 'F':
16892- return in(t, float_set) ? pt::fixed_upper : pt::none;
16893- case 'g':
16894- return in(t, float_set) ? pt::general_lower : pt::none;
16895- case 'G':
16896- return in(t, float_set) ? pt::general_upper : pt::none;
16897- case 'c':
16898- return in(t, integral_set) ? pt::chr : pt::none;
16899- case 's':
16900- return in(t, string_set | cstring_set) ? pt::string : pt::none;
16901- case 'p':
16902- return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
16903- default:
16904- return pt::none;
16905+ case 'd': return in(t, integral_set) ? pt::dec : pt::none;
16906+ case 'o': return in(t, integral_set) ? pt::oct : pt::none;
16907+ case 'X': upper = true; FMT_FALLTHROUGH;
16908+ case 'x': return in(t, integral_set) ? pt::hex : pt::none;
16909+ case 'E': upper = true; FMT_FALLTHROUGH;
16910+ case 'e': return in(t, float_set) ? pt::exp : pt::none;
16911+ case 'F': upper = true; FMT_FALLTHROUGH;
16912+ case 'f': return in(t, float_set) ? pt::fixed : pt::none;
16913+ case 'G': upper = true; FMT_FALLTHROUGH;
16914+ case 'g': return in(t, float_set) ? pt::general : pt::none;
16915+ case 'A': upper = true; FMT_FALLTHROUGH;
16916+ case 'a': return in(t, float_set) ? pt::hexfloat : pt::none;
16917+ case 'c': return in(t, integral_set) ? pt::chr : pt::none;
16918+ case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none;
16919+ case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
16920+ default: return pt::none;
16921 }
16922 }
16923
16924 template <typename Char, typename Context>
16925 void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
16926 basic_format_args<Context> args) {
16927- using iterator = buffer_appender<Char>;
16928+ using iterator = basic_appender<Char>;
16929 auto out = iterator(buf);
16930 auto context = basic_printf_context<Char>(out, args);
16931- auto parse_ctx = basic_format_parse_context<Char>(format);
16932+ auto parse_ctx = parse_context<Char>(format);
16933
16934 // Returns the argument with specified index or, if arg_index is -1, the next
16935 // argument.
16936@@ -441,12 +429,12 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
16937 }
16938 write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
16939
16940- auto specs = format_specs<Char>();
16941- specs.align = align::right;
16942+ auto specs = format_specs();
16943+ specs.set_align(align::right);
16944
16945 // Parse argument index, flags and width.
16946 int arg_index = parse_header(it, end, specs, get_arg);
16947- if (arg_index == 0) throw_format_error("argument not found");
16948+ if (arg_index == 0) report_error("argument not found");
16949
16950 // Parse precision.
16951 if (it != end && *it == '.') {
16952@@ -456,8 +444,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
16953 specs.precision = parse_nonnegative_int(it, end, 0);
16954 } else if (c == '*') {
16955 ++it;
16956- specs.precision = static_cast<int>(
16957- visit_format_arg(printf_precision_handler(), get_arg(-1)));
16958+ specs.precision =
16959+ static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
16960 } else {
16961 specs.precision = 0;
16962 }
16963@@ -466,25 +454,26 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
16964 auto arg = get_arg(arg_index);
16965 // For d, i, o, u, x, and X conversion specifiers, if a precision is
16966 // specified, the '0' flag is ignored
16967- if (specs.precision >= 0 && arg.is_integral()) {
16968+ if (specs.precision >= 0 && is_integral_type(arg.type())) {
16969 // Ignore '0' for non-numeric types or if '-' present.
16970- specs.fill[0] = ' ';
16971+ specs.set_fill(' ');
16972 }
16973 if (specs.precision >= 0 && arg.type() == type::cstring_type) {
16974- auto str = visit_format_arg(get_cstring<Char>(), arg);
16975+ auto str = arg.visit(get_cstring<Char>());
16976 auto str_end = str + specs.precision;
16977 auto nul = std::find(str, str_end, Char());
16978 auto sv = basic_string_view<Char>(
16979 str, to_unsigned(nul != str_end ? nul - str : specs.precision));
16980- arg = make_arg<basic_printf_context<Char>>(sv);
16981+ arg = sv;
16982 }
16983- if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
16984- if (specs.fill[0] == '0') {
16985- if (arg.is_arithmetic() && specs.align != align::left)
16986- specs.align = align::numeric;
16987- else
16988- specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
16989- // flag is also present.
16990+ if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
16991+ if (specs.fill_unit<Char>() == '0') {
16992+ if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
16993+ specs.set_align(align::numeric);
16994+ } else {
16995+ // Ignore '0' flag for non-numeric types or if '-' flag is also present.
16996+ specs.set_fill(' ');
16997+ }
16998 }
16999
17000 // Parse length and convert the argument to the required type.
17001@@ -509,51 +498,43 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
17002 convert_arg<long>(arg, t);
17003 }
17004 break;
17005- case 'j':
17006- convert_arg<intmax_t>(arg, t);
17007- break;
17008- case 'z':
17009- convert_arg<size_t>(arg, t);
17010- break;
17011- case 't':
17012- convert_arg<std::ptrdiff_t>(arg, t);
17013- break;
17014+ case 'j': convert_arg<intmax_t>(arg, t); break;
17015+ case 'z': convert_arg<size_t>(arg, t); break;
17016+ case 't': convert_arg<std::ptrdiff_t>(arg, t); break;
17017 case 'L':
17018 // printf produces garbage when 'L' is omitted for long double, no
17019 // need to do the same.
17020 break;
17021- default:
17022- --it;
17023- convert_arg<void>(arg, c);
17024+ default: --it; convert_arg<void>(arg, c);
17025 }
17026
17027 // Parse type.
17028- if (it == end) throw_format_error("invalid format string");
17029+ if (it == end) report_error("invalid format string");
17030 char type = static_cast<char>(*it++);
17031- if (arg.is_integral()) {
17032+ if (is_integral_type(arg.type())) {
17033 // Normalize type.
17034 switch (type) {
17035 case 'i':
17036- case 'u':
17037- type = 'd';
17038- break;
17039+ case 'u': type = 'd'; break;
17040 case 'c':
17041- visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
17042+ arg.visit(char_converter<basic_printf_context<Char>>(arg));
17043 break;
17044 }
17045 }
17046- specs.type = parse_printf_presentation_type(type, arg.type());
17047- if (specs.type == presentation_type::none)
17048- throw_format_error("invalid format specifier");
17049+ bool upper = false;
17050+ specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
17051+ if (specs.type() == presentation_type::none)
17052+ report_error("invalid format specifier");
17053+ if (upper) specs.set_upper();
17054
17055 start = it;
17056
17057 // Format argument.
17058- visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
17059+ arg.visit(printf_arg_formatter<Char>(out, specs, context));
17060 }
17061 write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
17062 }
17063-FMT_END_DETAIL_NAMESPACE
17064+} // namespace detail
17065
17066 using printf_context = basic_printf_context<char>;
17067 using wprintf_context = basic_printf_context<wchar_t>;
17068@@ -561,56 +542,44 @@ using wprintf_context = basic_printf_context<wchar_t>;
17069 using printf_args = basic_format_args<printf_context>;
17070 using wprintf_args = basic_format_args<wprintf_context>;
17071
17072-/**
17073- \rst
17074- Constructs an `~fmt::format_arg_store` object that contains references to
17075- arguments and can be implicitly converted to `~fmt::printf_args`.
17076- \endrst
17077- */
17078-template <typename... T>
17079-inline auto make_printf_args(const T&... args)
17080- -> format_arg_store<printf_context, T...> {
17081- return {args...};
17082+/// Constructs an `format_arg_store` object that contains references to
17083+/// arguments and can be implicitly converted to `printf_args`.
17084+template <typename Char = char, typename... T>
17085+inline auto make_printf_args(T&... args)
17086+ -> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
17087+ return fmt::make_format_args<basic_printf_context<Char>>(args...);
17088 }
17089
17090-// DEPRECATED!
17091-template <typename... T>
17092-inline auto make_wprintf_args(const T&... args)
17093- -> format_arg_store<wprintf_context, T...> {
17094- return {args...};
17095-}
17096+template <typename Char> struct vprintf_args {
17097+ using type = basic_format_args<basic_printf_context<Char>>;
17098+};
17099
17100 template <typename Char>
17101-inline auto vsprintf(
17102- basic_string_view<Char> fmt,
17103- basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
17104+inline auto vsprintf(basic_string_view<Char> fmt,
17105+ typename vprintf_args<Char>::type args)
17106 -> std::basic_string<Char> {
17107 auto buf = basic_memory_buffer<Char>();
17108 detail::vprintf(buf, fmt, args);
17109- return to_string(buf);
17110+ return {buf.data(), buf.size()};
17111 }
17112
17113 /**
17114- \rst
17115- Formats arguments and returns the result as a string.
17116-
17117- **Example**::
17118-
17119- std::string message = fmt::sprintf("The answer is %d", 42);
17120- \endrst
17121-*/
17122-template <typename S, typename... T,
17123- typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
17124+ * Formats `args` according to specifications in `fmt` and returns the result
17125+ * as as string.
17126+ *
17127+ * **Example**:
17128+ *
17129+ * std::string message = fmt::sprintf("The answer is %d", 42);
17130+ */
17131+template <typename S, typename... T, typename Char = detail::char_t<S>>
17132 inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
17133 return vsprintf(detail::to_string_view(fmt),
17134 fmt::make_format_args<basic_printf_context<Char>>(args...));
17135 }
17136
17137 template <typename Char>
17138-inline auto vfprintf(
17139- std::FILE* f, basic_string_view<Char> fmt,
17140- basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
17141- -> int {
17142+inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
17143+ typename vprintf_args<Char>::type args) -> int {
17144 auto buf = basic_memory_buffer<Char>();
17145 detail::vprintf(buf, fmt, args);
17146 size_t size = buf.size();
17147@@ -620,36 +589,33 @@ inline auto vfprintf(
17148 }
17149
17150 /**
17151- \rst
17152- Prints formatted data to the file *f*.
17153-
17154- **Example**::
17155-
17156- fmt::fprintf(stderr, "Don't %s!", "panic");
17157- \endrst
17158+ * Formats `args` according to specifications in `fmt` and writes the output
17159+ * to `f`.
17160+ *
17161+ * **Example**:
17162+ *
17163+ * fmt::fprintf(stderr, "Don't %s!", "panic");
17164 */
17165-template <typename S, typename... T, typename Char = char_t<S>>
17166+template <typename S, typename... T, typename Char = detail::char_t<S>>
17167 inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
17168 return vfprintf(f, detail::to_string_view(fmt),
17169- fmt::make_format_args<basic_printf_context<Char>>(args...));
17170+ make_printf_args<Char>(args...));
17171 }
17172
17173 template <typename Char>
17174-FMT_DEPRECATED inline auto vprintf(
17175- basic_string_view<Char> fmt,
17176- basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
17177+FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
17178+ typename vprintf_args<Char>::type args)
17179 -> int {
17180 return vfprintf(stdout, fmt, args);
17181 }
17182
17183 /**
17184- \rst
17185- Prints formatted data to ``stdout``.
17186-
17187- **Example**::
17188-
17189- fmt::printf("Elapsed time: %.2f seconds", 1.23);
17190- \endrst
17191+ * Formats `args` according to specifications in `fmt` and writes the output
17192+ * to `stdout`.
17193+ *
17194+ * **Example**:
17195+ *
17196+ * fmt::printf("Elapsed time: %.2f seconds", 1.23);
17197 */
17198 template <typename... T>
17199 inline auto printf(string_view fmt, const T&... args) -> int {
17200@@ -658,7 +624,7 @@ inline auto printf(string_view fmt, const T&... args) -> int {
17201 template <typename... T>
17202 FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
17203 const T&... args) -> int {
17204- return vfprintf(stdout, fmt, make_wprintf_args(args...));
17205+ return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
17206 }
17207
17208 FMT_END_EXPORT
17209diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h
17210index 266b9e1..77d645f 100644
17211--- a/include/fmt/ranges.h
17212+++ b/include/fmt/ranges.h
17213@@ -1,78 +1,38 @@
17214-// Formatting library for C++ - experimental range support
17215+// Formatting library for C++ - range and tuple support
17216 //
17217-// Copyright (c) 2012 - present, Victor Zverovich
17218+// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
17219 // All rights reserved.
17220 //
17221 // For the license information refer to format.h.
17222-//
17223-// Copyright (c) 2018 - present, Remotion (Igor Schulz)
17224-// All Rights Reserved
17225-// {fmt} support for ranges, containers and types tuple interface.
17226
17227 #ifndef FMT_RANGES_H_
17228 #define FMT_RANGES_H_
17229
17230-#include <initializer_list>
17231-#include <tuple>
17232-#include <type_traits>
17233+#ifndef FMT_MODULE
17234+# include <initializer_list>
17235+# include <iterator>
17236+# include <string>
17237+# include <tuple>
17238+# include <type_traits>
17239+# include <utility>
17240+#endif
17241
17242 #include "format.h"
17243
17244 FMT_BEGIN_NAMESPACE
17245
17246-namespace detail {
17247-
17248-template <typename Range, typename OutputIt>
17249-auto copy(const Range& range, OutputIt out) -> OutputIt {
17250- for (auto it = range.begin(), end = range.end(); it != end; ++it)
17251- *out++ = *it;
17252- return out;
17253-}
17254-
17255-template <typename OutputIt>
17256-auto copy(const char* str, OutputIt out) -> OutputIt {
17257- while (*str) *out++ = *str++;
17258- return out;
17259-}
17260-
17261-template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
17262- *out++ = ch;
17263- return out;
17264-}
17265-
17266-template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
17267- *out++ = ch;
17268- return out;
17269-}
17270-
17271-// Returns true if T has a std::string-like interface, like std::string_view.
17272-template <typename T> class is_std_string_like {
17273- template <typename U>
17274- static auto check(U* p)
17275- -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
17276- template <typename> static void check(...);
17277-
17278- public:
17279- static constexpr const bool value =
17280- is_string<T>::value ||
17281- std::is_convertible<T, std_string_view<char>>::value ||
17282- !std::is_void<decltype(check<T>(nullptr))>::value;
17283-};
17284+FMT_EXPORT
17285+enum class range_format { disabled, map, set, sequence, string, debug_string };
17286
17287-template <typename Char>
17288-struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
17289+namespace detail {
17290
17291 template <typename T> class is_map {
17292 template <typename U> static auto check(U*) -> typename U::mapped_type;
17293 template <typename> static void check(...);
17294
17295 public:
17296-#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED!
17297- static constexpr const bool value = false;
17298-#else
17299 static constexpr const bool value =
17300 !std::is_void<decltype(check<T>(nullptr))>::value;
17301-#endif
17302 };
17303
17304 template <typename T> class is_set {
17305@@ -80,26 +40,10 @@ template <typename T> class is_set {
17306 template <typename> static void check(...);
17307
17308 public:
17309-#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED!
17310- static constexpr const bool value = false;
17311-#else
17312 static constexpr const bool value =
17313 !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
17314-#endif
17315 };
17316
17317-template <typename... Ts> struct conditional_helper {};
17318-
17319-template <typename T, typename _ = void> struct is_range_ : std::false_type {};
17320-
17321-#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
17322-
17323-# define FMT_DECLTYPE_RETURN(val) \
17324- ->decltype(val) { return val; } \
17325- static_assert( \
17326- true, "") // This makes it so that a semicolon is required after the
17327- // macro, which helps clang-format handle the formatting.
17328-
17329 // C array overload
17330 template <typename T, std::size_t N>
17331 auto range_begin(const T (&arr)[N]) -> const T* {
17332@@ -114,17 +58,21 @@ template <typename T, typename Enable = void>
17333 struct has_member_fn_begin_end_t : std::false_type {};
17334
17335 template <typename T>
17336-struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
17337+struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
17338 decltype(std::declval<T>().end())>>
17339 : std::true_type {};
17340
17341-// Member function overload
17342+// Member function overloads.
17343 template <typename T>
17344-auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
17345+auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
17346+ return static_cast<T&&>(rng).begin();
17347+}
17348 template <typename T>
17349-auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
17350+auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
17351+ return static_cast<T&&>(rng).end();
17352+}
17353
17354-// ADL overload. Only participates in overload resolution if member functions
17355+// ADL overloads. Only participate in overload resolution if member functions
17356 // are not found.
17357 template <typename T>
17358 auto range_begin(T&& rng)
17359@@ -145,31 +93,30 @@ struct has_mutable_begin_end : std::false_type {};
17360
17361 template <typename T>
17362 struct has_const_begin_end<
17363- T,
17364- void_t<
17365- decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
17366- decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
17367+ T, void_t<decltype(*detail::range_begin(
17368+ std::declval<const remove_cvref_t<T>&>())),
17369+ decltype(detail::range_end(
17370+ std::declval<const remove_cvref_t<T>&>()))>>
17371 : std::true_type {};
17372
17373 template <typename T>
17374 struct has_mutable_begin_end<
17375- T, void_t<decltype(detail::range_begin(std::declval<T>())),
17376- decltype(detail::range_end(std::declval<T>())),
17377+ T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
17378+ decltype(detail::range_end(std::declval<T&>())),
17379 // the extra int here is because older versions of MSVC don't
17380 // SFINAE properly unless there are distinct types
17381 int>> : std::true_type {};
17382
17383+template <typename T, typename _ = void> struct is_range_ : std::false_type {};
17384 template <typename T>
17385 struct is_range_<T, void>
17386 : std::integral_constant<bool, (has_const_begin_end<T>::value ||
17387 has_mutable_begin_end<T>::value)> {};
17388-# undef FMT_DECLTYPE_RETURN
17389-#endif
17390
17391 // tuple_size and tuple_element check.
17392 template <typename T> class is_tuple_like_ {
17393- template <typename U>
17394- static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
17395+ template <typename U, typename V = typename std::remove_cv<U>::type>
17396+ static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
17397 template <typename> static void check(...);
17398
17399 public:
17400@@ -187,7 +134,7 @@ template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
17401 template <typename T, T... N> struct integer_sequence {
17402 using value_type = T;
17403
17404- static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
17405+ static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
17406 };
17407
17408 template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
17409@@ -210,16 +157,17 @@ class is_tuple_formattable_ {
17410 static constexpr const bool value = false;
17411 };
17412 template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
17413- template <std::size_t... Is>
17414- static std::true_type check2(index_sequence<Is...>,
17415- integer_sequence<bool, (Is == Is)...>);
17416- static std::false_type check2(...);
17417- template <std::size_t... Is>
17418- static decltype(check2(
17419+ template <size_t... Is>
17420+ static auto all_true(index_sequence<Is...>,
17421+ integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
17422+ static auto all_true(...) -> std::false_type;
17423+
17424+ template <size_t... Is>
17425+ static auto check(index_sequence<Is...>) -> decltype(all_true(
17426 index_sequence<Is...>{},
17427- integer_sequence<
17428- bool, (is_formattable<typename std::tuple_element<Is, T>::type,
17429- C>::value)...>{})) check(index_sequence<Is...>);
17430+ integer_sequence<bool,
17431+ (is_formattable<typename std::tuple_element<Is, T>::type,
17432+ C>::value)...>{}));
17433
17434 public:
17435 static constexpr const bool value =
17436@@ -296,21 +244,32 @@ FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
17437 template <typename Formatter>
17438 FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
17439
17440+template <typename T>
17441+struct range_format_kind_
17442+ : std::integral_constant<range_format,
17443+ std::is_same<uncvref_type<T>, T>::value
17444+ ? range_format::disabled
17445+ : is_map<T>::value ? range_format::map
17446+ : is_set<T>::value ? range_format::set
17447+ : range_format::sequence> {};
17448+
17449+template <range_format K>
17450+using range_format_constant = std::integral_constant<range_format, K>;
17451+
17452 // These are not generic lambdas for compatibility with C++11.
17453-template <typename ParseContext> struct parse_empty_specs {
17454+template <typename Char> struct parse_empty_specs {
17455 template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
17456 f.parse(ctx);
17457 detail::maybe_set_debug_format(f, true);
17458 }
17459- ParseContext& ctx;
17460+ parse_context<Char>& ctx;
17461 };
17462 template <typename FormatContext> struct format_tuple_element {
17463 using char_type = typename FormatContext::char_type;
17464
17465 template <typename T>
17466 void operator()(const formatter<T, char_type>& f, const T& v) {
17467- if (i > 0)
17468- ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
17469+ if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
17470 ctx.advance_to(f.format(v, ctx));
17471 ++i;
17472 }
17473@@ -359,68 +318,56 @@ struct formatter<Tuple, Char,
17474 closing_bracket_ = close;
17475 }
17476
17477- template <typename ParseContext>
17478- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
17479+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
17480 auto it = ctx.begin();
17481- if (it != ctx.end() && *it != '}')
17482- FMT_THROW(format_error("invalid format specifier"));
17483- detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
17484+ auto end = ctx.end();
17485+ if (it != end && detail::to_ascii(*it) == 'n') {
17486+ ++it;
17487+ set_brackets({}, {});
17488+ set_separator({});
17489+ }
17490+ if (it != end && *it != '}') report_error("invalid format specifier");
17491+ ctx.advance_to(it);
17492+ detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
17493 return it;
17494 }
17495
17496 template <typename FormatContext>
17497 auto format(const Tuple& value, FormatContext& ctx) const
17498 -> decltype(ctx.out()) {
17499- ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
17500+ ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
17501 detail::for_each2(
17502 formatters_, value,
17503 detail::format_tuple_element<FormatContext>{0, ctx, separator_});
17504- return detail::copy_str<Char>(closing_bracket_, ctx.out());
17505+ return detail::copy<Char>(closing_bracket_, ctx.out());
17506 }
17507 };
17508
17509 template <typename T, typename Char> struct is_range {
17510 static constexpr const bool value =
17511- detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
17512- !std::is_convertible<T, std::basic_string<Char>>::value &&
17513- !std::is_convertible<T, detail::std_string_view<Char>>::value;
17514+ detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
17515 };
17516
17517 namespace detail {
17518-template <typename Context> struct range_mapper {
17519- using mapper = arg_mapper<Context>;
17520-
17521- template <typename T,
17522- FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
17523- static auto map(T&& value) -> T&& {
17524- return static_cast<T&&>(value);
17525- }
17526- template <typename T,
17527- FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
17528- static auto map(T&& value)
17529- -> decltype(mapper().map(static_cast<T&&>(value))) {
17530- return mapper().map(static_cast<T&&>(value));
17531- }
17532-};
17533
17534 template <typename Char, typename Element>
17535-using range_formatter_type =
17536- formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
17537- std::declval<Element>()))>,
17538- Char>;
17539+using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
17540
17541 template <typename R>
17542 using maybe_const_range =
17543 conditional_t<has_const_begin_end<R>::value, const R, R>;
17544
17545-// Workaround a bug in MSVC 2015 and earlier.
17546-#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
17547 template <typename R, typename Char>
17548 struct is_formattable_delayed
17549 : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
17550-#endif
17551 } // namespace detail
17552
17553+template <typename...> struct conjunction : std::true_type {};
17554+template <typename P> struct conjunction<P> : P {};
17555+template <typename P1, typename... Pn>
17556+struct conjunction<P1, Pn...>
17557+ : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
17558+
17559 template <typename T, typename Char, typename Enable = void>
17560 struct range_formatter;
17561
17562@@ -436,6 +383,24 @@ struct range_formatter<
17563 detail::string_literal<Char, '['>{};
17564 basic_string_view<Char> closing_bracket_ =
17565 detail::string_literal<Char, ']'>{};
17566+ bool is_debug = false;
17567+
17568+ template <typename Output, typename It, typename Sentinel, typename U = T,
17569+ FMT_ENABLE_IF(std::is_same<U, Char>::value)>
17570+ auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
17571+ auto buf = basic_memory_buffer<Char>();
17572+ for (; it != end; ++it) buf.push_back(*it);
17573+ auto specs = format_specs();
17574+ specs.set_type(presentation_type::debug);
17575+ return detail::write<Char>(
17576+ out, basic_string_view<Char>(buf.data(), buf.size()), specs);
17577+ }
17578+
17579+ template <typename Output, typename It, typename Sentinel, typename U = T,
17580+ FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
17581+ auto write_debug_string(Output& out, It, Sentinel) const -> Output {
17582+ return out;
17583+ }
17584
17585 public:
17586 FMT_CONSTEXPR range_formatter() {}
17587@@ -454,21 +419,40 @@ struct range_formatter<
17588 closing_bracket_ = close;
17589 }
17590
17591- template <typename ParseContext>
17592- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
17593+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
17594 auto it = ctx.begin();
17595 auto end = ctx.end();
17596+ detail::maybe_set_debug_format(underlying_, true);
17597+ if (it == end) return underlying_.parse(ctx);
17598
17599- if (it != end && *it == 'n') {
17600+ switch (detail::to_ascii(*it)) {
17601+ case 'n':
17602+ set_brackets({}, {});
17603+ ++it;
17604+ break;
17605+ case '?':
17606+ is_debug = true;
17607 set_brackets({}, {});
17608 ++it;
17609+ if (it == end || *it != 's') report_error("invalid format specifier");
17610+ FMT_FALLTHROUGH;
17611+ case 's':
17612+ if (!std::is_same<T, Char>::value)
17613+ report_error("invalid format specifier");
17614+ if (!is_debug) {
17615+ set_brackets(detail::string_literal<Char, '"'>{},
17616+ detail::string_literal<Char, '"'>{});
17617+ set_separator({});
17618+ detail::maybe_set_debug_format(underlying_, false);
17619+ }
17620+ ++it;
17621+ return it;
17622 }
17623
17624 if (it != end && *it != '}') {
17625- if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
17626+ if (*it != ':') report_error("invalid format specifier");
17627+ detail::maybe_set_debug_format(underlying_, false);
17628 ++it;
17629- } else {
17630- detail::maybe_set_debug_format(underlying_, true);
17631 }
17632
17633 ctx.advance_to(it);
17634@@ -477,105 +461,220 @@ struct range_formatter<
17635
17636 template <typename R, typename FormatContext>
17637 auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
17638- detail::range_mapper<buffer_context<Char>> mapper;
17639 auto out = ctx.out();
17640- out = detail::copy_str<Char>(opening_bracket_, out);
17641- int i = 0;
17642 auto it = detail::range_begin(range);
17643 auto end = detail::range_end(range);
17644+ if (is_debug) return write_debug_string(out, std::move(it), end);
17645+
17646+ out = detail::copy<Char>(opening_bracket_, out);
17647+ int i = 0;
17648 for (; it != end; ++it) {
17649- if (i > 0) out = detail::copy_str<Char>(separator_, out);
17650+ if (i > 0) out = detail::copy<Char>(separator_, out);
17651 ctx.advance_to(out);
17652- out = underlying_.format(mapper.map(*it), ctx);
17653+ auto&& item = *it; // Need an lvalue
17654+ out = underlying_.format(item, ctx);
17655 ++i;
17656 }
17657- out = detail::copy_str<Char>(closing_bracket_, out);
17658+ out = detail::copy<Char>(closing_bracket_, out);
17659 return out;
17660 }
17661 };
17662
17663-enum class range_format { disabled, map, set, sequence, string, debug_string };
17664+FMT_EXPORT
17665+template <typename T, typename Char, typename Enable = void>
17666+struct range_format_kind
17667+ : conditional_t<
17668+ is_range<T, Char>::value, detail::range_format_kind_<T>,
17669+ std::integral_constant<range_format, range_format::disabled>> {};
17670
17671-namespace detail {
17672-template <typename T>
17673-struct range_format_kind_
17674- : std::integral_constant<range_format,
17675- std::is_same<uncvref_type<T>, T>::value
17676- ? range_format::disabled
17677- : is_map<T>::value ? range_format::map
17678- : is_set<T>::value ? range_format::set
17679- : range_format::sequence> {};
17680+template <typename R, typename Char>
17681+struct formatter<
17682+ R, Char,
17683+ enable_if_t<conjunction<
17684+ bool_constant<
17685+ range_format_kind<R, Char>::value != range_format::disabled &&
17686+ range_format_kind<R, Char>::value != range_format::map &&
17687+ range_format_kind<R, Char>::value != range_format::string &&
17688+ range_format_kind<R, Char>::value != range_format::debug_string>,
17689+ detail::is_formattable_delayed<R, Char>>::value>> {
17690+ private:
17691+ using range_type = detail::maybe_const_range<R>;
17692+ range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
17693
17694-template <range_format K, typename R, typename Char, typename Enable = void>
17695-struct range_default_formatter;
17696+ public:
17697+ using nonlocking = void;
17698+
17699+ FMT_CONSTEXPR formatter() {
17700+ if (detail::const_check(range_format_kind<R, Char>::value !=
17701+ range_format::set))
17702+ return;
17703+ range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
17704+ detail::string_literal<Char, '}'>{});
17705+ }
17706
17707-template <range_format K>
17708-using range_format_constant = std::integral_constant<range_format, K>;
17709+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
17710+ return range_formatter_.parse(ctx);
17711+ }
17712
17713-template <range_format K, typename R, typename Char>
17714-struct range_default_formatter<
17715- K, R, Char,
17716- enable_if_t<(K == range_format::sequence || K == range_format::map ||
17717- K == range_format::set)>> {
17718- using range_type = detail::maybe_const_range<R>;
17719- range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
17720+ template <typename FormatContext>
17721+ auto format(range_type& range, FormatContext& ctx) const
17722+ -> decltype(ctx.out()) {
17723+ return range_formatter_.format(range, ctx);
17724+ }
17725+};
17726+
17727+// A map formatter.
17728+template <typename R, typename Char>
17729+struct formatter<
17730+ R, Char,
17731+ enable_if_t<conjunction<
17732+ bool_constant<range_format_kind<R, Char>::value == range_format::map>,
17733+ detail::is_formattable_delayed<R, Char>>::value>> {
17734+ private:
17735+ using map_type = detail::maybe_const_range<R>;
17736+ using element_type = detail::uncvref_type<map_type>;
17737
17738- FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
17739+ decltype(detail::tuple::get_formatters<element_type, Char>(
17740+ detail::tuple_index_sequence<element_type>())) formatters_;
17741+ bool no_delimiters_ = false;
17742
17743- FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
17744- underlying_.set_brackets(detail::string_literal<Char, '{'>{},
17745- detail::string_literal<Char, '}'>{});
17746+ public:
17747+ FMT_CONSTEXPR formatter() {}
17748+
17749+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
17750+ auto it = ctx.begin();
17751+ auto end = ctx.end();
17752+ if (it != end) {
17753+ if (detail::to_ascii(*it) == 'n') {
17754+ no_delimiters_ = true;
17755+ ++it;
17756+ }
17757+ if (it != end && *it != '}') {
17758+ if (*it != ':') report_error("invalid format specifier");
17759+ ++it;
17760+ }
17761+ ctx.advance_to(it);
17762+ }
17763+ detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
17764+ return it;
17765 }
17766
17767- FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
17768- underlying_.set_brackets(detail::string_literal<Char, '{'>{},
17769- detail::string_literal<Char, '}'>{});
17770- underlying_.underlying().set_brackets({}, {});
17771- underlying_.underlying().set_separator(
17772- detail::string_literal<Char, ':', ' '>{});
17773+ template <typename FormatContext>
17774+ auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
17775+ auto out = ctx.out();
17776+ basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
17777+ if (!no_delimiters_) out = detail::copy<Char>(open, out);
17778+ int i = 0;
17779+ basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
17780+ for (auto&& value : map) {
17781+ if (i > 0) out = detail::copy<Char>(sep, out);
17782+ ctx.advance_to(out);
17783+ detail::for_each2(formatters_, value,
17784+ detail::format_tuple_element<FormatContext>{
17785+ 0, ctx, detail::string_literal<Char, ':', ' '>{}});
17786+ ++i;
17787+ }
17788+ basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
17789+ if (!no_delimiters_) out = detail::copy<Char>(close, out);
17790+ return out;
17791 }
17792+};
17793
17794- FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
17795+// A (debug_)string formatter.
17796+template <typename R, typename Char>
17797+struct formatter<
17798+ R, Char,
17799+ enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
17800+ range_format_kind<R, Char>::value ==
17801+ range_format::debug_string>> {
17802+ private:
17803+ using range_type = detail::maybe_const_range<R>;
17804+ using string_type =
17805+ conditional_t<std::is_constructible<
17806+ detail::std_string_view<Char>,
17807+ decltype(detail::range_begin(std::declval<R>())),
17808+ decltype(detail::range_end(std::declval<R>()))>::value,
17809+ detail::std_string_view<Char>, std::basic_string<Char>>;
17810
17811- template <typename ParseContext>
17812- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
17813+ formatter<string_type, Char> underlying_;
17814+
17815+ public:
17816+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
17817 return underlying_.parse(ctx);
17818 }
17819
17820 template <typename FormatContext>
17821 auto format(range_type& range, FormatContext& ctx) const
17822 -> decltype(ctx.out()) {
17823- return underlying_.format(range, ctx);
17824+ auto out = ctx.out();
17825+ if (detail::const_check(range_format_kind<R, Char>::value ==
17826+ range_format::debug_string))
17827+ *out++ = '"';
17828+ out = underlying_.format(
17829+ string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
17830+ if (detail::const_check(range_format_kind<R, Char>::value ==
17831+ range_format::debug_string))
17832+ *out++ = '"';
17833+ return out;
17834 }
17835 };
17836-} // namespace detail
17837
17838-template <typename T, typename Char, typename Enable = void>
17839-struct range_format_kind
17840- : conditional_t<
17841- is_range<T, Char>::value, detail::range_format_kind_<T>,
17842- std::integral_constant<range_format, range_format::disabled>> {};
17843+template <typename It, typename Sentinel, typename Char = char>
17844+struct join_view : detail::view {
17845+ It begin;
17846+ Sentinel end;
17847+ basic_string_view<Char> sep;
17848
17849-template <typename R, typename Char>
17850-struct formatter<
17851- R, Char,
17852- enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
17853- range_format::disabled>
17854-// Workaround a bug in MSVC 2015 and earlier.
17855-#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
17856- ,
17857- detail::is_formattable_delayed<R, Char>
17858+ join_view(It b, Sentinel e, basic_string_view<Char> s)
17859+ : begin(std::move(b)), end(e), sep(s) {}
17860+};
17861+
17862+template <typename It, typename Sentinel, typename Char>
17863+struct formatter<join_view<It, Sentinel, Char>, Char> {
17864+ private:
17865+ using value_type =
17866+#ifdef __cpp_lib_ranges
17867+ std::iter_value_t<It>;
17868+#else
17869+ typename std::iterator_traits<It>::value_type;
17870 #endif
17871- >::value>>
17872- : detail::range_default_formatter<range_format_kind<R, Char>::value, R,
17873- Char> {
17874+ formatter<remove_cvref_t<value_type>, Char> value_formatter_;
17875+
17876+ using view = conditional_t<std::is_copy_constructible<It>::value,
17877+ const join_view<It, Sentinel, Char>,
17878+ join_view<It, Sentinel, Char>>;
17879+
17880+ public:
17881+ using nonlocking = void;
17882+
17883+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
17884+ return value_formatter_.parse(ctx);
17885+ }
17886+
17887+ template <typename FormatContext>
17888+ auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
17889+ using iter =
17890+ conditional_t<std::is_copy_constructible<view>::value, It, It&>;
17891+ iter it = value.begin;
17892+ auto out = ctx.out();
17893+ if (it == value.end) return out;
17894+ out = value_formatter_.format(*it, ctx);
17895+ ++it;
17896+ while (it != value.end) {
17897+ out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
17898+ ctx.advance_to(out);
17899+ out = value_formatter_.format(*it, ctx);
17900+ ++it;
17901+ }
17902+ return out;
17903+ }
17904 };
17905
17906-template <typename Char, typename... T> struct tuple_join_view : detail::view {
17907- const std::tuple<T...>& tuple;
17908+template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
17909+ const Tuple& tuple;
17910 basic_string_view<Char> sep;
17911
17912- tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
17913+ tuple_join_view(const Tuple& t, basic_string_view<Char> s)
17914 : tuple(t), sep{s} {}
17915 };
17916
17917@@ -586,65 +685,64 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
17918 # define FMT_TUPLE_JOIN_SPECIFIERS 0
17919 #endif
17920
17921-template <typename Char, typename... T>
17922-struct formatter<tuple_join_view<Char, T...>, Char> {
17923- template <typename ParseContext>
17924- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
17925- return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
17926+template <typename Char, typename Tuple>
17927+struct formatter<tuple_join_view<Char, Tuple>, Char,
17928+ enable_if_t<is_tuple_like<Tuple>::value>> {
17929+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
17930+ return do_parse(ctx, std::tuple_size<Tuple>());
17931 }
17932
17933 template <typename FormatContext>
17934- auto format(const tuple_join_view<Char, T...>& value,
17935+ auto format(const tuple_join_view<Char, Tuple>& value,
17936 FormatContext& ctx) const -> typename FormatContext::iterator {
17937- return do_format(value, ctx,
17938- std::integral_constant<size_t, sizeof...(T)>());
17939+ return do_format(value, ctx, std::tuple_size<Tuple>());
17940 }
17941
17942 private:
17943- std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
17944+ decltype(detail::tuple::get_formatters<Tuple, Char>(
17945+ detail::tuple_index_sequence<Tuple>())) formatters_;
17946
17947- template <typename ParseContext>
17948- FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
17949+ FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
17950 std::integral_constant<size_t, 0>)
17951- -> decltype(ctx.begin()) {
17952+ -> const Char* {
17953 return ctx.begin();
17954 }
17955
17956- template <typename ParseContext, size_t N>
17957- FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
17958+ template <size_t N>
17959+ FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
17960 std::integral_constant<size_t, N>)
17961- -> decltype(ctx.begin()) {
17962+ -> const Char* {
17963 auto end = ctx.begin();
17964 #if FMT_TUPLE_JOIN_SPECIFIERS
17965- end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
17966+ end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
17967 if (N > 1) {
17968 auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
17969 if (end != end1)
17970- FMT_THROW(format_error("incompatible format specs for tuple elements"));
17971+ report_error("incompatible format specs for tuple elements");
17972 }
17973 #endif
17974 return end;
17975 }
17976
17977 template <typename FormatContext>
17978- auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
17979+ auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
17980 std::integral_constant<size_t, 0>) const ->
17981 typename FormatContext::iterator {
17982 return ctx.out();
17983 }
17984
17985 template <typename FormatContext, size_t N>
17986- auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
17987+ auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
17988 std::integral_constant<size_t, N>) const ->
17989 typename FormatContext::iterator {
17990- auto out = std::get<sizeof...(T) - N>(formatters_)
17991- .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
17992- if (N > 1) {
17993- out = std::copy(value.sep.begin(), value.sep.end(), out);
17994- ctx.advance_to(out);
17995- return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
17996- }
17997- return out;
17998+ using std::get;
17999+ auto out =
18000+ std::get<std::tuple_size<Tuple>::value - N>(formatters_)
18001+ .format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
18002+ if (N <= 1) return out;
18003+ out = detail::copy<Char>(value.sep, out);
18004+ ctx.advance_to(out);
18005+ return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
18006 }
18007 };
18008
18009@@ -668,8 +766,11 @@ template <typename Container> struct all {
18010 } // namespace detail
18011
18012 template <typename T, typename Char>
18013-struct formatter<T, Char,
18014- enable_if_t<detail::is_container_adaptor_like<T>::value>>
18015+struct formatter<
18016+ T, Char,
18017+ enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
18018+ bool_constant<range_format_kind<T, Char>::value ==
18019+ range_format::disabled>>::value>>
18020 : formatter<detail::all<typename T::container_type>, Char> {
18021 using all = detail::all<typename T::container_type>;
18022 template <typename FormatContext>
18023@@ -685,40 +786,57 @@ struct formatter<T, Char,
18024
18025 FMT_BEGIN_EXPORT
18026
18027-/**
18028- \rst
18029- Returns an object that formats `tuple` with elements separated by `sep`.
18030-
18031- **Example**::
18032+/// Returns a view that formats the iterator range `[begin, end)` with elements
18033+/// separated by `sep`.
18034+template <typename It, typename Sentinel>
18035+auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
18036+ return {std::move(begin), end, sep};
18037+}
18038
18039- std::tuple<int, char> t = {1, 'a'};
18040- fmt::print("{}", fmt::join(t, ", "));
18041- // Output: "1, a"
18042- \endrst
18043+/**
18044+ * Returns a view that formats `range` with elements separated by `sep`.
18045+ *
18046+ * **Example**:
18047+ *
18048+ * auto v = std::vector<int>{1, 2, 3};
18049+ * fmt::print("{}", fmt::join(v, ", "));
18050+ * // Output: 1, 2, 3
18051+ *
18052+ * `fmt::join` applies passed format specifiers to the range elements:
18053+ *
18054+ * fmt::print("{:02}", fmt::join(v, ", "));
18055+ * // Output: 01, 02, 03
18056 */
18057-template <typename... T>
18058-FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
18059- -> tuple_join_view<char, T...> {
18060- return {tuple, sep};
18061+template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
18062+auto join(Range&& r, string_view sep)
18063+ -> join_view<decltype(detail::range_begin(r)),
18064+ decltype(detail::range_end(r))> {
18065+ return {detail::range_begin(r), detail::range_end(r), sep};
18066 }
18067
18068-template <typename... T>
18069-FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
18070- basic_string_view<wchar_t> sep)
18071- -> tuple_join_view<wchar_t, T...> {
18072+/**
18073+ * Returns an object that formats `std::tuple` with elements separated by `sep`.
18074+ *
18075+ * **Example**:
18076+ *
18077+ * auto t = std::tuple<int, char>{1, 'a'};
18078+ * fmt::print("{}", fmt::join(t, ", "));
18079+ * // Output: 1, a
18080+ */
18081+template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
18082+FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
18083+ -> tuple_join_view<char, Tuple> {
18084 return {tuple, sep};
18085 }
18086
18087 /**
18088- \rst
18089- Returns an object that formats `initializer_list` with elements separated by
18090- `sep`.
18091-
18092- **Example**::
18093-
18094- fmt::print("{}", fmt::join({1, 2, 3}, ", "));
18095- // Output: "1, 2, 3"
18096- \endrst
18097+ * Returns an object that formats `std::initializer_list` with elements
18098+ * separated by `sep`.
18099+ *
18100+ * **Example**:
18101+ *
18102+ * fmt::print("{}", fmt::join({1, 2, 3}, ", "));
18103+ * // Output: "1, 2, 3"
18104 */
18105 template <typename T>
18106 auto join(std::initializer_list<T> list, string_view sep)
18107diff --git a/include/fmt/std.h b/include/fmt/std.h
18108index ac39565..1b0ea5a 100644
18109--- a/include/fmt/std.h
18110+++ b/include/fmt/std.h
18111@@ -8,30 +8,48 @@
18112 #ifndef FMT_STD_H_
18113 #define FMT_STD_H_
18114
18115-#include <cstdlib>
18116-#include <exception>
18117-#include <memory>
18118-#include <thread>
18119-#include <type_traits>
18120-#include <typeinfo>
18121-#include <utility>
18122-
18123+#include "format.h"
18124 #include "ostream.h"
18125
18126-#if FMT_HAS_INCLUDE(<version>)
18127-# include <version>
18128-#endif
18129-// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
18130-#if FMT_CPLUSPLUS >= 201703L
18131-# if FMT_HAS_INCLUDE(<filesystem>)
18132-# include <filesystem>
18133+#ifndef FMT_MODULE
18134+# include <atomic>
18135+# include <bitset>
18136+# include <complex>
18137+# include <cstdlib>
18138+# include <exception>
18139+# include <functional>
18140+# include <memory>
18141+# include <thread>
18142+# include <type_traits>
18143+# include <typeinfo>
18144+# include <utility>
18145+# include <vector>
18146+
18147+// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
18148+# if FMT_CPLUSPLUS >= 201703L
18149+# if FMT_HAS_INCLUDE(<filesystem>) && \
18150+ (!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0)
18151+# include <filesystem>
18152+# endif
18153+# if FMT_HAS_INCLUDE(<variant>)
18154+# include <variant>
18155+# endif
18156+# if FMT_HAS_INCLUDE(<optional>)
18157+# include <optional>
18158+# endif
18159 # endif
18160-# if FMT_HAS_INCLUDE(<variant>)
18161-# include <variant>
18162+// Use > instead of >= in the version check because <source_location> may be
18163+// available after C++17 but before C++20 is marked as implemented.
18164+# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
18165+# include <source_location>
18166 # endif
18167-# if FMT_HAS_INCLUDE(<optional>)
18168-# include <optional>
18169+# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
18170+# include <expected>
18171 # endif
18172+#endif // FMT_MODULE
18173+
18174+#if FMT_HAS_INCLUDE(<version>)
18175+# include <version>
18176 #endif
18177
18178 // GCC 4 does not support FMT_HAS_INCLUDE.
18179@@ -44,67 +62,157 @@
18180 # endif
18181 #endif
18182
18183-#ifdef __cpp_lib_filesystem
18184+// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
18185+#ifndef FMT_CPP_LIB_FILESYSTEM
18186+# ifdef __cpp_lib_filesystem
18187+# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
18188+# else
18189+# define FMT_CPP_LIB_FILESYSTEM 0
18190+# endif
18191+#endif
18192+
18193+#ifndef FMT_CPP_LIB_VARIANT
18194+# ifdef __cpp_lib_variant
18195+# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
18196+# else
18197+# define FMT_CPP_LIB_VARIANT 0
18198+# endif
18199+#endif
18200+
18201+#if FMT_CPP_LIB_FILESYSTEM
18202 FMT_BEGIN_NAMESPACE
18203
18204 namespace detail {
18205
18206-template <typename Char>
18207-void write_escaped_path(basic_memory_buffer<Char>& quoted,
18208- const std::filesystem::path& p) {
18209- write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
18210-}
18211-# ifdef _WIN32
18212-template <>
18213-inline void write_escaped_path<char>(memory_buffer& quoted,
18214- const std::filesystem::path& p) {
18215- auto buf = basic_memory_buffer<wchar_t>();
18216- write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
18217- // Convert UTF-16 to UTF-8.
18218- if (!to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()}))
18219- FMT_THROW(std::runtime_error("invalid utf16"));
18220+template <typename Char, typename PathChar>
18221+auto get_path_string(const std::filesystem::path& p,
18222+ const std::basic_string<PathChar>& native) {
18223+ if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
18224+ return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
18225+ else
18226+ return p.string<Char>();
18227 }
18228-# endif
18229-template <>
18230-inline void write_escaped_path<std::filesystem::path::value_type>(
18231- basic_memory_buffer<std::filesystem::path::value_type>& quoted,
18232- const std::filesystem::path& p) {
18233- write_escaped_string<std::filesystem::path::value_type>(
18234- std::back_inserter(quoted), p.native());
18235+
18236+template <typename Char, typename PathChar>
18237+void write_escaped_path(basic_memory_buffer<Char>& quoted,
18238+ const std::filesystem::path& p,
18239+ const std::basic_string<PathChar>& native) {
18240+ if constexpr (std::is_same_v<Char, char> &&
18241+ std::is_same_v<PathChar, wchar_t>) {
18242+ auto buf = basic_memory_buffer<wchar_t>();
18243+ write_escaped_string<wchar_t>(std::back_inserter(buf), native);
18244+ bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
18245+ FMT_ASSERT(valid, "invalid utf16");
18246+ } else if constexpr (std::is_same_v<Char, PathChar>) {
18247+ write_escaped_string<std::filesystem::path::value_type>(
18248+ std::back_inserter(quoted), native);
18249+ } else {
18250+ write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
18251+ }
18252 }
18253
18254 } // namespace detail
18255
18256-FMT_EXPORT
18257-template <typename Char>
18258-struct formatter<std::filesystem::path, Char>
18259- : formatter<basic_string_view<Char>> {
18260- template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
18261- auto out = formatter<basic_string_view<Char>>::parse(ctx);
18262- this->set_debug_format(false);
18263- return out;
18264+template <typename Char> struct formatter<std::filesystem::path, Char> {
18265+ private:
18266+ format_specs specs_;
18267+ detail::arg_ref<Char> width_ref_;
18268+ bool debug_ = false;
18269+ char path_type_ = 0;
18270+
18271+ public:
18272+ FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
18273+
18274+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
18275+ auto it = ctx.begin(), end = ctx.end();
18276+ if (it == end) return it;
18277+
18278+ it = detail::parse_align(it, end, specs_);
18279+ if (it == end) return it;
18280+
18281+ Char c = *it;
18282+ if ((c >= '0' && c <= '9') || c == '{')
18283+ it = detail::parse_width(it, end, specs_, width_ref_, ctx);
18284+ if (it != end && *it == '?') {
18285+ debug_ = true;
18286+ ++it;
18287+ }
18288+ if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
18289+ return it;
18290 }
18291+
18292 template <typename FormatContext>
18293- auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
18294- typename FormatContext::iterator {
18295+ auto format(const std::filesystem::path& p, FormatContext& ctx) const {
18296+ auto specs = specs_;
18297+ auto path_string =
18298+ !path_type_ ? p.native()
18299+ : p.generic_string<std::filesystem::path::value_type>();
18300+
18301+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
18302+ ctx);
18303+ if (!debug_) {
18304+ auto s = detail::get_path_string<Char>(p, path_string);
18305+ return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
18306+ }
18307 auto quoted = basic_memory_buffer<Char>();
18308- detail::write_escaped_path(quoted, p);
18309- return formatter<basic_string_view<Char>>::format(
18310- basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
18311+ detail::write_escaped_path(quoted, p, path_string);
18312+ return detail::write(ctx.out(),
18313+ basic_string_view<Char>(quoted.data(), quoted.size()),
18314+ specs);
18315 }
18316 };
18317+
18318+class path : public std::filesystem::path {
18319+ public:
18320+ auto display_string() const -> std::string {
18321+ const std::filesystem::path& base = *this;
18322+ return fmt::format(FMT_STRING("{}"), base);
18323+ }
18324+ auto system_string() const -> std::string { return string(); }
18325+
18326+ auto generic_display_string() const -> std::string {
18327+ const std::filesystem::path& base = *this;
18328+ return fmt::format(FMT_STRING("{:g}"), base);
18329+ }
18330+ auto generic_system_string() const -> std::string { return generic_string(); }
18331+};
18332+
18333 FMT_END_NAMESPACE
18334-#endif
18335+#endif // FMT_CPP_LIB_FILESYSTEM
18336
18337 FMT_BEGIN_NAMESPACE
18338-FMT_EXPORT
18339+template <std::size_t N, typename Char>
18340+struct formatter<std::bitset<N>, Char>
18341+ : nested_formatter<basic_string_view<Char>, Char> {
18342+ private:
18343+ // Functor because C++11 doesn't support generic lambdas.
18344+ struct writer {
18345+ const std::bitset<N>& bs;
18346+
18347+ template <typename OutputIt>
18348+ FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
18349+ for (auto pos = N; pos > 0; --pos) {
18350+ out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
18351+ }
18352+
18353+ return out;
18354+ }
18355+ };
18356+
18357+ public:
18358+ template <typename FormatContext>
18359+ auto format(const std::bitset<N>& bs, FormatContext& ctx) const
18360+ -> decltype(ctx.out()) {
18361+ return this->write_padded(ctx, writer{bs});
18362+ }
18363+};
18364+
18365 template <typename Char>
18366 struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
18367 FMT_END_NAMESPACE
18368
18369 #ifdef __cpp_lib_optional
18370 FMT_BEGIN_NAMESPACE
18371-FMT_EXPORT
18372 template <typename T, typename Char>
18373 struct formatter<std::optional<T>, Char,
18374 std::enable_if_t<is_formattable<T, Char>::value>> {
18375@@ -126,13 +234,13 @@ struct formatter<std::optional<T>, Char,
18376 FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
18377
18378 public:
18379- template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
18380+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
18381 maybe_set_debug_format(underlying_, true);
18382 return underlying_.parse(ctx);
18383 }
18384
18385 template <typename FormatContext>
18386- auto format(std::optional<T> const& opt, FormatContext& ctx) const
18387+ auto format(const std::optional<T>& opt, FormatContext& ctx) const
18388 -> decltype(ctx.out()) {
18389 if (!opt) return detail::write<Char>(ctx.out(), none);
18390
18391@@ -146,24 +254,80 @@ struct formatter<std::optional<T>, Char,
18392 FMT_END_NAMESPACE
18393 #endif // __cpp_lib_optional
18394
18395-#ifdef __cpp_lib_variant
18396+#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
18397+
18398 FMT_BEGIN_NAMESPACE
18399-FMT_EXPORT
18400-template <typename Char> struct formatter<std::monostate, Char> {
18401- template <typename ParseContext>
18402- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
18403+namespace detail {
18404+
18405+template <typename Char, typename OutputIt, typename T>
18406+auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
18407+ if constexpr (has_to_string_view<T>::value)
18408+ return write_escaped_string<Char>(out, detail::to_string_view(v));
18409+ if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
18410+ return write<Char>(out, v);
18411+}
18412+
18413+} // namespace detail
18414+
18415+FMT_END_NAMESPACE
18416+#endif
18417+
18418+#ifdef __cpp_lib_expected
18419+FMT_BEGIN_NAMESPACE
18420+
18421+template <typename T, typename E, typename Char>
18422+struct formatter<std::expected<T, E>, Char,
18423+ std::enable_if_t<(std::is_void<T>::value ||
18424+ is_formattable<T, Char>::value) &&
18425+ is_formattable<E, Char>::value>> {
18426+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
18427 return ctx.begin();
18428 }
18429
18430 template <typename FormatContext>
18431- auto format(const std::monostate&, FormatContext& ctx) const
18432+ auto format(const std::expected<T, E>& value, FormatContext& ctx) const
18433+ -> decltype(ctx.out()) {
18434+ auto out = ctx.out();
18435+
18436+ if (value.has_value()) {
18437+ out = detail::write<Char>(out, "expected(");
18438+ if constexpr (!std::is_void<T>::value)
18439+ out = detail::write_escaped_alternative<Char>(out, *value);
18440+ } else {
18441+ out = detail::write<Char>(out, "unexpected(");
18442+ out = detail::write_escaped_alternative<Char>(out, value.error());
18443+ }
18444+ *out++ = ')';
18445+ return out;
18446+ }
18447+};
18448+FMT_END_NAMESPACE
18449+#endif // __cpp_lib_expected
18450+
18451+#ifdef __cpp_lib_source_location
18452+FMT_BEGIN_NAMESPACE
18453+template <> struct formatter<std::source_location> {
18454+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
18455+
18456+ template <typename FormatContext>
18457+ auto format(const std::source_location& loc, FormatContext& ctx) const
18458 -> decltype(ctx.out()) {
18459 auto out = ctx.out();
18460- out = detail::write<Char>(out, "monostate");
18461+ out = detail::write(out, loc.file_name());
18462+ out = detail::write(out, ':');
18463+ out = detail::write<char>(out, loc.line());
18464+ out = detail::write(out, ':');
18465+ out = detail::write<char>(out, loc.column());
18466+ out = detail::write(out, ": ");
18467+ out = detail::write(out, loc.function_name());
18468 return out;
18469 }
18470 };
18471+FMT_END_NAMESPACE
18472+#endif
18473
18474+#if FMT_CPP_LIB_VARIANT
18475+FMT_BEGIN_NAMESPACE
18476 namespace detail {
18477
18478 template <typename T>
18479@@ -186,17 +350,8 @@ template <typename T, typename C> class is_variant_formattable_ {
18480 decltype(check(variant_index_sequence<T>{}))::value;
18481 };
18482
18483-template <typename Char, typename OutputIt, typename T>
18484-auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
18485- if constexpr (is_string<T>::value)
18486- return write_escaped_string<Char>(out, detail::to_string_view(v));
18487- else if constexpr (std::is_same_v<T, Char>)
18488- return write_escaped_char(out, v);
18489- else
18490- return write<Char>(out, v);
18491-}
18492-
18493 } // namespace detail
18494+
18495 template <typename T> struct is_variant_like {
18496 static constexpr const bool value = detail::is_variant_like_<T>::value;
18497 };
18498@@ -206,14 +361,24 @@ template <typename T, typename C> struct is_variant_formattable {
18499 detail::is_variant_formattable_<T, C>::value;
18500 };
18501
18502-FMT_EXPORT
18503+template <typename Char> struct formatter<std::monostate, Char> {
18504+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
18505+ return ctx.begin();
18506+ }
18507+
18508+ template <typename FormatContext>
18509+ auto format(const std::monostate&, FormatContext& ctx) const
18510+ -> decltype(ctx.out()) {
18511+ return detail::write<Char>(ctx.out(), "monostate");
18512+ }
18513+};
18514+
18515 template <typename Variant, typename Char>
18516 struct formatter<
18517 Variant, Char,
18518 std::enable_if_t<std::conjunction_v<
18519 is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
18520- template <typename ParseContext>
18521- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
18522+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
18523 return ctx.begin();
18524 }
18525
18526@@ -223,13 +388,14 @@ struct formatter<
18527 auto out = ctx.out();
18528
18529 out = detail::write<Char>(out, "variant(");
18530- try {
18531+ FMT_TRY {
18532 std::visit(
18533 [&](const auto& v) {
18534- out = detail::write_variant_alternative<Char>(out, v);
18535+ out = detail::write_escaped_alternative<Char>(out, v);
18536 },
18537 value);
18538- } catch (const std::bad_variant_access&) {
18539+ }
18540+ FMT_CATCH(const std::bad_variant_access&) {
18541 detail::write<Char>(out, "valueless by exception");
18542 }
18543 *out++ = ')';
18544@@ -237,113 +403,308 @@ struct formatter<
18545 }
18546 };
18547 FMT_END_NAMESPACE
18548-#endif // __cpp_lib_variant
18549+#endif // FMT_CPP_LIB_VARIANT
18550
18551 FMT_BEGIN_NAMESPACE
18552-FMT_EXPORT
18553-template <typename Char> struct formatter<std::error_code, Char> {
18554- template <typename ParseContext>
18555- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
18556- return ctx.begin();
18557+template <> struct formatter<std::error_code> {
18558+ private:
18559+ format_specs specs_;
18560+ detail::arg_ref<char> width_ref_;
18561+
18562+ public:
18563+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
18564+ auto it = ctx.begin(), end = ctx.end();
18565+ if (it == end) return it;
18566+
18567+ it = detail::parse_align(it, end, specs_);
18568+ if (it == end) return it;
18569+
18570+ char c = *it;
18571+ if ((c >= '0' && c <= '9') || c == '{')
18572+ it = detail::parse_width(it, end, specs_, width_ref_, ctx);
18573+ return it;
18574 }
18575
18576 template <typename FormatContext>
18577- FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
18578+ FMT_CONSTEXPR20 auto format(const std::error_code& ec,
18579+ FormatContext& ctx) const -> decltype(ctx.out()) {
18580+ auto specs = specs_;
18581+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
18582+ ctx);
18583+ memory_buffer buf;
18584+ buf.append(string_view(ec.category().name()));
18585+ buf.push_back(':');
18586+ detail::write<char>(appender(buf), ec.value());
18587+ return detail::write<char>(ctx.out(), string_view(buf.data(), buf.size()),
18588+ specs);
18589+ }
18590+};
18591+
18592+#if FMT_USE_RTTI
18593+namespace detail {
18594+
18595+template <typename Char, typename OutputIt>
18596+auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
18597+# ifdef FMT_HAS_ABI_CXA_DEMANGLE
18598+ int status = 0;
18599+ std::size_t size = 0;
18600+ std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
18601+ abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
18602+
18603+ string_view demangled_name_view;
18604+ if (demangled_name_ptr) {
18605+ demangled_name_view = demangled_name_ptr.get();
18606+
18607+ // Normalization of stdlib inline namespace names.
18608+ // libc++ inline namespaces.
18609+ // std::__1::* -> std::*
18610+ // std::__1::__fs::* -> std::*
18611+ // libstdc++ inline namespaces.
18612+ // std::__cxx11::* -> std::*
18613+ // std::filesystem::__cxx11::* -> std::filesystem::*
18614+ if (demangled_name_view.starts_with("std::")) {
18615+ char* begin = demangled_name_ptr.get();
18616+ char* to = begin + 5; // std::
18617+ for (char *from = to, *end = begin + demangled_name_view.size();
18618+ from < end;) {
18619+ // This is safe, because demangled_name is NUL-terminated.
18620+ if (from[0] == '_' && from[1] == '_') {
18621+ char* next = from + 1;
18622+ while (next < end && *next != ':') next++;
18623+ if (next[0] == ':' && next[1] == ':') {
18624+ from = next + 2;
18625+ continue;
18626+ }
18627+ }
18628+ *to++ = *from++;
18629+ }
18630+ demangled_name_view = {begin, detail::to_unsigned(to - begin)};
18631+ }
18632+ } else {
18633+ demangled_name_view = string_view(ti.name());
18634+ }
18635+ return detail::write_bytes<Char>(out, demangled_name_view);
18636+# elif FMT_MSC_VERSION
18637+ const string_view demangled_name(ti.name());
18638+ for (std::size_t i = 0; i < demangled_name.size(); ++i) {
18639+ auto sub = demangled_name;
18640+ sub.remove_prefix(i);
18641+ if (sub.starts_with("enum ")) {
18642+ i += 4;
18643+ continue;
18644+ }
18645+ if (sub.starts_with("class ") || sub.starts_with("union ")) {
18646+ i += 5;
18647+ continue;
18648+ }
18649+ if (sub.starts_with("struct ")) {
18650+ i += 6;
18651+ continue;
18652+ }
18653+ if (*sub.begin() != ' ') *out++ = *sub.begin();
18654+ }
18655+ return out;
18656+# else
18657+ return detail::write_bytes<Char>(out, string_view(ti.name()));
18658+# endif
18659+}
18660+
18661+} // namespace detail
18662+
18663+template <typename Char>
18664+struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
18665+ > {
18666+ public:
18667+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
18668+ return ctx.begin();
18669+ }
18670+
18671+ template <typename Context>
18672+ auto format(const std::type_info& ti, Context& ctx) const
18673 -> decltype(ctx.out()) {
18674- auto out = ctx.out();
18675- out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
18676- out = detail::write<Char>(out, Char(':'));
18677- out = detail::write<Char>(out, ec.value());
18678- return out;
18679+ return detail::write_demangled_name<Char>(ctx.out(), ti);
18680 }
18681 };
18682+#endif
18683
18684-FMT_EXPORT
18685 template <typename T, typename Char>
18686 struct formatter<
18687- T, Char,
18688+ T, Char, // DEPRECATED! Mixing code unit types.
18689 typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
18690 private:
18691 bool with_typename_ = false;
18692
18693 public:
18694- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
18695- -> decltype(ctx.begin()) {
18696+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
18697 auto it = ctx.begin();
18698 auto end = ctx.end();
18699 if (it == end || *it == '}') return it;
18700 if (*it == 't') {
18701 ++it;
18702- with_typename_ = true;
18703+ with_typename_ = FMT_USE_RTTI != 0;
18704 }
18705 return it;
18706 }
18707
18708- template <typename OutputIt>
18709- auto format(const std::exception& ex,
18710- basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
18711- format_specs<Char> spec;
18712+ template <typename Context>
18713+ auto format(const std::exception& ex, Context& ctx) const
18714+ -> decltype(ctx.out()) {
18715 auto out = ctx.out();
18716- if (!with_typename_)
18717- return detail::write_bytes(out, string_view(ex.what()), spec);
18718-
18719- const std::type_info& ti = typeid(ex);
18720-#ifdef FMT_HAS_ABI_CXA_DEMANGLE
18721- int status = 0;
18722- std::size_t size = 0;
18723- std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
18724- abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
18725-
18726- string_view demangled_name_view;
18727- if (demangled_name_ptr) {
18728- demangled_name_view = demangled_name_ptr.get();
18729-
18730- // Normalization of stdlib inline namespace names.
18731- // libc++ inline namespaces.
18732- // std::__1::* -> std::*
18733- // std::__1::__fs::* -> std::*
18734- // libstdc++ inline namespaces.
18735- // std::__cxx11::* -> std::*
18736- // std::filesystem::__cxx11::* -> std::filesystem::*
18737- if (demangled_name_view.starts_with("std::")) {
18738- char* begin = demangled_name_ptr.get();
18739- char* to = begin + 5; // std::
18740- for (char *from = to, *end = begin + demangled_name_view.size();
18741- from < end;) {
18742- // This is safe, because demangled_name is NUL-terminated.
18743- if (from[0] == '_' && from[1] == '_') {
18744- char* next = from + 1;
18745- while (next < end && *next != ':') next++;
18746- if (next[0] == ':' && next[1] == ':') {
18747- from = next + 2;
18748- continue;
18749- }
18750- }
18751- *to++ = *from++;
18752- }
18753- demangled_name_view = {begin, detail::to_unsigned(to - begin)};
18754- }
18755- } else {
18756- demangled_name_view = string_view(ti.name());
18757+#if FMT_USE_RTTI
18758+ if (with_typename_) {
18759+ out = detail::write_demangled_name<Char>(out, typeid(ex));
18760+ *out++ = ':';
18761+ *out++ = ' ';
18762 }
18763- out = detail::write_bytes(out, demangled_name_view, spec);
18764-#elif FMT_MSC_VERSION
18765- string_view demangled_name_view(ti.name());
18766- if (demangled_name_view.starts_with("class "))
18767- demangled_name_view.remove_prefix(6);
18768- else if (demangled_name_view.starts_with("struct "))
18769- demangled_name_view.remove_prefix(7);
18770- out = detail::write_bytes(out, demangled_name_view, spec);
18771-#else
18772- out = detail::write_bytes(out, string_view(ti.name()), spec);
18773 #endif
18774- out = detail::write<Char>(out, Char(':'));
18775- out = detail::write<Char>(out, Char(' '));
18776- out = detail::write_bytes(out, string_view(ex.what()), spec);
18777+ return detail::write_bytes<Char>(out, string_view(ex.what()));
18778+ }
18779+};
18780+
18781+namespace detail {
18782+
18783+template <typename T, typename Enable = void>
18784+struct has_flip : std::false_type {};
18785+
18786+template <typename T>
18787+struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
18788+ : std::true_type {};
18789+
18790+template <typename T> struct is_bit_reference_like {
18791+ static constexpr const bool value =
18792+ std::is_convertible<T, bool>::value &&
18793+ std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
18794+};
18795+
18796+#ifdef _LIBCPP_VERSION
18797+
18798+// Workaround for libc++ incompatibility with C++ standard.
18799+// According to the Standard, `bitset::operator[] const` returns bool.
18800+template <typename C>
18801+struct is_bit_reference_like<std::__bit_const_reference<C>> {
18802+ static constexpr const bool value = true;
18803+};
18804+
18805+#endif
18806+
18807+} // namespace detail
18808+
18809+// We can't use std::vector<bool, Allocator>::reference and
18810+// std::bitset<N>::reference because the compiler can't deduce Allocator and N
18811+// in partial specialization.
18812+template <typename BitRef, typename Char>
18813+struct formatter<BitRef, Char,
18814+ enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
18815+ : formatter<bool, Char> {
18816+ template <typename FormatContext>
18817+ FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
18818+ -> decltype(ctx.out()) {
18819+ return formatter<bool, Char>::format(v, ctx);
18820+ }
18821+};
18822+
18823+template <typename T, typename Deleter>
18824+auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
18825+ return p.get();
18826+}
18827+template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
18828+ return p.get();
18829+}
18830+
18831+template <typename T, typename Char>
18832+struct formatter<std::atomic<T>, Char,
18833+ enable_if_t<is_formattable<T, Char>::value>>
18834+ : formatter<T, Char> {
18835+ template <typename FormatContext>
18836+ auto format(const std::atomic<T>& v, FormatContext& ctx) const
18837+ -> decltype(ctx.out()) {
18838+ return formatter<T, Char>::format(v.load(), ctx);
18839+ }
18840+};
18841+
18842+#ifdef __cpp_lib_atomic_flag_test
18843+template <typename Char>
18844+struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
18845+ template <typename FormatContext>
18846+ auto format(const std::atomic_flag& v, FormatContext& ctx) const
18847+ -> decltype(ctx.out()) {
18848+ return formatter<bool, Char>::format(v.test(), ctx);
18849+ }
18850+};
18851+#endif // __cpp_lib_atomic_flag_test
18852
18853+template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
18854+ private:
18855+ detail::dynamic_format_specs<Char> specs_;
18856+
18857+ template <typename FormatContext, typename OutputIt>
18858+ FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
18859+ detail::dynamic_format_specs<Char>& specs,
18860+ FormatContext& ctx, OutputIt out) const
18861+ -> OutputIt {
18862+ if (c.real() != 0) {
18863+ *out++ = Char('(');
18864+ out = detail::write<Char>(out, c.real(), specs, ctx.locale());
18865+ specs.set_sign(sign::plus);
18866+ out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
18867+ if (!detail::isfinite(c.imag())) *out++ = Char(' ');
18868+ *out++ = Char('i');
18869+ *out++ = Char(')');
18870+ return out;
18871+ }
18872+ out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
18873+ if (!detail::isfinite(c.imag())) *out++ = Char(' ');
18874+ *out++ = Char('i');
18875 return out;
18876 }
18877+
18878+ public:
18879+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
18880+ if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
18881+ return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
18882+ detail::type_constant<T, Char>::value);
18883+ }
18884+
18885+ template <typename FormatContext>
18886+ auto format(const std::complex<T>& c, FormatContext& ctx) const
18887+ -> decltype(ctx.out()) {
18888+ auto specs = specs_;
18889+ if (specs.dynamic()) {
18890+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
18891+ specs.width_ref, ctx);
18892+ detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
18893+ specs.precision_ref, ctx);
18894+ }
18895+
18896+ if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
18897+ auto buf = basic_memory_buffer<Char>();
18898+
18899+ auto outer_specs = format_specs();
18900+ outer_specs.width = specs.width;
18901+ outer_specs.copy_fill_from(specs);
18902+ outer_specs.set_align(specs.align());
18903+
18904+ specs.width = 0;
18905+ specs.set_fill({});
18906+ specs.set_align(align::none);
18907+
18908+ do_format(c, specs, ctx, basic_appender<Char>(buf));
18909+ return detail::write<Char>(ctx.out(),
18910+ basic_string_view<Char>(buf.data(), buf.size()),
18911+ outer_specs);
18912+ }
18913+};
18914+
18915+template <typename T, typename Char>
18916+struct formatter<std::reference_wrapper<T>, Char,
18917+ enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
18918+ : formatter<remove_cvref_t<T>, Char> {
18919+ template <typename FormatContext>
18920+ auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
18921+ -> decltype(ctx.out()) {
18922+ return formatter<remove_cvref_t<T>, Char>::format(ref.get(), ctx);
18923+ }
18924 };
18925-FMT_END_NAMESPACE
18926
18927+FMT_END_NAMESPACE
18928 #endif // FMT_STD_H_
18929diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h
18930index 625ec36..d959010 100644
18931--- a/include/fmt/xchar.h
18932+++ b/include/fmt/xchar.h
18933@@ -8,12 +8,16 @@
18934 #ifndef FMT_XCHAR_H_
18935 #define FMT_XCHAR_H_
18936
18937-#include <cwchar>
18938-
18939+#include "color.h"
18940 #include "format.h"
18941-
18942-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
18943-# include <locale>
18944+#include "ostream.h"
18945+#include "ranges.h"
18946+
18947+#ifndef FMT_MODULE
18948+# include <cwchar>
18949+# if FMT_USE_LOCALE
18950+# include <locale>
18951+# endif
18952 #endif
18953
18954 FMT_BEGIN_NAMESPACE
18955@@ -22,10 +26,26 @@ namespace detail {
18956 template <typename T>
18957 using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
18958
18959-inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
18960- loc_value value, const format_specs<wchar_t>& specs,
18961- locale_ref loc) -> bool {
18962-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
18963+template <typename S, typename = void> struct format_string_char {};
18964+
18965+template <typename S>
18966+struct format_string_char<
18967+ S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
18968+ using type = char_t<S>;
18969+};
18970+
18971+template <typename S>
18972+struct format_string_char<
18973+ S, enable_if_t<std::is_base_of<detail::compile_string, S>::value>> {
18974+ using type = typename S::char_type;
18975+};
18976+
18977+template <typename S>
18978+using format_string_char_t = typename format_string_char<S>::type;
18979+
18980+inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
18981+ const format_specs& specs, locale_ref loc) -> bool {
18982+#if FMT_USE_LOCALE
18983 auto& numpunct =
18984 std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
18985 auto separator = std::wstring();
18986@@ -40,41 +60,79 @@ inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
18987 FMT_BEGIN_EXPORT
18988
18989 using wstring_view = basic_string_view<wchar_t>;
18990-using wformat_parse_context = basic_format_parse_context<wchar_t>;
18991-using wformat_context = buffer_context<wchar_t>;
18992+using wformat_parse_context = parse_context<wchar_t>;
18993+using wformat_context = buffered_context<wchar_t>;
18994 using wformat_args = basic_format_args<wformat_context>;
18995 using wmemory_buffer = basic_memory_buffer<wchar_t>;
18996
18997-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
18998-// Workaround broken conversion on older gcc.
18999-template <typename... Args> using wformat_string = wstring_view;
19000-inline auto runtime(wstring_view s) -> wstring_view { return s; }
19001-#else
19002-template <typename... Args>
19003-using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
19004+template <typename Char, typename... T> struct basic_fstring {
19005+ private:
19006+ basic_string_view<Char> str_;
19007+
19008+ static constexpr int num_static_named_args =
19009+ detail::count_static_named_args<T...>();
19010+
19011+ using checker = detail::format_string_checker<
19012+ Char, static_cast<int>(sizeof...(T)), num_static_named_args,
19013+ num_static_named_args != detail::count_named_args<T...>()>;
19014+
19015+ using arg_pack = detail::arg_pack<T...>;
19016+
19017+ public:
19018+ using t = basic_fstring;
19019+
19020+ template <typename S,
19021+ FMT_ENABLE_IF(
19022+ std::is_convertible<const S&, basic_string_view<Char>>::value)>
19023+ FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) {
19024+ if (FMT_USE_CONSTEVAL)
19025+ detail::parse_format_string<Char>(s, checker(s, arg_pack()));
19026+ }
19027+ template <typename S,
19028+ FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
19029+ std::is_same<typename S::char_type, Char>::value)>
19030+ FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) {
19031+ FMT_CONSTEXPR auto sv = basic_string_view<Char>(S());
19032+ FMT_CONSTEXPR int ignore =
19033+ (parse_format_string(sv, checker(sv, arg_pack())), 0);
19034+ detail::ignore_unused(ignore);
19035+ }
19036+ basic_fstring(runtime_format_string<Char> fmt) : str_(fmt.str) {}
19037+
19038+ operator basic_string_view<Char>() const { return str_; }
19039+ auto get() const -> basic_string_view<Char> { return str_; }
19040+};
19041+
19042+template <typename Char, typename... T>
19043+using basic_format_string = basic_fstring<Char, T...>;
19044+
19045+template <typename... T>
19046+using wformat_string = typename basic_format_string<wchar_t, T...>::t;
19047 inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
19048 return {{s}};
19049 }
19050-#endif
19051
19052 template <> struct is_char<wchar_t> : std::true_type {};
19053-template <> struct is_char<detail::char8_type> : std::true_type {};
19054 template <> struct is_char<char16_t> : std::true_type {};
19055 template <> struct is_char<char32_t> : std::true_type {};
19056
19057+#ifdef __cpp_char8_t
19058+template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
19059+#endif
19060+
19061 template <typename... T>
19062-constexpr format_arg_store<wformat_context, T...> make_wformat_args(
19063- const T&... args) {
19064- return {args...};
19065+constexpr auto make_wformat_args(T&... args)
19066+ -> decltype(fmt::make_format_args<wformat_context>(args...)) {
19067+ return fmt::make_format_args<wformat_context>(args...);
19068 }
19069
19070+#if !FMT_USE_NONTYPE_TEMPLATE_ARGS
19071 inline namespace literals {
19072-#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
19073-constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
19074+inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg<wchar_t> {
19075 return {s};
19076 }
19077-#endif
19078 } // namespace literals
19079+#endif
19080
19081 template <typename It, typename Sentinel>
19082 auto join(It begin, Sentinel end, wstring_view sep)
19083@@ -82,9 +140,9 @@ auto join(It begin, Sentinel end, wstring_view sep)
19084 return {begin, end, sep};
19085 }
19086
19087-template <typename Range>
19088+template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
19089 auto join(Range&& range, wstring_view sep)
19090- -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
19091+ -> join_view<decltype(std::begin(range)), decltype(std::end(range)),
19092 wchar_t> {
19093 return join(std::begin(range), std::end(range), sep);
19094 }
19095@@ -95,13 +153,19 @@ auto join(std::initializer_list<T> list, wstring_view sep)
19096 return join(std::begin(list), std::end(list), sep);
19097 }
19098
19099+template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
19100+auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
19101+ -> tuple_join_view<wchar_t, Tuple> {
19102+ return {tuple, sep};
19103+}
19104+
19105 template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
19106-auto vformat(basic_string_view<Char> format_str,
19107- basic_format_args<buffer_context<type_identity_t<Char>>> args)
19108+auto vformat(basic_string_view<Char> fmt,
19109+ typename detail::vformat_args<Char>::type args)
19110 -> std::basic_string<Char> {
19111 auto buf = basic_memory_buffer<Char>();
19112- detail::vformat_to(buf, format_str, args);
19113- return to_string(buf);
19114+ detail::vformat_to(buf, fmt, args);
19115+ return {buf.data(), buf.size()};
19116 }
19117
19118 template <typename... T>
19119@@ -109,110 +173,122 @@ auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
19120 return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
19121 }
19122
19123+template <typename OutputIt, typename... T>
19124+auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
19125+ -> OutputIt {
19126+ return vformat_to(out, fmt::wstring_view(fmt),
19127+ fmt::make_wformat_args(args...));
19128+}
19129+
19130 // Pass char_t as a default template parameter instead of using
19131 // std::basic_string<char_t<S>> to reduce the symbol size.
19132-template <typename S, typename... T, typename Char = char_t<S>,
19133+template <typename S, typename... T,
19134+ typename Char = detail::format_string_char_t<S>,
19135 FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
19136 !std::is_same<Char, wchar_t>::value)>
19137-auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
19138- return vformat(detail::to_string_view(format_str),
19139- fmt::make_format_args<buffer_context<Char>>(args...));
19140+auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
19141+ return vformat(detail::to_string_view(fmt),
19142+ fmt::make_format_args<buffered_context<Char>>(args...));
19143 }
19144
19145-template <typename Locale, typename S, typename Char = char_t<S>,
19146+template <typename Locale, typename S,
19147+ typename Char = detail::format_string_char_t<S>,
19148 FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
19149 detail::is_exotic_char<Char>::value)>
19150-inline auto vformat(
19151- const Locale& loc, const S& format_str,
19152- basic_format_args<buffer_context<type_identity_t<Char>>> args)
19153+inline auto vformat(const Locale& loc, const S& fmt,
19154+ typename detail::vformat_args<Char>::type args)
19155 -> std::basic_string<Char> {
19156- return detail::vformat(loc, detail::to_string_view(format_str), args);
19157+ auto buf = basic_memory_buffer<Char>();
19158+ detail::vformat_to(buf, detail::to_string_view(fmt), args,
19159+ detail::locale_ref(loc));
19160+ return {buf.data(), buf.size()};
19161 }
19162
19163-template <typename Locale, typename S, typename... T, typename Char = char_t<S>,
19164+template <typename Locale, typename S, typename... T,
19165+ typename Char = detail::format_string_char_t<S>,
19166 FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
19167 detail::is_exotic_char<Char>::value)>
19168-inline auto format(const Locale& loc, const S& format_str, T&&... args)
19169+inline auto format(const Locale& loc, const S& fmt, T&&... args)
19170 -> std::basic_string<Char> {
19171- return detail::vformat(loc, detail::to_string_view(format_str),
19172- fmt::make_format_args<buffer_context<Char>>(args...));
19173+ return vformat(loc, detail::to_string_view(fmt),
19174+ fmt::make_format_args<buffered_context<Char>>(args...));
19175 }
19176
19177-template <typename OutputIt, typename S, typename Char = char_t<S>,
19178+template <typename OutputIt, typename S,
19179+ typename Char = detail::format_string_char_t<S>,
19180 FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
19181 detail::is_exotic_char<Char>::value)>
19182-auto vformat_to(OutputIt out, const S& format_str,
19183- basic_format_args<buffer_context<type_identity_t<Char>>> args)
19184- -> OutputIt {
19185+auto vformat_to(OutputIt out, const S& fmt,
19186+ typename detail::vformat_args<Char>::type args) -> OutputIt {
19187 auto&& buf = detail::get_buffer<Char>(out);
19188- detail::vformat_to(buf, detail::to_string_view(format_str), args);
19189+ detail::vformat_to(buf, detail::to_string_view(fmt), args);
19190 return detail::get_iterator(buf, out);
19191 }
19192
19193 template <typename OutputIt, typename S, typename... T,
19194- typename Char = char_t<S>,
19195- FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
19196- detail::is_exotic_char<Char>::value)>
19197+ typename Char = detail::format_string_char_t<S>,
19198+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
19199+ !std::is_same<Char, char>::value &&
19200+ !std::is_same<Char, wchar_t>::value)>
19201 inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
19202 return vformat_to(out, detail::to_string_view(fmt),
19203- fmt::make_format_args<buffer_context<Char>>(args...));
19204+ fmt::make_format_args<buffered_context<Char>>(args...));
19205 }
19206
19207 template <typename Locale, typename S, typename OutputIt, typename... Args,
19208- typename Char = char_t<S>,
19209+ typename Char = detail::format_string_char_t<S>,
19210 FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
19211 detail::is_locale<Locale>::value&&
19212 detail::is_exotic_char<Char>::value)>
19213-inline auto vformat_to(
19214- OutputIt out, const Locale& loc, const S& format_str,
19215- basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
19216+inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
19217+ typename detail::vformat_args<Char>::type args)
19218+ -> OutputIt {
19219 auto&& buf = detail::get_buffer<Char>(out);
19220- vformat_to(buf, detail::to_string_view(format_str), args,
19221- detail::locale_ref(loc));
19222+ vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
19223 return detail::get_iterator(buf, out);
19224 }
19225
19226-template <
19227- typename OutputIt, typename Locale, typename S, typename... T,
19228- typename Char = char_t<S>,
19229- bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
19230- detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
19231-inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
19232+template <typename Locale, typename OutputIt, typename S, typename... T,
19233+ typename Char = detail::format_string_char_t<S>,
19234+ bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
19235+ detail::is_locale<Locale>::value &&
19236+ detail::is_exotic_char<Char>::value>
19237+inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
19238 T&&... args) ->
19239 typename std::enable_if<enable, OutputIt>::type {
19240- return vformat_to(out, loc, detail::to_string_view(format_str),
19241- fmt::make_format_args<buffer_context<Char>>(args...));
19242+ return vformat_to(out, loc, detail::to_string_view(fmt),
19243+ fmt::make_format_args<buffered_context<Char>>(args...));
19244 }
19245
19246 template <typename OutputIt, typename Char, typename... Args,
19247 FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
19248 detail::is_exotic_char<Char>::value)>
19249-inline auto vformat_to_n(
19250- OutputIt out, size_t n, basic_string_view<Char> format_str,
19251- basic_format_args<buffer_context<type_identity_t<Char>>> args)
19252+inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
19253+ typename detail::vformat_args<Char>::type args)
19254 -> format_to_n_result<OutputIt> {
19255 using traits = detail::fixed_buffer_traits;
19256 auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
19257- detail::vformat_to(buf, format_str, args);
19258+ detail::vformat_to(buf, fmt, args);
19259 return {buf.out(), buf.count()};
19260 }
19261
19262 template <typename OutputIt, typename S, typename... T,
19263- typename Char = char_t<S>,
19264+ typename Char = detail::format_string_char_t<S>,
19265 FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
19266 detail::is_exotic_char<Char>::value)>
19267 inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
19268 -> format_to_n_result<OutputIt> {
19269- return vformat_to_n(out, n, detail::to_string_view(fmt),
19270- fmt::make_format_args<buffer_context<Char>>(args...));
19271+ return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
19272+ fmt::make_format_args<buffered_context<Char>>(args...));
19273 }
19274
19275-template <typename S, typename... T, typename Char = char_t<S>,
19276+template <typename S, typename... T,
19277+ typename Char = detail::format_string_char_t<S>,
19278 FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
19279 inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
19280 auto buf = detail::counting_buffer<Char>();
19281 detail::vformat_to(buf, detail::to_string_view(fmt),
19282- fmt::make_format_args<buffer_context<Char>>(args...));
19283+ fmt::make_format_args<buffered_context<Char>>(args...));
19284 return buf.count();
19285 }
19286
19287@@ -246,9 +322,48 @@ template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
19288 return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
19289 }
19290
19291-/**
19292- Converts *value* to ``std::wstring`` using the default format for type *T*.
19293- */
19294+inline auto vformat(text_style ts, wstring_view fmt, wformat_args args)
19295+ -> std::wstring {
19296+ auto buf = wmemory_buffer();
19297+ detail::vformat_to(buf, ts, fmt, args);
19298+ return {buf.data(), buf.size()};
19299+}
19300+
19301+template <typename... T>
19302+inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
19303+ -> std::wstring {
19304+ return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
19305+}
19306+
19307+template <typename... T>
19308+FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string<T...> fmt,
19309+ const T&... args) {
19310+ vprint(f, ts, fmt, fmt::make_wformat_args(args...));
19311+}
19312+
19313+template <typename... T>
19314+FMT_DEPRECATED void print(text_style ts, wformat_string<T...> fmt,
19315+ const T&... args) {
19316+ return print(stdout, ts, fmt, args...);
19317+}
19318+
19319+inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
19320+ auto buffer = basic_memory_buffer<wchar_t>();
19321+ detail::vformat_to(buffer, fmt, args);
19322+ detail::write_buffer(os, buffer);
19323+}
19324+
19325+template <typename... T>
19326+void print(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
19327+ vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
19328+}
19329+
19330+template <typename... T>
19331+void println(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
19332+ print(os, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
19333+}
19334+
19335+/// Converts `value` to `std::wstring` using the default format for type `T`.
19336 template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
19337 return format(FMT_STRING(L"{}"), value);
19338 }
diff --git a/meta-oe/recipes-support/btop/btop_1.4.0.bb b/meta-oe/recipes-support/btop/btop_1.4.2.bb
index 8afda1c5d3..6fa76ac8ad 100644
--- a/meta-oe/recipes-support/btop/btop_1.4.0.bb
+++ b/meta-oe/recipes-support/btop/btop_1.4.2.bb
@@ -5,9 +5,8 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57"
5SECTION = "console/utils" 5SECTION = "console/utils"
6 6
7SRC_URI = "git://github.com/aristocratos/btop.git;protocol=https;branch=main \ 7SRC_URI = "git://github.com/aristocratos/btop.git;protocol=https;branch=main \
8 file://0001-fmt-Update-headers-from-11.1.4.patch \
9 " 8 "
10SRCREV = "6c0cedd8912785f0f353af389e72a0ffc69984a2" 9SRCREV = "274d0c78e5f18514dfbea23cee9d1c5431eb75e0"
11 10
12S = "${WORKDIR}/git" 11S = "${WORKDIR}/git"
13 12