diff options
| -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.0.bb | 4 |
2 files changed, 19341 insertions, 1 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 new file mode 100644 index 0000000000..50834e57e8 --- /dev/null +++ b/meta-oe/recipes-support/btop/btop/0001-fmt-Update-headers-from-11.1.4.patch | |||
| @@ -0,0 +1,19338 @@ | |||
| 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.0.bb index adfb2418b0..8afda1c5d3 100644 --- a/meta-oe/recipes-support/btop/btop_1.4.0.bb +++ b/meta-oe/recipes-support/btop/btop_1.4.0.bb | |||
| @@ -4,7 +4,9 @@ LICENSE = "Apache-2.0" | |||
| 4 | LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57" | 4 | 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 | SRCREV = "6c0cedd8912785f0f353af389e72a0ffc69984a2" | 10 | SRCREV = "6c0cedd8912785f0f353af389e72a0ffc69984a2" |
| 9 | 11 | ||
| 10 | S = "${WORKDIR}/git" | 12 | S = "${WORKDIR}/git" |
