diff options
author | Markus Volk <f_l_k@t-online.de> | 2025-05-08 21:18:11 +0200 |
---|---|---|
committer | Khem Raj <raj.khem@gmail.com> | 2025-05-08 13:08:30 -0700 |
commit | 330ade641e519b4939ff7f965715cd28552b3cc7 (patch) | |
tree | ccebbe272e0abb79b15ba26cc6599197bab68db5 | |
parent | d5f076161b5f2cd66f70a73eb39f4ed09c4f3449 (diff) | |
download | meta-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.patch | 19338 | ||||
-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 @@ | |||
1 | From 236f243428b898e3ababb611c648ec120abd2857 Mon Sep 17 00:00:00 2001 | ||
2 | From: Khem Raj <raj.khem@gmail.com> | ||
3 | Date: Tue, 11 Mar 2025 20:02:10 -0700 | ||
4 | Subject: [PATCH] fmt: Update headers from 11.1.4+ | ||
5 | |||
6 | Newer compilers e.g. clang-20+ are not able to compile the fmt headers | ||
7 | from old snapshop, therefore bring the latest from libfmt upstream | ||
8 | |||
9 | Upstream-Status: Submitted [https://github.com/aristocratos/btop/pull/1063] | ||
10 | Signed-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 | |||
29 | diff --git a/include/fmt/args.h b/include/fmt/args.h | ||
30 | index 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 | ||
321 | diff --git a/include/fmt/base.h b/include/fmt/base.h | ||
322 | new file mode 100644 | ||
323 | index 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_ | ||
3301 | diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h | ||
3302 | index 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(>m); | ||
4598 | std::tm ltm = gmtime(gt); | ||
4599 | std::time_t lt = std::mktime(<m); | ||
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 | |||
5625 | diff --git a/include/fmt/color.h b/include/fmt/color.h | ||
5626 | index 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) | ||
6244 | diff --git a/include/fmt/compile.h b/include/fmt/compile.h | ||
6245 | index 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 | ||
6724 | diff --git a/include/fmt/core.h b/include/fmt/core.h | ||
6725 | index 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" | ||
9638 | diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h | ||
9639 | index 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 | |||
10486 | diff --git a/include/fmt/format.h b/include/fmt/format.h | ||
10487 | index 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_ | ||
15685 | diff --git a/include/fmt/os.h b/include/fmt/os.h | ||
15686 | index 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 | ||
16181 | diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h | ||
16182 | index 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_ | ||
16450 | diff --git a/include/fmt/printf.h b/include/fmt/printf.h | ||
16451 | index 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 | ||
17209 | diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h | ||
17210 | index 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) | ||
18107 | diff --git a/include/fmt/std.h b/include/fmt/std.h | ||
18108 | index 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_ | ||
18929 | diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h | ||
18930 | index 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" | |||
5 | SECTION = "console/utils" | 5 | SECTION = "console/utils" |
6 | 6 | ||
7 | SRC_URI = "git://github.com/aristocratos/btop.git;protocol=https;branch=main \ | 7 | SRC_URI = "git://github.com/aristocratos/btop.git;protocol=https;branch=main \ |
8 | file://0001-fmt-Update-headers-from-11.1.4.patch \ | ||
9 | " | 8 | " |
10 | SRCREV = "6c0cedd8912785f0f353af389e72a0ffc69984a2" | 9 | SRCREV = "274d0c78e5f18514dfbea23cee9d1c5431eb75e0" |
11 | 10 | ||
12 | S = "${WORKDIR}/git" | 11 | S = "${WORKDIR}/git" |
13 | 12 | ||