From patchwork Thu May 8 19:18:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Markus Volk X-Patchwork-Id: 64532 Return-Path: Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 14572C3ABBE for ; Thu, 8 May 2025 19:18:37 +0000 (UTC) Received: from mailout06.t-online.de (mailout06.t-online.de [194.25.134.19]) by mx.groups.io with SMTP id smtpd.web11.4486.1746731908374856858 for ; Thu, 08 May 2025 12:18:29 -0700 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: t-online.de, ip: 194.25.134.19, mailfrom: f_l_k@t-online.de) Received: from fwd71.aul.t-online.de (fwd71.aul.t-online.de [10.223.144.97]) by mailout06.t-online.de (Postfix) with SMTP id 7F884A62 for ; Thu, 8 May 2025 21:18:25 +0200 (CEST) Received: from intel-corei7-64.fritz.box ([79.219.236.121]) by fwd71.t-online.de with (TLSv1.3:TLS_AES_256_GCM_SHA384 encrypted) esmtp id 1uD6lC-0PyqUz0; Thu, 8 May 2025 21:18:22 +0200 From: Markus Volk To: openembedded-devel@lists.openembedded.org Subject: [meta-oe][PATCH] btop: update 1.4.0 -> 1.4.2 Date: Thu, 8 May 2025 21:18:11 +0200 Message-ID: <20250508191815.536397-1-f_l_k@t-online.de> X-Mailer: git-send-email 2.49.0 MIME-Version: 1.0 X-TOI-EXPURGATEID: 150726::1746731903-19FF955F-70258969/10/3071143950 SUSPECT URL X-TOI-MSGID: 6ca14e2f-0b5c-4857-9086-01395fc6328e List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Thu, 08 May 2025 19:18:37 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-devel/message/117367 - remove submitted patch Fix process arguments appearing outside proc box by replacing ASCII control codes with blankspace, issue #1080 | @aristocratos Fix problems shown by clang-tidy's performance checks | @imwints Fix wrong error message and documentation of renamed option --utf-force | @t-webber @imwints CMake: Remove option to use mold | @imwints Update Terminus font link, fix typo, spelling, and grammar | @QinCai-rui Please clang with sanitizers | @bad-co-de Fix MacOS tree-mode + aggregate memory/thread scaling issue | @xaskii Fix typo: Mhz -> MHz | @NyCodeGHG v1.4.1 Various code fixes | @imwints Various code fixes | @bad-co-de Fixed typo | @polluks Move the config parser in it's own module | @imwints Adding a menu option to show bitrates in base 10 separate from the setting to show bytes/bits in base 10 | @georgev93 Allow MidnightBSD to build btop using the existing freebsd support. | @laffer1 Use XDG_STATE_HOME to save logs | @imwints Bump CMake version to 3.25 required for LINUX variable | @imwints Replace brackets with arrows in net and proc box | @taha-yassine Bump bundled fmt to 11.1.4 | @imwints cmake: link to CMAKE_DL_LIBS | @alalazo Fix phoenix-night.theme marked as executable | @sertonix Add Kanagawa-lotus and Kanagawa-wave themes | @philikarus Bump NetBSD version to 10.1 and FreeBSD version to 14.2. | @fraggerfox Add dark version of adwaita theme: adwaita-dark | @k0tran Resetting last selection on page navigation in optionsMenu to avoid unordered_map error | @seth-wood Share the CPU name trimming code between platforms | @yarrick Update Ryzen name trimming | @yarrick Drop macos 12 build, add v14 and v15 | @yarrick Fix cmake-macos workflow | @yarrick Bump version of deprecated upload-artifact step | @yarrick Update obsolete egrep call | @tywkeene Fix menu crash when GPU_SUPPORT=false, issue #989 | @aristocratos Add 'Everforest Ligth Medium' theme | @mstuttgart Support intel GPUs before Gen-6 (patch from upstream) | @w8jcik intel_name_lookup_shim.c (get_intel_device_name): Fix SEGFAULT | @artyom-poptsov Fix rsmi_measure_pcie_speeds not saving, issue #934 | @aristocratos Show GPU Watt fractions when below 100W | @aristocratos Signed-off-by: Markus Volk --- .../0001-fmt-Update-headers-from-11.1.4.patch | 19338 ---------------- .../btop/{btop_1.4.0.bb => btop_1.4.2.bb} | 3 +- 2 files changed, 1 insertion(+), 19340 deletions(-) delete mode 100644 meta-oe/recipes-support/btop/btop/0001-fmt-Update-headers-from-11.1.4.patch rename meta-oe/recipes-support/btop/{btop_1.4.0.bb => btop_1.4.2.bb} (86%) diff --git a/meta-oe/recipes-support/btop/btop/0001-fmt-Update-headers-from-11.1.4.patch b/meta-oe/recipes-support/btop/btop/0001-fmt-Update-headers-from-11.1.4.patch deleted file mode 100644 index 50834e57e8..0000000000 --- a/meta-oe/recipes-support/btop/btop/0001-fmt-Update-headers-from-11.1.4.patch +++ /dev/null @@ -1,19338 +0,0 @@ -From 236f243428b898e3ababb611c648ec120abd2857 Mon Sep 17 00:00:00 2001 -From: Khem Raj -Date: Tue, 11 Mar 2025 20:02:10 -0700 -Subject: [PATCH] fmt: Update headers from 11.1.4+ - -Newer compilers e.g. clang-20+ are not able to compile the fmt headers -from old snapshop, therefore bring the latest from libfmt upstream - -Upstream-Status: Submitted [https://github.com/aristocratos/btop/pull/1063] -Signed-off-by: Khem Raj ---- - include/fmt/args.h | 174 +- - include/fmt/base.h | 2974 +++++++++++++++++++++++++++++++ - include/fmt/chrono.h | 1534 ++++++++-------- - include/fmt/color.h | 444 ++--- - include/fmt/compile.h | 280 ++- - include/fmt/core.h | 2908 +----------------------------- - include/fmt/format-inl.h | 529 ++++-- - include/fmt/format.h | 3617 +++++++++++++++++--------------------- - include/fmt/os.h | 300 ++-- - include/fmt/ostream.h | 189 +- - include/fmt/printf.h | 426 +++-- - include/fmt/ranges.h | 624 ++++--- - include/fmt/std.h | 677 +++++-- - include/fmt/xchar.h | 275 ++- - 14 files changed, 7656 insertions(+), 7295 deletions(-) - create mode 100644 include/fmt/base.h - -diff --git a/include/fmt/args.h b/include/fmt/args.h -index a3966d1..3ff4788 100644 ---- a/include/fmt/args.h -+++ b/include/fmt/args.h -@@ -1,4 +1,4 @@ --// Formatting library for C++ - dynamic format arguments -+// Formatting library for C++ - dynamic argument lists - // - // Copyright (c) 2012 - present, Victor Zverovich - // All rights reserved. -@@ -8,34 +8,39 @@ - #ifndef FMT_ARGS_H_ - #define FMT_ARGS_H_ - --#include // std::reference_wrapper --#include // std::unique_ptr --#include -+#ifndef FMT_MODULE -+# include // std::reference_wrapper -+# include // std::unique_ptr -+# include -+#endif - --#include "core.h" -+#include "format.h" // std_string_view - - FMT_BEGIN_NAMESPACE -- - namespace detail { - - template struct is_reference_wrapper : std::false_type {}; - template - struct is_reference_wrapper> : std::true_type {}; - --template const T& unwrap(const T& v) { return v; } --template const T& unwrap(const std::reference_wrapper& v) { -+template auto unwrap(const T& v) -> const T& { return v; } -+template -+auto unwrap(const std::reference_wrapper& v) -> const T& { - return static_cast(v); - } - --class dynamic_arg_list { -- // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for -- // templates it doesn't complain about inability to deduce single translation -- // unit for placing vtable. So storage_node_base is made a fake template. -- template struct node { -- virtual ~node() = default; -- std::unique_ptr> next; -- }; -+// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC -+// 2022 (v17.10.0). -+// -+// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for -+// templates it doesn't complain about inability to deduce single translation -+// unit for placing vtable. So node is made a fake template. -+template struct node { -+ virtual ~node() = default; -+ std::unique_ptr> next; -+}; - -+class dynamic_arg_list { - template struct typed_node : node<> { - T value; - -@@ -50,7 +55,7 @@ class dynamic_arg_list { - std::unique_ptr> head_; - - public: -- template const T& push(const Arg& arg) { -+ template auto push(const Arg& arg) -> const T& { - auto new_node = std::unique_ptr>(new typed_node(arg)); - auto& value = new_node->value; - new_node->next = std::move(head_); -@@ -61,28 +66,18 @@ class dynamic_arg_list { - } // namespace detail - - /** -- \rst -- A dynamic version of `fmt::format_arg_store`. -- It's equipped with a storage to potentially temporary objects which lifetimes -- could be shorter than the format arguments object. -- -- It can be implicitly converted into `~fmt::basic_format_args` for passing -- into type-erased formatting functions such as `~fmt::vformat`. -- \endrst -+ * A dynamic list of formatting arguments with storage. -+ * -+ * It can be implicitly converted into `fmt::basic_format_args` for passing -+ * into type-erased formatting functions such as `fmt::vformat`. - */ --template --class dynamic_format_arg_store --#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -- // Workaround a GCC template argument substitution bug. -- : public basic_format_args --#endif --{ -+template class dynamic_format_arg_store { - private: - using char_type = typename Context::char_type; - - template struct need_copy { - static constexpr detail::type mapped_type = -- detail::mapped_type_constant::value; -+ detail::mapped_type_constant::value; - - enum { - value = !(detail::is_reference_wrapper::value || -@@ -95,7 +90,7 @@ class dynamic_format_arg_store - }; - - template -- using stored_type = conditional_t< -+ using stored_t = conditional_t< - std::is_convertible>::value && - !detail::is_reference_wrapper::value, - std::basic_string, T>; -@@ -110,80 +105,72 @@ class dynamic_format_arg_store - - friend class basic_format_args; - -- unsigned long long get_types() const { -- return detail::is_unpacked_bit | data_.size() | -- (named_info_.empty() -- ? 0ULL -- : static_cast(detail::has_named_args_bit)); -- } -- -- const basic_format_arg* data() const { -+ auto data() const -> const basic_format_arg* { - return named_info_.empty() ? data_.data() : data_.data() + 1; - } - - template void emplace_arg(const T& arg) { -- data_.emplace_back(detail::make_arg(arg)); -+ data_.emplace_back(arg); - } - - template - void emplace_arg(const detail::named_arg& arg) { -- if (named_info_.empty()) { -- constexpr const detail::named_arg_info* zero_ptr{nullptr}; -- data_.insert(data_.begin(), {zero_ptr, 0}); -- } -- data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); -+ if (named_info_.empty()) -+ data_.insert(data_.begin(), basic_format_arg(nullptr, 0)); -+ data_.emplace_back(detail::unwrap(arg.value)); - auto pop_one = [](std::vector>* data) { - data->pop_back(); - }; - std::unique_ptr>, decltype(pop_one)> - guard{&data_, pop_one}; - named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); -- data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; -+ data_[0] = {named_info_.data(), named_info_.size()}; - guard.release(); - } - - public: - constexpr dynamic_format_arg_store() = default; - -+ operator basic_format_args() const { -+ return basic_format_args(data(), static_cast(data_.size()), -+ !named_info_.empty()); -+ } -+ - /** -- \rst -- Adds an argument into the dynamic store for later passing to a formatting -- function. -- -- Note that custom types and string types (but not string views) are copied -- into the store dynamically allocating memory if necessary. -- -- **Example**:: -- -- fmt::dynamic_format_arg_store store; -- store.push_back(42); -- store.push_back("abc"); -- store.push_back(1.5f); -- std::string result = fmt::vformat("{} and {} and {}", store); -- \endrst -- */ -+ * Adds an argument into the dynamic store for later passing to a formatting -+ * function. -+ * -+ * Note that custom types and string types (but not string views) are copied -+ * into the store dynamically allocating memory if necessary. -+ * -+ * **Example**: -+ * -+ * fmt::dynamic_format_arg_store store; -+ * store.push_back(42); -+ * store.push_back("abc"); -+ * store.push_back(1.5f); -+ * std::string result = fmt::vformat("{} and {} and {}", store); -+ */ - template void push_back(const T& arg) { - if (detail::const_check(need_copy::value)) -- emplace_arg(dynamic_args_.push>(arg)); -+ emplace_arg(dynamic_args_.push>(arg)); - else - emplace_arg(detail::unwrap(arg)); - } - - /** -- \rst -- Adds a reference to the argument into the dynamic store for later passing to -- a formatting function. -- -- **Example**:: -- -- fmt::dynamic_format_arg_store store; -- char band[] = "Rolling Stones"; -- store.push_back(std::cref(band)); -- band[9] = 'c'; // Changing str affects the output. -- std::string result = fmt::vformat("{}", store); -- // result == "Rolling Scones" -- \endrst -- */ -+ * Adds a reference to the argument into the dynamic store for later passing -+ * to a formatting function. -+ * -+ * **Example**: -+ * -+ * fmt::dynamic_format_arg_store store; -+ * char band[] = "Rolling Stones"; -+ * store.push_back(std::cref(band)); -+ * band[9] = 'c'; // Changing str affects the output. -+ * std::string result = fmt::vformat("{}", store); -+ * // result == "Rolling Scones" -+ */ - template void push_back(std::reference_wrapper arg) { - static_assert( - need_copy::value, -@@ -192,41 +179,40 @@ class dynamic_format_arg_store - } - - /** -- Adds named argument into the dynamic store for later passing to a formatting -- function. ``std::reference_wrapper`` is supported to avoid copying of the -- argument. The name is always copied into the store. -- */ -+ * Adds named argument into the dynamic store for later passing to a -+ * formatting function. `std::reference_wrapper` is supported to avoid -+ * copying of the argument. The name is always copied into the store. -+ */ - template - void push_back(const detail::named_arg& arg) { - const char_type* arg_name = - dynamic_args_.push>(arg.name).c_str(); - if (detail::const_check(need_copy::value)) { - emplace_arg( -- fmt::arg(arg_name, dynamic_args_.push>(arg.value))); -+ fmt::arg(arg_name, dynamic_args_.push>(arg.value))); - } else { - emplace_arg(fmt::arg(arg_name, arg.value)); - } - } - -- /** Erase all elements from the store */ -+ /// Erase all elements from the store. - void clear() { - data_.clear(); - named_info_.clear(); -- dynamic_args_ = detail::dynamic_arg_list(); -+ dynamic_args_ = {}; - } - -- /** -- \rst -- Reserves space to store at least *new_cap* arguments including -- *new_cap_named* named arguments. -- \endrst -- */ -+ /// Reserves space to store at least `new_cap` arguments including -+ /// `new_cap_named` named arguments. - void reserve(size_t new_cap, size_t new_cap_named) { - FMT_ASSERT(new_cap >= new_cap_named, -- "Set of arguments includes set of named arguments"); -+ "set of arguments includes set of named arguments"); - data_.reserve(new_cap); - named_info_.reserve(new_cap_named); - } -+ -+ /// Returns the number of elements in the store. -+ size_t size() const noexcept { return data_.size(); } - }; - - FMT_END_NAMESPACE -diff --git a/include/fmt/base.h b/include/fmt/base.h -new file mode 100644 -index 0000000..fe73e37 ---- /dev/null -+++ b/include/fmt/base.h -@@ -0,0 +1,2974 @@ -+// Formatting library for C++ - the base API for char/UTF-8 -+// -+// Copyright (c) 2012 - present, Victor Zverovich -+// All rights reserved. -+// -+// For the license information refer to format.h. -+ -+#ifndef FMT_BASE_H_ -+#define FMT_BASE_H_ -+ -+#if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE) -+# define FMT_MODULE -+#endif -+ -+#ifndef FMT_MODULE -+# include // CHAR_BIT -+# include // FILE -+# include // memcmp -+ -+# include // std::enable_if -+#endif -+ -+// The fmt library version in the form major * 10000 + minor * 100 + patch. -+#define FMT_VERSION 110104 -+ -+// Detect compiler versions. -+#if defined(__clang__) && !defined(__ibmxl__) -+# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) -+#else -+# define FMT_CLANG_VERSION 0 -+#endif -+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) -+# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -+#else -+# define FMT_GCC_VERSION 0 -+#endif -+#if defined(__ICL) -+# define FMT_ICC_VERSION __ICL -+#elif defined(__INTEL_COMPILER) -+# define FMT_ICC_VERSION __INTEL_COMPILER -+#else -+# define FMT_ICC_VERSION 0 -+#endif -+#if defined(_MSC_VER) -+# define FMT_MSC_VERSION _MSC_VER -+#else -+# define FMT_MSC_VERSION 0 -+#endif -+ -+// Detect standard library versions. -+#ifdef _GLIBCXX_RELEASE -+# define FMT_GLIBCXX_RELEASE _GLIBCXX_RELEASE -+#else -+# define FMT_GLIBCXX_RELEASE 0 -+#endif -+#ifdef _LIBCPP_VERSION -+# define FMT_LIBCPP_VERSION _LIBCPP_VERSION -+#else -+# define FMT_LIBCPP_VERSION 0 -+#endif -+ -+#ifdef _MSVC_LANG -+# define FMT_CPLUSPLUS _MSVC_LANG -+#else -+# define FMT_CPLUSPLUS __cplusplus -+#endif -+ -+// Detect __has_*. -+#ifdef __has_feature -+# define FMT_HAS_FEATURE(x) __has_feature(x) -+#else -+# define FMT_HAS_FEATURE(x) 0 -+#endif -+#ifdef __has_include -+# define FMT_HAS_INCLUDE(x) __has_include(x) -+#else -+# define FMT_HAS_INCLUDE(x) 0 -+#endif -+#ifdef __has_builtin -+# define FMT_HAS_BUILTIN(x) __has_builtin(x) -+#else -+# define FMT_HAS_BUILTIN(x) 0 -+#endif -+#ifdef __has_cpp_attribute -+# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -+#else -+# define FMT_HAS_CPP_ATTRIBUTE(x) 0 -+#endif -+ -+#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ -+ (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) -+ -+#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ -+ (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) -+ -+// Detect C++14 relaxed constexpr. -+#ifdef FMT_USE_CONSTEXPR -+// Use the provided definition. -+#elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L -+// GCC only allows constexpr member functions in non-literal types since 7.2: -+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297. -+# define FMT_USE_CONSTEXPR 1 -+#elif FMT_ICC_VERSION -+# define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628 -+#elif FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 -+# define FMT_USE_CONSTEXPR 1 -+#else -+# define FMT_USE_CONSTEXPR 0 -+#endif -+#if FMT_USE_CONSTEXPR -+# define FMT_CONSTEXPR constexpr -+#else -+# define FMT_CONSTEXPR -+#endif -+ -+// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. -+#if !defined(__cpp_lib_is_constant_evaluated) -+# define FMT_USE_CONSTEVAL 0 -+#elif FMT_CPLUSPLUS < 201709L -+# define FMT_USE_CONSTEVAL 0 -+#elif FMT_GLIBCXX_RELEASE && FMT_GLIBCXX_RELEASE < 10 -+# define FMT_USE_CONSTEVAL 0 -+#elif FMT_LIBCPP_VERSION && FMT_LIBCPP_VERSION < 10000 -+# define FMT_USE_CONSTEVAL 0 -+#elif defined(__apple_build_version__) && __apple_build_version__ < 14000029L -+# define FMT_USE_CONSTEVAL 0 // consteval is broken in Apple clang < 14. -+#elif FMT_MSC_VERSION && FMT_MSC_VERSION < 1929 -+# define FMT_USE_CONSTEVAL 0 // consteval is broken in MSVC VS2019 < 16.10. -+#elif defined(__cpp_consteval) -+# define FMT_USE_CONSTEVAL 1 -+#elif FMT_GCC_VERSION >= 1002 || FMT_CLANG_VERSION >= 1101 -+# define FMT_USE_CONSTEVAL 1 -+#else -+# define FMT_USE_CONSTEVAL 0 -+#endif -+#if FMT_USE_CONSTEVAL -+# define FMT_CONSTEVAL consteval -+# define FMT_CONSTEXPR20 constexpr -+#else -+# define FMT_CONSTEVAL -+# define FMT_CONSTEXPR20 -+#endif -+ -+// Check if exceptions are disabled. -+#ifdef FMT_USE_EXCEPTIONS -+// Use the provided definition. -+#elif defined(__GNUC__) && !defined(__EXCEPTIONS) -+# define FMT_USE_EXCEPTIONS 0 -+#elif defined(__clang__) && !defined(__cpp_exceptions) -+# define FMT_USE_EXCEPTIONS 0 -+#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS -+# define FMT_USE_EXCEPTIONS 0 -+#else -+# define FMT_USE_EXCEPTIONS 1 -+#endif -+#if FMT_USE_EXCEPTIONS -+# define FMT_TRY try -+# define FMT_CATCH(x) catch (x) -+#else -+# define FMT_TRY if (true) -+# define FMT_CATCH(x) if (false) -+#endif -+ -+#ifdef FMT_NO_UNIQUE_ADDRESS -+// Use the provided definition. -+#elif FMT_CPLUSPLUS < 202002L -+// Not supported. -+#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address) -+# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] -+// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485). -+#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION -+# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] -+#endif -+#ifndef FMT_NO_UNIQUE_ADDRESS -+# define FMT_NO_UNIQUE_ADDRESS -+#endif -+ -+#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) -+# define FMT_FALLTHROUGH [[fallthrough]] -+#elif defined(__clang__) -+# define FMT_FALLTHROUGH [[clang::fallthrough]] -+#elif FMT_GCC_VERSION >= 700 && \ -+ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -+# define FMT_FALLTHROUGH [[gnu::fallthrough]] -+#else -+# define FMT_FALLTHROUGH -+#endif -+ -+// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. -+#if FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__) -+# define FMT_NORETURN [[noreturn]] -+#else -+# define FMT_NORETURN -+#endif -+ -+#ifdef FMT_NODISCARD -+// Use the provided definition. -+#elif FMT_HAS_CPP17_ATTRIBUTE(nodiscard) -+# define FMT_NODISCARD [[nodiscard]] -+#else -+# define FMT_NODISCARD -+#endif -+ -+#ifdef FMT_DEPRECATED -+// Use the provided definition. -+#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) -+# define FMT_DEPRECATED [[deprecated]] -+#else -+# define FMT_DEPRECATED /* deprecated */ -+#endif -+ -+#ifdef FMT_ALWAYS_INLINE -+// Use the provided definition. -+#elif FMT_GCC_VERSION || FMT_CLANG_VERSION -+# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) -+#else -+# define FMT_ALWAYS_INLINE inline -+#endif -+// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode. -+#ifdef NDEBUG -+# define FMT_INLINE FMT_ALWAYS_INLINE -+#else -+# define FMT_INLINE inline -+#endif -+ -+#if FMT_GCC_VERSION || FMT_CLANG_VERSION -+# define FMT_VISIBILITY(value) __attribute__((visibility(value))) -+#else -+# define FMT_VISIBILITY(value) -+#endif -+ -+// Detect pragmas. -+#define FMT_PRAGMA_IMPL(x) _Pragma(#x) -+#if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER) -+// Workaround a _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884 -+// and an nvhpc warning: https://github.com/fmtlib/fmt/pull/2582. -+# define FMT_PRAGMA_GCC(x) FMT_PRAGMA_IMPL(GCC x) -+#else -+# define FMT_PRAGMA_GCC(x) -+#endif -+#if FMT_CLANG_VERSION -+# define FMT_PRAGMA_CLANG(x) FMT_PRAGMA_IMPL(clang x) -+#else -+# define FMT_PRAGMA_CLANG(x) -+#endif -+#if FMT_MSC_VERSION -+# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) -+#else -+# define FMT_MSC_WARNING(...) -+#endif -+ -+#ifndef FMT_BEGIN_NAMESPACE -+# define FMT_BEGIN_NAMESPACE \ -+ namespace fmt { \ -+ inline namespace v11 { -+# define FMT_END_NAMESPACE \ -+ } \ -+ } -+#endif -+ -+#ifndef FMT_EXPORT -+# define FMT_EXPORT -+# define FMT_BEGIN_EXPORT -+# define FMT_END_EXPORT -+#endif -+ -+#ifdef _WIN32 -+# define FMT_WIN32 1 -+#else -+# define FMT_WIN32 0 -+#endif -+ -+#if !defined(FMT_HEADER_ONLY) && FMT_WIN32 -+# if defined(FMT_LIB_EXPORT) -+# define FMT_API __declspec(dllexport) -+# elif defined(FMT_SHARED) -+# define FMT_API __declspec(dllimport) -+# endif -+#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) -+# define FMT_API FMT_VISIBILITY("default") -+#endif -+#ifndef FMT_API -+# define FMT_API -+#endif -+ -+#ifndef FMT_OPTIMIZE_SIZE -+# define FMT_OPTIMIZE_SIZE 0 -+#endif -+ -+// FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher -+// per-call binary size by passing built-in types through the extension API. -+#ifndef FMT_BUILTIN_TYPES -+# define FMT_BUILTIN_TYPES 1 -+#endif -+ -+#define FMT_APPLY_VARIADIC(expr) \ -+ using unused = int[]; \ -+ (void)unused { 0, (expr, 0)... } -+ -+// Enable minimal optimizations for more compact code in debug mode. -+FMT_PRAGMA_GCC(push_options) -+#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) -+FMT_PRAGMA_GCC(optimize("Og")) -+#endif -+FMT_PRAGMA_CLANG(diagnostic push) -+ -+FMT_BEGIN_NAMESPACE -+ -+// Implementations of enable_if_t and other metafunctions for older systems. -+template -+using enable_if_t = typename std::enable_if::type; -+template -+using conditional_t = typename std::conditional::type; -+template using bool_constant = std::integral_constant; -+template -+using remove_reference_t = typename std::remove_reference::type; -+template -+using remove_const_t = typename std::remove_const::type; -+template -+using remove_cvref_t = typename std::remove_cv>::type; -+template -+using make_unsigned_t = typename std::make_unsigned::type; -+template -+using underlying_t = typename std::underlying_type::type; -+template using decay_t = typename std::decay::type; -+using nullptr_t = decltype(nullptr); -+ -+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 -+// A workaround for gcc 4.9 to make void_t work in a SFINAE context. -+template struct void_t_impl { -+ using type = void; -+}; -+template using void_t = typename void_t_impl::type; -+#else -+template using void_t = void; -+#endif -+ -+struct monostate { -+ constexpr monostate() {} -+}; -+ -+// An enable_if helper to be used in template parameters which results in much -+// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed -+// to workaround a bug in MSVC 2019 (see #1140 and #1186). -+#ifdef FMT_DOC -+# define FMT_ENABLE_IF(...) -+#else -+# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 -+#endif -+ -+template constexpr auto min_of(T a, T b) -> T { -+ return a < b ? a : b; -+} -+template constexpr auto max_of(T a, T b) -> T { -+ return a > b ? a : b; -+} -+ -+namespace detail { -+// Suppresses "unused variable" warnings with the method described in -+// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. -+// (void)var does not work on many Intel compilers. -+template FMT_CONSTEXPR void ignore_unused(const T&...) {} -+ -+constexpr auto is_constant_evaluated(bool default_value = false) noexcept -+ -> bool { -+// Workaround for incompatibility between clang 14 and libstdc++ consteval-based -+// std::is_constant_evaluated: https://github.com/fmtlib/fmt/issues/3247. -+#if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \ -+ (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) -+ ignore_unused(default_value); -+ return __builtin_is_constant_evaluated(); -+#elif defined(__cpp_lib_is_constant_evaluated) -+ ignore_unused(default_value); -+ return std::is_constant_evaluated(); -+#else -+ return default_value; -+#endif -+} -+ -+// Suppresses "conditional expression is constant" warnings. -+template FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T { -+ return val; -+} -+ -+FMT_NORETURN FMT_API void assert_fail(const char* file, int line, -+ const char* message); -+ -+#if defined(FMT_ASSERT) -+// Use the provided definition. -+#elif defined(NDEBUG) -+// FMT_ASSERT is not empty to avoid -Wempty-body. -+# define FMT_ASSERT(condition, message) \ -+ fmt::detail::ignore_unused((condition), (message)) -+#else -+# define FMT_ASSERT(condition, message) \ -+ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ -+ ? (void)0 \ -+ : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) -+#endif -+ -+#ifdef FMT_USE_INT128 -+// Use the provided definition. -+#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ -+ !(FMT_CLANG_VERSION && FMT_MSC_VERSION) -+# define FMT_USE_INT128 1 -+using int128_opt = __int128_t; // An optional native 128-bit integer. -+using uint128_opt = __uint128_t; -+inline auto map(int128_opt x) -> int128_opt { return x; } -+inline auto map(uint128_opt x) -> uint128_opt { return x; } -+#else -+# define FMT_USE_INT128 0 -+#endif -+#if !FMT_USE_INT128 -+enum class int128_opt {}; -+enum class uint128_opt {}; -+// Reduce template instantiations. -+inline auto map(int128_opt) -> monostate { return {}; } -+inline auto map(uint128_opt) -> monostate { return {}; } -+#endif -+ -+#ifndef FMT_USE_BITINT -+# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500) -+#endif -+ -+#if FMT_USE_BITINT -+FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension") -+template using bitint = _BitInt(N); -+template using ubitint = unsigned _BitInt(N); -+#else -+template struct bitint {}; -+template struct ubitint {}; -+#endif // FMT_USE_BITINT -+ -+// Casts a nonnegative integer to unsigned. -+template -+FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t { -+ FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); -+ return static_cast>(value); -+} -+ -+template -+using unsigned_char = conditional_t; -+ -+// A heuristic to detect std::string and std::[experimental::]string_view. -+// It is mainly used to avoid dependency on <[experimental/]string_view>. -+template -+struct is_std_string_like : std::false_type {}; -+template -+struct is_std_string_like().find_first_of( -+ typename T::value_type(), 0))>> -+ : std::is_convertible().data()), -+ const typename T::value_type*> {}; -+ -+// Check if the literal encoding is UTF-8. -+enum { is_utf8_enabled = "\u00A7"[1] == '\xA7' }; -+enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled }; -+ -+#ifndef FMT_UNICODE -+# define FMT_UNICODE 1 -+#endif -+ -+static_assert(!FMT_UNICODE || use_utf8, -+ "Unicode support requires compiling with /utf-8"); -+ -+template constexpr const char* narrow(const T*) { return nullptr; } -+constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; } -+ -+template -+FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) -+ -> int { -+ if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); -+ for (; n != 0; ++s1, ++s2, --n) { -+ if (*s1 < *s2) return -1; -+ if (*s1 > *s2) return 1; -+ } -+ return 0; -+} -+ -+namespace adl { -+using namespace std; -+ -+template -+auto invoke_back_inserter() -+ -> decltype(back_inserter(std::declval())); -+} // namespace adl -+ -+template -+struct is_back_insert_iterator : std::false_type {}; -+ -+template -+struct is_back_insert_iterator< -+ It, bool_constant()), -+ It>::value>> : std::true_type {}; -+ -+// Extracts a reference to the container from *insert_iterator. -+template -+inline FMT_CONSTEXPR20 auto get_container(OutputIt it) -> -+ typename OutputIt::container_type& { -+ struct accessor : OutputIt { -+ FMT_CONSTEXPR20 accessor(OutputIt base) : OutputIt(base) {} -+ using OutputIt::container; -+ }; -+ return *accessor(it).container; -+} -+} // namespace detail -+ -+// Parsing-related public API and forward declarations. -+FMT_BEGIN_EXPORT -+ -+/** -+ * An implementation of `std::basic_string_view` for pre-C++17. It provides a -+ * subset of the API. `fmt::basic_string_view` is used for format strings even -+ * if `std::basic_string_view` is available to prevent issues when a library is -+ * compiled with a different `-std` option than the client code (which is not -+ * recommended). -+ */ -+template class basic_string_view { -+ private: -+ const Char* data_; -+ size_t size_; -+ -+ public: -+ using value_type = Char; -+ using iterator = const Char*; -+ -+ constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} -+ -+ /// Constructs a string reference object from a C string and a size. -+ constexpr basic_string_view(const Char* s, size_t count) noexcept -+ : data_(s), size_(count) {} -+ -+ constexpr basic_string_view(nullptr_t) = delete; -+ -+ /// Constructs a string reference object from a C string. -+#if FMT_GCC_VERSION -+ FMT_ALWAYS_INLINE -+#endif -+ FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) { -+#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION -+ if (std::is_same::value) { -+ size_ = __builtin_strlen(detail::narrow(s)); -+ return; -+ } -+#endif -+ size_t len = 0; -+ while (*s++) ++len; -+ size_ = len; -+ } -+ -+ /// Constructs a string reference from a `std::basic_string` or a -+ /// `std::basic_string_view` object. -+ template ::value&& std::is_same< -+ typename S::value_type, Char>::value)> -+ FMT_CONSTEXPR basic_string_view(const S& s) noexcept -+ : data_(s.data()), size_(s.size()) {} -+ -+ /// Returns a pointer to the string data. -+ constexpr auto data() const noexcept -> const Char* { return data_; } -+ -+ /// Returns the string size. -+ constexpr auto size() const noexcept -> size_t { return size_; } -+ -+ constexpr auto begin() const noexcept -> iterator { return data_; } -+ constexpr auto end() const noexcept -> iterator { return data_ + size_; } -+ -+ constexpr auto operator[](size_t pos) const noexcept -> const Char& { -+ return data_[pos]; -+ } -+ -+ FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { -+ data_ += n; -+ size_ -= n; -+ } -+ -+ FMT_CONSTEXPR auto starts_with(basic_string_view sv) const noexcept -+ -> bool { -+ return size_ >= sv.size_ && detail::compare(data_, sv.data_, sv.size_) == 0; -+ } -+ FMT_CONSTEXPR auto starts_with(Char c) const noexcept -> bool { -+ return size_ >= 1 && *data_ == c; -+ } -+ FMT_CONSTEXPR auto starts_with(const Char* s) const -> bool { -+ return starts_with(basic_string_view(s)); -+ } -+ -+ // Lexicographically compare this string reference to other. -+ FMT_CONSTEXPR auto compare(basic_string_view other) const -> int { -+ int result = -+ detail::compare(data_, other.data_, min_of(size_, other.size_)); -+ if (result != 0) return result; -+ return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); -+ } -+ -+ FMT_CONSTEXPR friend auto operator==(basic_string_view lhs, -+ basic_string_view rhs) -> bool { -+ return lhs.compare(rhs) == 0; -+ } -+ friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { -+ return lhs.compare(rhs) != 0; -+ } -+ friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { -+ return lhs.compare(rhs) < 0; -+ } -+ friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { -+ return lhs.compare(rhs) <= 0; -+ } -+ friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { -+ return lhs.compare(rhs) > 0; -+ } -+ friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { -+ return lhs.compare(rhs) >= 0; -+ } -+}; -+ -+using string_view = basic_string_view; -+ -+/// Specifies if `T` is an extended character type. Can be specialized by users. -+template struct is_xchar : std::false_type {}; -+template <> struct is_xchar : std::true_type {}; -+template <> struct is_xchar : std::true_type {}; -+template <> struct is_xchar : std::true_type {}; -+#ifdef __cpp_char8_t -+template <> struct is_xchar : std::true_type {}; -+#endif -+ -+// DEPRECATED! Will be replaced with an alias to prevent specializations. -+template struct is_char : is_xchar {}; -+template <> struct is_char : std::true_type {}; -+ -+template class basic_appender; -+using appender = basic_appender; -+ -+// Checks whether T is a container with contiguous storage. -+template struct is_contiguous : std::false_type {}; -+ -+class context; -+template class generic_context; -+template class parse_context; -+ -+// Longer aliases for C++20 compatibility. -+template using basic_format_parse_context = parse_context; -+using format_parse_context = parse_context; -+template -+using basic_format_context = -+ conditional_t::value, context, -+ generic_context>; -+using format_context = context; -+ -+template -+using buffered_context = -+ conditional_t::value, context, -+ generic_context, Char>>; -+ -+template class basic_format_arg; -+template class basic_format_args; -+ -+// A separate type would result in shorter symbols but break ABI compatibility -+// between clang and gcc on ARM (#1919). -+using format_args = basic_format_args; -+ -+// A formatter for objects of type T. -+template -+struct formatter { -+ // A deleted default constructor indicates a disabled formatter. -+ formatter() = delete; -+}; -+ -+/// Reports a format error at compile time or, via a `format_error` exception, -+/// at runtime. -+// This function is intentionally not constexpr to give a compile-time error. -+FMT_NORETURN FMT_API void report_error(const char* message); -+ -+enum class presentation_type : unsigned char { -+ // Common specifiers: -+ none = 0, -+ debug = 1, // '?' -+ string = 2, // 's' (string, bool) -+ -+ // Integral, bool and character specifiers: -+ dec = 3, // 'd' -+ hex, // 'x' or 'X' -+ oct, // 'o' -+ bin, // 'b' or 'B' -+ chr, // 'c' -+ -+ // String and pointer specifiers: -+ pointer = 3, // 'p' -+ -+ // Floating-point specifiers: -+ exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation) -+ fixed, // 'f' or 'F' -+ general, // 'g' or 'G' -+ hexfloat // 'a' or 'A' -+}; -+ -+enum class align { none, left, right, center, numeric }; -+enum class sign { none, minus, plus, space }; -+enum class arg_id_kind { none, index, name }; -+ -+// Basic format specifiers for built-in and string types. -+class basic_specs { -+ private: -+ // Data is arranged as follows: -+ // -+ // 0 1 2 3 -+ // 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 -+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+ // |type |align| w | p | s |u|#|L| f | unused | -+ // +-----+-----+---+---+---+-+-+-+-----+---------------------------+ -+ // -+ // w - dynamic width info -+ // p - dynamic precision info -+ // s - sign -+ // u - uppercase (e.g. 'X' for 'x') -+ // # - alternate form ('#') -+ // L - localized -+ // f - fill size -+ // -+ // Bitfields are not used because of compiler bugs such as gcc bug 61414. -+ enum : unsigned { -+ type_mask = 0x00007, -+ align_mask = 0x00038, -+ width_mask = 0x000C0, -+ precision_mask = 0x00300, -+ sign_mask = 0x00C00, -+ uppercase_mask = 0x01000, -+ alternate_mask = 0x02000, -+ localized_mask = 0x04000, -+ fill_size_mask = 0x38000, -+ -+ align_shift = 3, -+ width_shift = 6, -+ precision_shift = 8, -+ sign_shift = 10, -+ fill_size_shift = 15, -+ -+ max_fill_size = 4 -+ }; -+ -+ unsigned data_ = 1 << fill_size_shift; -+ static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, ""); -+ -+ // Character (code unit) type is erased to prevent template bloat. -+ char fill_data_[max_fill_size] = {' '}; -+ -+ FMT_CONSTEXPR void set_fill_size(size_t size) { -+ data_ = (data_ & ~fill_size_mask) | -+ (static_cast(size) << fill_size_shift); -+ } -+ -+ public: -+ constexpr auto type() const -> presentation_type { -+ return static_cast(data_ & type_mask); -+ } -+ FMT_CONSTEXPR void set_type(presentation_type t) { -+ data_ = (data_ & ~type_mask) | static_cast(t); -+ } -+ -+ constexpr auto align() const -> align { -+ return static_cast((data_ & align_mask) >> align_shift); -+ } -+ FMT_CONSTEXPR void set_align(fmt::align a) { -+ data_ = (data_ & ~align_mask) | (static_cast(a) << align_shift); -+ } -+ -+ constexpr auto dynamic_width() const -> arg_id_kind { -+ return static_cast((data_ & width_mask) >> width_shift); -+ } -+ FMT_CONSTEXPR void set_dynamic_width(arg_id_kind w) { -+ data_ = (data_ & ~width_mask) | (static_cast(w) << width_shift); -+ } -+ -+ FMT_CONSTEXPR auto dynamic_precision() const -> arg_id_kind { -+ return static_cast((data_ & precision_mask) >> -+ precision_shift); -+ } -+ FMT_CONSTEXPR void set_dynamic_precision(arg_id_kind p) { -+ data_ = (data_ & ~precision_mask) | -+ (static_cast(p) << precision_shift); -+ } -+ -+ constexpr bool dynamic() const { -+ return (data_ & (width_mask | precision_mask)) != 0; -+ } -+ -+ constexpr auto sign() const -> sign { -+ return static_cast((data_ & sign_mask) >> sign_shift); -+ } -+ FMT_CONSTEXPR void set_sign(fmt::sign s) { -+ data_ = (data_ & ~sign_mask) | (static_cast(s) << sign_shift); -+ } -+ -+ constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; } -+ FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; } -+ -+ constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; } -+ FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; } -+ FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; } -+ -+ constexpr auto localized() const -> bool { -+ return (data_ & localized_mask) != 0; -+ } -+ FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; } -+ -+ constexpr auto fill_size() const -> size_t { -+ return (data_ & fill_size_mask) >> fill_size_shift; -+ } -+ -+ template ::value)> -+ constexpr auto fill() const -> const Char* { -+ return fill_data_; -+ } -+ template ::value)> -+ constexpr auto fill() const -> const Char* { -+ return nullptr; -+ } -+ -+ template constexpr auto fill_unit() const -> Char { -+ using uchar = unsigned char; -+ return static_cast(static_cast(fill_data_[0]) | -+ (static_cast(fill_data_[1]) << 8) | -+ (static_cast(fill_data_[2]) << 16)); -+ } -+ -+ FMT_CONSTEXPR void set_fill(char c) { -+ fill_data_[0] = c; -+ set_fill_size(1); -+ } -+ -+ template -+ FMT_CONSTEXPR void set_fill(basic_string_view s) { -+ auto size = s.size(); -+ set_fill_size(size); -+ if (size == 1) { -+ unsigned uchar = static_cast>(s[0]); -+ fill_data_[0] = static_cast(uchar); -+ fill_data_[1] = static_cast(uchar >> 8); -+ fill_data_[2] = static_cast(uchar >> 16); -+ return; -+ } -+ FMT_ASSERT(size <= max_fill_size, "invalid fill"); -+ for (size_t i = 0; i < size; ++i) -+ fill_data_[i & 3] = static_cast(s[i]); -+ } -+ -+ FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) { -+ set_fill_size(specs.fill_size()); -+ for (size_t i = 0; i < max_fill_size; ++i) -+ fill_data_[i] = specs.fill_data_[i]; -+ } -+}; -+ -+// Format specifiers for built-in and string types. -+struct format_specs : basic_specs { -+ int width; -+ int precision; -+ -+ constexpr format_specs() : width(0), precision(-1) {} -+}; -+ -+/** -+ * Parsing context consisting of a format string range being parsed and an -+ * argument counter for automatic indexing. -+ */ -+template class parse_context { -+ private: -+ basic_string_view fmt_; -+ int next_arg_id_; -+ -+ enum { use_constexpr_cast = !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200 }; -+ -+ FMT_CONSTEXPR void do_check_arg_id(int arg_id); -+ -+ public: -+ using char_type = Char; -+ using iterator = const Char*; -+ -+ constexpr explicit parse_context(basic_string_view fmt, -+ int next_arg_id = 0) -+ : fmt_(fmt), next_arg_id_(next_arg_id) {} -+ -+ /// Returns an iterator to the beginning of the format string range being -+ /// parsed. -+ constexpr auto begin() const noexcept -> iterator { return fmt_.begin(); } -+ -+ /// Returns an iterator past the end of the format string range being parsed. -+ constexpr auto end() const noexcept -> iterator { return fmt_.end(); } -+ -+ /// Advances the begin iterator to `it`. -+ FMT_CONSTEXPR void advance_to(iterator it) { -+ fmt_.remove_prefix(detail::to_unsigned(it - begin())); -+ } -+ -+ /// Reports an error if using the manual argument indexing; otherwise returns -+ /// the next argument index and switches to the automatic indexing. -+ FMT_CONSTEXPR auto next_arg_id() -> int { -+ if (next_arg_id_ < 0) { -+ report_error("cannot switch from manual to automatic argument indexing"); -+ return 0; -+ } -+ int id = next_arg_id_++; -+ do_check_arg_id(id); -+ return id; -+ } -+ -+ /// Reports an error if using the automatic argument indexing; otherwise -+ /// switches to the manual indexing. -+ FMT_CONSTEXPR void check_arg_id(int id) { -+ if (next_arg_id_ > 0) { -+ report_error("cannot switch from automatic to manual argument indexing"); -+ return; -+ } -+ next_arg_id_ = -1; -+ do_check_arg_id(id); -+ } -+ FMT_CONSTEXPR void check_arg_id(basic_string_view) { -+ next_arg_id_ = -1; -+ } -+ FMT_CONSTEXPR void check_dynamic_spec(int arg_id); -+}; -+ -+FMT_END_EXPORT -+ -+namespace detail { -+ -+// Constructs fmt::basic_string_view from types implicitly convertible -+// to it, deducing Char. Explicitly convertible types such as the ones returned -+// from FMT_STRING are intentionally excluded. -+template ::value)> -+constexpr auto to_string_view(const Char* s) -> basic_string_view { -+ return s; -+} -+template ::value)> -+constexpr auto to_string_view(const T& s) -+ -> basic_string_view { -+ return s; -+} -+template -+constexpr auto to_string_view(basic_string_view s) -+ -> basic_string_view { -+ return s; -+} -+ -+template -+struct has_to_string_view : std::false_type {}; -+// detail:: is intentional since to_string_view is not an extension point. -+template -+struct has_to_string_view< -+ T, void_t()))>> -+ : std::true_type {}; -+ -+/// String's character (code unit) type. detail:: is intentional to prevent ADL. -+template ()))> -+using char_t = typename V::value_type; -+ -+enum class type { -+ none_type, -+ // Integer types should go first, -+ int_type, -+ uint_type, -+ long_long_type, -+ ulong_long_type, -+ int128_type, -+ uint128_type, -+ bool_type, -+ char_type, -+ last_integer_type = char_type, -+ // followed by floating-point types. -+ float_type, -+ double_type, -+ long_double_type, -+ last_numeric_type = long_double_type, -+ cstring_type, -+ string_type, -+ pointer_type, -+ custom_type -+}; -+ -+// Maps core type T to the corresponding type enum constant. -+template -+struct type_constant : std::integral_constant {}; -+ -+#define FMT_TYPE_CONSTANT(Type, constant) \ -+ template \ -+ struct type_constant \ -+ : std::integral_constant {} -+ -+FMT_TYPE_CONSTANT(int, int_type); -+FMT_TYPE_CONSTANT(unsigned, uint_type); -+FMT_TYPE_CONSTANT(long long, long_long_type); -+FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); -+FMT_TYPE_CONSTANT(int128_opt, int128_type); -+FMT_TYPE_CONSTANT(uint128_opt, uint128_type); -+FMT_TYPE_CONSTANT(bool, bool_type); -+FMT_TYPE_CONSTANT(Char, char_type); -+FMT_TYPE_CONSTANT(float, float_type); -+FMT_TYPE_CONSTANT(double, double_type); -+FMT_TYPE_CONSTANT(long double, long_double_type); -+FMT_TYPE_CONSTANT(const Char*, cstring_type); -+FMT_TYPE_CONSTANT(basic_string_view, string_type); -+FMT_TYPE_CONSTANT(const void*, pointer_type); -+ -+constexpr auto is_integral_type(type t) -> bool { -+ return t > type::none_type && t <= type::last_integer_type; -+} -+constexpr auto is_arithmetic_type(type t) -> bool { -+ return t > type::none_type && t <= type::last_numeric_type; -+} -+ -+constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } -+constexpr auto in(type t, int set) -> bool { -+ return ((set >> static_cast(t)) & 1) != 0; -+} -+ -+// Bitsets of types. -+enum { -+ sint_set = -+ set(type::int_type) | set(type::long_long_type) | set(type::int128_type), -+ uint_set = set(type::uint_type) | set(type::ulong_long_type) | -+ set(type::uint128_type), -+ bool_set = set(type::bool_type), -+ char_set = set(type::char_type), -+ float_set = set(type::float_type) | set(type::double_type) | -+ set(type::long_double_type), -+ string_set = set(type::string_type), -+ cstring_set = set(type::cstring_type), -+ pointer_set = set(type::pointer_type) -+}; -+ -+struct view {}; -+ -+template struct named_arg; -+template struct is_named_arg : std::false_type {}; -+template struct is_static_named_arg : std::false_type {}; -+ -+template -+struct is_named_arg> : std::true_type {}; -+ -+template struct named_arg : view { -+ const Char* name; -+ const T& value; -+ -+ named_arg(const Char* n, const T& v) : name(n), value(v) {} -+ static_assert(!is_named_arg::value, "nested named arguments"); -+}; -+ -+template constexpr auto count() -> int { return B ? 1 : 0; } -+template constexpr auto count() -> int { -+ return (B1 ? 1 : 0) + count(); -+} -+ -+template constexpr auto count_named_args() -> int { -+ return count::value...>(); -+} -+template constexpr auto count_static_named_args() -> int { -+ return count::value...>(); -+} -+ -+template struct named_arg_info { -+ const Char* name; -+ int id; -+}; -+ -+// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13. -+template -+FMT_CONSTEXPR void check_for_duplicate(named_arg_info* named_args, -+ int named_arg_index, -+ basic_string_view arg_name) { -+ for (int i = 0; i < named_arg_index; ++i) { -+ if (named_args[i].name == arg_name) report_error("duplicate named arg"); -+ } -+} -+ -+template ::value)> -+void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) { -+ ++arg_index; -+} -+template ::value)> -+void init_named_arg(named_arg_info* named_args, int& arg_index, -+ int& named_arg_index, const T& arg) { -+ check_for_duplicate(named_args, named_arg_index, arg.name); -+ named_args[named_arg_index++] = {arg.name, arg_index++}; -+} -+ -+template ::value)> -+FMT_CONSTEXPR void init_static_named_arg(named_arg_info*, int& arg_index, -+ int&) { -+ ++arg_index; -+} -+template ::value)> -+FMT_CONSTEXPR void init_static_named_arg(named_arg_info* named_args, -+ int& arg_index, int& named_arg_index) { -+ check_for_duplicate(named_args, named_arg_index, T::name); -+ named_args[named_arg_index++] = {T::name, arg_index++}; -+} -+ -+// To minimize the number of types we need to deal with, long is translated -+// either to int or to long long depending on its size. -+enum { long_short = sizeof(long) == sizeof(int) }; -+using long_type = conditional_t; -+using ulong_type = conditional_t; -+ -+template -+using format_as_result = -+ remove_cvref_t()))>; -+template -+using format_as_member_result = -+ remove_cvref_t::format_as(std::declval()))>; -+ -+template -+struct use_format_as : std::false_type {}; -+// format_as member is only used to avoid injection into the std namespace. -+template -+struct use_format_as_member : std::false_type {}; -+ -+// Only map owning types because mapping views can be unsafe. -+template -+struct use_format_as< -+ T, bool_constant>::value>> -+ : std::true_type {}; -+template -+struct use_format_as_member< -+ T, bool_constant>::value>> -+ : std::true_type {}; -+ -+template > -+using use_formatter = -+ bool_constant<(std::is_class::value || std::is_enum::value || -+ std::is_union::value || std::is_array::value) && -+ !has_to_string_view::value && !is_named_arg::value && -+ !use_format_as::value && !use_format_as_member::value>; -+ -+template > -+auto has_formatter_impl(T* p, buffered_context* ctx = nullptr) -+ -> decltype(formatter().format(*p, *ctx), std::true_type()); -+template auto has_formatter_impl(...) -> std::false_type; -+ -+// T can be const-qualified to check if it is const-formattable. -+template constexpr auto has_formatter() -> bool { -+ return decltype(has_formatter_impl(static_cast(nullptr)))::value; -+} -+ -+// Maps formatting argument types to natively supported types or user-defined -+// types with formatters. Returns void on errors to be SFINAE-friendly. -+template struct type_mapper { -+ static auto map(signed char) -> int; -+ static auto map(unsigned char) -> unsigned; -+ static auto map(short) -> int; -+ static auto map(unsigned short) -> unsigned; -+ static auto map(int) -> int; -+ static auto map(unsigned) -> unsigned; -+ static auto map(long) -> long_type; -+ static auto map(unsigned long) -> ulong_type; -+ static auto map(long long) -> long long; -+ static auto map(unsigned long long) -> unsigned long long; -+ static auto map(int128_opt) -> int128_opt; -+ static auto map(uint128_opt) -> uint128_opt; -+ static auto map(bool) -> bool; -+ -+ template -+ static auto map(bitint) -> conditional_t; -+ template -+ static auto map(ubitint) -+ -> conditional_t; -+ -+ template ::value)> -+ static auto map(T) -> conditional_t< -+ std::is_same::value || std::is_same::value, Char, void>; -+ -+ static auto map(float) -> float; -+ static auto map(double) -> double; -+ static auto map(long double) -> long double; -+ -+ static auto map(Char*) -> const Char*; -+ static auto map(const Char*) -> const Char*; -+ template , -+ FMT_ENABLE_IF(!std::is_pointer::value)> -+ static auto map(const T&) -> conditional_t::value, -+ basic_string_view, void>; -+ -+ static auto map(void*) -> const void*; -+ static auto map(const void*) -> const void*; -+ static auto map(volatile void*) -> const void*; -+ static auto map(const volatile void*) -> const void*; -+ static auto map(nullptr_t) -> const void*; -+ template ::value || -+ std::is_member_pointer::value)> -+ static auto map(const T&) -> void; -+ -+ template ::value)> -+ static auto map(const T& x) -> decltype(map(format_as(x))); -+ template ::value)> -+ static auto map(const T& x) -> decltype(map(formatter::format_as(x))); -+ -+ template ::value)> -+ static auto map(T&) -> conditional_t(), T&, void>; -+ -+ template ::value)> -+ static auto map(const T& named_arg) -> decltype(map(named_arg.value)); -+}; -+ -+// detail:: is used to workaround a bug in MSVC 2017. -+template -+using mapped_t = decltype(detail::type_mapper::map(std::declval())); -+ -+// A type constant after applying type_mapper. -+template -+using mapped_type_constant = type_constant, Char>; -+ -+template ::value> -+using stored_type_constant = std::integral_constant< -+ type, Context::builtin_types || TYPE == type::int_type ? TYPE -+ : type::custom_type>; -+// A parse context with extra data used only in compile-time checks. -+template -+class compile_parse_context : public parse_context { -+ private: -+ int num_args_; -+ const type* types_; -+ using base = parse_context; -+ -+ public: -+ FMT_CONSTEXPR explicit compile_parse_context(basic_string_view fmt, -+ int num_args, const type* types, -+ int next_arg_id = 0) -+ : base(fmt, next_arg_id), num_args_(num_args), types_(types) {} -+ -+ constexpr auto num_args() const -> int { return num_args_; } -+ constexpr auto arg_type(int id) const -> type { return types_[id]; } -+ -+ FMT_CONSTEXPR auto next_arg_id() -> int { -+ int id = base::next_arg_id(); -+ if (id >= num_args_) report_error("argument not found"); -+ return id; -+ } -+ -+ FMT_CONSTEXPR void check_arg_id(int id) { -+ base::check_arg_id(id); -+ if (id >= num_args_) report_error("argument not found"); -+ } -+ using base::check_arg_id; -+ -+ FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { -+ ignore_unused(arg_id); -+ if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) -+ report_error("width/precision is not integer"); -+ } -+}; -+ -+// An argument reference. -+template union arg_ref { -+ FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {} -+ FMT_CONSTEXPR arg_ref(basic_string_view n) : name(n) {} -+ -+ int index; -+ basic_string_view name; -+}; -+ -+// Format specifiers with width and precision resolved at formatting rather -+// than parsing time to allow reusing the same parsed specifiers with -+// different sets of arguments (precompilation of format strings). -+template struct dynamic_format_specs : format_specs { -+ arg_ref width_ref; -+ arg_ref precision_ref; -+}; -+ -+// Converts a character to ASCII. Returns '\0' on conversion failure. -+template ::value)> -+constexpr auto to_ascii(Char c) -> char { -+ return c <= 0xff ? static_cast(c) : '\0'; -+} -+ -+// Returns the number of code units in a code point or 1 on error. -+template -+FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { -+ if (const_check(sizeof(Char) != 1)) return 1; -+ auto c = static_cast(*begin); -+ return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1; -+} -+ -+// Parses the range [begin, end) as an unsigned integer. This function assumes -+// that the range is non-empty and the first character is a digit. -+template -+FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, -+ int error_value) noexcept -> int { -+ FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); -+ unsigned value = 0, prev = 0; -+ auto p = begin; -+ do { -+ prev = value; -+ value = value * 10 + unsigned(*p - '0'); -+ ++p; -+ } while (p != end && '0' <= *p && *p <= '9'); -+ auto num_digits = p - begin; -+ begin = p; -+ int digits10 = static_cast(sizeof(int) * CHAR_BIT * 3 / 10); -+ if (num_digits <= digits10) return static_cast(value); -+ // Check for overflow. -+ unsigned max = INT_MAX; -+ return num_digits == digits10 + 1 && -+ prev * 10ull + unsigned(p[-1] - '0') <= max -+ ? static_cast(value) -+ : error_value; -+} -+ -+FMT_CONSTEXPR inline auto parse_align(char c) -> align { -+ switch (c) { -+ case '<': return align::left; -+ case '>': return align::right; -+ case '^': return align::center; -+ } -+ return align::none; -+} -+ -+template constexpr auto is_name_start(Char c) -> bool { -+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; -+} -+ -+template -+FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end, -+ Handler&& handler) -> const Char* { -+ Char c = *begin; -+ if (c >= '0' && c <= '9') { -+ int index = 0; -+ if (c != '0') -+ index = parse_nonnegative_int(begin, end, INT_MAX); -+ else -+ ++begin; -+ if (begin == end || (*begin != '}' && *begin != ':')) -+ report_error("invalid format string"); -+ else -+ handler.on_index(index); -+ return begin; -+ } -+ if (FMT_OPTIMIZE_SIZE > 1 || !is_name_start(c)) { -+ report_error("invalid format string"); -+ return begin; -+ } -+ auto it = begin; -+ do { -+ ++it; -+ } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); -+ handler.on_name({begin, to_unsigned(it - begin)}); -+ return it; -+} -+ -+template struct dynamic_spec_handler { -+ parse_context& ctx; -+ arg_ref& ref; -+ arg_id_kind& kind; -+ -+ FMT_CONSTEXPR void on_index(int id) { -+ ref = id; -+ kind = arg_id_kind::index; -+ ctx.check_arg_id(id); -+ ctx.check_dynamic_spec(id); -+ } -+ FMT_CONSTEXPR void on_name(basic_string_view id) { -+ ref = id; -+ kind = arg_id_kind::name; -+ ctx.check_arg_id(id); -+ } -+}; -+ -+template struct parse_dynamic_spec_result { -+ const Char* end; -+ arg_id_kind kind; -+}; -+ -+// Parses integer | "{" [arg_id] "}". -+template -+FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, -+ int& value, arg_ref& ref, -+ parse_context& ctx) -+ -> parse_dynamic_spec_result { -+ FMT_ASSERT(begin != end, ""); -+ auto kind = arg_id_kind::none; -+ if ('0' <= *begin && *begin <= '9') { -+ int val = parse_nonnegative_int(begin, end, -1); -+ if (val == -1) report_error("number is too big"); -+ value = val; -+ } else { -+ if (*begin == '{') { -+ ++begin; -+ if (begin != end) { -+ Char c = *begin; -+ if (c == '}' || c == ':') { -+ int id = ctx.next_arg_id(); -+ ref = id; -+ kind = arg_id_kind::index; -+ ctx.check_dynamic_spec(id); -+ } else { -+ begin = parse_arg_id(begin, end, -+ dynamic_spec_handler{ctx, ref, kind}); -+ } -+ } -+ if (begin != end && *begin == '}') return {++begin, kind}; -+ } -+ report_error("invalid format string"); -+ } -+ return {begin, kind}; -+} -+ -+template -+FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, -+ format_specs& specs, arg_ref& width_ref, -+ parse_context& ctx) -> const Char* { -+ auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); -+ specs.set_dynamic_width(result.kind); -+ return result.end; -+} -+ -+template -+FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, -+ format_specs& specs, -+ arg_ref& precision_ref, -+ parse_context& ctx) -> const Char* { -+ ++begin; -+ if (begin == end) { -+ report_error("invalid precision"); -+ return begin; -+ } -+ auto result = -+ parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx); -+ specs.set_dynamic_precision(result.kind); -+ return result.end; -+} -+ -+enum class state { start, align, sign, hash, zero, width, precision, locale }; -+ -+// Parses standard format specifiers. -+template -+FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, -+ dynamic_format_specs& specs, -+ parse_context& ctx, type arg_type) -+ -> const Char* { -+ auto c = '\0'; -+ if (end - begin > 1) { -+ auto next = to_ascii(begin[1]); -+ c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; -+ } else { -+ if (begin == end) return begin; -+ c = to_ascii(*begin); -+ } -+ -+ struct { -+ state current_state = state::start; -+ FMT_CONSTEXPR void operator()(state s, bool valid = true) { -+ if (current_state >= s || !valid) -+ report_error("invalid format specifier"); -+ current_state = s; -+ } -+ } enter_state; -+ -+ using pres = presentation_type; -+ constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; -+ struct { -+ const Char*& begin; -+ format_specs& specs; -+ type arg_type; -+ -+ FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { -+ if (!in(arg_type, set)) report_error("invalid format specifier"); -+ specs.set_type(pres_type); -+ return begin + 1; -+ } -+ } parse_presentation_type{begin, specs, arg_type}; -+ -+ for (;;) { -+ switch (c) { -+ case '<': -+ case '>': -+ case '^': -+ enter_state(state::align); -+ specs.set_align(parse_align(c)); -+ ++begin; -+ break; -+ case '+': -+ case ' ': -+ specs.set_sign(c == ' ' ? sign::space : sign::plus); -+ FMT_FALLTHROUGH; -+ case '-': -+ enter_state(state::sign, in(arg_type, sint_set | float_set)); -+ ++begin; -+ break; -+ case '#': -+ enter_state(state::hash, is_arithmetic_type(arg_type)); -+ specs.set_alt(); -+ ++begin; -+ break; -+ case '0': -+ enter_state(state::zero); -+ if (!is_arithmetic_type(arg_type)) -+ report_error("format specifier requires numeric argument"); -+ if (specs.align() == align::none) { -+ // Ignore 0 if align is specified for compatibility with std::format. -+ specs.set_align(align::numeric); -+ specs.set_fill('0'); -+ } -+ ++begin; -+ break; -+ // clang-format off -+ case '1': case '2': case '3': case '4': case '5': -+ case '6': case '7': case '8': case '9': case '{': -+ // clang-format on -+ enter_state(state::width); -+ begin = parse_width(begin, end, specs, specs.width_ref, ctx); -+ break; -+ case '.': -+ enter_state(state::precision, -+ in(arg_type, float_set | string_set | cstring_set)); -+ begin = parse_precision(begin, end, specs, specs.precision_ref, ctx); -+ break; -+ case 'L': -+ enter_state(state::locale, is_arithmetic_type(arg_type)); -+ specs.set_localized(); -+ ++begin; -+ break; -+ case 'd': return parse_presentation_type(pres::dec, integral_set); -+ case 'X': specs.set_upper(); FMT_FALLTHROUGH; -+ case 'x': return parse_presentation_type(pres::hex, integral_set); -+ case 'o': return parse_presentation_type(pres::oct, integral_set); -+ case 'B': specs.set_upper(); FMT_FALLTHROUGH; -+ case 'b': return parse_presentation_type(pres::bin, integral_set); -+ case 'E': specs.set_upper(); FMT_FALLTHROUGH; -+ case 'e': return parse_presentation_type(pres::exp, float_set); -+ case 'F': specs.set_upper(); FMT_FALLTHROUGH; -+ case 'f': return parse_presentation_type(pres::fixed, float_set); -+ case 'G': specs.set_upper(); FMT_FALLTHROUGH; -+ case 'g': return parse_presentation_type(pres::general, float_set); -+ case 'A': specs.set_upper(); FMT_FALLTHROUGH; -+ case 'a': return parse_presentation_type(pres::hexfloat, float_set); -+ case 'c': -+ if (arg_type == type::bool_type) report_error("invalid format specifier"); -+ return parse_presentation_type(pres::chr, integral_set); -+ case 's': -+ return parse_presentation_type(pres::string, -+ bool_set | string_set | cstring_set); -+ case 'p': -+ return parse_presentation_type(pres::pointer, pointer_set | cstring_set); -+ case '?': -+ return parse_presentation_type(pres::debug, -+ char_set | string_set | cstring_set); -+ case '}': return begin; -+ default: { -+ if (*begin == '}') return begin; -+ // Parse fill and alignment. -+ auto fill_end = begin + code_point_length(begin); -+ if (end - fill_end <= 0) { -+ report_error("invalid format specifier"); -+ return begin; -+ } -+ if (*begin == '{') { -+ report_error("invalid fill character '{'"); -+ return begin; -+ } -+ auto alignment = parse_align(to_ascii(*fill_end)); -+ enter_state(state::align, alignment != align::none); -+ specs.set_fill( -+ basic_string_view(begin, to_unsigned(fill_end - begin))); -+ specs.set_align(alignment); -+ begin = fill_end + 1; -+ } -+ } -+ if (begin == end) return begin; -+ c = to_ascii(*begin); -+ } -+} -+ -+template -+FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin, -+ const Char* end, -+ Handler&& handler) -+ -> const Char* { -+ ++begin; -+ if (begin == end) { -+ handler.on_error("invalid format string"); -+ return end; -+ } -+ int arg_id = 0; -+ switch (*begin) { -+ case '}': -+ handler.on_replacement_field(handler.on_arg_id(), begin); -+ return begin + 1; -+ case '{': handler.on_text(begin, begin + 1); return begin + 1; -+ case ':': arg_id = handler.on_arg_id(); break; -+ default: { -+ struct id_adapter { -+ Handler& handler; -+ int arg_id; -+ -+ FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } -+ FMT_CONSTEXPR void on_name(basic_string_view id) { -+ arg_id = handler.on_arg_id(id); -+ } -+ } adapter = {handler, 0}; -+ begin = parse_arg_id(begin, end, adapter); -+ arg_id = adapter.arg_id; -+ Char c = begin != end ? *begin : Char(); -+ if (c == '}') { -+ handler.on_replacement_field(arg_id, begin); -+ return begin + 1; -+ } -+ if (c != ':') { -+ handler.on_error("missing '}' in format string"); -+ return end; -+ } -+ break; -+ } -+ } -+ begin = handler.on_format_specs(arg_id, begin + 1, end); -+ if (begin == end || *begin != '}') -+ return handler.on_error("unknown format specifier"), end; -+ return begin + 1; -+} -+ -+template -+FMT_CONSTEXPR void parse_format_string(basic_string_view fmt, -+ Handler&& handler) { -+ auto begin = fmt.data(), end = begin + fmt.size(); -+ auto p = begin; -+ while (p != end) { -+ auto c = *p++; -+ if (c == '{') { -+ handler.on_text(begin, p - 1); -+ begin = p = parse_replacement_field(p - 1, end, handler); -+ } else if (c == '}') { -+ if (p == end || *p != '}') -+ return handler.on_error("unmatched '}' in format string"); -+ handler.on_text(begin, p); -+ begin = ++p; -+ } -+ } -+ handler.on_text(begin, end); -+} -+ -+// Checks char specs and returns true iff the presentation type is char-like. -+FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { -+ auto type = specs.type(); -+ if (type != presentation_type::none && type != presentation_type::chr && -+ type != presentation_type::debug) { -+ return false; -+ } -+ if (specs.align() == align::numeric || specs.sign() != sign::none || -+ specs.alt()) { -+ report_error("invalid format specifier for char"); -+ } -+ return true; -+} -+ -+// A base class for compile-time strings. -+struct compile_string {}; -+ -+template -+FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769). -+FMT_CONSTEXPR auto invoke_parse(parse_context& ctx) -> const Char* { -+ using mapped_type = remove_cvref_t>; -+ constexpr bool formattable = -+ std::is_constructible>::value; -+ if (!formattable) return ctx.begin(); // Error is reported in the value ctor. -+ using formatted_type = conditional_t; -+ return formatter().parse(ctx); -+} -+ -+template struct arg_pack {}; -+ -+template -+class format_string_checker { -+ private: -+ type types_[max_of(1, NUM_ARGS)]; -+ named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)]; -+ compile_parse_context context_; -+ -+ using parse_func = auto (*)(parse_context&) -> const Char*; -+ parse_func parse_funcs_[max_of(1, NUM_ARGS)]; -+ -+ public: -+ template -+ FMT_CONSTEXPR explicit format_string_checker(basic_string_view fmt, -+ arg_pack) -+ : types_{mapped_type_constant::value...}, -+ named_args_{}, -+ context_(fmt, NUM_ARGS, types_), -+ parse_funcs_{&invoke_parse...} { -+ int arg_index = 0, named_arg_index = 0; -+ FMT_APPLY_VARIADIC( -+ init_static_named_arg(named_args_, arg_index, named_arg_index)); -+ ignore_unused(arg_index, named_arg_index); -+ } -+ -+ FMT_CONSTEXPR void on_text(const Char*, const Char*) {} -+ -+ FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } -+ FMT_CONSTEXPR auto on_arg_id(int id) -> int { -+ context_.check_arg_id(id); -+ return id; -+ } -+ FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -+ for (int i = 0; i < NUM_NAMED_ARGS; ++i) { -+ if (named_args_[i].name == id) return named_args_[i].id; -+ } -+ if (!DYNAMIC_NAMES) on_error("argument not found"); -+ return -1; -+ } -+ -+ FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { -+ on_format_specs(id, begin, begin); // Call parse() on empty specs. -+ } -+ -+ FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end) -+ -> const Char* { -+ context_.advance_to(begin); -+ if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_); -+ while (begin != end && *begin != '}') ++begin; -+ return begin; -+ } -+ -+ FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) { -+ report_error(message); -+ } -+}; -+ -+/// A contiguous memory buffer with an optional growing ability. It is an -+/// internal class and shouldn't be used directly, only via `memory_buffer`. -+template class buffer { -+ private: -+ T* ptr_; -+ size_t size_; -+ size_t capacity_; -+ -+ using grow_fun = void (*)(buffer& buf, size_t capacity); -+ grow_fun grow_; -+ -+ protected: -+ // Don't initialize ptr_ since it is not accessed to save a few cycles. -+ FMT_MSC_WARNING(suppress : 26495) -+ FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept -+ : size_(sz), capacity_(sz), grow_(grow) {} -+ -+ constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0, -+ size_t cap = 0) noexcept -+ : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {} -+ -+ FMT_CONSTEXPR20 ~buffer() = default; -+ buffer(buffer&&) = default; -+ -+ /// Sets the buffer data and capacity. -+ FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { -+ ptr_ = buf_data; -+ capacity_ = buf_capacity; -+ } -+ -+ public: -+ using value_type = T; -+ using const_reference = const T&; -+ -+ buffer(const buffer&) = delete; -+ void operator=(const buffer&) = delete; -+ -+ auto begin() noexcept -> T* { return ptr_; } -+ auto end() noexcept -> T* { return ptr_ + size_; } -+ -+ auto begin() const noexcept -> const T* { return ptr_; } -+ auto end() const noexcept -> const T* { return ptr_ + size_; } -+ -+ /// Returns the size of this buffer. -+ constexpr auto size() const noexcept -> size_t { return size_; } -+ -+ /// Returns the capacity of this buffer. -+ constexpr auto capacity() const noexcept -> size_t { return capacity_; } -+ -+ /// Returns a pointer to the buffer data (not null-terminated). -+ FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } -+ FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } -+ -+ /// Clears this buffer. -+ FMT_CONSTEXPR void clear() { size_ = 0; } -+ -+ // Tries resizing the buffer to contain `count` elements. If T is a POD type -+ // the new elements may not be initialized. -+ FMT_CONSTEXPR void try_resize(size_t count) { -+ try_reserve(count); -+ size_ = min_of(count, capacity_); -+ } -+ -+ // Tries increasing the buffer capacity to `new_capacity`. It can increase the -+ // capacity by a smaller amount than requested but guarantees there is space -+ // for at least one additional element either by increasing the capacity or by -+ // flushing the buffer if it is full. -+ FMT_CONSTEXPR void try_reserve(size_t new_capacity) { -+ if (new_capacity > capacity_) grow_(*this, new_capacity); -+ } -+ -+ FMT_CONSTEXPR void push_back(const T& value) { -+ try_reserve(size_ + 1); -+ ptr_[size_++] = value; -+ } -+ -+ /// Appends data to the end of the buffer. -+ template -+// Workaround for MSVC2019 to fix error C2893: Failed to specialize function -+// template 'void fmt::v11::detail::buffer::append(const U *,const U *)'. -+#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940 -+ FMT_CONSTEXPR20 -+#endif -+ void -+ append(const U* begin, const U* end) { -+ while (begin != end) { -+ auto count = to_unsigned(end - begin); -+ try_reserve(size_ + count); -+ auto free_cap = capacity_ - size_; -+ if (free_cap < count) count = free_cap; -+ // A loop is faster than memcpy on small sizes. -+ T* out = ptr_ + size_; -+ for (size_t i = 0; i < count; ++i) out[i] = begin[i]; -+ size_ += count; -+ begin += count; -+ } -+ } -+ -+ template FMT_CONSTEXPR auto operator[](Idx index) -> T& { -+ return ptr_[index]; -+ } -+ template -+ FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { -+ return ptr_[index]; -+ } -+}; -+ -+struct buffer_traits { -+ constexpr explicit buffer_traits(size_t) {} -+ constexpr auto count() const -> size_t { return 0; } -+ constexpr auto limit(size_t size) const -> size_t { return size; } -+}; -+ -+class fixed_buffer_traits { -+ private: -+ size_t count_ = 0; -+ size_t limit_; -+ -+ public: -+ constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} -+ constexpr auto count() const -> size_t { return count_; } -+ FMT_CONSTEXPR auto limit(size_t size) -> size_t { -+ size_t n = limit_ > count_ ? limit_ - count_ : 0; -+ count_ += size; -+ return min_of(size, n); -+ } -+}; -+ -+// A buffer that writes to an output iterator when flushed. -+template -+class iterator_buffer : public Traits, public buffer { -+ private: -+ OutputIt out_; -+ enum { buffer_size = 256 }; -+ T data_[buffer_size]; -+ -+ static FMT_CONSTEXPR void grow(buffer& buf, size_t) { -+ if (buf.size() == buffer_size) static_cast(buf).flush(); -+ } -+ -+ void flush() { -+ auto size = this->size(); -+ this->clear(); -+ const T* begin = data_; -+ const T* end = begin + this->limit(size); -+ while (begin != end) *out_++ = *begin++; -+ } -+ -+ public: -+ explicit iterator_buffer(OutputIt out, size_t n = buffer_size) -+ : Traits(n), buffer(grow, data_, 0, buffer_size), out_(out) {} -+ iterator_buffer(iterator_buffer&& other) noexcept -+ : Traits(other), -+ buffer(grow, data_, 0, buffer_size), -+ out_(other.out_) {} -+ ~iterator_buffer() { -+ // Don't crash if flush fails during unwinding. -+ FMT_TRY { flush(); } -+ FMT_CATCH(...) {} -+ } -+ -+ auto out() -> OutputIt { -+ flush(); -+ return out_; -+ } -+ auto count() const -> size_t { return Traits::count() + this->size(); } -+}; -+ -+template -+class iterator_buffer : public fixed_buffer_traits, -+ public buffer { -+ private: -+ T* out_; -+ enum { buffer_size = 256 }; -+ T data_[buffer_size]; -+ -+ static FMT_CONSTEXPR void grow(buffer& buf, size_t) { -+ if (buf.size() == buf.capacity()) -+ static_cast(buf).flush(); -+ } -+ -+ void flush() { -+ size_t n = this->limit(this->size()); -+ if (this->data() == out_) { -+ out_ += n; -+ this->set(data_, buffer_size); -+ } -+ this->clear(); -+ } -+ -+ public: -+ explicit iterator_buffer(T* out, size_t n = buffer_size) -+ : fixed_buffer_traits(n), buffer(grow, out, 0, n), out_(out) {} -+ iterator_buffer(iterator_buffer&& other) noexcept -+ : fixed_buffer_traits(other), -+ buffer(static_cast(other)), -+ out_(other.out_) { -+ if (this->data() != out_) { -+ this->set(data_, buffer_size); -+ this->clear(); -+ } -+ } -+ ~iterator_buffer() { flush(); } -+ -+ auto out() -> T* { -+ flush(); -+ return out_; -+ } -+ auto count() const -> size_t { -+ return fixed_buffer_traits::count() + this->size(); -+ } -+}; -+ -+template class iterator_buffer : public buffer { -+ public: -+ explicit iterator_buffer(T* out, size_t = 0) -+ : buffer([](buffer&, size_t) {}, out, 0, ~size_t()) {} -+ -+ auto out() -> T* { return &*this->end(); } -+}; -+ -+template -+class container_buffer : public buffer { -+ private: -+ using value_type = typename Container::value_type; -+ -+ static FMT_CONSTEXPR void grow(buffer& buf, size_t capacity) { -+ auto& self = static_cast(buf); -+ self.container.resize(capacity); -+ self.set(&self.container[0], capacity); -+ } -+ -+ public: -+ Container& container; -+ -+ explicit container_buffer(Container& c) -+ : buffer(grow, c.size()), container(c) {} -+}; -+ -+// A buffer that writes to a container with the contiguous storage. -+template -+class iterator_buffer< -+ OutputIt, -+ enable_if_t::value && -+ is_contiguous::value, -+ typename OutputIt::container_type::value_type>> -+ : public container_buffer { -+ private: -+ using base = container_buffer; -+ -+ public: -+ explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {} -+ explicit iterator_buffer(OutputIt out, size_t = 0) -+ : base(get_container(out)) {} -+ -+ auto out() -> OutputIt { return OutputIt(this->container); } -+}; -+ -+// A buffer that counts the number of code units written discarding the output. -+template class counting_buffer : public buffer { -+ private: -+ enum { buffer_size = 256 }; -+ T data_[buffer_size]; -+ size_t count_ = 0; -+ -+ static FMT_CONSTEXPR void grow(buffer& buf, size_t) { -+ if (buf.size() != buffer_size) return; -+ static_cast(buf).count_ += buf.size(); -+ buf.clear(); -+ } -+ -+ public: -+ FMT_CONSTEXPR counting_buffer() : buffer(grow, data_, 0, buffer_size) {} -+ -+ constexpr auto count() const noexcept -> size_t { -+ return count_ + this->size(); -+ } -+}; -+ -+template -+struct is_back_insert_iterator> : std::true_type {}; -+ -+template -+struct has_back_insert_iterator_container_append : std::false_type {}; -+template -+struct has_back_insert_iterator_container_append< -+ OutputIt, InputIt, -+ void_t()) -+ .append(std::declval(), -+ std::declval()))>> : std::true_type {}; -+ -+// An optimized version of std::copy with the output value type (T). -+template ::value&& -+ has_back_insert_iterator_container_append< -+ OutputIt, InputIt>::value)> -+FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) -+ -> OutputIt { -+ get_container(out).append(begin, end); -+ return out; -+} -+ -+template ::value && -+ !has_back_insert_iterator_container_append< -+ OutputIt, InputIt>::value)> -+FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) -+ -> OutputIt { -+ auto& c = get_container(out); -+ c.insert(c.end(), begin, end); -+ return out; -+} -+ -+template ::value)> -+FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { -+ while (begin != end) *out++ = static_cast(*begin++); -+ return out; -+} -+ -+template -+FMT_CONSTEXPR auto copy(basic_string_view s, OutputIt out) -> OutputIt { -+ return copy(s.begin(), s.end(), out); -+} -+ -+template -+struct is_buffer_appender : std::false_type {}; -+template -+struct is_buffer_appender< -+ It, bool_constant< -+ is_back_insert_iterator::value && -+ std::is_base_of, -+ typename It::container_type>::value>> -+ : std::true_type {}; -+ -+// Maps an output iterator to a buffer. -+template ::value)> -+auto get_buffer(OutputIt out) -> iterator_buffer { -+ return iterator_buffer(out); -+} -+template ::value)> -+auto get_buffer(OutputIt out) -> buffer& { -+ return get_container(out); -+} -+ -+template -+auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { -+ return buf.out(); -+} -+template -+auto get_iterator(buffer&, OutputIt out) -> OutputIt { -+ return out; -+} -+ -+// This type is intentionally undefined, only used for errors. -+template struct type_is_unformattable_for; -+ -+template struct string_value { -+ const Char* data; -+ size_t size; -+ auto str() const -> basic_string_view { return {data, size}; } -+}; -+ -+template struct custom_value { -+ using char_type = typename Context::char_type; -+ void* value; -+ void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); -+}; -+ -+template struct named_arg_value { -+ const named_arg_info* data; -+ size_t size; -+}; -+ -+struct custom_tag {}; -+ -+#if !FMT_BUILTIN_TYPES -+# define FMT_BUILTIN , monostate -+#else -+# define FMT_BUILTIN -+#endif -+ -+// A formatting argument value. -+template class value { -+ public: -+ using char_type = typename Context::char_type; -+ -+ union { -+ monostate no_value; -+ int int_value; -+ unsigned uint_value; -+ long long long_long_value; -+ unsigned long long ulong_long_value; -+ int128_opt int128_value; -+ uint128_opt uint128_value; -+ bool bool_value; -+ char_type char_value; -+ float float_value; -+ double double_value; -+ long double long_double_value; -+ const void* pointer; -+ string_value string; -+ custom_value custom; -+ named_arg_value named_args; -+ }; -+ -+ constexpr FMT_INLINE value() : no_value() {} -+ constexpr FMT_INLINE value(signed char x) : int_value(x) {} -+ constexpr FMT_INLINE value(unsigned char x FMT_BUILTIN) : uint_value(x) {} -+ constexpr FMT_INLINE value(signed short x) : int_value(x) {} -+ constexpr FMT_INLINE value(unsigned short x FMT_BUILTIN) : uint_value(x) {} -+ constexpr FMT_INLINE value(int x) : int_value(x) {} -+ constexpr FMT_INLINE value(unsigned x FMT_BUILTIN) : uint_value(x) {} -+ FMT_CONSTEXPR FMT_INLINE value(long x FMT_BUILTIN) : value(long_type(x)) {} -+ FMT_CONSTEXPR FMT_INLINE value(unsigned long x FMT_BUILTIN) -+ : value(ulong_type(x)) {} -+ constexpr FMT_INLINE value(long long x FMT_BUILTIN) : long_long_value(x) {} -+ constexpr FMT_INLINE value(unsigned long long x FMT_BUILTIN) -+ : ulong_long_value(x) {} -+ FMT_INLINE value(int128_opt x FMT_BUILTIN) : int128_value(x) {} -+ FMT_INLINE value(uint128_opt x FMT_BUILTIN) : uint128_value(x) {} -+ constexpr FMT_INLINE value(bool x FMT_BUILTIN) : bool_value(x) {} -+ -+ template -+ constexpr FMT_INLINE value(bitint x FMT_BUILTIN) : long_long_value(x) { -+ static_assert(N <= 64, "unsupported _BitInt"); -+ } -+ template -+ constexpr FMT_INLINE value(ubitint x FMT_BUILTIN) : ulong_long_value(x) { -+ static_assert(N <= 64, "unsupported _BitInt"); -+ } -+ -+ template ::value)> -+ constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { -+ static_assert( -+ std::is_same::value || std::is_same::value, -+ "mixing character types is disallowed"); -+ } -+ -+ constexpr FMT_INLINE value(float x FMT_BUILTIN) : float_value(x) {} -+ constexpr FMT_INLINE value(double x FMT_BUILTIN) : double_value(x) {} -+ FMT_INLINE value(long double x FMT_BUILTIN) : long_double_value(x) {} -+ -+ FMT_CONSTEXPR FMT_INLINE value(char_type* x FMT_BUILTIN) { -+ string.data = x; -+ if (is_constant_evaluated()) string.size = 0; -+ } -+ FMT_CONSTEXPR FMT_INLINE value(const char_type* x FMT_BUILTIN) { -+ string.data = x; -+ if (is_constant_evaluated()) string.size = 0; -+ } -+ template , -+ FMT_ENABLE_IF(!std::is_pointer::value)> -+ FMT_CONSTEXPR value(const T& x FMT_BUILTIN) { -+ static_assert(std::is_same::value, -+ "mixing character types is disallowed"); -+ auto sv = to_string_view(x); -+ string.data = sv.data(); -+ string.size = sv.size(); -+ } -+ FMT_INLINE value(void* x FMT_BUILTIN) : pointer(x) {} -+ FMT_INLINE value(const void* x FMT_BUILTIN) : pointer(x) {} -+ FMT_INLINE value(volatile void* x FMT_BUILTIN) -+ : pointer(const_cast(x)) {} -+ FMT_INLINE value(const volatile void* x FMT_BUILTIN) -+ : pointer(const_cast(x)) {} -+ FMT_INLINE value(nullptr_t) : pointer(nullptr) {} -+ -+ template ::value || -+ std::is_member_pointer::value)> -+ value(const T&) { -+ // Formatting of arbitrary pointers is disallowed. If you want to format a -+ // pointer cast it to `void*` or `const void*`. In particular, this forbids -+ // formatting of `[const] volatile char*` printed as bool by iostreams. -+ static_assert(sizeof(T) == 0, -+ "formatting of non-void pointers is disallowed"); -+ } -+ -+ template ::value)> -+ value(const T& x) : value(format_as(x)) {} -+ template ::value)> -+ value(const T& x) : value(formatter::format_as(x)) {} -+ -+ template ::value)> -+ value(const T& named_arg) : value(named_arg.value) {} -+ -+ template ::value || !FMT_BUILTIN_TYPES)> -+ FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {} -+ -+ FMT_ALWAYS_INLINE value(const named_arg_info* args, size_t size) -+ : named_args{args, size} {} -+ -+ private: -+ template ())> -+ FMT_CONSTEXPR value(T& x, custom_tag) { -+ using value_type = remove_const_t; -+ // T may overload operator& e.g. std::vector::reference in libc++. -+ if (!is_constant_evaluated()) { -+ custom.value = -+ const_cast(&reinterpret_cast(x)); -+ } else { -+ custom.value = nullptr; -+#if defined(__cpp_if_constexpr) -+ if constexpr (std::is_same*>::value) -+ custom.value = const_cast(&x); -+#endif -+ } -+ custom.format = format_custom>; -+ } -+ -+ template ())> -+ FMT_CONSTEXPR value(const T&, custom_tag) { -+ // Cannot format an argument; to make type T formattable provide a -+ // formatter specialization: https://fmt.dev/latest/api.html#udt. -+ type_is_unformattable_for _; -+ } -+ -+ // Formats an argument of a custom type, such as a user-defined class. -+ template -+ static void format_custom(void* arg, parse_context& parse_ctx, -+ Context& ctx) { -+ auto f = Formatter(); -+ parse_ctx.advance_to(f.parse(parse_ctx)); -+ using qualified_type = -+ conditional_t(), const T, T>; -+ // format must be const for compatibility with std::format and compilation. -+ const auto& cf = f; -+ ctx.advance_to(cf.format(*static_cast(arg), ctx)); -+ } -+}; -+ -+enum { packed_arg_bits = 4 }; -+// Maximum number of arguments with packed types. -+enum { max_packed_args = 62 / packed_arg_bits }; -+enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; -+enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; -+ -+template -+struct is_output_iterator : std::false_type {}; -+ -+template <> struct is_output_iterator : std::true_type {}; -+ -+template -+struct is_output_iterator< -+ It, T, -+ enable_if_t&>()++), -+ T>::value>> : std::true_type {}; -+ -+#ifndef FMT_USE_LOCALE -+# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) -+#endif -+ -+// A type-erased reference to an std::locale to avoid a heavy include. -+class locale_ref { -+#if FMT_USE_LOCALE -+ private: -+ const void* locale_; // A type-erased pointer to std::locale. -+ -+ public: -+ constexpr locale_ref() : locale_(nullptr) {} -+ template locale_ref(const Locale& loc); -+ -+ inline explicit operator bool() const noexcept { return locale_ != nullptr; } -+#endif // FMT_USE_LOCALE -+ -+ public: -+ template auto get() const -> Locale; -+}; -+ -+template constexpr auto encode_types() -> unsigned long long { -+ return 0; -+} -+ -+template -+constexpr auto encode_types() -> unsigned long long { -+ return static_cast(stored_type_constant::value) | -+ (encode_types() << packed_arg_bits); -+} -+ -+template -+constexpr auto make_descriptor() -> unsigned long long { -+ return NUM_ARGS <= max_packed_args ? encode_types() -+ : is_unpacked_bit | NUM_ARGS; -+} -+ -+template -+using arg_t = conditional_t, -+ basic_format_arg>; -+ -+template -+struct named_arg_store { -+ // args_[0].named_args points to named_args to avoid bloating format_args. -+ arg_t args[1 + NUM_ARGS]; -+ named_arg_info named_args[NUM_NAMED_ARGS]; -+ -+ template -+ FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) -+ : args{{named_args, NUM_NAMED_ARGS}, values...} { -+ int arg_index = 0, named_arg_index = 0; -+ FMT_APPLY_VARIADIC( -+ init_named_arg(named_args, arg_index, named_arg_index, values)); -+ } -+ -+ named_arg_store(named_arg_store&& rhs) { -+ args[0] = {named_args, NUM_NAMED_ARGS}; -+ for (size_t i = 1; i < sizeof(args) / sizeof(*args); ++i) -+ args[i] = rhs.args[i]; -+ for (size_t i = 0; i < NUM_NAMED_ARGS; ++i) -+ named_args[i] = rhs.named_args[i]; -+ } -+ -+ named_arg_store(const named_arg_store& rhs) = delete; -+ named_arg_store& operator=(const named_arg_store& rhs) = delete; -+ named_arg_store& operator=(named_arg_store&& rhs) = delete; -+ operator const arg_t*() const { return args + 1; } -+}; -+ -+// An array of references to arguments. It can be implicitly converted to -+// `basic_format_args` for passing into type-erased formatting functions -+// such as `vformat`. It is a plain struct to reduce binary size in debug mode. -+template -+struct format_arg_store { -+ // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. -+ using type = -+ conditional_t[max_of(1, NUM_ARGS)], -+ named_arg_store>; -+ type args; -+}; -+ -+// TYPE can be different from type_constant, e.g. for __float128. -+template struct native_formatter { -+ private: -+ dynamic_format_specs specs_; -+ -+ public: -+ using nonlocking = void; -+ -+ FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { -+ if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); -+ auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE); -+ if (const_check(TYPE == type::char_type)) check_char_specs(specs_); -+ return end; -+ } -+ -+ template -+ FMT_CONSTEXPR void set_debug_format(bool set = true) { -+ specs_.set_type(set ? presentation_type::debug : presentation_type::none); -+ } -+ -+ FMT_PRAGMA_CLANG(diagnostic ignored "-Wundefined-inline") -+ template -+ FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const -+ -> decltype(ctx.out()); -+}; -+ -+template -+struct locking -+ : bool_constant::value == type::custom_type> {}; -+template -+struct locking>::nonlocking>> -+ : std::false_type {}; -+ -+template FMT_CONSTEXPR inline auto is_locking() -> bool { -+ return locking::value; -+} -+template -+FMT_CONSTEXPR inline auto is_locking() -> bool { -+ return locking::value || is_locking(); -+} -+ -+FMT_API void vformat_to(buffer& buf, string_view fmt, format_args args, -+ locale_ref loc = {}); -+ -+#if FMT_WIN32 -+FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool); -+#else // format_args is passed by reference since it is defined later. -+inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {} -+#endif -+} // namespace detail -+ -+// The main public API. -+ -+template -+FMT_CONSTEXPR void parse_context::do_check_arg_id(int arg_id) { -+ // Argument id is only checked at compile time during parsing because -+ // formatting has its own validation. -+ if (detail::is_constant_evaluated() && use_constexpr_cast) { -+ auto ctx = static_cast*>(this); -+ if (arg_id >= ctx->num_args()) report_error("argument not found"); -+ } -+} -+ -+template -+FMT_CONSTEXPR void parse_context::check_dynamic_spec(int arg_id) { -+ using detail::compile_parse_context; -+ if (detail::is_constant_evaluated() && use_constexpr_cast) -+ static_cast*>(this)->check_dynamic_spec(arg_id); -+} -+ -+FMT_BEGIN_EXPORT -+ -+// An output iterator that appends to a buffer. It is used instead of -+// back_insert_iterator to reduce symbol sizes and avoid dependency. -+template class basic_appender { -+ protected: -+ detail::buffer* container; -+ -+ public: -+ using container_type = detail::buffer; -+ -+ FMT_CONSTEXPR basic_appender(detail::buffer& buf) : container(&buf) {} -+ -+ FMT_CONSTEXPR20 auto operator=(T c) -> basic_appender& { -+ container->push_back(c); -+ return *this; -+ } -+ FMT_CONSTEXPR20 auto operator*() -> basic_appender& { return *this; } -+ FMT_CONSTEXPR20 auto operator++() -> basic_appender& { return *this; } -+ FMT_CONSTEXPR20 auto operator++(int) -> basic_appender { return *this; } -+}; -+ -+// A formatting argument. Context is a template parameter for the compiled API -+// where output can be unbuffered. -+template class basic_format_arg { -+ private: -+ detail::value value_; -+ detail::type type_; -+ -+ friend class basic_format_args; -+ -+ using char_type = typename Context::char_type; -+ -+ public: -+ class handle { -+ private: -+ detail::custom_value custom_; -+ -+ public: -+ explicit handle(detail::custom_value custom) : custom_(custom) {} -+ -+ void format(parse_context& parse_ctx, Context& ctx) const { -+ custom_.format(custom_.value, parse_ctx, ctx); -+ } -+ }; -+ -+ constexpr basic_format_arg() : type_(detail::type::none_type) {} -+ basic_format_arg(const detail::named_arg_info* args, size_t size) -+ : value_(args, size) {} -+ template -+ basic_format_arg(T&& val) -+ : value_(val), type_(detail::stored_type_constant::value) {} -+ -+ constexpr explicit operator bool() const noexcept { -+ return type_ != detail::type::none_type; -+ } -+ auto type() const -> detail::type { return type_; } -+ -+ /** -+ * Visits an argument dispatching to the appropriate visit method based on -+ * the argument type. For example, if the argument type is `double` then -+ * `vis(value)` will be called with the value of type `double`. -+ */ -+ template -+ FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) { -+ using detail::map; -+ switch (type_) { -+ case detail::type::none_type: break; -+ case detail::type::int_type: return vis(value_.int_value); -+ case detail::type::uint_type: return vis(value_.uint_value); -+ case detail::type::long_long_type: return vis(value_.long_long_value); -+ case detail::type::ulong_long_type: return vis(value_.ulong_long_value); -+ case detail::type::int128_type: return vis(map(value_.int128_value)); -+ case detail::type::uint128_type: return vis(map(value_.uint128_value)); -+ case detail::type::bool_type: return vis(value_.bool_value); -+ case detail::type::char_type: return vis(value_.char_value); -+ case detail::type::float_type: return vis(value_.float_value); -+ case detail::type::double_type: return vis(value_.double_value); -+ case detail::type::long_double_type: return vis(value_.long_double_value); -+ case detail::type::cstring_type: return vis(value_.string.data); -+ case detail::type::string_type: return vis(value_.string.str()); -+ case detail::type::pointer_type: return vis(value_.pointer); -+ case detail::type::custom_type: return vis(handle(value_.custom)); -+ } -+ return vis(monostate()); -+ } -+ -+ auto format_custom(const char_type* parse_begin, -+ parse_context& parse_ctx, Context& ctx) -+ -> bool { -+ if (type_ != detail::type::custom_type) return false; -+ parse_ctx.advance_to(parse_begin); -+ value_.custom.format(value_.custom.value, parse_ctx, ctx); -+ return true; -+ } -+}; -+ -+/** -+ * A view of a collection of formatting arguments. To avoid lifetime issues it -+ * should only be used as a parameter type in type-erased functions such as -+ * `vformat`: -+ * -+ * void vlog(fmt::string_view fmt, fmt::format_args args); // OK -+ * fmt::format_args args = fmt::make_format_args(); // Dangling reference -+ */ -+template class basic_format_args { -+ private: -+ // A descriptor that contains information about formatting arguments. -+ // If the number of arguments is less or equal to max_packed_args then -+ // argument types are passed in the descriptor. This reduces binary code size -+ // per formatting function call. -+ unsigned long long desc_; -+ union { -+ // If is_packed() returns true then argument values are stored in values_; -+ // otherwise they are stored in args_. This is done to improve cache -+ // locality and reduce compiled code size since storing larger objects -+ // may require more code (at least on x86-64) even if the same amount of -+ // data is actually copied to stack. It saves ~10% on the bloat test. -+ const detail::value* values_; -+ const basic_format_arg* args_; -+ }; -+ -+ constexpr auto is_packed() const -> bool { -+ return (desc_ & detail::is_unpacked_bit) == 0; -+ } -+ constexpr auto has_named_args() const -> bool { -+ return (desc_ & detail::has_named_args_bit) != 0; -+ } -+ -+ FMT_CONSTEXPR auto type(int index) const -> detail::type { -+ int shift = index * detail::packed_arg_bits; -+ unsigned mask = (1 << detail::packed_arg_bits) - 1; -+ return static_cast((desc_ >> shift) & mask); -+ } -+ -+ template -+ using store = -+ detail::format_arg_store; -+ -+ public: -+ using format_arg = basic_format_arg; -+ -+ constexpr basic_format_args() : desc_(0), args_(nullptr) {} -+ -+ /// Constructs a `basic_format_args` object from `format_arg_store`. -+ template -+ constexpr FMT_ALWAYS_INLINE basic_format_args( -+ const store& s) -+ : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), -+ values_(s.args) {} -+ -+ template detail::max_packed_args)> -+ constexpr basic_format_args(const store& s) -+ : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), -+ args_(s.args) {} -+ -+ /// Constructs a `basic_format_args` object from a dynamic list of arguments. -+ constexpr basic_format_args(const format_arg* args, int count, -+ bool has_named = false) -+ : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) | -+ (has_named ? +detail::has_named_args_bit : 0)), -+ args_(args) {} -+ -+ /// Returns the argument with the specified id. -+ FMT_CONSTEXPR auto get(int id) const -> format_arg { -+ auto arg = format_arg(); -+ if (!is_packed()) { -+ if (id < max_size()) arg = args_[id]; -+ return arg; -+ } -+ if (static_cast(id) >= detail::max_packed_args) return arg; -+ arg.type_ = type(id); -+ if (arg.type_ != detail::type::none_type) arg.value_ = values_[id]; -+ return arg; -+ } -+ -+ template -+ auto get(basic_string_view name) const -> format_arg { -+ int id = get_id(name); -+ return id >= 0 ? get(id) : format_arg(); -+ } -+ -+ template -+ FMT_CONSTEXPR auto get_id(basic_string_view name) const -> int { -+ if (!has_named_args()) return -1; -+ const auto& named_args = -+ (is_packed() ? values_[-1] : args_[-1].value_).named_args; -+ for (size_t i = 0; i < named_args.size; ++i) { -+ if (named_args.data[i].name == name) return named_args.data[i].id; -+ } -+ return -1; -+ } -+ -+ auto max_size() const -> int { -+ unsigned long long max_packed = detail::max_packed_args; -+ return static_cast(is_packed() ? max_packed -+ : desc_ & ~detail::is_unpacked_bit); -+ } -+}; -+ -+// A formatting context. -+class context { -+ private: -+ appender out_; -+ format_args args_; -+ FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; -+ -+ public: -+ /// The character type for the output. -+ using char_type = char; -+ -+ using iterator = appender; -+ using format_arg = basic_format_arg; -+ using parse_context_type FMT_DEPRECATED = parse_context<>; -+ template using formatter_type FMT_DEPRECATED = formatter; -+ enum { builtin_types = FMT_BUILTIN_TYPES }; -+ -+ /// Constructs a `context` object. References to the arguments are stored -+ /// in the object so make sure they have appropriate lifetimes. -+ FMT_CONSTEXPR context(iterator out, format_args args, -+ detail::locale_ref loc = {}) -+ : out_(out), args_(args), loc_(loc) {} -+ context(context&&) = default; -+ context(const context&) = delete; -+ void operator=(const context&) = delete; -+ -+ FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); } -+ inline auto arg(string_view name) const -> format_arg { -+ return args_.get(name); -+ } -+ FMT_CONSTEXPR auto arg_id(string_view name) const -> int { -+ return args_.get_id(name); -+ } -+ auto args() const -> const format_args& { return args_; } -+ -+ // Returns an iterator to the beginning of the output range. -+ FMT_CONSTEXPR auto out() const -> iterator { return out_; } -+ -+ // Advances the begin iterator to `it`. -+ FMT_CONSTEXPR void advance_to(iterator) {} -+ -+ FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; } -+}; -+ -+template struct runtime_format_string { -+ basic_string_view str; -+}; -+ -+/** -+ * Creates a runtime format string. -+ * -+ * **Example**: -+ * -+ * // Check format string at runtime instead of compile-time. -+ * fmt::print(fmt::runtime("{:d}"), "I am not a number"); -+ */ -+inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } -+ -+/// A compile-time format string. Use `format_string` in the public API to -+/// prevent type deduction. -+template struct fstring { -+ private: -+ static constexpr int num_static_named_args = -+ detail::count_static_named_args(); -+ -+ using checker = detail::format_string_checker< -+ char, static_cast(sizeof...(T)), num_static_named_args, -+ num_static_named_args != detail::count_named_args()>; -+ -+ using arg_pack = detail::arg_pack; -+ -+ public: -+ string_view str; -+ using t = fstring; -+ -+ // Reports a compile-time error if S is not a valid format string for T. -+ template -+ FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { -+ using namespace detail; -+ static_assert(count<(std::is_base_of>::value && -+ std::is_reference::value)...>() == 0, -+ "passing views as lvalues is disallowed"); -+ if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack())); -+#ifdef FMT_ENFORCE_COMPILE_STRING -+ static_assert( -+ FMT_USE_CONSTEVAL && sizeof(s) != 0, -+ "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); -+#endif -+ } -+ template ::value)> -+ FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) { -+ auto sv = string_view(str); -+ if (FMT_USE_CONSTEVAL) -+ detail::parse_format_string(sv, checker(sv, arg_pack())); -+#ifdef FMT_ENFORCE_COMPILE_STRING -+ static_assert( -+ FMT_USE_CONSTEVAL && sizeof(s) != 0, -+ "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); -+#endif -+ } -+ template ::value&& -+ std::is_same::value)> -+ FMT_ALWAYS_INLINE fstring(const S&) : str(S()) { -+ FMT_CONSTEXPR auto sv = string_view(S()); -+ FMT_CONSTEXPR int unused = -+ (parse_format_string(sv, checker(sv, arg_pack())), 0); -+ detail::ignore_unused(unused); -+ } -+ fstring(runtime_format_string<> fmt) : str(fmt.str) {} -+ -+ // Returning by reference generates better code in debug mode. -+ FMT_ALWAYS_INLINE operator const string_view&() const { return str; } -+ auto get() const -> string_view { return str; } -+}; -+ -+template using format_string = typename fstring::t; -+ -+template -+using is_formattable = bool_constant::value, int*, T>, Char>, -+ void>::value>; -+#ifdef __cpp_concepts -+template -+concept formattable = is_formattable, Char>::value; -+#endif -+ -+template -+using has_formatter FMT_DEPRECATED = std::is_constructible>; -+ -+// A formatter specialization for natively supported types. -+template -+struct formatter::value != -+ detail::type::custom_type>> -+ : detail::native_formatter::value> { -+}; -+ -+/** -+ * Constructs an object that stores references to arguments and can be -+ * implicitly converted to `format_args`. `Context` can be omitted in which case -+ * it defaults to `context`. See `arg` for lifetime considerations. -+ */ -+// Take arguments by lvalue references to avoid some lifetime issues, e.g. -+// auto args = make_format_args(std::string()); -+template (), -+ unsigned long long DESC = detail::make_descriptor()> -+constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args) -+ -> detail::format_arg_store { -+ // Suppress warnings for pathological types convertible to detail::value. -+ FMT_PRAGMA_GCC(diagnostic ignored "-Wconversion") -+ return {{args...}}; -+} -+ -+template -+using vargs = -+ detail::format_arg_store(), -+ detail::make_descriptor()>; -+ -+/** -+ * Returns a named argument to be used in a formatting function. -+ * It should only be used in a call to a formatting function. -+ * -+ * **Example**: -+ * -+ * fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); -+ */ -+template -+inline auto arg(const Char* name, const T& arg) -> detail::named_arg { -+ return {name, arg}; -+} -+ -+/// Formats a string and writes the output to `out`. -+template , -+ char>::value)> -+auto vformat_to(OutputIt&& out, string_view fmt, format_args args) -+ -> remove_cvref_t { -+ auto&& buf = detail::get_buffer(out); -+ detail::vformat_to(buf, fmt, args, {}); -+ return detail::get_iterator(buf, out); -+} -+ -+/** -+ * Formats `args` according to specifications in `fmt`, writes the result to -+ * the output iterator `out` and returns the iterator past the end of the output -+ * range. `format_to` does not append a terminating null character. -+ * -+ * **Example**: -+ * -+ * auto out = std::vector(); -+ * fmt::format_to(std::back_inserter(out), "{}", 42); -+ */ -+template , -+ char>::value)> -+FMT_INLINE auto format_to(OutputIt&& out, format_string fmt, T&&... args) -+ -> remove_cvref_t { -+ return vformat_to(out, fmt.str, vargs{{args...}}); -+} -+ -+template struct format_to_n_result { -+ /// Iterator past the end of the output range. -+ OutputIt out; -+ /// Total (not truncated) output size. -+ size_t size; -+}; -+ -+template ::value)> -+auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) -+ -> format_to_n_result { -+ using traits = detail::fixed_buffer_traits; -+ auto buf = detail::iterator_buffer(out, n); -+ detail::vformat_to(buf, fmt, args, {}); -+ return {buf.out(), buf.count()}; -+} -+ -+/** -+ * Formats `args` according to specifications in `fmt`, writes up to `n` -+ * characters of the result to the output iterator `out` and returns the total -+ * (not truncated) output size and the iterator past the end of the output -+ * range. `format_to_n` does not append a terminating null character. -+ */ -+template ::value)> -+FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, -+ T&&... args) -> format_to_n_result { -+ return vformat_to_n(out, n, fmt.str, vargs{{args...}}); -+} -+ -+struct format_to_result { -+ /// Pointer to just after the last successful write in the array. -+ char* out; -+ /// Specifies if the output was truncated. -+ bool truncated; -+ -+ FMT_CONSTEXPR operator char*() const { -+ // Report truncation to prevent silent data loss. -+ if (truncated) report_error("output is truncated"); -+ return out; -+ } -+}; -+ -+template -+auto vformat_to(char (&out)[N], string_view fmt, format_args args) -+ -> format_to_result { -+ auto result = vformat_to_n(out, N, fmt, args); -+ return {result.out, result.size > N}; -+} -+ -+template -+FMT_INLINE auto format_to(char (&out)[N], format_string fmt, T&&... args) -+ -> format_to_result { -+ auto result = vformat_to_n(out, N, fmt.str, vargs{{args...}}); -+ return {result.out, result.size > N}; -+} -+ -+/// Returns the number of chars in the output of `format(fmt, args...)`. -+template -+FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, -+ T&&... args) -> size_t { -+ auto buf = detail::counting_buffer<>(); -+ detail::vformat_to(buf, fmt.str, vargs{{args...}}, {}); -+ return buf.count(); -+} -+ -+FMT_API void vprint(string_view fmt, format_args args); -+FMT_API void vprint(FILE* f, string_view fmt, format_args args); -+FMT_API void vprintln(FILE* f, string_view fmt, format_args args); -+FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args); -+ -+/** -+ * Formats `args` according to specifications in `fmt` and writes the output -+ * to `stdout`. -+ * -+ * **Example**: -+ * -+ * fmt::print("The answer is {}.", 42); -+ */ -+template -+FMT_INLINE void print(format_string fmt, T&&... args) { -+ vargs va = {{args...}}; -+ if (detail::const_check(!detail::use_utf8)) -+ return detail::vprint_mojibake(stdout, fmt.str, va, false); -+ return detail::is_locking() ? vprint_buffered(stdout, fmt.str, va) -+ : vprint(fmt.str, va); -+} -+ -+/** -+ * Formats `args` according to specifications in `fmt` and writes the -+ * output to the file `f`. -+ * -+ * **Example**: -+ * -+ * fmt::print(stderr, "Don't {}!", "panic"); -+ */ -+template -+FMT_INLINE void print(FILE* f, format_string fmt, T&&... args) { -+ vargs va = {{args...}}; -+ if (detail::const_check(!detail::use_utf8)) -+ return detail::vprint_mojibake(f, fmt.str, va, false); -+ return detail::is_locking() ? vprint_buffered(f, fmt.str, va) -+ : vprint(f, fmt.str, va); -+} -+ -+/// Formats `args` according to specifications in `fmt` and writes the output -+/// to the file `f` followed by a newline. -+template -+FMT_INLINE void println(FILE* f, format_string fmt, T&&... args) { -+ vargs va = {{args...}}; -+ return detail::const_check(detail::use_utf8) -+ ? vprintln(f, fmt.str, va) -+ : detail::vprint_mojibake(f, fmt.str, va, true); -+} -+ -+/// Formats `args` according to specifications in `fmt` and writes the output -+/// to `stdout` followed by a newline. -+template -+FMT_INLINE void println(format_string fmt, T&&... args) { -+ return fmt::println(stdout, fmt, static_cast(args)...); -+} -+ -+FMT_END_EXPORT -+FMT_PRAGMA_CLANG(diagnostic pop) -+FMT_PRAGMA_GCC(pop_options) -+FMT_END_NAMESPACE -+ -+#ifdef FMT_HEADER_ONLY -+# include "format.h" -+#endif -+#endif // FMT_BASE_H_ -diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h -index 43daeeb..50c777c 100644 ---- a/include/fmt/chrono.h -+++ b/include/fmt/chrono.h -@@ -8,51 +8,36 @@ - #ifndef FMT_CHRONO_H_ - #define FMT_CHRONO_H_ - --#include --#include --#include // std::isfinite --#include // std::memcpy --#include --#include --#include --#include --#include -+#ifndef FMT_MODULE -+# include -+# include -+# include // std::isfinite -+# include // std::memcpy -+# include -+# include -+# include -+# include -+# include -+#endif - - #include "format.h" - --FMT_BEGIN_NAMESPACE -- --// Check if std::chrono::local_t is available. --#ifndef FMT_USE_LOCAL_TIME --# ifdef __cpp_lib_chrono --# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) --# else --# define FMT_USE_LOCAL_TIME 0 --# endif --#endif -+namespace fmt_detail { -+struct time_zone { -+ template -+ auto to_sys(T) -+ -> std::chrono::time_point { -+ return {}; -+ } -+}; -+template inline auto current_zone(T...) -> time_zone* { -+ return nullptr; -+} - --// Check if std::chrono::utc_timestamp is available. --#ifndef FMT_USE_UTC_TIME --# ifdef __cpp_lib_chrono --# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) --# else --# define FMT_USE_UTC_TIME 0 --# endif --#endif -+template inline void _tzset(T...) {} -+} // namespace fmt_detail - --// Enable tzset. --#ifndef FMT_USE_TZSET --// UWP doesn't provide _tzset. --# if FMT_HAS_INCLUDE("winapifamily.h") --# include --# endif --# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ -- (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) --# define FMT_USE_TZSET 1 --# else --# define FMT_USE_TZSET 0 --# endif --#endif -+FMT_BEGIN_NAMESPACE - - // Enable safe chrono durations, unless explicitly disabled. - #ifndef FMT_SAFE_DURATION_CAST -@@ -72,7 +57,8 @@ template ::value && - std::numeric_limits::is_signed == - std::numeric_limits::is_signed)> --FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { -+FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) -+ -> To { - ec = 0; - using F = std::numeric_limits; - using T = std::numeric_limits; -@@ -93,15 +79,14 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { - return static_cast(from); - } - --/** -- * converts From to To, without loss. If the dynamic value of from -- * can't be converted to To without loss, ec is set. -- */ -+/// Converts From to To, without loss. If the dynamic value of from -+/// can't be converted to To without loss, ec is set. - template ::value && - std::numeric_limits::is_signed != - std::numeric_limits::is_signed)> --FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { -+FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) -+ -> To { - ec = 0; - using F = std::numeric_limits; - using T = std::numeric_limits; -@@ -133,7 +118,8 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { - - template ::value)> --FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { -+FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) -+ -> To { - ec = 0; - return from; - } // function -@@ -154,7 +140,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { - // clang-format on - template ::value)> --FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { -+FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { - ec = 0; - using T = std::numeric_limits; - static_assert(std::is_floating_point::value, "From must be floating"); -@@ -176,72 +162,18 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { - - template ::value)> --FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { -+FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { - ec = 0; - static_assert(std::is_floating_point::value, "From must be floating"); - return from; - } - --/** -- * safe duration cast between integral durations -- */ --template ::value), -- FMT_ENABLE_IF(std::is_integral::value)> --To safe_duration_cast(std::chrono::duration from, -- int& ec) { -- using From = std::chrono::duration; -- ec = 0; -- // the basic idea is that we need to convert from count() in the from type -- // to count() in the To type, by multiplying it with this: -- struct Factor -- : std::ratio_divide {}; -- -- static_assert(Factor::num > 0, "num must be positive"); -- static_assert(Factor::den > 0, "den must be positive"); -- -- // the conversion is like this: multiply from.count() with Factor::num -- // /Factor::den and convert it to To::rep, all this without -- // overflow/underflow. let's start by finding a suitable type that can hold -- // both To, From and Factor::num -- using IntermediateRep = -- typename std::common_type::type; -- -- // safe conversion to IntermediateRep -- IntermediateRep count = -- lossless_integral_conversion(from.count(), ec); -- if (ec) return {}; -- // multiply with Factor::num without overflow or underflow -- if (detail::const_check(Factor::num != 1)) { -- const auto max1 = detail::max_value() / Factor::num; -- if (count > max1) { -- ec = 1; -- return {}; -- } -- const auto min1 = -- (std::numeric_limits::min)() / Factor::num; -- if (detail::const_check(!std::is_unsigned::value) && -- count < min1) { -- ec = 1; -- return {}; -- } -- count *= Factor::num; -- } -- -- if (detail::const_check(Factor::den != 1)) count /= Factor::den; -- auto tocount = lossless_integral_conversion(count, ec); -- return ec ? To() : To(tocount); --} -- --/** -- * safe duration_cast between floating point durations -- */ -+/// Safe duration_cast between floating point durations - template ::value), - FMT_ENABLE_IF(std::is_floating_point::value)> --To safe_duration_cast(std::chrono::duration from, -- int& ec) { -+auto safe_duration_cast(std::chrono::duration from, -+ int& ec) -> To { - using From = std::chrono::duration; - ec = 0; - if (std::isnan(from.count())) { -@@ -315,18 +247,95 @@ To safe_duration_cast(std::chrono::duration from, - } // namespace safe_duration_cast - #endif - -+namespace detail { -+ -+// Check if std::chrono::utc_time is available. -+#ifdef FMT_USE_UTC_TIME -+// Use the provided definition. -+#elif defined(__cpp_lib_chrono) -+# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) -+#else -+# define FMT_USE_UTC_TIME 0 -+#endif -+#if FMT_USE_UTC_TIME -+using utc_clock = std::chrono::utc_clock; -+#else -+struct utc_clock { -+ template void to_sys(T); -+}; -+#endif -+ -+// Check if std::chrono::local_time is available. -+#ifdef FMT_USE_LOCAL_TIME -+// Use the provided definition. -+#elif defined(__cpp_lib_chrono) -+# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) -+#else -+# define FMT_USE_LOCAL_TIME 0 -+#endif -+#if FMT_USE_LOCAL_TIME -+using local_t = std::chrono::local_t; -+#else -+struct local_t {}; -+#endif -+ -+} // namespace detail -+ -+template -+using sys_time = std::chrono::time_point; -+ -+template -+using utc_time = std::chrono::time_point; -+ -+template -+using local_time = std::chrono::time_point; -+ -+namespace detail { -+ - // Prevents expansion of a preceding token as a function-style macro. - // Usage: f FMT_NOMACRO() - #define FMT_NOMACRO - --namespace detail { - template struct null {}; --inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } --inline null<> localtime_s(...) { return null<>(); } --inline null<> gmtime_r(...) { return null<>(); } --inline null<> gmtime_s(...) { return null<>(); } -+inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); } -+inline auto localtime_s(...) -> null<> { return null<>(); } -+inline auto gmtime_r(...) -> null<> { return null<>(); } -+inline auto gmtime_s(...) -> null<> { return null<>(); } -+ -+// It is defined here and not in ostream.h because the latter has expensive -+// includes. -+template class formatbuf : public StreamBuf { -+ private: -+ using char_type = typename StreamBuf::char_type; -+ using streamsize = decltype(std::declval().sputn(nullptr, 0)); -+ using int_type = typename StreamBuf::int_type; -+ using traits_type = typename StreamBuf::traits_type; - --inline const std::locale& get_classic_locale() { -+ buffer& buffer_; -+ -+ public: -+ explicit formatbuf(buffer& buf) : buffer_(buf) {} -+ -+ protected: -+ // The put area is always empty. This makes the implementation simpler and has -+ // the advantage that the streambuf and the buffer are always in sync and -+ // sputc never writes into uninitialized memory. A disadvantage is that each -+ // call to sputc always results in a (virtual) call to overflow. There is no -+ // disadvantage here for sputn since this always results in a call to xsputn. -+ -+ auto overflow(int_type ch) -> int_type override { -+ if (!traits_type::eq_int_type(ch, traits_type::eof())) -+ buffer_.push_back(static_cast(ch)); -+ return ch; -+ } -+ -+ auto xsputn(const char_type* s, streamsize count) -> streamsize override { -+ buffer_.append(s, s + count); -+ return count; -+ } -+}; -+ -+inline auto get_classic_locale() -> const std::locale& { - static const auto& locale = std::locale::classic(); - return locale; - } -@@ -336,24 +345,18 @@ template struct codecvt_result { - CodeUnit buf[max_size]; - CodeUnit* end; - }; --template --constexpr const size_t codecvt_result::max_size; - - template --void write_codecvt(codecvt_result& out, string_view in_buf, -+void write_codecvt(codecvt_result& out, string_view in, - const std::locale& loc) { --#if FMT_CLANG_VERSION --# pragma clang diagnostic push --# pragma clang diagnostic ignored "-Wdeprecated" -+ FMT_PRAGMA_CLANG(diagnostic push) -+ FMT_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated") - auto& f = std::use_facet>(loc); --# pragma clang diagnostic pop --#else -- auto& f = std::use_facet>(loc); --#endif -+ FMT_PRAGMA_CLANG(diagnostic pop) - auto mb = std::mbstate_t(); - const char* from_next = nullptr; -- auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, -- std::begin(out.buf), std::end(out.buf), out.end); -+ auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf), -+ std::end(out.buf), out.end); - if (result != std::codecvt_base::ok) - FMT_THROW(format_error("failed to format time")); - } -@@ -361,11 +364,12 @@ void write_codecvt(codecvt_result& out, string_view in_buf, - template - auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) - -> OutputIt { -- if (detail::is_utf8() && loc != get_classic_locale()) { -+ if (const_check(detail::use_utf8) && loc != get_classic_locale()) { - // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and - // gcc-4. --#if FMT_MSC_VERSION != 0 || \ -- (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) -+#if FMT_MSC_VERSION != 0 || \ -+ (defined(__GLIBCXX__) && \ -+ (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0)) - // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 - // and newer. - using code_unit = wchar_t; -@@ -381,9 +385,9 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) - to_utf8>(); - if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) - FMT_THROW(format_error("failed to format time")); -- return copy_str(u.c_str(), u.c_str() + u.size(), out); -+ return copy(u.c_str(), u.c_str() + u.size(), out); - } -- return copy_str(in.data(), in.data() + in.size(), out); -+ return copy(in.data(), in.data() + in.size(), out); - } - - template OutputIt { - codecvt_result unit; - write_codecvt(unit, sv, loc); -- return copy_str(unit.buf, unit.end, out); -+ return copy(unit.buf, unit.end, out); - } - - template & buf, const std::tm& time, - auto&& format_buf = formatbuf>(buf); - auto&& os = std::basic_ostream(&format_buf); - os.imbue(loc); -- using iterator = std::ostreambuf_iterator; -- const auto& facet = std::use_facet>(loc); -+ const auto& facet = std::use_facet>(loc); - auto end = facet.put(os, os, Char(' '), &time, format, modifier); - if (end.failed()) FMT_THROW(format_error("failed to format time")); - } -@@ -432,38 +435,129 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc, - return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); - } - -+template -+struct is_same_arithmetic_type -+ : public std::integral_constant::value && -+ std::is_integral::value) || -+ (std::is_floating_point::value && -+ std::is_floating_point::value)> { -+}; -+ -+FMT_NORETURN inline void throw_duration_error() { -+ FMT_THROW(format_error("cannot format duration")); -+} -+ -+// Cast one integral duration to another with an overflow check. -+template ::value&& -+ std::is_integral::value)> -+auto duration_cast(std::chrono::duration from) -> To { -+#if !FMT_SAFE_DURATION_CAST -+ return std::chrono::duration_cast(from); -+#else -+ // The conversion factor: to.count() == factor * from.count(). -+ using factor = std::ratio_divide; -+ -+ using common_rep = typename std::common_type::type; -+ -+ int ec = 0; -+ auto count = safe_duration_cast::lossless_integral_conversion( -+ from.count(), ec); -+ if (ec) throw_duration_error(); -+ -+ // Multiply from.count() by factor and check for overflow. -+ if (const_check(factor::num != 1)) { -+ if (count > max_value() / factor::num) throw_duration_error(); -+ const auto min = (std::numeric_limits::min)() / factor::num; -+ if (const_check(!std::is_unsigned::value) && count < min) -+ throw_duration_error(); -+ count *= factor::num; -+ } -+ if (const_check(factor::den != 1)) count /= factor::den; -+ auto to = -+ To(safe_duration_cast::lossless_integral_conversion( -+ count, ec)); -+ if (ec) throw_duration_error(); -+ return to; -+#endif -+} -+ -+template ::value&& -+ std::is_floating_point::value)> -+auto duration_cast(std::chrono::duration from) -> To { -+#if FMT_SAFE_DURATION_CAST -+ // Throwing version of safe_duration_cast is only available for -+ // integer to integer or float to float casts. -+ int ec; -+ To to = safe_duration_cast::safe_duration_cast(from, ec); -+ if (ec) throw_duration_error(); -+ return to; -+#else -+ // Standard duration cast, may overflow. -+ return std::chrono::duration_cast(from); -+#endif -+} -+ -+template < -+ typename To, typename FromRep, typename FromPeriod, -+ FMT_ENABLE_IF(!is_same_arithmetic_type::value)> -+auto duration_cast(std::chrono::duration from) -> To { -+ // Mixed integer <-> float cast is not supported by safe_duration_cast. -+ return std::chrono::duration_cast(from); -+} -+ -+template -+auto to_time_t(sys_time time_point) -> std::time_t { -+ // Cannot use std::chrono::system_clock::to_time_t since this would first -+ // require a cast to std::chrono::system_clock::time_point, which could -+ // overflow. -+ return detail::duration_cast>( -+ time_point.time_since_epoch()) -+ .count(); -+} -+ -+// Workaround a bug in libstdc++ which sets __cpp_lib_chrono to 201907 without -+// providing current_zone(): https://github.com/fmtlib/fmt/issues/4160. -+template FMT_CONSTEXPR auto has_current_zone() -> bool { -+ using namespace std::chrono; -+ using namespace fmt_detail; -+ return !std::is_same::value; -+} - } // namespace detail - - FMT_BEGIN_EXPORT - - /** -- Converts given time since epoch as ``std::time_t`` value into calendar time, -- expressed in local time. Unlike ``std::localtime``, this function is -- thread-safe on most platforms. -+ * Converts given time since epoch as `std::time_t` value into calendar time, -+ * expressed in local time. Unlike `std::localtime`, this function is -+ * thread-safe on most platforms. - */ --inline std::tm localtime(std::time_t time) { -+inline auto localtime(std::time_t time) -> std::tm { - struct dispatcher { - std::time_t time_; - std::tm tm_; - -- dispatcher(std::time_t t) : time_(t) {} -+ inline dispatcher(std::time_t t) : time_(t) {} - -- bool run() { -+ inline auto run() -> bool { - using namespace fmt::detail; - return handle(localtime_r(&time_, &tm_)); - } - -- bool handle(std::tm* tm) { return tm != nullptr; } -+ inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } - -- bool handle(detail::null<>) { -+ inline auto handle(detail::null<>) -> bool { - using namespace fmt::detail; - return fallback(localtime_s(&tm_, &time_)); - } - -- bool fallback(int res) { return res == 0; } -+ inline auto fallback(int res) -> bool { return res == 0; } - - #if !FMT_MSC_VERSION -- bool fallback(detail::null<>) { -+ inline auto fallback(detail::null<>) -> bool { - using namespace fmt::detail; - std::tm* tm = std::localtime(&time_); - if (tm) tm_ = *tm; -@@ -478,102 +572,61 @@ inline std::tm localtime(std::time_t time) { - } - - #if FMT_USE_LOCAL_TIME --template -+template ())> - inline auto localtime(std::chrono::local_time time) -> std::tm { -- return localtime(std::chrono::system_clock::to_time_t( -- std::chrono::current_zone()->to_sys(time))); -+ using namespace std::chrono; -+ using namespace fmt_detail; -+ return localtime(detail::to_time_t(current_zone()->to_sys(time))); - } - #endif - - /** -- Converts given time since epoch as ``std::time_t`` value into calendar time, -- expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this -- function is thread-safe on most platforms. -+ * Converts given time since epoch as `std::time_t` value into calendar time, -+ * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this -+ * function is thread-safe on most platforms. - */ --inline std::tm gmtime(std::time_t time) { -+inline auto gmtime(std::time_t time) -> std::tm { - struct dispatcher { - std::time_t time_; - std::tm tm_; - -- dispatcher(std::time_t t) : time_(t) {} -+ inline dispatcher(std::time_t t) : time_(t) {} - -- bool run() { -+ inline auto run() -> bool { - using namespace fmt::detail; - return handle(gmtime_r(&time_, &tm_)); - } - -- bool handle(std::tm* tm) { return tm != nullptr; } -+ inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } - -- bool handle(detail::null<>) { -+ inline auto handle(detail::null<>) -> bool { - using namespace fmt::detail; - return fallback(gmtime_s(&tm_, &time_)); - } - -- bool fallback(int res) { return res == 0; } -+ inline auto fallback(int res) -> bool { return res == 0; } - - #if !FMT_MSC_VERSION -- bool fallback(detail::null<>) { -+ inline auto fallback(detail::null<>) -> bool { - std::tm* tm = std::gmtime(&time_); - if (tm) tm_ = *tm; - return tm != nullptr; - } - #endif - }; -- dispatcher gt(time); -+ auto gt = dispatcher(time); - // Too big time values may be unsupported. - if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); - return gt.tm_; - } - --inline std::tm gmtime( -- std::chrono::time_point time_point) { -- return gmtime(std::chrono::system_clock::to_time_t(time_point)); -+template -+inline auto gmtime(sys_time time_point) -> std::tm { -+ return gmtime(detail::to_time_t(time_point)); - } - --FMT_BEGIN_DETAIL_NAMESPACE -- --// DEPRECATED! --template --FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, -- format_specs& specs) -> const Char* { -- FMT_ASSERT(begin != end, ""); -- auto align = align::none; -- auto p = begin + code_point_length(begin); -- if (end - p <= 0) p = begin; -- for (;;) { -- switch (to_ascii(*p)) { -- case '<': -- align = align::left; -- break; -- case '>': -- align = align::right; -- break; -- case '^': -- align = align::center; -- break; -- } -- if (align != align::none) { -- if (p != begin) { -- auto c = *begin; -- if (c == '}') return begin; -- if (c == '{') { -- throw_format_error("invalid fill character '{'"); -- return begin; -- } -- specs.fill = {begin, to_unsigned(p - begin)}; -- begin = p + 1; -- } else { -- ++begin; -- } -- break; -- } else if (p == begin) { -- break; -- } -- p = begin; -- } -- specs.align = align; -- return begin; --} -+namespace detail { - - // Writes two-digit numbers a, b and c separated by sep to buf. - // The method by Pavel Novikov based on -@@ -609,12 +662,14 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b, - } - } - --template FMT_CONSTEXPR inline const char* get_units() { -+template -+FMT_CONSTEXPR inline auto get_units() -> const char* { - if (std::is_same::value) return "as"; - if (std::is_same::value) return "fs"; - if (std::is_same::value) return "ps"; - if (std::is_same::value) return "ns"; -- if (std::is_same::value) return "µs"; -+ if (std::is_same::value) -+ return detail::use_utf8 ? "µs" : "us"; - if (std::is_same::value) return "ms"; - if (std::is_same::value) return "cs"; - if (std::is_same::value) return "ds"; -@@ -627,8 +682,9 @@ template FMT_CONSTEXPR inline const char* get_units() { - if (std::is_same::value) return "Ts"; - if (std::is_same::value) return "Ps"; - if (std::is_same::value) return "Es"; -- if (std::is_same>::value) return "m"; -+ if (std::is_same>::value) return "min"; - if (std::is_same>::value) return "h"; -+ if (std::is_same>::value) return "d"; - return nullptr; - } - -@@ -640,12 +696,10 @@ enum class numeric_system { - - // Glibc extensions for formatting numeric values. - enum class pad_type { -- unspecified, -+ // Pad a numeric result string with zeros (the default). -+ zero, - // Do not pad a numeric result string. - none, -- // Pad a numeric result string with zeros even if the conversion specifier -- // character uses space-padding by default. -- zero, - // Pad a numeric result string with spaces. - space, - }; -@@ -653,7 +707,7 @@ enum class pad_type { - template - auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { - if (pad == pad_type::none) return out; -- return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); -+ return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); - } - - template -@@ -664,14 +718,13 @@ auto write_padding(OutputIt out, pad_type pad) -> OutputIt { - - // Parses a put_time-like format string and invokes handler actions. - template --FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, -- const Char* end, -- Handler&& handler) { -+FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, -+ Handler&& handler) -> const Char* { - if (begin == end || *begin == '}') return begin; - if (*begin != '%') FMT_THROW(format_error("invalid format")); - auto ptr = begin; -- pad_type pad = pad_type::unspecified; - while (ptr != end) { -+ pad_type pad = pad_type::zero; - auto c = *ptr; - if (c == '}') break; - if (c != '%') { -@@ -691,17 +744,11 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - pad = pad_type::none; - ++ptr; - break; -- case '0': -- pad = pad_type::zero; -- ++ptr; -- break; - } - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { -- case '%': -- handler.on_text(ptr - 1, ptr); -- break; -+ case '%': handler.on_text(ptr - 1, ptr); break; - case 'n': { - const Char newline[] = {'\n'}; - handler.on_text(newline, newline + 1); -@@ -713,145 +760,66 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - break; - } - // Year: -- case 'Y': -- handler.on_year(numeric_system::standard); -- break; -- case 'y': -- handler.on_short_year(numeric_system::standard); -- break; -- case 'C': -- handler.on_century(numeric_system::standard); -- break; -- case 'G': -- handler.on_iso_week_based_year(); -- break; -- case 'g': -- handler.on_iso_week_based_short_year(); -- break; -+ case 'Y': handler.on_year(numeric_system::standard, pad); break; -+ case 'y': handler.on_short_year(numeric_system::standard); break; -+ case 'C': handler.on_century(numeric_system::standard); break; -+ case 'G': handler.on_iso_week_based_year(); break; -+ case 'g': handler.on_iso_week_based_short_year(); break; - // Day of the week: -- case 'a': -- handler.on_abbr_weekday(); -- break; -- case 'A': -- handler.on_full_weekday(); -- break; -- case 'w': -- handler.on_dec0_weekday(numeric_system::standard); -- break; -- case 'u': -- handler.on_dec1_weekday(numeric_system::standard); -- break; -+ case 'a': handler.on_abbr_weekday(); break; -+ case 'A': handler.on_full_weekday(); break; -+ case 'w': handler.on_dec0_weekday(numeric_system::standard); break; -+ case 'u': handler.on_dec1_weekday(numeric_system::standard); break; - // Month: - case 'b': -- case 'h': -- handler.on_abbr_month(); -- break; -- case 'B': -- handler.on_full_month(); -- break; -- case 'm': -- handler.on_dec_month(numeric_system::standard); -- break; -+ case 'h': handler.on_abbr_month(); break; -+ case 'B': handler.on_full_month(); break; -+ case 'm': handler.on_dec_month(numeric_system::standard, pad); break; - // Day of the year/month: - case 'U': -- handler.on_dec0_week_of_year(numeric_system::standard); -+ handler.on_dec0_week_of_year(numeric_system::standard, pad); - break; - case 'W': -- handler.on_dec1_week_of_year(numeric_system::standard); -- break; -- case 'V': -- handler.on_iso_week_of_year(numeric_system::standard); -- break; -- case 'j': -- handler.on_day_of_year(); -- break; -- case 'd': -- handler.on_day_of_month(numeric_system::standard); -+ handler.on_dec1_week_of_year(numeric_system::standard, pad); - break; -+ case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad); break; -+ case 'j': handler.on_day_of_year(pad); break; -+ case 'd': handler.on_day_of_month(numeric_system::standard, pad); break; - case 'e': -- handler.on_day_of_month_space(numeric_system::standard); -+ handler.on_day_of_month(numeric_system::standard, pad_type::space); - break; - // Hour, minute, second: -- case 'H': -- handler.on_24_hour(numeric_system::standard, pad); -- break; -- case 'I': -- handler.on_12_hour(numeric_system::standard, pad); -- break; -- case 'M': -- handler.on_minute(numeric_system::standard, pad); -- break; -- case 'S': -- handler.on_second(numeric_system::standard, pad); -- break; -+ case 'H': handler.on_24_hour(numeric_system::standard, pad); break; -+ case 'I': handler.on_12_hour(numeric_system::standard, pad); break; -+ case 'M': handler.on_minute(numeric_system::standard, pad); break; -+ case 'S': handler.on_second(numeric_system::standard, pad); break; - // Other: -- case 'c': -- handler.on_datetime(numeric_system::standard); -- break; -- case 'x': -- handler.on_loc_date(numeric_system::standard); -- break; -- case 'X': -- handler.on_loc_time(numeric_system::standard); -- break; -- case 'D': -- handler.on_us_date(); -- break; -- case 'F': -- handler.on_iso_date(); -- break; -- case 'r': -- handler.on_12_hour_time(); -- break; -- case 'R': -- handler.on_24_hour_time(); -- break; -- case 'T': -- handler.on_iso_time(); -- break; -- case 'p': -- handler.on_am_pm(); -- break; -- case 'Q': -- handler.on_duration_value(); -- break; -- case 'q': -- handler.on_duration_unit(); -- break; -- case 'z': -- handler.on_utc_offset(numeric_system::standard); -- break; -- case 'Z': -- handler.on_tz_name(); -- break; -+ case 'c': handler.on_datetime(numeric_system::standard); break; -+ case 'x': handler.on_loc_date(numeric_system::standard); break; -+ case 'X': handler.on_loc_time(numeric_system::standard); break; -+ case 'D': handler.on_us_date(); break; -+ case 'F': handler.on_iso_date(); break; -+ case 'r': handler.on_12_hour_time(); break; -+ case 'R': handler.on_24_hour_time(); break; -+ case 'T': handler.on_iso_time(); break; -+ case 'p': handler.on_am_pm(); break; -+ case 'Q': handler.on_duration_value(); break; -+ case 'q': handler.on_duration_unit(); break; -+ case 'z': handler.on_utc_offset(numeric_system::standard); break; -+ case 'Z': handler.on_tz_name(); break; - // Alternative representation: - case 'E': { - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { -- case 'Y': -- handler.on_year(numeric_system::alternative); -- break; -- case 'y': -- handler.on_offset_year(); -- break; -- case 'C': -- handler.on_century(numeric_system::alternative); -- break; -- case 'c': -- handler.on_datetime(numeric_system::alternative); -- break; -- case 'x': -- handler.on_loc_date(numeric_system::alternative); -- break; -- case 'X': -- handler.on_loc_time(numeric_system::alternative); -- break; -- case 'z': -- handler.on_utc_offset(numeric_system::alternative); -- break; -- default: -- FMT_THROW(format_error("invalid format")); -+ case 'Y': handler.on_year(numeric_system::alternative, pad); break; -+ case 'y': handler.on_offset_year(); break; -+ case 'C': handler.on_century(numeric_system::alternative); break; -+ case 'c': handler.on_datetime(numeric_system::alternative); break; -+ case 'x': handler.on_loc_date(numeric_system::alternative); break; -+ case 'X': handler.on_loc_time(numeric_system::alternative); break; -+ case 'z': handler.on_utc_offset(numeric_system::alternative); break; -+ default: FMT_THROW(format_error("invalid format")); - } - break; - } -@@ -859,54 +827,34 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { -- case 'y': -- handler.on_short_year(numeric_system::alternative); -- break; -- case 'm': -- handler.on_dec_month(numeric_system::alternative); -- break; -+ case 'y': handler.on_short_year(numeric_system::alternative); break; -+ case 'm': handler.on_dec_month(numeric_system::alternative, pad); break; - case 'U': -- handler.on_dec0_week_of_year(numeric_system::alternative); -+ handler.on_dec0_week_of_year(numeric_system::alternative, pad); - break; - case 'W': -- handler.on_dec1_week_of_year(numeric_system::alternative); -+ handler.on_dec1_week_of_year(numeric_system::alternative, pad); - break; - case 'V': -- handler.on_iso_week_of_year(numeric_system::alternative); -+ handler.on_iso_week_of_year(numeric_system::alternative, pad); - break; - case 'd': -- handler.on_day_of_month(numeric_system::alternative); -+ handler.on_day_of_month(numeric_system::alternative, pad); - break; - case 'e': -- handler.on_day_of_month_space(numeric_system::alternative); -- break; -- case 'w': -- handler.on_dec0_weekday(numeric_system::alternative); -- break; -- case 'u': -- handler.on_dec1_weekday(numeric_system::alternative); -- break; -- case 'H': -- handler.on_24_hour(numeric_system::alternative, pad); -+ handler.on_day_of_month(numeric_system::alternative, pad_type::space); - break; -- case 'I': -- handler.on_12_hour(numeric_system::alternative, pad); -- break; -- case 'M': -- handler.on_minute(numeric_system::alternative, pad); -- break; -- case 'S': -- handler.on_second(numeric_system::alternative, pad); -- break; -- case 'z': -- handler.on_utc_offset(numeric_system::alternative); -- break; -- default: -- FMT_THROW(format_error("invalid format")); -+ case 'w': handler.on_dec0_weekday(numeric_system::alternative); break; -+ case 'u': handler.on_dec1_weekday(numeric_system::alternative); break; -+ case 'H': handler.on_24_hour(numeric_system::alternative, pad); break; -+ case 'I': handler.on_12_hour(numeric_system::alternative, pad); break; -+ case 'M': handler.on_minute(numeric_system::alternative, pad); break; -+ case 'S': handler.on_second(numeric_system::alternative, pad); break; -+ case 'z': handler.on_utc_offset(numeric_system::alternative); break; -+ default: FMT_THROW(format_error("invalid format")); - } - break; -- default: -- FMT_THROW(format_error("invalid format")); -+ default: FMT_THROW(format_error("invalid format")); - } - begin = ptr; - } -@@ -918,7 +866,7 @@ template struct null_chrono_spec_handler { - FMT_CONSTEXPR void unsupported() { - static_cast(this)->unsupported(); - } -- FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_year(numeric_system, pad_type) { unsupported(); } - FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_offset_year() { unsupported(); } - FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } -@@ -930,13 +878,20 @@ template struct null_chrono_spec_handler { - FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_abbr_month() { unsupported(); } - FMT_CONSTEXPR void on_full_month() { unsupported(); } -- FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } -- FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } -- FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } -- FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } -- FMT_CONSTEXPR void on_day_of_year() { unsupported(); } -- FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } -- FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) { unsupported(); } -+ FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) { -+ unsupported(); -+ } -+ FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) { -+ unsupported(); -+ } -+ FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) { -+ unsupported(); -+ } -+ FMT_CONSTEXPR void on_day_of_year(pad_type) { unsupported(); } -+ FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) { -+ unsupported(); -+ } - FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } -@@ -957,11 +912,13 @@ template struct null_chrono_spec_handler { - }; - - struct tm_format_checker : null_chrono_spec_handler { -- FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } -+ FMT_NORETURN inline void unsupported() { -+ FMT_THROW(format_error("no format")); -+ } - - template - FMT_CONSTEXPR void on_text(const Char*, const Char*) {} -- FMT_CONSTEXPR void on_year(numeric_system) {} -+ FMT_CONSTEXPR void on_year(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_short_year(numeric_system) {} - FMT_CONSTEXPR void on_offset_year() {} - FMT_CONSTEXPR void on_century(numeric_system) {} -@@ -973,13 +930,12 @@ struct tm_format_checker : null_chrono_spec_handler { - FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} - FMT_CONSTEXPR void on_abbr_month() {} - FMT_CONSTEXPR void on_full_month() {} -- FMT_CONSTEXPR void on_dec_month(numeric_system) {} -- FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} -- FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} -- FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} -- FMT_CONSTEXPR void on_day_of_year() {} -- FMT_CONSTEXPR void on_day_of_month(numeric_system) {} -- FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} -+ FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) {} -+ FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {} -+ FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {} -+ FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {} -+ FMT_CONSTEXPR void on_day_of_year(pad_type) {} -+ FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} -@@ -997,25 +953,25 @@ struct tm_format_checker : null_chrono_spec_handler { - FMT_CONSTEXPR void on_tz_name() {} - }; - --inline const char* tm_wday_full_name(int wday) { -+inline auto tm_wday_full_name(int wday) -> const char* { - static constexpr const char* full_name_list[] = { - "Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday"}; - return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; - } --inline const char* tm_wday_short_name(int wday) { -+inline auto tm_wday_short_name(int wday) -> const char* { - static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat"}; - return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; - } - --inline const char* tm_mon_full_name(int mon) { -+inline auto tm_mon_full_name(int mon) -> const char* { - static constexpr const char* full_name_list[] = { - "January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December"}; - return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; - } --inline const char* tm_mon_short_name(int mon) { -+inline auto tm_mon_short_name(int mon) -> const char* { - static constexpr const char* short_name_list[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", -@@ -1035,33 +991,33 @@ template - struct has_member_data_tm_zone> - : std::true_type {}; - --#if FMT_USE_TZSET - inline void tzset_once() { -- static bool init = []() -> bool { -+ static bool init = []() { -+ using namespace fmt_detail; - _tzset(); -- return true; -+ return false; - }(); - ignore_unused(init); - } --#endif - - // Converts value to Int and checks that it's in the range [0, upper). - template ::value)> --inline Int to_nonnegative_int(T value, Int upper) { -- FMT_ASSERT(std::is_unsigned::value || -- (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), -- "invalid value"); -- (void)upper; -+inline auto to_nonnegative_int(T value, Int upper) -> Int { -+ if (!std::is_unsigned::value && -+ (value < 0 || to_unsigned(value) > to_unsigned(upper))) { -+ FMT_THROW(fmt::format_error("chrono value is out of range")); -+ } - return static_cast(value); - } - template ::value)> --inline Int to_nonnegative_int(T value, Int upper) { -- if (value < 0 || value > static_cast(upper)) -+inline auto to_nonnegative_int(T value, Int upper) -> Int { -+ auto int_value = static_cast(value); -+ if (int_value < 0 || value > static_cast(upper)) - FMT_THROW(format_error("invalid value")); -- return static_cast(value); -+ return int_value; - } - --constexpr long long pow10(std::uint32_t n) { -+constexpr auto pow10(std::uint32_t n) -> long long { - return n == 0 ? 1 : 10 * pow10(n - 1); - } - -@@ -1093,17 +1049,16 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { - using subsecond_precision = std::chrono::duration< - typename std::common_type::type, -- std::ratio<1, detail::pow10(num_fractional_digits)>>; -+ std::ratio<1, pow10(num_fractional_digits)>>; - -- const auto fractional = -- d - std::chrono::duration_cast(d); -+ const auto fractional = d - detail::duration_cast(d); - const auto subseconds = - std::chrono::treat_as_floating_point< - typename subsecond_precision::rep>::value - ? fractional.count() -- : std::chrono::duration_cast(fractional).count(); -+ : detail::duration_cast(fractional).count(); - auto n = static_cast>(subseconds); -- const int num_digits = detail::count_digits(n); -+ const int num_digits = count_digits(n); - - int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); - if (precision < 0) { -@@ -1111,22 +1066,25 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { - if (std::ratio_less::value) { - *out++ = '.'; -- out = std::fill_n(out, leading_zeroes, '0'); -- out = format_decimal(out, n, num_digits).end; -+ out = detail::fill_n(out, leading_zeroes, '0'); -+ out = format_decimal(out, n, num_digits); - } -- } else { -+ } else if (precision > 0) { - *out++ = '.'; -- leading_zeroes = (std::min)(leading_zeroes, precision); -- out = std::fill_n(out, leading_zeroes, '0'); -+ leading_zeroes = min_of(leading_zeroes, precision); - int remaining = precision - leading_zeroes; -- if (remaining != 0 && remaining < num_digits) { -- n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); -- out = format_decimal(out, n, remaining).end; -+ out = detail::fill_n(out, leading_zeroes, '0'); -+ if (remaining < num_digits) { -+ int num_truncated_digits = num_digits - remaining; -+ n /= to_unsigned(pow10(to_unsigned(num_truncated_digits))); -+ if (n != 0) out = format_decimal(out, n, remaining); - return; - } -- out = format_decimal(out, n, num_digits).end; -- remaining -= num_digits; -- out = std::fill_n(out, remaining, '0'); -+ if (n != 0) { -+ out = format_decimal(out, n, num_digits); -+ remaining -= num_digits; -+ } -+ out = detail::fill_n(out, remaining, '0'); - } - } - -@@ -1152,11 +1110,11 @@ void write_floating_seconds(memory_buffer& buf, Duration duration, - num_fractional_digits = 6; - } - -- format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), -- std::fmod(val * static_cast(Duration::period::num) / -- static_cast(Duration::period::den), -- static_cast(60)), -- num_fractional_digits); -+ fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), -+ std::fmod(val * static_cast(Duration::period::num) / -+ static_cast(Duration::period::den), -+ static_cast(60)), -+ num_fractional_digits); - } - - template (l); - } - -- // Algorithm: -- // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date -+ // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. - auto iso_year_weeks(long long curr_year) const noexcept -> int { - const auto prev_year = curr_year - 1; - const auto curr_p = -@@ -1268,29 +1225,28 @@ class tm_writer { - } - } - -- void write_year_extended(long long year) { -+ void write_year_extended(long long year, pad_type pad) { - // At least 4 characters. - int width = 4; -- if (year < 0) { -- *out_++ = '-'; -+ bool negative = year < 0; -+ if (negative) { - year = 0 - year; - --width; - } - uint32_or_64_or_128_t n = to_unsigned(year); - const int num_digits = count_digits(n); -- if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); -- out_ = format_decimal(out_, n, num_digits).end; -- } -- void write_year(long long year) { -- if (year >= 0 && year < 10000) { -- write2(static_cast(year / 100)); -- write2(static_cast(year % 100)); -- } else { -- write_year_extended(year); -+ if (negative && pad == pad_type::zero) *out_++ = '-'; -+ if (width > num_digits) { -+ out_ = detail::write_padding(out_, pad, width - num_digits); - } -+ if (negative && pad != pad_type::zero) *out_++ = '-'; -+ out_ = format_decimal(out_, n, num_digits); -+ } -+ void write_year(long long year, pad_type pad) { -+ write_year_extended(year, pad); - } - -- void write_utc_offset(long offset, numeric_system ns) { -+ void write_utc_offset(long long offset, numeric_system ns) { - if (offset < 0) { - *out_++ = '-'; - offset = -offset; -@@ -1302,6 +1258,7 @@ class tm_writer { - if (ns != numeric_system::standard) *out_++ = ':'; - write2(static_cast(offset % 60)); - } -+ - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { - write_utc_offset(tm.tm_gmtoff, ns); -@@ -1309,9 +1266,7 @@ class tm_writer { - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { - #if defined(_WIN32) && defined(_UCRT) --# if FMT_USE_TZSET - tzset_once(); --# endif - long offset = 0; - _get_timezone(&offset); - if (tm.tm_isdst) { -@@ -1328,7 +1283,7 @@ class tm_writer { - std::time_t gt = std::mktime(>m); - std::tm ltm = gmtime(gt); - std::time_t lt = std::mktime(<m); -- long offset = gt - lt; -+ long long offset = gt - lt; - write_utc_offset(offset, ns); - #endif - } -@@ -1358,10 +1313,10 @@ class tm_writer { - subsecs_(subsecs), - tm_(tm) {} - -- OutputIt out() const { return out_; } -+ auto out() const -> OutputIt { return out_; } - - FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { -- out_ = copy_str(begin, end, out_); -+ out_ = copy(begin, end, out_); - } - - void on_abbr_weekday() { -@@ -1408,11 +1363,11 @@ class tm_writer { - *out_++ = ' '; - on_abbr_month(); - *out_++ = ' '; -- on_day_of_month_space(numeric_system::standard); -+ on_day_of_month(numeric_system::standard, pad_type::space); - *out_++ = ' '; - on_iso_time(); - *out_++ = ' '; -- on_year(numeric_system::standard); -+ on_year(numeric_system::standard, pad_type::space); - } else { - format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); - } -@@ -1434,31 +1389,31 @@ class tm_writer { - write_digit2_separated(buf, to_unsigned(tm_mon() + 1), - to_unsigned(tm_mday()), - to_unsigned(split_year_lower(tm_year())), '/'); -- out_ = copy_str(std::begin(buf), std::end(buf), out_); -+ out_ = copy(std::begin(buf), std::end(buf), out_); - } - void on_iso_date() { - auto year = tm_year(); - char buf[10]; - size_t offset = 0; - if (year >= 0 && year < 10000) { -- copy2(buf, digits2(static_cast(year / 100))); -+ write2digits(buf, static_cast(year / 100)); - } else { - offset = 4; -- write_year_extended(year); -+ write_year_extended(year, pad_type::zero); - year = 0; - } - write_digit2_separated(buf + 2, static_cast(year % 100), - to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), - '-'); -- out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); -+ out_ = copy(std::begin(buf) + offset, std::end(buf), out_); - } - - void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } - void on_tz_name() { format_tz_name_impl(tm_); } - -- void on_year(numeric_system ns) { -+ void on_year(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) -- return write_year(tm_year()); -+ return write_year(tm_year(), pad); - format_localized('Y', 'E'); - } - void on_short_year(numeric_system ns) { -@@ -1489,56 +1444,57 @@ class tm_writer { - } - } - -- void on_dec_month(numeric_system ns) { -+ void on_dec_month(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) -- return write2(tm_mon() + 1); -+ return write2(tm_mon() + 1, pad); - format_localized('m', 'O'); - } - -- void on_dec0_week_of_year(numeric_system ns) { -+ void on_dec0_week_of_year(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) -- return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); -+ return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week, -+ pad); - format_localized('U', 'O'); - } -- void on_dec1_week_of_year(numeric_system ns) { -+ void on_dec1_week_of_year(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) { - auto wday = tm_wday(); - write2((tm_yday() + days_per_week - - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / -- days_per_week); -+ days_per_week, -+ pad); - } else { - format_localized('W', 'O'); - } - } -- void on_iso_week_of_year(numeric_system ns) { -+ void on_iso_week_of_year(numeric_system ns, pad_type pad) { - if (is_classic_ || ns == numeric_system::standard) -- return write2(tm_iso_week_of_year()); -+ return write2(tm_iso_week_of_year(), pad); - format_localized('V', 'O'); - } - -- void on_iso_week_based_year() { write_year(tm_iso_week_year()); } -+ void on_iso_week_based_year() { -+ write_year(tm_iso_week_year(), pad_type::zero); -+ } - void on_iso_week_based_short_year() { - write2(split_year_lower(tm_iso_week_year())); - } - -- void on_day_of_year() { -+ void on_day_of_year(pad_type pad) { - auto yday = tm_yday() + 1; -- write1(yday / 100); -- write2(yday % 100); -- } -- void on_day_of_month(numeric_system ns) { -- if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); -- format_localized('d', 'O'); -- } -- void on_day_of_month_space(numeric_system ns) { -- if (is_classic_ || ns == numeric_system::standard) { -- auto mday = to_unsigned(tm_mday()) % 100; -- const char* d2 = digits2(mday); -- *out_++ = mday < 10 ? ' ' : d2[0]; -- *out_++ = d2[1]; -+ auto digit1 = yday / 100; -+ if (digit1 != 0) { -+ write1(digit1); - } else { -- format_localized('e', 'O'); -+ out_ = detail::write_padding(out_, pad); - } -+ write2(yday % 100, pad); -+ } -+ -+ void on_day_of_month(numeric_system ns, pad_type pad) { -+ if (is_classic_ || ns == numeric_system::standard) -+ return write2(tm_mday(), pad); -+ format_localized('d', 'O'); - } - - void on_24_hour(numeric_system ns, pad_type pad) { -@@ -1566,7 +1522,7 @@ class tm_writer { - write_floating_seconds(buf, *subsecs_); - if (buf.size() > 1) { - // Remove the leading "0", write something like ".123". -- out_ = std::copy(buf.begin() + 1, buf.end(), out_); -+ out_ = copy(buf.begin() + 1, buf.end(), out_); - } - } else { - write_fractional_seconds(out_, *subsecs_); -@@ -1583,7 +1539,7 @@ class tm_writer { - char buf[8]; - write_digit2_separated(buf, to_unsigned(tm_hour12()), - to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); -- out_ = copy_str(std::begin(buf), std::end(buf), out_); -+ out_ = copy(std::begin(buf), std::end(buf), out_); - *out_++ = ' '; - on_am_pm(); - } else { -@@ -1598,7 +1554,7 @@ class tm_writer { - void on_iso_time() { - on_24_hour_time(); - *out_++ = ':'; -- on_second(numeric_system::standard, pad_type::unspecified); -+ on_second(numeric_system::standard, pad_type::zero); - } - - void on_am_pm() { -@@ -1618,10 +1574,11 @@ class tm_writer { - struct chrono_format_checker : null_chrono_spec_handler { - bool has_precision_integral = false; - -- FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } -+ FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); } - - template - FMT_CONSTEXPR void on_text(const Char*, const Char*) {} -+ FMT_CONSTEXPR void on_day_of_year(pad_type) {} - FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} - FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} -@@ -1631,25 +1588,24 @@ struct chrono_format_checker : null_chrono_spec_handler { - FMT_CONSTEXPR void on_iso_time() {} - FMT_CONSTEXPR void on_am_pm() {} - FMT_CONSTEXPR void on_duration_value() const { -- if (has_precision_integral) { -+ if (has_precision_integral) - FMT_THROW(format_error("precision not allowed for this argument type")); -- } - } - FMT_CONSTEXPR void on_duration_unit() {} - }; - - template ::value&& has_isfinite::value)> --inline bool isfinite(T) { -+inline auto isfinite(T) -> bool { - return true; - } - - template ::value)> --inline T mod(T x, int y) { -+inline auto mod(T x, int y) -> T { - return x % static_cast(y); - } - template ::value)> --inline T mod(T x, int y) { -+inline auto mod(T x, int y) -> T { - return std::fmod(x, static_cast(y)); - } - -@@ -1664,71 +1620,60 @@ template struct make_unsigned_or_unchanged { - using type = typename std::make_unsigned::type; - }; - --#if FMT_SAFE_DURATION_CAST --// throwing version of safe_duration_cast --template --To fmt_safe_duration_cast(std::chrono::duration from) { -- int ec; -- To to = safe_duration_cast::safe_duration_cast(from, ec); -- if (ec) FMT_THROW(format_error("cannot format duration")); -- return to; --} --#endif -- - template ::value)> --inline std::chrono::duration get_milliseconds( -- std::chrono::duration d) { -+inline auto get_milliseconds(std::chrono::duration d) -+ -> std::chrono::duration { - // this may overflow and/or the result may not fit in the - // target type. - #if FMT_SAFE_DURATION_CAST - using CommonSecondsType = - typename std::common_type::type; -- const auto d_as_common = fmt_safe_duration_cast(d); -+ const auto d_as_common = detail::duration_cast(d); - const auto d_as_whole_seconds = -- fmt_safe_duration_cast(d_as_common); -+ detail::duration_cast(d_as_common); - // this conversion should be nonproblematic - const auto diff = d_as_common - d_as_whole_seconds; - const auto ms = -- fmt_safe_duration_cast>(diff); -+ detail::duration_cast>(diff); - return ms; - #else -- auto s = std::chrono::duration_cast(d); -- return std::chrono::duration_cast(d - s); -+ auto s = detail::duration_cast(d); -+ return detail::duration_cast(d - s); - #endif - } - - template ::value)> --OutputIt format_duration_value(OutputIt out, Rep val, int) { -+auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt { - return write(out, val); - } - - template ::value)> --OutputIt format_duration_value(OutputIt out, Rep val, int precision) { -- auto specs = format_specs(); -+auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { -+ auto specs = format_specs(); - specs.precision = precision; -- specs.type = precision >= 0 ? presentation_type::fixed_lower -- : presentation_type::general_lower; -+ specs.set_type(precision >= 0 ? presentation_type::fixed -+ : presentation_type::general); - return write(out, val, specs); - } - - template --OutputIt copy_unit(string_view unit, OutputIt out, Char) { -- return std::copy(unit.begin(), unit.end(), out); -+auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt { -+ return copy(unit.begin(), unit.end(), out); - } - - template --OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { -+auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt { - // This works when wchar_t is UTF-32 because units only contain characters - // that have the same representation in UTF-16 and UTF-32. - utf8_to_utf16 u(unit); -- return std::copy(u.c_str(), u.c_str() + u.size(), out); -+ return copy(u.c_str(), u.c_str() + u.size(), out); - } - - template --OutputIt format_duration_unit(OutputIt out) { -+auto format_duration_unit(OutputIt out) -> OutputIt { - if (const char* unit = get_units()) - return copy_unit(string_view(unit), out, Char()); - *out++ = '['; -@@ -1750,14 +1695,14 @@ class get_locale { - bool has_locale_ = false; - - public: -- get_locale(bool localized, locale_ref loc) : has_locale_(localized) { -+ inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) { - if (localized) - ::new (&locale_) std::locale(loc.template get()); - } -- ~get_locale() { -+ inline ~get_locale() { - if (has_locale_) locale_.~locale(); - } -- operator const std::locale&() const { -+ inline operator const std::locale&() const { - return has_locale_ ? locale_ : get_classic_locale(); - } - }; -@@ -1795,18 +1740,12 @@ struct chrono_formatter { - - // this may overflow and/or the result may not fit in the - // target type. --#if FMT_SAFE_DURATION_CAST - // might need checked conversion (rep!=Rep) -- auto tmpval = std::chrono::duration(val); -- s = fmt_safe_duration_cast(tmpval); --#else -- s = std::chrono::duration_cast( -- std::chrono::duration(val)); --#endif -+ s = detail::duration_cast(std::chrono::duration(val)); - } - - // returns true if nan or inf, writes to out. -- bool handle_nan_inf() { -+ auto handle_nan_inf() -> bool { - if (isfinite(val)) { - return false; - } -@@ -1823,17 +1762,22 @@ struct chrono_formatter { - return true; - } - -- Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } -+ auto days() const -> Rep { return static_cast(s.count() / 86400); } -+ auto hour() const -> Rep { -+ return static_cast(mod((s.count() / 3600), 24)); -+ } - -- Rep hour12() const { -+ auto hour12() const -> Rep { - Rep hour = static_cast(mod((s.count() / 3600), 12)); - return hour <= 0 ? 12 : hour; - } - -- Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } -- Rep second() const { return static_cast(mod(s.count(), 60)); } -+ auto minute() const -> Rep { -+ return static_cast(mod((s.count() / 60), 60)); -+ } -+ auto second() const -> Rep { return static_cast(mod(s.count(), 60)); } - -- std::tm time() const { -+ auto time() const -> std::tm { - auto time = std::tm(); - time.tm_hour = to_nonnegative_int(hour(), 24); - time.tm_min = to_nonnegative_int(minute(), 60); -@@ -1848,7 +1792,7 @@ struct chrono_formatter { - } - } - -- void write(Rep value, int width, pad_type pad = pad_type::unspecified) { -+ void write(Rep value, int width, pad_type pad = pad_type::zero) { - write_sign(); - if (isnan(value)) return write_nan(); - uint32_or_64_or_128_t n = -@@ -1857,7 +1801,7 @@ struct chrono_formatter { - if (width > num_digits) { - out = detail::write_padding(out, pad, width - num_digits); - } -- out = format_decimal(out, n, num_digits).end; -+ out = format_decimal(out, n, num_digits); - } - - void write_nan() { std::copy_n("nan", 3, out); } -@@ -1874,7 +1818,7 @@ struct chrono_formatter { - } - - void on_text(const char_type* begin, const char_type* end) { -- std::copy(begin, end, out); -+ copy(begin, end, out); - } - - // These are not implemented because durations don't have date information. -@@ -1891,19 +1835,22 @@ struct chrono_formatter { - void on_iso_date() {} - void on_utc_offset(numeric_system) {} - void on_tz_name() {} -- void on_year(numeric_system) {} -+ void on_year(numeric_system, pad_type) {} - void on_short_year(numeric_system) {} - void on_offset_year() {} - void on_century(numeric_system) {} - void on_iso_week_based_year() {} - void on_iso_week_based_short_year() {} -- void on_dec_month(numeric_system) {} -- void on_dec0_week_of_year(numeric_system) {} -- void on_dec1_week_of_year(numeric_system) {} -- void on_iso_week_of_year(numeric_system) {} -- void on_day_of_year() {} -- void on_day_of_month(numeric_system) {} -- void on_day_of_month_space(numeric_system) {} -+ void on_dec_month(numeric_system, pad_type) {} -+ void on_dec0_week_of_year(numeric_system, pad_type) {} -+ void on_dec1_week_of_year(numeric_system, pad_type) {} -+ void on_iso_week_of_year(numeric_system, pad_type) {} -+ void on_day_of_month(numeric_system, pad_type) {} -+ -+ void on_day_of_year(pad_type) { -+ if (handle_nan_inf()) return; -+ write(days(), 0); -+ } - - void on_24_hour(numeric_system ns, pad_type pad) { - if (handle_nan_inf()) return; -@@ -1944,7 +1891,7 @@ struct chrono_formatter { - if (buf.size() < 2 || buf[1] == '.') { - out = detail::write_padding(out, pad); - } -- out = std::copy(buf.begin(), buf.end(), out); -+ out = copy(buf.begin(), buf.end(), out); - } else { - write(second(), 2, pad); - write_fractional_seconds( -@@ -1978,7 +1925,7 @@ struct chrono_formatter { - on_24_hour_time(); - *out++ = ':'; - if (handle_nan_inf()) return; -- on_second(numeric_system::standard, pad_type::unspecified); -+ on_second(numeric_system::standard, pad_type::zero); - } - - void on_am_pm() { -@@ -1997,268 +1944,391 @@ struct chrono_formatter { - } - }; - --FMT_END_DETAIL_NAMESPACE -+} // namespace detail - - #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 - using weekday = std::chrono::weekday; -+using day = std::chrono::day; -+using month = std::chrono::month; -+using year = std::chrono::year; -+using year_month_day = std::chrono::year_month_day; - #else - // A fallback version of weekday. - class weekday { - private: -- unsigned char value; -+ unsigned char value_; - - public: - weekday() = default; -- explicit constexpr weekday(unsigned wd) noexcept -- : value(static_cast(wd != 7 ? wd : 0)) {} -- constexpr unsigned c_encoding() const noexcept { return value; } -+ constexpr explicit weekday(unsigned wd) noexcept -+ : value_(static_cast(wd != 7 ? wd : 0)) {} -+ constexpr auto c_encoding() const noexcept -> unsigned { return value_; } -+}; -+ -+class day { -+ private: -+ unsigned char value_; -+ -+ public: -+ day() = default; -+ constexpr explicit day(unsigned d) noexcept -+ : value_(static_cast(d)) {} -+ constexpr explicit operator unsigned() const noexcept { return value_; } -+}; -+ -+class month { -+ private: -+ unsigned char value_; -+ -+ public: -+ month() = default; -+ constexpr explicit month(unsigned m) noexcept -+ : value_(static_cast(m)) {} -+ constexpr explicit operator unsigned() const noexcept { return value_; } -+}; -+ -+class year { -+ private: -+ int value_; -+ -+ public: -+ year() = default; -+ constexpr explicit year(int y) noexcept : value_(y) {} -+ constexpr explicit operator int() const noexcept { return value_; } - }; - --class year_month_day {}; -+class year_month_day { -+ private: -+ fmt::year year_; -+ fmt::month month_; -+ fmt::day day_; -+ -+ public: -+ year_month_day() = default; -+ constexpr year_month_day(const year& y, const month& m, const day& d) noexcept -+ : year_(y), month_(m), day_(d) {} -+ constexpr auto year() const noexcept -> fmt::year { return year_; } -+ constexpr auto month() const noexcept -> fmt::month { return month_; } -+ constexpr auto day() const noexcept -> fmt::day { return day_; } -+}; - #endif - --// A rudimentary weekday formatter. --template struct formatter { -+template -+struct formatter : private formatter { - private: -- bool localized = false; -+ bool localized_ = false; -+ bool use_tm_formatter_ = false; - - public: -- FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) -- -> decltype(ctx.begin()) { -- auto begin = ctx.begin(), end = ctx.end(); -- if (begin != end && *begin == 'L') { -- ++begin; -- localized = true; -+ FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { -+ auto it = ctx.begin(), end = ctx.end(); -+ if (it != end && *it == 'L') { -+ ++it; -+ localized_ = true; -+ return it; - } -- return begin; -+ use_tm_formatter_ = it != end && *it != '}'; -+ return use_tm_formatter_ ? formatter::parse(ctx) : it; - } - - template - auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { - auto time = std::tm(); - time.tm_wday = static_cast(wd.c_encoding()); -- detail::get_locale loc(localized, ctx.locale()); -+ if (use_tm_formatter_) return formatter::format(time, ctx); -+ detail::get_locale loc(localized_, ctx.locale()); - auto w = detail::tm_writer(loc, ctx.out(), time); - w.on_abbr_weekday(); - return w.out(); - } - }; - -+template -+struct formatter : private formatter { -+ private: -+ bool use_tm_formatter_ = false; -+ -+ public: -+ FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { -+ auto it = ctx.begin(), end = ctx.end(); -+ use_tm_formatter_ = it != end && *it != '}'; -+ return use_tm_formatter_ ? formatter::parse(ctx) : it; -+ } -+ -+ template -+ auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { -+ auto time = std::tm(); -+ time.tm_mday = static_cast(static_cast(d)); -+ if (use_tm_formatter_) return formatter::format(time, ctx); -+ detail::get_locale loc(false, ctx.locale()); -+ auto w = detail::tm_writer(loc, ctx.out(), time); -+ w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero); -+ return w.out(); -+ } -+}; -+ -+template -+struct formatter : private formatter { -+ private: -+ bool localized_ = false; -+ bool use_tm_formatter_ = false; -+ -+ public: -+ FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { -+ auto it = ctx.begin(), end = ctx.end(); -+ if (it != end && *it == 'L') { -+ ++it; -+ localized_ = true; -+ return it; -+ } -+ use_tm_formatter_ = it != end && *it != '}'; -+ return use_tm_formatter_ ? formatter::parse(ctx) : it; -+ } -+ -+ template -+ auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { -+ auto time = std::tm(); -+ time.tm_mon = static_cast(static_cast(m)) - 1; -+ if (use_tm_formatter_) return formatter::format(time, ctx); -+ detail::get_locale loc(localized_, ctx.locale()); -+ auto w = detail::tm_writer(loc, ctx.out(), time); -+ w.on_abbr_month(); -+ return w.out(); -+ } -+}; -+ -+template -+struct formatter : private formatter { -+ private: -+ bool use_tm_formatter_ = false; -+ -+ public: -+ FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { -+ auto it = ctx.begin(), end = ctx.end(); -+ use_tm_formatter_ = it != end && *it != '}'; -+ return use_tm_formatter_ ? formatter::parse(ctx) : it; -+ } -+ -+ template -+ auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { -+ auto time = std::tm(); -+ time.tm_year = static_cast(y) - 1900; -+ if (use_tm_formatter_) return formatter::format(time, ctx); -+ detail::get_locale loc(false, ctx.locale()); -+ auto w = detail::tm_writer(loc, ctx.out(), time); -+ w.on_year(detail::numeric_system::standard, detail::pad_type::zero); -+ return w.out(); -+ } -+}; -+ -+template -+struct formatter : private formatter { -+ private: -+ bool use_tm_formatter_ = false; -+ -+ public: -+ FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { -+ auto it = ctx.begin(), end = ctx.end(); -+ use_tm_formatter_ = it != end && *it != '}'; -+ return use_tm_formatter_ ? formatter::parse(ctx) : it; -+ } -+ -+ template -+ auto format(year_month_day val, FormatContext& ctx) const -+ -> decltype(ctx.out()) { -+ auto time = std::tm(); -+ time.tm_year = static_cast(val.year()) - 1900; -+ time.tm_mon = static_cast(static_cast(val.month())) - 1; -+ time.tm_mday = static_cast(static_cast(val.day())); -+ if (use_tm_formatter_) return formatter::format(time, ctx); -+ detail::get_locale loc(true, ctx.locale()); -+ auto w = detail::tm_writer(loc, ctx.out(), time); -+ w.on_iso_date(); -+ return w.out(); -+ } -+}; -+ - template - struct formatter, Char> { - private: -- format_specs specs; -- int precision = -1; -- using arg_ref_type = detail::arg_ref; -- arg_ref_type width_ref; -- arg_ref_type precision_ref; -- bool localized = false; -- basic_string_view format_str; -- using duration = std::chrono::duration; -+ format_specs specs_; -+ detail::arg_ref width_ref_; -+ detail::arg_ref precision_ref_; -+ bool localized_ = false; -+ basic_string_view fmt_; - -- using iterator = typename basic_format_parse_context::iterator; -- struct parse_range { -- iterator begin; -- iterator end; -- }; -- -- FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { -- auto begin = ctx.begin(), end = ctx.end(); -- if (begin == end || *begin == '}') return {begin, begin}; -+ public: -+ FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { -+ auto it = ctx.begin(), end = ctx.end(); -+ if (it == end || *it == '}') return it; - -- begin = detail::parse_align(begin, end, specs); -- if (begin == end) return {begin, begin}; -+ it = detail::parse_align(it, end, specs_); -+ if (it == end) return it; - -- begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); -- if (begin == end) return {begin, begin}; -+ Char c = *it; -+ if ((c >= '0' && c <= '9') || c == '{') { -+ it = detail::parse_width(it, end, specs_, width_ref_, ctx); -+ if (it == end) return it; -+ } - - auto checker = detail::chrono_format_checker(); -- if (*begin == '.') { -+ if (*it == '.') { - checker.has_precision_integral = !std::is_floating_point::value; -- begin = -- detail::parse_precision(begin, end, precision, precision_ref, ctx); -+ it = detail::parse_precision(it, end, specs_, precision_ref_, ctx); - } -- if (begin != end && *begin == 'L') { -- ++begin; -- localized = true; -+ if (it != end && *it == 'L') { -+ localized_ = true; -+ ++it; - } -- end = detail::parse_chrono_format(begin, end, checker); -- return {begin, end}; -- } -- -- public: -- FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) -- -> decltype(ctx.begin()) { -- auto range = do_parse(ctx); -- format_str = basic_string_view( -- &*range.begin, detail::to_unsigned(range.end - range.begin)); -- return range.end; -+ end = detail::parse_chrono_format(it, end, checker); -+ fmt_ = {it, detail::to_unsigned(end - it)}; -+ return end; - } - - template -- auto format(const duration& d, FormatContext& ctx) const -+ auto format(std::chrono::duration d, FormatContext& ctx) const - -> decltype(ctx.out()) { -- auto specs_copy = specs; -- auto precision_copy = precision; -- auto begin = format_str.begin(), end = format_str.end(); -+ auto specs = specs_; -+ auto precision = specs.precision; -+ specs.precision = -1; -+ auto begin = fmt_.begin(), end = fmt_.end(); - // As a possible future optimization, we could avoid extra copying if width - // is not specified. -- basic_memory_buffer buf; -- auto out = std::back_inserter(buf); -- detail::handle_dynamic_spec(specs_copy.width, -- width_ref, ctx); -- detail::handle_dynamic_spec(precision_copy, -- precision_ref, ctx); -+ auto buf = basic_memory_buffer(); -+ auto out = basic_appender(buf); -+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, -+ ctx); -+ detail::handle_dynamic_spec(specs.dynamic_precision(), precision, -+ precision_ref_, ctx); - if (begin == end || *begin == '}') { -- out = detail::format_duration_value(out, d.count(), precision_copy); -+ out = detail::format_duration_value(out, d.count(), precision); - detail::format_duration_unit(out); - } else { -- detail::chrono_formatter f( -- ctx, out, d); -- f.precision = precision_copy; -- f.localized = localized; -+ using chrono_formatter = -+ detail::chrono_formatter; -+ auto f = chrono_formatter(ctx, out, d); -+ f.precision = precision; -+ f.localized = localized_; - detail::parse_chrono_format(begin, end, f); - } - return detail::write( -- ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); -+ ctx.out(), basic_string_view(buf.data(), buf.size()), specs); - } - }; - --template --struct formatter, -- Char> : formatter { -- FMT_CONSTEXPR formatter() { -- this->format_str = detail::string_literal{}; -+template struct formatter { -+ private: -+ format_specs specs_; -+ detail::arg_ref width_ref_; -+ -+ protected: -+ basic_string_view fmt_; -+ -+ template -+ auto do_format(const std::tm& tm, FormatContext& ctx, -+ const Duration* subsecs) const -> decltype(ctx.out()) { -+ auto specs = specs_; -+ auto buf = basic_memory_buffer(); -+ auto out = basic_appender(buf); -+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, -+ ctx); -+ -+ auto loc_ref = ctx.locale(); -+ detail::get_locale loc(static_cast(loc_ref), loc_ref); -+ auto w = -+ detail::tm_writer(loc, out, tm, subsecs); -+ detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w); -+ return detail::write( -+ ctx.out(), basic_string_view(buf.data(), buf.size()), specs); - } - -- template -- auto format(std::chrono::time_point val, -- FormatContext& ctx) const -> decltype(ctx.out()) { -- using period = typename Duration::period; -- if (period::num != 1 || period::den != 1 || -- std::is_floating_point::value) { -- const auto epoch = val.time_since_epoch(); -- auto subsecs = std::chrono::duration_cast( -- epoch - std::chrono::duration_cast(epoch)); -- -- if (subsecs.count() < 0) { -- auto second = std::chrono::duration_cast( -- std::chrono::seconds(1)); -- if (epoch.count() < ((Duration::min)() + second).count()) -- FMT_THROW(format_error("duration is too small")); -- subsecs += second; -- val -= second; -- } -+ public: -+ FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { -+ auto it = ctx.begin(), end = ctx.end(); -+ if (it == end || *it == '}') return it; - -- return formatter::do_format( -- gmtime(std::chrono::time_point_cast(val)), ctx, -- &subsecs); -+ it = detail::parse_align(it, end, specs_); -+ if (it == end) return it; -+ -+ Char c = *it; -+ if ((c >= '0' && c <= '9') || c == '{') { -+ it = detail::parse_width(it, end, specs_, width_ref_, ctx); -+ if (it == end) return it; - } - -- return formatter::format( -- gmtime(std::chrono::time_point_cast(val)), ctx); -+ end = detail::parse_chrono_format(it, end, detail::tm_format_checker()); -+ // Replace the default format string only if the new spec is not empty. -+ if (end != it) fmt_ = {it, detail::to_unsigned(end - it)}; -+ return end; -+ } -+ -+ template -+ auto format(const std::tm& tm, FormatContext& ctx) const -+ -> decltype(ctx.out()) { -+ return do_format(tm, ctx, nullptr); - } - }; - --#if FMT_USE_LOCAL_TIME - template --struct formatter, Char> -- : formatter { -+struct formatter, Char> : formatter { - FMT_CONSTEXPR formatter() { -- this->format_str = detail::string_literal{}; -+ this->fmt_ = detail::string_literal(); - } - - template -- auto format(std::chrono::local_time val, FormatContext& ctx) const -+ auto format(sys_time val, FormatContext& ctx) const - -> decltype(ctx.out()) { -+ std::tm tm = gmtime(val); - using period = typename Duration::period; -- if (period::num != 1 || period::den != 1 || -- std::is_floating_point::value) { -- const auto epoch = val.time_since_epoch(); -- const auto subsecs = std::chrono::duration_cast( -- epoch - std::chrono::duration_cast(epoch)); -- -- return formatter::do_format( -- localtime(std::chrono::time_point_cast(val)), -- ctx, &subsecs); -+ if (detail::const_check( -+ period::num == 1 && period::den == 1 && -+ !std::is_floating_point::value)) { -+ return formatter::format(tm, ctx); - } -- -- return formatter::format( -- localtime(std::chrono::time_point_cast(val)), -- ctx); -+ Duration epoch = val.time_since_epoch(); -+ Duration subsecs = detail::duration_cast( -+ epoch - detail::duration_cast(epoch)); -+ if (subsecs.count() < 0) { -+ auto second = detail::duration_cast(std::chrono::seconds(1)); -+ if (tm.tm_sec != 0) -+ --tm.tm_sec; -+ else -+ tm = gmtime(val - second); -+ subsecs += detail::duration_cast(std::chrono::seconds(1)); -+ } -+ return formatter::do_format(tm, ctx, &subsecs); - } - }; --#endif - --#if FMT_USE_UTC_TIME --template --struct formatter, -- Char> -- : formatter, -- Char> { -+template -+struct formatter, Char> -+ : formatter, Char> { - template -- auto format(std::chrono::time_point val, -- FormatContext& ctx) const -> decltype(ctx.out()) { -- return formatter< -- std::chrono::time_point, -- Char>::format(std::chrono::utc_clock::to_sys(val), ctx); -+ auto format(utc_time val, FormatContext& ctx) const -+ -> decltype(ctx.out()) { -+ return formatter, Char>::format( -+ detail::utc_clock::to_sys(val), ctx); - } - }; --#endif -- --template struct formatter { -- private: -- format_specs specs; -- detail::arg_ref width_ref; -- -- protected: -- basic_string_view format_str; -- -- FMT_CONSTEXPR auto do_parse(basic_format_parse_context& ctx) -- -> decltype(ctx.begin()) { -- auto begin = ctx.begin(), end = ctx.end(); -- if (begin == end || *begin == '}') return begin; -- -- begin = detail::parse_align(begin, end, specs); -- if (begin == end) return end; -- -- begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); -- if (begin == end) return end; -- -- end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); -- // Replace default format_str only if the new spec is not empty. -- if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)}; -- return end; -- } -- -- template -- auto do_format(const std::tm& tm, FormatContext& ctx, -- const Duration* subsecs) const -> decltype(ctx.out()) { -- auto specs_copy = specs; -- basic_memory_buffer buf; -- auto out = std::back_inserter(buf); -- detail::handle_dynamic_spec(specs_copy.width, -- width_ref, ctx); - -- const auto loc_ref = ctx.locale(); -- detail::get_locale loc(static_cast(loc_ref), loc_ref); -- auto w = -- detail::tm_writer(loc, out, tm, subsecs); -- detail::parse_chrono_format(format_str.begin(), format_str.end(), w); -- return detail::write( -- ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); -- } -- -- public: -- FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) -- -> decltype(ctx.begin()) { -- return this->do_parse(ctx); -+template -+struct formatter, Char> : formatter { -+ FMT_CONSTEXPR formatter() { -+ this->fmt_ = detail::string_literal(); - } - - template -- auto format(const std::tm& tm, FormatContext& ctx) const -+ auto format(local_time val, FormatContext& ctx) const - -> decltype(ctx.out()) { -- return do_format(tm, ctx, nullptr); -+ using period = typename Duration::period; -+ if (period::num == 1 && period::den == 1 && -+ !std::is_floating_point::value) { -+ return formatter::format(localtime(val), ctx); -+ } -+ auto epoch = val.time_since_epoch(); -+ auto subsecs = detail::duration_cast( -+ epoch - detail::duration_cast(epoch)); -+ return formatter::do_format(localtime(val), ctx, &subsecs); - } - }; - -diff --git a/include/fmt/color.h b/include/fmt/color.h -index d175448..638f15b 100644 ---- a/include/fmt/color.h -+++ b/include/fmt/color.h -@@ -190,11 +190,11 @@ enum class emphasis : uint8_t { - // rgb is a struct for red, green and blue colors. - // Using the name "rgb" makes some editors show the color in a tooltip. - struct rgb { -- FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} -- FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} -- FMT_CONSTEXPR rgb(uint32_t hex) -+ constexpr rgb() : r(0), g(0), b(0) {} -+ constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} -+ constexpr rgb(uint32_t hex) - : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} -- FMT_CONSTEXPR rgb(color hex) -+ constexpr rgb(color hex) - : r((uint32_t(hex) >> 16) & 0xFF), - g((uint32_t(hex) >> 8) & 0xFF), - b(uint32_t(hex) & 0xFF) {} -@@ -203,136 +203,174 @@ struct rgb { - uint8_t b; - }; - --FMT_BEGIN_DETAIL_NAMESPACE -+namespace detail { - --// color is a struct of either a rgb color or a terminal color. -+// A bit-packed variant of an RGB color, a terminal color, or unset color. -+// see text_style for the bit-packing scheme. - struct color_type { -- FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} -- FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { -- value.rgb_color = static_cast(rgb_color); -+ constexpr color_type() noexcept = default; -+ constexpr color_type(color rgb_color) noexcept -+ : value_(static_cast(rgb_color) | (1 << 24)) {} -+ constexpr color_type(rgb rgb_color) noexcept -+ : color_type(static_cast( -+ (static_cast(rgb_color.r) << 16) | -+ (static_cast(rgb_color.g) << 8) | rgb_color.b)) {} -+ constexpr color_type(terminal_color term_color) noexcept -+ : value_(static_cast(term_color) | (3 << 24)) {} -+ -+ constexpr auto is_terminal_color() const noexcept -> bool { -+ return (value_ & (1 << 25)) != 0; - } -- FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { -- value.rgb_color = (static_cast(rgb_color.r) << 16) | -- (static_cast(rgb_color.g) << 8) | rgb_color.b; -- } -- FMT_CONSTEXPR color_type(terminal_color term_color) noexcept -- : is_rgb(), value{} { -- value.term_color = static_cast(term_color); -+ -+ constexpr auto value() const noexcept -> uint32_t { -+ return value_ & 0xFFFFFF; - } -- bool is_rgb; -- union color_union { -- uint8_t term_color; -- uint32_t rgb_color; -- } value; --}; - --FMT_END_DETAIL_NAMESPACE -+ constexpr color_type(uint32_t value) noexcept : value_(value) {} -+ -+ uint32_t value_ = 0; -+}; -+} // namespace detail - --/** A text style consisting of foreground and background colors and emphasis. */ -+/// A text style consisting of foreground and background colors and emphasis. - class text_style { -+ // The information is packed as follows: -+ // ┌──┐ -+ // │ 0│─┐ -+ // │..│ ├── foreground color value -+ // │23│─┘ -+ // ├──┤ -+ // │24│─┬── discriminator for the above value. 00 if unset, 01 if it's -+ // │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused) -+ // ├──┤ -+ // │26│──── overflow bit, always zero (see below) -+ // ├──┤ -+ // │27│─┐ -+ // │..│ │ -+ // │50│ │ -+ // ├──┤ │ -+ // │51│ ├── background color (same format as the foreground color) -+ // │52│ │ -+ // ├──┤ │ -+ // │53│─┘ -+ // ├──┤ -+ // │54│─┐ -+ // │..│ ├── emphases -+ // │61│─┘ -+ // ├──┤ -+ // │62│─┬── unused -+ // │63│─┘ -+ // └──┘ -+ // The overflow bits are there to make operator|= efficient. -+ // When ORing, we must throw if, for either the foreground or background, -+ // one style specifies a terminal color and the other specifies any color -+ // (terminal or RGB); in other words, if one discriminator is 11 and the -+ // other is 11 or 01. -+ // -+ // We do that check by adding the styles. Consider what adding does to each -+ // possible pair of discriminators: -+ // 00 + 00 = 000 -+ // 01 + 00 = 001 -+ // 11 + 00 = 011 -+ // 01 + 01 = 010 -+ // 11 + 01 = 100 (!!) -+ // 11 + 11 = 110 (!!) -+ // In the last two cases, the ones we want to catch, the third bit——the -+ // overflow bit——is set. Bingo. -+ // -+ // We must take into account the possible carry bit from the bits -+ // before the discriminator. The only potentially problematic case is -+ // 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry -+ // bit is impossible in that case, because 00 (unset color) means the -+ // 24 bits that precede the discriminator are all zero. -+ // -+ // This test can be applied to both colors simultaneously. -+ - public: - FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept -- : set_foreground_color(), set_background_color(), ems(em) {} -- -- FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { -- if (!set_foreground_color) { -- set_foreground_color = rhs.set_foreground_color; -- foreground_color = rhs.foreground_color; -- } else if (rhs.set_foreground_color) { -- if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) -- FMT_THROW(format_error("can't OR a terminal color")); -- foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; -- } -+ : style_(static_cast(em) << 54) {} - -- if (!set_background_color) { -- set_background_color = rhs.set_background_color; -- background_color = rhs.background_color; -- } else if (rhs.set_background_color) { -- if (!background_color.is_rgb || !rhs.background_color.is_rgb) -- FMT_THROW(format_error("can't OR a terminal color")); -- background_color.value.rgb_color |= rhs.background_color.value.rgb_color; -- } -- -- ems = static_cast(static_cast(ems) | -- static_cast(rhs.ems)); -+ FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& { -+ if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0) -+ report_error("can't OR a terminal color"); -+ style_ |= rhs.style_; - return *this; - } - -- friend FMT_CONSTEXPR text_style operator|(text_style lhs, -- const text_style& rhs) { -+ friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs) -+ -> text_style { - return lhs |= rhs; - } - -- FMT_CONSTEXPR bool has_foreground() const noexcept { -- return set_foreground_color; -+ FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool { -+ return style_ == rhs.style_; -+ } -+ -+ FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool { -+ return !(*this == rhs); - } -- FMT_CONSTEXPR bool has_background() const noexcept { -- return set_background_color; -+ -+ FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { -+ return (style_ & (1 << 24)) != 0; - } -- FMT_CONSTEXPR bool has_emphasis() const noexcept { -- return static_cast(ems) != 0; -+ FMT_CONSTEXPR auto has_background() const noexcept -> bool { -+ return (style_ & (1ULL << 51)) != 0; - } -- FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { -+ FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { -+ return (style_ >> 54) != 0; -+ } -+ FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { - FMT_ASSERT(has_foreground(), "no foreground specified for this style"); -- return foreground_color; -+ return style_ & 0x3FFFFFF; - } -- FMT_CONSTEXPR detail::color_type get_background() const noexcept { -+ FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { - FMT_ASSERT(has_background(), "no background specified for this style"); -- return background_color; -+ return (style_ >> 27) & 0x3FFFFFF; - } -- FMT_CONSTEXPR emphasis get_emphasis() const noexcept { -+ FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { - FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); -- return ems; -+ return static_cast(style_ >> 54); - } - - private: -- FMT_CONSTEXPR text_style(bool is_foreground, -- detail::color_type text_color) noexcept -- : set_foreground_color(), set_background_color(), ems() { -- if (is_foreground) { -- foreground_color = text_color; -- set_foreground_color = true; -- } else { -- background_color = text_color; -- set_background_color = true; -- } -- } -+ FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {} - -- friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; -+ friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept -+ -> text_style; - -- friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; -+ friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept -+ -> text_style; - -- detail::color_type foreground_color; -- detail::color_type background_color; -- bool set_foreground_color; -- bool set_background_color; -- emphasis ems; -+ uint64_t style_ = 0; - }; - --/** Creates a text style from the foreground (text) color. */ --FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { -- return text_style(true, foreground); -+/// Creates a text style from the foreground (text) color. -+FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept -+ -> text_style { -+ return foreground.value_; - } - --/** Creates a text style from the background color. */ --FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { -- return text_style(false, background); -+/// Creates a text style from the background color. -+FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept -+ -> text_style { -+ return static_cast(background.value_) << 27; - } - --FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { -+FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept -+ -> text_style { - return text_style(lhs) | rhs; - } - --FMT_BEGIN_DETAIL_NAMESPACE -+namespace detail { - - template struct ansi_color_escape { -- FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, -+ FMT_CONSTEXPR ansi_color_escape(color_type text_color, - const char* esc) noexcept { - // If we have a terminal color, we need to output another escape code - // sequence. -- if (!text_color.is_rgb) { -+ if (text_color.is_terminal_color()) { - bool is_background = esc == string_view("\x1b[48;2;"); -- uint32_t value = text_color.value.term_color; -+ uint32_t value = text_color.value(); - // Background ASCII codes are the same as the foreground ones but with - // 10 more. - if (is_background) value += 10u; -@@ -356,7 +394,7 @@ template struct ansi_color_escape { - for (int i = 0; i < 7; i++) { - buffer[i] = static_cast(esc[i]); - } -- rgb color(text_color.value.rgb_color); -+ rgb color(text_color.value()); - to_esc(color.r, buffer + 7, ';'); - to_esc(color.g, buffer + 11, ';'); - to_esc(color.b, buffer + 15, 'm'); -@@ -385,9 +423,9 @@ template struct ansi_color_escape { - } - FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } - -- FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } -- FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { -- return buffer + std::char_traits::length(buffer); -+ FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } -+ FMT_CONSTEXPR20 auto end() const noexcept -> const Char* { -+ return buffer + basic_string_view(buffer).size(); - } - - private: -@@ -401,25 +439,27 @@ template struct ansi_color_escape { - out[2] = static_cast('0' + c % 10); - out[3] = static_cast(delimiter); - } -- static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { -+ static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept -+ -> bool { - return static_cast(em) & static_cast(mask); - } - }; - - template --FMT_CONSTEXPR ansi_color_escape make_foreground_color( -- detail::color_type foreground) noexcept { -+FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept -+ -> ansi_color_escape { - return ansi_color_escape(foreground, "\x1b[38;2;"); - } - - template --FMT_CONSTEXPR ansi_color_escape make_background_color( -- detail::color_type background) noexcept { -+FMT_CONSTEXPR auto make_background_color(color_type background) noexcept -+ -> ansi_color_escape { - return ansi_color_escape(background, "\x1b[48;2;"); - } - - template --FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept { -+FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept -+ -> ansi_color_escape { - return ansi_color_escape(em); - } - -@@ -428,149 +468,116 @@ template inline void reset_color(buffer& buffer) { - buffer.append(reset_color.begin(), reset_color.end()); - } - --template struct styled_arg { -+template struct styled_arg : view { - const T& value; - text_style style; -+ styled_arg(const T& v, text_style s) : value(v), style(s) {} - }; - - template --void vformat_to(buffer& buf, const text_style& ts, -- basic_string_view format_str, -- basic_format_args>> args) { -- bool has_style = false; -+void vformat_to(buffer& buf, text_style ts, basic_string_view fmt, -+ basic_format_args> args) { - if (ts.has_emphasis()) { -- has_style = true; -- auto emphasis = detail::make_emphasis(ts.get_emphasis()); -+ auto emphasis = make_emphasis(ts.get_emphasis()); - buf.append(emphasis.begin(), emphasis.end()); - } - if (ts.has_foreground()) { -- has_style = true; -- auto foreground = detail::make_foreground_color(ts.get_foreground()); -+ auto foreground = make_foreground_color(ts.get_foreground()); - buf.append(foreground.begin(), foreground.end()); - } - if (ts.has_background()) { -- has_style = true; -- auto background = detail::make_background_color(ts.get_background()); -+ auto background = make_background_color(ts.get_background()); - buf.append(background.begin(), background.end()); - } -- detail::vformat_to(buf, format_str, args, {}); -- if (has_style) detail::reset_color(buf); -+ vformat_to(buf, fmt, args); -+ if (ts != text_style()) reset_color(buf); - } -+} // namespace detail - --FMT_END_DETAIL_NAMESPACE -- --inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, -- format_args args) { -- // Legacy wide streams are not supported. -+inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) { - auto buf = memory_buffer(); - detail::vformat_to(buf, ts, fmt, args); -- if (detail::is_utf8()) { -- detail::print(f, string_view(buf.begin(), buf.size())); -- return; -- } -- buf.push_back('\0'); -- int result = std::fputs(buf.data(), f); -- if (result < 0) -- FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); -+ print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size())); - } - - /** -- \rst -- Formats a string and prints it to the specified file stream using ANSI -- escape sequences to specify text formatting. -- -- **Example**:: -- -- fmt::print(fmt::emphasis::bold | fg(fmt::color::red), -- "Elapsed time: {0:.2f} seconds", 1.23); -- \endrst -+ * Formats a string and prints it to the specified file stream using ANSI -+ * escape sequences to specify text formatting. -+ * -+ * **Example**: -+ * -+ * fmt::print(fmt::emphasis::bold | fg(fmt::color::red), -+ * "Elapsed time: {0:.2f} seconds", 1.23); - */ --template ::value)> --void print(std::FILE* f, const text_style& ts, const S& format_str, -- const Args&... args) { -- vprint(f, ts, format_str, -- fmt::make_format_args>>(args...)); -+template -+void print(FILE* f, text_style ts, format_string fmt, T&&... args) { -+ vprint(f, ts, fmt.str, vargs{{args...}}); - } - - /** -- \rst -- Formats a string and prints it to stdout using ANSI escape sequences to -- specify text formatting. -- -- **Example**:: -- -- fmt::print(fmt::emphasis::bold | fg(fmt::color::red), -- "Elapsed time: {0:.2f} seconds", 1.23); -- \endrst -+ * Formats a string and prints it to stdout using ANSI escape sequences to -+ * specify text formatting. -+ * -+ * **Example**: -+ * -+ * fmt::print(fmt::emphasis::bold | fg(fmt::color::red), -+ * "Elapsed time: {0:.2f} seconds", 1.23); - */ --template ::value)> --void print(const text_style& ts, const S& format_str, const Args&... args) { -- return print(stdout, ts, format_str, args...); -+template -+void print(text_style ts, format_string fmt, T&&... args) { -+ return print(stdout, ts, fmt, std::forward(args)...); - } - --template > --inline std::basic_string vformat( -- const text_style& ts, const S& format_str, -- basic_format_args>> args) { -- basic_memory_buffer buf; -- detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); -+inline auto vformat(text_style ts, string_view fmt, format_args args) -+ -> std::string { -+ auto buf = memory_buffer(); -+ detail::vformat_to(buf, ts, fmt, args); - return fmt::to_string(buf); - } - - /** -- \rst -- Formats arguments and returns the result as a string using ANSI -- escape sequences to specify text formatting. -- -- **Example**:: -- -- #include -- std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), -- "The answer is {}", 42); -- \endrst --*/ --template > --inline std::basic_string format(const text_style& ts, const S& format_str, -- const Args&... args) { -- return fmt::vformat(ts, detail::to_string_view(format_str), -- fmt::make_format_args>(args...)); -+ * Formats arguments and returns the result as a string using ANSI escape -+ * sequences to specify text formatting. -+ * -+ * **Example**: -+ * -+ * ``` -+ * #include -+ * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), -+ * "The answer is {}", 42); -+ * ``` -+ */ -+template -+inline auto format(text_style ts, format_string fmt, T&&... args) -+ -> std::string { -+ return fmt::vformat(ts, fmt.str, vargs{{args...}}); - } - --/** -- Formats a string with the given text_style and writes the output to ``out``. -- */ --template ::value)> --OutputIt vformat_to( -- OutputIt out, const text_style& ts, basic_string_view format_str, -- basic_format_args>> args) { -- auto&& buf = detail::get_buffer(out); -- detail::vformat_to(buf, ts, format_str, args); -+/// Formats a string with the given text_style and writes the output to `out`. -+template ::value)> -+auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args) -+ -> OutputIt { -+ auto&& buf = detail::get_buffer(out); -+ detail::vformat_to(buf, ts, fmt, args); - return detail::get_iterator(buf, out); - } - - /** -- \rst -- Formats arguments with the given text_style, writes the result to the output -- iterator ``out`` and returns the iterator past the end of the output range. -- -- **Example**:: -- -- std::vector out; -- fmt::format_to(std::back_inserter(out), -- fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); -- \endrst --*/ --template >::value&& -- detail::is_string::value> --inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, -- Args&&... args) -> -- typename std::enable_if::type { -- return vformat_to(out, ts, detail::to_string_view(format_str), -- fmt::make_format_args>>(args...)); -+ * Formats arguments with the given text style, writes the result to the output -+ * iterator `out` and returns the iterator past the end of the output range. -+ * -+ * **Example**: -+ * -+ * std::vector out; -+ * fmt::format_to(std::back_inserter(out), -+ * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); -+ */ -+template ::value)> -+inline auto format_to(OutputIt out, text_style ts, format_string fmt, -+ T&&... args) -> OutputIt { -+ return vformat_to(out, ts, fmt.str, vargs{{args...}}); - } - - template -@@ -579,47 +586,44 @@ struct formatter, Char> : formatter { - auto format(const detail::styled_arg& arg, FormatContext& ctx) const - -> decltype(ctx.out()) { - const auto& ts = arg.style; -- const auto& value = arg.value; - auto out = ctx.out(); - - bool has_style = false; - if (ts.has_emphasis()) { - has_style = true; - auto emphasis = detail::make_emphasis(ts.get_emphasis()); -- out = std::copy(emphasis.begin(), emphasis.end(), out); -+ out = detail::copy(emphasis.begin(), emphasis.end(), out); - } - if (ts.has_foreground()) { - has_style = true; - auto foreground = - detail::make_foreground_color(ts.get_foreground()); -- out = std::copy(foreground.begin(), foreground.end(), out); -+ out = detail::copy(foreground.begin(), foreground.end(), out); - } - if (ts.has_background()) { - has_style = true; - auto background = - detail::make_background_color(ts.get_background()); -- out = std::copy(background.begin(), background.end(), out); -+ out = detail::copy(background.begin(), background.end(), out); - } -- out = formatter::format(value, ctx); -+ out = formatter::format(arg.value, ctx); - if (has_style) { - auto reset_color = string_view("\x1b[0m"); -- out = std::copy(reset_color.begin(), reset_color.end(), out); -+ out = detail::copy(reset_color.begin(), reset_color.end(), out); - } - return out; - } - }; - - /** -- \rst -- Returns an argument that will be formatted using ANSI escape sequences, -- to be used in a formatting function. -- -- **Example**:: -- -- fmt::print("Elapsed time: {0:.2f} seconds", -- fmt::styled(1.23, fmt::fg(fmt::color::green) | -- fmt::bg(fmt::color::blue))); -- \endrst -+ * Returns an argument that will be formatted using ANSI escape sequences, -+ * to be used in a formatting function. -+ * -+ * **Example**: -+ * -+ * fmt::print("Elapsed time: {0:.2f} seconds", -+ * fmt::styled(1.23, fmt::fg(fmt::color::green) | -+ * fmt::bg(fmt::color::blue))); - */ - template - FMT_CONSTEXPR auto styled(const T& value, text_style ts) -diff --git a/include/fmt/compile.h b/include/fmt/compile.h -index 34a581d..08d9427 100644 ---- a/include/fmt/compile.h -+++ b/include/fmt/compile.h -@@ -8,134 +8,41 @@ - #ifndef FMT_COMPILE_H_ - #define FMT_COMPILE_H_ - -+#ifndef FMT_MODULE -+# include // std::back_inserter -+#endif -+ - #include "format.h" - - FMT_BEGIN_NAMESPACE --namespace detail { -- --template --FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, -- counting_iterator it) { -- return it + (end - begin); --} -- --template class truncating_iterator_base { -- protected: -- OutputIt out_; -- size_t limit_; -- size_t count_ = 0; -- -- truncating_iterator_base() : out_(), limit_(0) {} -- -- truncating_iterator_base(OutputIt out, size_t limit) -- : out_(out), limit_(limit) {} -- -- public: -- using iterator_category = std::output_iterator_tag; -- using value_type = typename std::iterator_traits::value_type; -- using difference_type = std::ptrdiff_t; -- using pointer = void; -- using reference = void; -- FMT_UNCHECKED_ITERATOR(truncating_iterator_base); -- -- OutputIt base() const { return out_; } -- size_t count() const { return count_; } --}; -- --// An output iterator that truncates the output and counts the number of objects --// written to it. --template ::value_type>::type> --class truncating_iterator; -- --template --class truncating_iterator -- : public truncating_iterator_base { -- mutable typename truncating_iterator_base::value_type blackhole_; -- -- public: -- using value_type = typename truncating_iterator_base::value_type; -- -- truncating_iterator() = default; -- -- truncating_iterator(OutputIt out, size_t limit) -- : truncating_iterator_base(out, limit) {} -- -- truncating_iterator& operator++() { -- if (this->count_++ < this->limit_) ++this->out_; -- return *this; -- } -- -- truncating_iterator operator++(int) { -- auto it = *this; -- ++*this; -- return it; -- } -- -- value_type& operator*() const { -- return this->count_ < this->limit_ ? *this->out_ : blackhole_; -- } --}; -- --template --class truncating_iterator -- : public truncating_iterator_base { -- public: -- truncating_iterator() = default; -- -- truncating_iterator(OutputIt out, size_t limit) -- : truncating_iterator_base(out, limit) {} -- -- template truncating_iterator& operator=(T val) { -- if (this->count_++ < this->limit_) *this->out_++ = val; -- return *this; -- } -- -- truncating_iterator& operator++() { return *this; } -- truncating_iterator& operator++(int) { return *this; } -- truncating_iterator& operator*() { return *this; } --}; - - // A compile-time string which is compiled into fast formatting code. --class compiled_string {}; -+FMT_EXPORT class compiled_string {}; - - template - struct is_compiled_string : std::is_base_of {}; - --/** -- \rst -- Converts a string literal *s* into a format string that will be parsed at -- compile time and converted into efficient formatting code. Requires C++17 -- ``constexpr if`` compiler support. -- -- **Example**:: -+namespace detail { - -- // Converts 42 into std::string using the most efficient method and no -- // runtime format string processing. -- std::string s = fmt::format(FMT_COMPILE("{}"), 42); -- \endrst -+/** -+ * Converts a string literal `s` into a format string that will be parsed at -+ * compile time and converted into efficient formatting code. Requires C++17 -+ * `constexpr if` compiler support. -+ * -+ * **Example**: -+ * -+ * // Converts 42 into std::string using the most efficient method and no -+ * // runtime format string processing. -+ * std::string s = fmt::format(FMT_COMPILE("{}"), 42); - */ - #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) --# define FMT_COMPILE(s) \ -- FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) -+# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string) - #else - # define FMT_COMPILE(s) FMT_STRING(s) - #endif - --#if FMT_USE_NONTYPE_TEMPLATE_ARGS --template Str> --struct udl_compiled_string : compiled_string { -- using char_type = Char; -- explicit constexpr operator basic_string_view() const { -- return {Str.data, N - 1}; -- } --}; --#endif -- - template --const T& first(const T& value, const Tail&...) { -+auto first(const T& value, const Tail&...) -> const T& { - return value; - } - -@@ -153,6 +60,29 @@ constexpr const auto& get([[maybe_unused]] const T& first, - return detail::get(rest...); - } - -+# if FMT_USE_NONTYPE_TEMPLATE_ARGS -+template -+constexpr auto get_arg_index_by_name(basic_string_view name) -> int { -+ if constexpr (is_static_named_arg()) { -+ if (name == T::name) return N; -+ } -+ if constexpr (sizeof...(Args) > 0) -+ return get_arg_index_by_name(name); -+ (void)name; // Workaround an MSVC bug about "unused" parameter. -+ return -1; -+} -+# endif -+ -+template -+FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -+# if FMT_USE_NONTYPE_TEMPLATE_ARGS -+ if constexpr (sizeof...(Args) > 0) -+ return get_arg_index_by_name<0, Args...>(name); -+# endif -+ (void)name; -+ return -1; -+} -+ - template - constexpr int get_arg_index_by_name(basic_string_view name, - type_list) { -@@ -196,7 +126,8 @@ template struct code_unit { - - template - constexpr OutputIt format(OutputIt out, const Args&...) const { -- return write(out, value); -+ *out++ = value; -+ return out; - } - }; - -@@ -220,7 +151,13 @@ template struct field { - - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { -- return write(out, get_arg_checked(args...)); -+ const T& arg = get_arg_checked(args...); -+ if constexpr (std::is_convertible>::value) { -+ auto s = basic_string_view(arg); -+ return copy(s.begin(), s.end(), out); -+ } else { -+ return write(out, arg); -+ } - } - }; - -@@ -308,13 +245,12 @@ constexpr size_t parse_text(basic_string_view str, size_t pos) { - } - - template --constexpr auto compile_format_string(S format_str); -+constexpr auto compile_format_string(S fmt); - - template --constexpr auto parse_tail(T head, S format_str) { -- if constexpr (POS != -- basic_string_view(format_str).size()) { -- constexpr auto tail = compile_format_string(format_str); -+constexpr auto parse_tail(T head, S fmt) { -+ if constexpr (POS != basic_string_view(fmt).size()) { -+ constexpr auto tail = compile_format_string(fmt); - if constexpr (std::is_same, - unknown_format>()) - return tail; -@@ -346,6 +282,7 @@ constexpr parse_specs_result parse_specs(basic_string_view str, - } - - template struct arg_id_handler { -+ arg_id_kind kind; - arg_ref arg_id; - - constexpr int on_auto() { -@@ -353,25 +290,28 @@ template struct arg_id_handler { - return 0; - } - constexpr int on_index(int id) { -+ kind = arg_id_kind::index; - arg_id = arg_ref(id); - return 0; - } - constexpr int on_name(basic_string_view id) { -+ kind = arg_id_kind::name; - arg_id = arg_ref(id); - return 0; - } - }; - - template struct parse_arg_id_result { -+ arg_id_kind kind; - arg_ref arg_id; - const Char* arg_id_end; - }; - - template - constexpr auto parse_arg_id(const Char* begin, const Char* end) { -- auto handler = arg_id_handler{arg_ref{}}; -+ auto handler = arg_id_handler{arg_id_kind::none, arg_ref{}}; - auto arg_id_end = parse_arg_id(begin, end, handler); -- return parse_arg_id_result{handler.arg_id, arg_id_end}; -+ return parse_arg_id_result{handler.kind, handler.arg_id, arg_id_end}; - } - - template struct field_type { -@@ -385,14 +325,13 @@ struct field_type::value>> { - - template --constexpr auto parse_replacement_field_then_tail(S format_str) { -+constexpr auto parse_replacement_field_then_tail(S fmt) { - using char_type = typename S::char_type; -- constexpr auto str = basic_string_view(format_str); -+ constexpr auto str = basic_string_view(fmt); - constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); - if constexpr (c == '}') { - return parse_tail( -- field::type, ARG_INDEX>(), -- format_str); -+ field::type, ARG_INDEX>(), fmt); - } else if constexpr (c != ':') { - FMT_THROW(format_error("expected ':'")); - } else { -@@ -405,7 +344,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { - return parse_tail( - spec_field::type, ARG_INDEX>{ - result.fmt}, -- format_str); -+ fmt); - } - } - } -@@ -413,22 +352,21 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { - // Compiles a non-empty format string and returns the compiled representation - // or unknown_format() on unrecognized input. - template --constexpr auto compile_format_string(S format_str) { -+constexpr auto compile_format_string(S fmt) { - using char_type = typename S::char_type; -- constexpr auto str = basic_string_view(format_str); -+ constexpr auto str = basic_string_view(fmt); - if constexpr (str[POS] == '{') { - if constexpr (POS + 1 == str.size()) - FMT_THROW(format_error("unmatched '{' in format string")); - if constexpr (str[POS + 1] == '{') { -- return parse_tail(make_text(str, POS, 1), format_str); -+ return parse_tail(make_text(str, POS, 1), fmt); - } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { - static_assert(ID != manual_indexing_id, - "cannot switch from manual to automatic argument indexing"); - constexpr auto next_id = - ID != manual_indexing_id ? ID + 1 : manual_indexing_id; - return parse_replacement_field_then_tail, Args, -- POS + 1, ID, next_id>( -- format_str); -+ POS + 1, ID, next_id>(fmt); - } else { - constexpr auto arg_id_result = - parse_arg_id(str.data() + POS + 1, str.data() + str.size()); -@@ -436,28 +374,27 @@ constexpr auto compile_format_string(S format_str) { - constexpr char_type c = - arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); - static_assert(c == '}' || c == ':', "missing '}' in format string"); -- if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { -+ if constexpr (arg_id_result.kind == arg_id_kind::index) { - static_assert( - ID == manual_indexing_id || ID == 0, - "cannot switch from automatic to manual argument indexing"); -- constexpr auto arg_index = arg_id_result.arg_id.val.index; -+ constexpr auto arg_index = arg_id_result.arg_id.index; - return parse_replacement_field_then_tail, - Args, arg_id_end_pos, - arg_index, manual_indexing_id>( -- format_str); -- } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { -+ fmt); -+ } else if constexpr (arg_id_result.kind == arg_id_kind::name) { - constexpr auto arg_index = -- get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); -+ get_arg_index_by_name(arg_id_result.arg_id.name, Args{}); - if constexpr (arg_index >= 0) { - constexpr auto next_id = - ID != manual_indexing_id ? ID + 1 : manual_indexing_id; - return parse_replacement_field_then_tail< - decltype(get_type::value), Args, arg_id_end_pos, -- arg_index, next_id>(format_str); -+ arg_index, next_id>(fmt); - } else if constexpr (c == '}') { - return parse_tail( -- runtime_named_field{arg_id_result.arg_id.val.name}, -- format_str); -+ runtime_named_field{arg_id_result.arg_id.name}, fmt); - } else if constexpr (c == ':') { - return unknown_format(); // no type info for specs parsing - } -@@ -466,29 +403,26 @@ constexpr auto compile_format_string(S format_str) { - } else if constexpr (str[POS] == '}') { - if constexpr (POS + 1 == str.size()) - FMT_THROW(format_error("unmatched '}' in format string")); -- return parse_tail(make_text(str, POS, 1), format_str); -+ return parse_tail(make_text(str, POS, 1), fmt); - } else { - constexpr auto end = parse_text(str, POS + 1); - if constexpr (end - POS > 1) { -- return parse_tail(make_text(str, POS, end - POS), -- format_str); -+ return parse_tail(make_text(str, POS, end - POS), fmt); - } else { -- return parse_tail(code_unit{str[POS]}, -- format_str); -+ return parse_tail(code_unit{str[POS]}, fmt); - } - } - } - - template ::value)> --constexpr auto compile(S format_str) { -- constexpr auto str = basic_string_view(format_str); -+ FMT_ENABLE_IF(is_compiled_string::value)> -+constexpr auto compile(S fmt) { -+ constexpr auto str = basic_string_view(fmt); - if constexpr (str.size() == 0) { - return detail::make_text(str, 0, 0); - } else { - constexpr auto result = -- detail::compile_format_string, 0, 0>( -- format_str); -+ detail::compile_format_string, 0, 0>(fmt); - return result; - } - } -@@ -517,7 +451,7 @@ constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, - } - - template ::value)> -+ FMT_ENABLE_IF(is_compiled_string::value)> - FMT_INLINE std::basic_string format(const S&, - Args&&... args) { - if constexpr (std::is_same::value) { -@@ -544,7 +478,7 @@ FMT_INLINE std::basic_string format(const S&, - } - - template ::value)> -+ FMT_ENABLE_IF(is_compiled_string::value)> - FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { - constexpr auto compiled = detail::compile(S()); - if constexpr (std::is_same, -@@ -559,42 +493,42 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { - #endif - - template ::value)> --format_to_n_result format_to_n(OutputIt out, size_t n, -- const S& format_str, Args&&... args) { -- auto it = fmt::format_to(detail::truncating_iterator(out, n), -- format_str, std::forward(args)...); -- return {it.base(), it.count()}; -+ FMT_ENABLE_IF(is_compiled_string::value)> -+auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) -+ -> format_to_n_result { -+ using traits = detail::fixed_buffer_traits; -+ auto buf = detail::iterator_buffer(out, n); -+ fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); -+ return {buf.out(), buf.count()}; - } - - template ::value)> --FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, -- const Args&... args) { -- return fmt::format_to(detail::counting_iterator(), format_str, args...) -- .count(); -+ FMT_ENABLE_IF(is_compiled_string::value)> -+FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) -+ -> size_t { -+ auto buf = detail::counting_buffer<>(); -+ fmt::format_to(appender(buf), fmt, args...); -+ return buf.count(); - } - - template ::value)> --void print(std::FILE* f, const S& format_str, const Args&... args) { -- memory_buffer buffer; -- fmt::format_to(std::back_inserter(buffer), format_str, args...); -- detail::print(f, {buffer.data(), buffer.size()}); -+ FMT_ENABLE_IF(is_compiled_string::value)> -+void print(std::FILE* f, const S& fmt, const Args&... args) { -+ auto buf = memory_buffer(); -+ fmt::format_to(appender(buf), fmt, args...); -+ detail::print(f, {buf.data(), buf.size()}); - } - - template ::value)> --void print(const S& format_str, const Args&... args) { -- print(stdout, format_str, args...); -+ FMT_ENABLE_IF(is_compiled_string::value)> -+void print(const S& fmt, const Args&... args) { -+ print(stdout, fmt, args...); - } - - #if FMT_USE_NONTYPE_TEMPLATE_ARGS - inline namespace literals { --template constexpr auto operator""_cf() { -- using char_t = remove_cvref_t; -- return detail::udl_compiled_string(); -+template constexpr auto operator""_cf() { -+ return FMT_COMPILE(Str.data); - } - } // namespace literals - #endif -diff --git a/include/fmt/core.h b/include/fmt/core.h -index 4c31964..8ca735f 100644 ---- a/include/fmt/core.h -+++ b/include/fmt/core.h -@@ -1,2905 +1,5 @@ --// Formatting library for C++ - the core API for char/UTF-8 --// --// Copyright (c) 2012 - present, Victor Zverovich --// All rights reserved. --// --// For the license information refer to format.h. -+// This file is only provided for compatibility and may be removed in future -+// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h -+// otherwise. - --#ifndef FMT_CORE_H_ --#define FMT_CORE_H_ -- --#include // std::byte --#include // std::FILE --#include // std::strlen --#include --#include --#include --#include -- --// The fmt library version in the form major * 10000 + minor * 100 + patch. --#define FMT_VERSION 100001 -- --#if defined(__clang__) && !defined(__ibmxl__) --# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) --#else --# define FMT_CLANG_VERSION 0 --#endif -- --#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ -- !defined(__NVCOMPILER) --# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) --#else --# define FMT_GCC_VERSION 0 --#endif -- --#ifndef FMT_GCC_PRAGMA --// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. --# if FMT_GCC_VERSION >= 504 --# define FMT_GCC_PRAGMA(arg) _Pragma(arg) --# else --# define FMT_GCC_PRAGMA(arg) --# endif --#endif -- --#ifdef __ICL --# define FMT_ICC_VERSION __ICL --#elif defined(__INTEL_COMPILER) --# define FMT_ICC_VERSION __INTEL_COMPILER --#else --# define FMT_ICC_VERSION 0 --#endif -- --#ifdef _MSC_VER --# define FMT_MSC_VERSION _MSC_VER --# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) --#else --# define FMT_MSC_VERSION 0 --# define FMT_MSC_WARNING(...) --#endif -- --#ifdef _MSVC_LANG --# define FMT_CPLUSPLUS _MSVC_LANG --#else --# define FMT_CPLUSPLUS __cplusplus --#endif -- --#ifdef __has_feature --# define FMT_HAS_FEATURE(x) __has_feature(x) --#else --# define FMT_HAS_FEATURE(x) 0 --#endif -- --#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900 --# define FMT_HAS_INCLUDE(x) __has_include(x) --#else --# define FMT_HAS_INCLUDE(x) 0 --#endif -- --#ifdef __has_cpp_attribute --# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) --#else --# define FMT_HAS_CPP_ATTRIBUTE(x) 0 --#endif -- --#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ -- (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) -- --#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ -- (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) -- --// Check if relaxed C++14 constexpr is supported. --// GCC doesn't allow throw in constexpr until version 6 (bug 67371). --#ifndef FMT_USE_CONSTEXPR --# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ -- (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ -- !FMT_ICC_VERSION && !defined(__NVCC__) --# define FMT_USE_CONSTEXPR 1 --# else --# define FMT_USE_CONSTEXPR 0 --# endif --#endif --#if FMT_USE_CONSTEXPR --# define FMT_CONSTEXPR constexpr --#else --# define FMT_CONSTEXPR --#endif -- --#if ((FMT_CPLUSPLUS >= 202002L) && \ -- (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ -- (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) --# define FMT_CONSTEXPR20 constexpr --#else --# define FMT_CONSTEXPR20 --#endif -- --// Check if constexpr std::char_traits<>::{compare,length} are supported. --#if defined(__GLIBCXX__) --# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ -- _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. --# define FMT_CONSTEXPR_CHAR_TRAITS constexpr --# endif --#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ -- _LIBCPP_VERSION >= 4000 --# define FMT_CONSTEXPR_CHAR_TRAITS constexpr --#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L --# define FMT_CONSTEXPR_CHAR_TRAITS constexpr --#endif --#ifndef FMT_CONSTEXPR_CHAR_TRAITS --# define FMT_CONSTEXPR_CHAR_TRAITS --#endif -- --// Check if exceptions are disabled. --#ifndef FMT_EXCEPTIONS --# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ -- (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) --# define FMT_EXCEPTIONS 0 --# else --# define FMT_EXCEPTIONS 1 --# endif --#endif -- --// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. --#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ -- !defined(__NVCC__) --# define FMT_NORETURN [[noreturn]] --#else --# define FMT_NORETURN --#endif -- --#ifndef FMT_NODISCARD --# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) --# define FMT_NODISCARD [[nodiscard]] --# else --# define FMT_NODISCARD --# endif --#endif -- --#ifndef FMT_INLINE --# if FMT_GCC_VERSION || FMT_CLANG_VERSION --# define FMT_INLINE inline __attribute__((always_inline)) --# else --# define FMT_INLINE inline --# endif --#endif -- --#ifdef _MSC_VER --# define FMT_UNCHECKED_ITERATOR(It) \ -- using _Unchecked_type = It // Mark iterator as checked. --#else --# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It --#endif -- --#ifndef FMT_BEGIN_NAMESPACE --# define FMT_BEGIN_NAMESPACE \ -- namespace fmt { \ -- inline namespace v10 { --# define FMT_END_NAMESPACE \ -- } \ -- } --#endif -- --#ifndef FMT_EXPORT --# define FMT_EXPORT --# define FMT_BEGIN_EXPORT --# define FMT_END_EXPORT --#endif -- --#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) --# ifdef FMT_LIB_EXPORT --# define FMT_API __declspec(dllexport) --# elif defined(FMT_SHARED) --# define FMT_API __declspec(dllimport) --# endif --#else --# if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) --# if defined(__GNUC__) || defined(__clang__) --# define FMT_API __attribute__((visibility("default"))) --# endif --# endif --#endif --#ifndef FMT_API --# define FMT_API --#endif -- --// libc++ supports string_view in pre-c++17. --#if FMT_HAS_INCLUDE() && \ -- (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) --# include --# define FMT_USE_STRING_VIEW --#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L --# include --# define FMT_USE_EXPERIMENTAL_STRING_VIEW --#endif -- --#ifndef FMT_UNICODE --# define FMT_UNICODE !FMT_MSC_VERSION --#endif -- --#ifndef FMT_CONSTEVAL --# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ -- (!defined(__apple_build_version__) || \ -- __apple_build_version__ >= 14000029L) && \ -- FMT_CPLUSPLUS >= 202002L) || \ -- (defined(__cpp_consteval) && \ -- (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) --// consteval is broken in MSVC before VS2022 and Apple clang before 14. --# define FMT_CONSTEVAL consteval --# define FMT_HAS_CONSTEVAL --# else --# define FMT_CONSTEVAL --# endif --#endif -- --#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS --# if defined(__cpp_nontype_template_args) && \ -- ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ -- __cpp_nontype_template_args >= 201911L) && \ -- !defined(__NVCOMPILER) && !defined(__LCC__) --# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 --# else --# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 --# endif --#endif -- --// Enable minimal optimizations for more compact code in debug mode. --FMT_GCC_PRAGMA("GCC push_options") --#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ -- !defined(__CUDACC__) --FMT_GCC_PRAGMA("GCC optimize(\"Og\")") --#endif -- --FMT_BEGIN_NAMESPACE -- --// Implementations of enable_if_t and other metafunctions for older systems. --template --using enable_if_t = typename std::enable_if::type; --template --using conditional_t = typename std::conditional::type; --template using bool_constant = std::integral_constant; --template --using remove_reference_t = typename std::remove_reference::type; --template --using remove_const_t = typename std::remove_const::type; --template --using remove_cvref_t = typename std::remove_cv>::type; --template struct type_identity { using type = T; }; --template using type_identity_t = typename type_identity::type; --template --using underlying_t = typename std::underlying_type::type; -- --// Checks whether T is a container with contiguous storage. --template struct is_contiguous : std::false_type {}; --template --struct is_contiguous> : std::true_type {}; -- --struct monostate { -- constexpr monostate() {} --}; -- --// An enable_if helper to be used in template parameters which results in much --// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed --// to workaround a bug in MSVC 2019 (see #1140 and #1186). --#ifdef FMT_DOC --# define FMT_ENABLE_IF(...) --#else --# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 --#endif -- --// This is defined in core.h instead of format.h to avoid injecting in std. --#ifdef __cpp_lib_byte --inline auto format_as(std::byte b) -> unsigned char { -- return static_cast(b); --} --#endif -- --namespace detail { --// Suppresses "unused variable" warnings with the method described in --// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. --// (void)var does not work on many Intel compilers. --template FMT_CONSTEXPR void ignore_unused(const T&...) {} -- --constexpr FMT_INLINE auto is_constant_evaluated( -- bool default_value = false) noexcept -> bool { --// Workaround for incompatibility between libstdc++ consteval-based --// std::is_constant_evaluated() implementation and clang-14. --// https://github.com/fmtlib/fmt/issues/3247 --#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \ -- _GLIBCXX_RELEASE >= 12 && \ -- (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) -- ignore_unused(default_value); -- return __builtin_is_constant_evaluated(); --#elif defined(__cpp_lib_is_constant_evaluated) -- ignore_unused(default_value); -- return std::is_constant_evaluated(); --#else -- return default_value; --#endif --} -- --// Suppresses "conditional expression is constant" warnings. --template constexpr FMT_INLINE auto const_check(T value) -> T { -- return value; --} -- --FMT_NORETURN FMT_API void assert_fail(const char* file, int line, -- const char* message); -- --#ifndef FMT_ASSERT --# ifdef NDEBUG --// FMT_ASSERT is not empty to avoid -Wempty-body. --# define FMT_ASSERT(condition, message) \ -- fmt::detail::ignore_unused((condition), (message)) --# else --# define FMT_ASSERT(condition, message) \ -- ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ -- ? (void)0 \ -- : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) --# endif --#endif -- --#if defined(FMT_USE_STRING_VIEW) --template using std_string_view = std::basic_string_view; --#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) --template --using std_string_view = std::experimental::basic_string_view; --#else --template struct std_string_view {}; --#endif -- --#ifdef FMT_USE_INT128 --// Do nothing. --#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ -- !(FMT_CLANG_VERSION && FMT_MSC_VERSION) --# define FMT_USE_INT128 1 --using int128_opt = __int128_t; // An optional native 128-bit integer. --using uint128_opt = __uint128_t; --template inline auto convert_for_visit(T value) -> T { -- return value; --} --#else --# define FMT_USE_INT128 0 --#endif --#if !FMT_USE_INT128 --enum class int128_opt {}; --enum class uint128_opt {}; --// Reduce template instantiations. --template auto convert_for_visit(T) -> monostate { return {}; } --#endif -- --// Casts a nonnegative integer to unsigned. --template --FMT_CONSTEXPR auto to_unsigned(Int value) -> -- typename std::make_unsigned::type { -- FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); -- return static_cast::type>(value); --} -- --FMT_CONSTEXPR inline auto is_utf8() -> bool { -- FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; -- -- // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). -- using uchar = unsigned char; -- return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && -- uchar(section[1]) == 0xA7); --} --} // namespace detail -- --/** -- An implementation of ``std::basic_string_view`` for pre-C++17. It provides a -- subset of the API. ``fmt::basic_string_view`` is used for format strings even -- if ``std::string_view`` is available to prevent issues when a library is -- compiled with a different ``-std`` option than the client code (which is not -- recommended). -- */ --FMT_EXPORT --template class basic_string_view { -- private: -- const Char* data_; -- size_t size_; -- -- public: -- using value_type = Char; -- using iterator = const Char*; -- -- constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} -- -- /** Constructs a string reference object from a C string and a size. */ -- constexpr basic_string_view(const Char* s, size_t count) noexcept -- : data_(s), size_(count) {} -- -- /** -- \rst -- Constructs a string reference object from a C string computing -- the size with ``std::char_traits::length``. -- \endrst -- */ -- FMT_CONSTEXPR_CHAR_TRAITS -- FMT_INLINE -- basic_string_view(const Char* s) -- : data_(s), -- size_(detail::const_check(std::is_same::value && -- !detail::is_constant_evaluated(true)) -- ? std::strlen(reinterpret_cast(s)) -- : std::char_traits::length(s)) {} -- -- /** Constructs a string reference from a ``std::basic_string`` object. */ -- template -- FMT_CONSTEXPR basic_string_view( -- const std::basic_string& s) noexcept -- : data_(s.data()), size_(s.size()) {} -- -- template >::value)> -- FMT_CONSTEXPR basic_string_view(S s) noexcept -- : data_(s.data()), size_(s.size()) {} -- -- /** Returns a pointer to the string data. */ -- constexpr auto data() const noexcept -> const Char* { return data_; } -- -- /** Returns the string size. */ -- constexpr auto size() const noexcept -> size_t { return size_; } -- -- constexpr auto begin() const noexcept -> iterator { return data_; } -- constexpr auto end() const noexcept -> iterator { return data_ + size_; } -- -- constexpr auto operator[](size_t pos) const noexcept -> const Char& { -- return data_[pos]; -- } -- -- FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { -- data_ += n; -- size_ -= n; -- } -- -- FMT_CONSTEXPR_CHAR_TRAITS bool starts_with( -- basic_string_view sv) const noexcept { -- return size_ >= sv.size_ && -- std::char_traits::compare(data_, sv.data_, sv.size_) == 0; -- } -- FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept { -- return size_ >= 1 && std::char_traits::eq(*data_, c); -- } -- FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const { -- return starts_with(basic_string_view(s)); -- } -- -- // Lexicographically compare this string reference to other. -- FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { -- size_t str_size = size_ < other.size_ ? size_ : other.size_; -- int result = std::char_traits::compare(data_, other.data_, str_size); -- if (result == 0) -- result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); -- return result; -- } -- -- FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, -- basic_string_view rhs) -- -> bool { -- return lhs.compare(rhs) == 0; -- } -- friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { -- return lhs.compare(rhs) != 0; -- } -- friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { -- return lhs.compare(rhs) < 0; -- } -- friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { -- return lhs.compare(rhs) <= 0; -- } -- friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { -- return lhs.compare(rhs) > 0; -- } -- friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { -- return lhs.compare(rhs) >= 0; -- } --}; -- --FMT_EXPORT --using string_view = basic_string_view; -- --/** Specifies if ``T`` is a character type. Can be specialized by users. */ --FMT_EXPORT --template struct is_char : std::false_type {}; --template <> struct is_char : std::true_type {}; -- --namespace detail { -- --// A base class for compile-time strings. --struct compile_string {}; -- --template --struct is_compile_string : std::is_base_of {}; -- --template ::value)> --FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { -- return s; --} --template --inline auto to_string_view(const std::basic_string& s) -- -> basic_string_view { -- return s; --} --template --constexpr auto to_string_view(basic_string_view s) -- -> basic_string_view { -- return s; --} --template >::value)> --inline auto to_string_view(std_string_view s) -> basic_string_view { -- return s; --} --template ::value)> --constexpr auto to_string_view(const S& s) -- -> basic_string_view { -- return basic_string_view(s); --} --void to_string_view(...); -- --// Specifies whether S is a string type convertible to fmt::basic_string_view. --// It should be a constexpr function but MSVC 2017 fails to compile it in --// enable_if and MSVC 2015 fails to compile it as an alias template. --// ADL is intentionally disabled as to_string_view is not an extension point. --template --struct is_string -- : std::is_class()))> {}; -- --template struct char_t_impl {}; --template struct char_t_impl::value>> { -- using result = decltype(to_string_view(std::declval())); -- using type = typename result::value_type; --}; -- --enum class type { -- none_type, -- // Integer types should go first, -- int_type, -- uint_type, -- long_long_type, -- ulong_long_type, -- int128_type, -- uint128_type, -- bool_type, -- char_type, -- last_integer_type = char_type, -- // followed by floating-point types. -- float_type, -- double_type, -- long_double_type, -- last_numeric_type = long_double_type, -- cstring_type, -- string_type, -- pointer_type, -- custom_type --}; -- --// Maps core type T to the corresponding type enum constant. --template --struct type_constant : std::integral_constant {}; -- --#define FMT_TYPE_CONSTANT(Type, constant) \ -- template \ -- struct type_constant \ -- : std::integral_constant {} -- --FMT_TYPE_CONSTANT(int, int_type); --FMT_TYPE_CONSTANT(unsigned, uint_type); --FMT_TYPE_CONSTANT(long long, long_long_type); --FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); --FMT_TYPE_CONSTANT(int128_opt, int128_type); --FMT_TYPE_CONSTANT(uint128_opt, uint128_type); --FMT_TYPE_CONSTANT(bool, bool_type); --FMT_TYPE_CONSTANT(Char, char_type); --FMT_TYPE_CONSTANT(float, float_type); --FMT_TYPE_CONSTANT(double, double_type); --FMT_TYPE_CONSTANT(long double, long_double_type); --FMT_TYPE_CONSTANT(const Char*, cstring_type); --FMT_TYPE_CONSTANT(basic_string_view, string_type); --FMT_TYPE_CONSTANT(const void*, pointer_type); -- --constexpr bool is_integral_type(type t) { -- return t > type::none_type && t <= type::last_integer_type; --} --constexpr bool is_arithmetic_type(type t) { -- return t > type::none_type && t <= type::last_numeric_type; --} -- --constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } --constexpr auto in(type t, int set) -> bool { -- return ((set >> static_cast(t)) & 1) != 0; --} -- --// Bitsets of types. --enum { -- sint_set = -- set(type::int_type) | set(type::long_long_type) | set(type::int128_type), -- uint_set = set(type::uint_type) | set(type::ulong_long_type) | -- set(type::uint128_type), -- bool_set = set(type::bool_type), -- char_set = set(type::char_type), -- float_set = set(type::float_type) | set(type::double_type) | -- set(type::long_double_type), -- string_set = set(type::string_type), -- cstring_set = set(type::cstring_type), -- pointer_set = set(type::pointer_type) --}; -- --FMT_NORETURN FMT_API void throw_format_error(const char* message); -- --struct error_handler { -- constexpr error_handler() = default; -- -- // This function is intentionally not constexpr to give a compile-time error. -- FMT_NORETURN void on_error(const char* message) { -- throw_format_error(message); -- } --}; --} // namespace detail -- --/** String's character type. */ --template using char_t = typename detail::char_t_impl::type; -- --/** -- \rst -- Parsing context consisting of a format string range being parsed and an -- argument counter for automatic indexing. -- You can use the ``format_parse_context`` type alias for ``char`` instead. -- \endrst -- */ --FMT_EXPORT --template class basic_format_parse_context { -- private: -- basic_string_view format_str_; -- int next_arg_id_; -- -- FMT_CONSTEXPR void do_check_arg_id(int id); -- -- public: -- using char_type = Char; -- using iterator = const Char*; -- -- explicit constexpr basic_format_parse_context( -- basic_string_view format_str, int next_arg_id = 0) -- : format_str_(format_str), next_arg_id_(next_arg_id) {} -- -- /** -- Returns an iterator to the beginning of the format string range being -- parsed. -- */ -- constexpr auto begin() const noexcept -> iterator { -- return format_str_.begin(); -- } -- -- /** -- Returns an iterator past the end of the format string range being parsed. -- */ -- constexpr auto end() const noexcept -> iterator { return format_str_.end(); } -- -- /** Advances the begin iterator to ``it``. */ -- FMT_CONSTEXPR void advance_to(iterator it) { -- format_str_.remove_prefix(detail::to_unsigned(it - begin())); -- } -- -- /** -- Reports an error if using the manual argument indexing; otherwise returns -- the next argument index and switches to the automatic indexing. -- */ -- FMT_CONSTEXPR auto next_arg_id() -> int { -- if (next_arg_id_ < 0) { -- detail::throw_format_error( -- "cannot switch from manual to automatic argument indexing"); -- return 0; -- } -- int id = next_arg_id_++; -- do_check_arg_id(id); -- return id; -- } -- -- /** -- Reports an error if using the automatic argument indexing; otherwise -- switches to the manual indexing. -- */ -- FMT_CONSTEXPR void check_arg_id(int id) { -- if (next_arg_id_ > 0) { -- detail::throw_format_error( -- "cannot switch from automatic to manual argument indexing"); -- return; -- } -- next_arg_id_ = -1; -- do_check_arg_id(id); -- } -- FMT_CONSTEXPR void check_arg_id(basic_string_view) {} -- FMT_CONSTEXPR void check_dynamic_spec(int arg_id); --}; -- --FMT_EXPORT --using format_parse_context = basic_format_parse_context; -- --namespace detail { --// A parse context with extra data used only in compile-time checks. --template --class compile_parse_context : public basic_format_parse_context { -- private: -- int num_args_; -- const type* types_; -- using base = basic_format_parse_context; -- -- public: -- explicit FMT_CONSTEXPR compile_parse_context( -- basic_string_view format_str, int num_args, const type* types, -- int next_arg_id = 0) -- : base(format_str, next_arg_id), num_args_(num_args), types_(types) {} -- -- constexpr auto num_args() const -> int { return num_args_; } -- constexpr auto arg_type(int id) const -> type { return types_[id]; } -- -- FMT_CONSTEXPR auto next_arg_id() -> int { -- int id = base::next_arg_id(); -- if (id >= num_args_) throw_format_error("argument not found"); -- return id; -- } -- -- FMT_CONSTEXPR void check_arg_id(int id) { -- base::check_arg_id(id); -- if (id >= num_args_) throw_format_error("argument not found"); -- } -- using base::check_arg_id; -- -- FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { -- detail::ignore_unused(arg_id); --#if !defined(__LCC__) -- if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) -- throw_format_error("width/precision is not integer"); --#endif -- } --}; -- --// Extracts a reference to the container from back_insert_iterator. --template --inline auto get_container(std::back_insert_iterator it) -- -> Container& { -- using base = std::back_insert_iterator; -- struct accessor : base { -- accessor(base b) : base(b) {} -- using base::container; -- }; -- return *accessor(it).container; --} -- --template --FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) -- -> OutputIt { -- while (begin != end) *out++ = static_cast(*begin++); -- return out; --} -- --template , U>::value&& is_char::value)> --FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { -- if (is_constant_evaluated()) return copy_str(begin, end, out); -- auto size = to_unsigned(end - begin); -- if (size > 0) memcpy(out, begin, size * sizeof(U)); -- return out + size; --} -- --/** -- \rst -- A contiguous memory buffer with an optional growing ability. It is an internal -- class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. -- \endrst -- */ --template class buffer { -- private: -- T* ptr_; -- size_t size_; -- size_t capacity_; -- -- protected: -- // Don't initialize ptr_ since it is not accessed to save a few cycles. -- FMT_MSC_WARNING(suppress : 26495) -- buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} -- -- FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept -- : ptr_(p), size_(sz), capacity_(cap) {} -- -- FMT_CONSTEXPR20 ~buffer() = default; -- buffer(buffer&&) = default; -- -- /** Sets the buffer data and capacity. */ -- FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { -- ptr_ = buf_data; -- capacity_ = buf_capacity; -- } -- -- /** Increases the buffer capacity to hold at least *capacity* elements. */ -- virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; -- -- public: -- using value_type = T; -- using const_reference = const T&; -- -- buffer(const buffer&) = delete; -- void operator=(const buffer&) = delete; -- -- FMT_INLINE auto begin() noexcept -> T* { return ptr_; } -- FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; } -- -- FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; } -- FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; } -- -- /** Returns the size of this buffer. */ -- constexpr auto size() const noexcept -> size_t { return size_; } -- -- /** Returns the capacity of this buffer. */ -- constexpr auto capacity() const noexcept -> size_t { return capacity_; } -- -- /** Returns a pointer to the buffer data. */ -- FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } -- -- /** Returns a pointer to the buffer data. */ -- FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } -- -- /** Clears this buffer. */ -- void clear() { size_ = 0; } -- -- // Tries resizing the buffer to contain *count* elements. If T is a POD type -- // the new elements may not be initialized. -- FMT_CONSTEXPR20 void try_resize(size_t count) { -- try_reserve(count); -- size_ = count <= capacity_ ? count : capacity_; -- } -- -- // Tries increasing the buffer capacity to *new_capacity*. It can increase the -- // capacity by a smaller amount than requested but guarantees there is space -- // for at least one additional element either by increasing the capacity or by -- // flushing the buffer if it is full. -- FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { -- if (new_capacity > capacity_) grow(new_capacity); -- } -- -- FMT_CONSTEXPR20 void push_back(const T& value) { -- try_reserve(size_ + 1); -- ptr_[size_++] = value; -- } -- -- /** Appends data to the end of the buffer. */ -- template void append(const U* begin, const U* end); -- -- template FMT_CONSTEXPR auto operator[](Idx index) -> T& { -- return ptr_[index]; -- } -- template -- FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { -- return ptr_[index]; -- } --}; -- --struct buffer_traits { -- explicit buffer_traits(size_t) {} -- auto count() const -> size_t { return 0; } -- auto limit(size_t size) -> size_t { return size; } --}; -- --class fixed_buffer_traits { -- private: -- size_t count_ = 0; -- size_t limit_; -- -- public: -- explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} -- auto count() const -> size_t { return count_; } -- auto limit(size_t size) -> size_t { -- size_t n = limit_ > count_ ? limit_ - count_ : 0; -- count_ += size; -- return size < n ? size : n; -- } --}; -- --// A buffer that writes to an output iterator when flushed. --template --class iterator_buffer final : public Traits, public buffer { -- private: -- OutputIt out_; -- enum { buffer_size = 256 }; -- T data_[buffer_size]; -- -- protected: -- FMT_CONSTEXPR20 void grow(size_t) override { -- if (this->size() == buffer_size) flush(); -- } -- -- void flush() { -- auto size = this->size(); -- this->clear(); -- out_ = copy_str(data_, data_ + this->limit(size), out_); -- } -- -- public: -- explicit iterator_buffer(OutputIt out, size_t n = buffer_size) -- : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} -- iterator_buffer(iterator_buffer&& other) -- : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} -- ~iterator_buffer() { flush(); } -- -- auto out() -> OutputIt { -- flush(); -- return out_; -- } -- auto count() const -> size_t { return Traits::count() + this->size(); } --}; -- --template --class iterator_buffer final -- : public fixed_buffer_traits, -- public buffer { -- private: -- T* out_; -- enum { buffer_size = 256 }; -- T data_[buffer_size]; -- -- protected: -- FMT_CONSTEXPR20 void grow(size_t) override { -- if (this->size() == this->capacity()) flush(); -- } -- -- void flush() { -- size_t n = this->limit(this->size()); -- if (this->data() == out_) { -- out_ += n; -- this->set(data_, buffer_size); -- } -- this->clear(); -- } -- -- public: -- explicit iterator_buffer(T* out, size_t n = buffer_size) -- : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} -- iterator_buffer(iterator_buffer&& other) -- : fixed_buffer_traits(other), -- buffer(std::move(other)), -- out_(other.out_) { -- if (this->data() != out_) { -- this->set(data_, buffer_size); -- this->clear(); -- } -- } -- ~iterator_buffer() { flush(); } -- -- auto out() -> T* { -- flush(); -- return out_; -- } -- auto count() const -> size_t { -- return fixed_buffer_traits::count() + this->size(); -- } --}; -- --template class iterator_buffer final : public buffer { -- protected: -- FMT_CONSTEXPR20 void grow(size_t) override {} -- -- public: -- explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} -- -- auto out() -> T* { return &*this->end(); } --}; -- --// A buffer that writes to a container with the contiguous storage. --template --class iterator_buffer, -- enable_if_t::value, -- typename Container::value_type>> -- final : public buffer { -- private: -- Container& container_; -- -- protected: -- FMT_CONSTEXPR20 void grow(size_t capacity) override { -- container_.resize(capacity); -- this->set(&container_[0], capacity); -- } -- -- public: -- explicit iterator_buffer(Container& c) -- : buffer(c.size()), container_(c) {} -- explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) -- : iterator_buffer(get_container(out)) {} -- -- auto out() -> std::back_insert_iterator { -- return std::back_inserter(container_); -- } --}; -- --// A buffer that counts the number of code units written discarding the output. --template class counting_buffer final : public buffer { -- private: -- enum { buffer_size = 256 }; -- T data_[buffer_size]; -- size_t count_ = 0; -- -- protected: -- FMT_CONSTEXPR20 void grow(size_t) override { -- if (this->size() != buffer_size) return; -- count_ += this->size(); -- this->clear(); -- } -- -- public: -- counting_buffer() : buffer(data_, 0, buffer_size) {} -- -- auto count() -> size_t { return count_ + this->size(); } --}; --} // namespace detail -- --template --FMT_CONSTEXPR void basic_format_parse_context::do_check_arg_id(int id) { -- // Argument id is only checked at compile-time during parsing because -- // formatting has its own validation. -- if (detail::is_constant_evaluated() && -- (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { -- using context = detail::compile_parse_context; -- if (id >= static_cast(this)->num_args()) -- detail::throw_format_error("argument not found"); -- } --} -- --template --FMT_CONSTEXPR void basic_format_parse_context::check_dynamic_spec( -- int arg_id) { -- if (detail::is_constant_evaluated() && -- (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { -- using context = detail::compile_parse_context; -- static_cast(this)->check_dynamic_spec(arg_id); -- } --} -- --FMT_EXPORT template class basic_format_arg; --FMT_EXPORT template class basic_format_args; --FMT_EXPORT template class dynamic_format_arg_store; -- --// A formatter for objects of type T. --FMT_EXPORT --template --struct formatter { -- // A deleted default constructor indicates a disabled formatter. -- formatter() = delete; --}; -- --// Specifies if T has an enabled formatter specialization. A type can be --// formattable even if it doesn't have a formatter e.g. via a conversion. --template --using has_formatter = -- std::is_constructible>; -- --// An output iterator that appends to a buffer. --// It is used to reduce symbol sizes for the common case. --class appender : public std::back_insert_iterator> { -- using base = std::back_insert_iterator>; -- -- public: -- using std::back_insert_iterator>::back_insert_iterator; -- appender(base it) noexcept : base(it) {} -- FMT_UNCHECKED_ITERATOR(appender); -- -- auto operator++() noexcept -> appender& { return *this; } -- auto operator++(int) noexcept -> appender { return *this; } --}; -- --namespace detail { -- --template --constexpr auto has_const_formatter_impl(T*) -- -> decltype(typename Context::template formatter_type().format( -- std::declval(), std::declval()), -- true) { -- return true; --} --template --constexpr auto has_const_formatter_impl(...) -> bool { -- return false; --} --template --constexpr auto has_const_formatter() -> bool { -- return has_const_formatter_impl(static_cast(nullptr)); --} -- --template --using buffer_appender = conditional_t::value, appender, -- std::back_insert_iterator>>; -- --// Maps an output iterator to a buffer. --template --auto get_buffer(OutputIt out) -> iterator_buffer { -- return iterator_buffer(out); --} --template , Buf>::value)> --auto get_buffer(std::back_insert_iterator out) -> buffer& { -- return get_container(out); --} -- --template --FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { -- return buf.out(); --} --template --auto get_iterator(buffer&, OutputIt out) -> OutputIt { -- return out; --} -- --struct view {}; -- --template struct named_arg : view { -- const Char* name; -- const T& value; -- named_arg(const Char* n, const T& v) : name(n), value(v) {} --}; -- --template struct named_arg_info { -- const Char* name; -- int id; --}; -- --template --struct arg_data { -- // args_[0].named_args points to named_args_ to avoid bloating format_args. -- // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. -- T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)]; -- named_arg_info named_args_[NUM_NAMED_ARGS]; -- -- template -- arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} -- arg_data(const arg_data& other) = delete; -- auto args() const -> const T* { return args_ + 1; } -- auto named_args() -> named_arg_info* { return named_args_; } --}; -- --template --struct arg_data { -- // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. -- T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; -- -- template -- FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} -- FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } -- FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { -- return nullptr; -- } --}; -- --template --inline void init_named_args(named_arg_info*, int, int) {} -- --template struct is_named_arg : std::false_type {}; --template struct is_statically_named_arg : std::false_type {}; -- --template --struct is_named_arg> : std::true_type {}; -- --template ::value)> --void init_named_args(named_arg_info* named_args, int arg_count, -- int named_arg_count, const T&, const Tail&... args) { -- init_named_args(named_args, arg_count + 1, named_arg_count, args...); --} -- --template ::value)> --void init_named_args(named_arg_info* named_args, int arg_count, -- int named_arg_count, const T& arg, const Tail&... args) { -- named_args[named_arg_count++] = {arg.name, arg_count}; -- init_named_args(named_args, arg_count + 1, named_arg_count, args...); --} -- --template --FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, -- const Args&...) {} -- --template constexpr auto count() -> size_t { return B ? 1 : 0; } --template constexpr auto count() -> size_t { -- return (B1 ? 1 : 0) + count(); --} -- --template constexpr auto count_named_args() -> size_t { -- return count::value...>(); --} -- --template --constexpr auto count_statically_named_args() -> size_t { -- return count::value...>(); --} -- --struct unformattable {}; --struct unformattable_char : unformattable {}; --struct unformattable_pointer : unformattable {}; -- --template struct string_value { -- const Char* data; -- size_t size; --}; -- --template struct named_arg_value { -- const named_arg_info* data; -- size_t size; --}; -- --template struct custom_value { -- using parse_context = typename Context::parse_context_type; -- void* value; -- void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); --}; -- --// A formatting argument value. --template class value { -- public: -- using char_type = typename Context::char_type; -- -- union { -- monostate no_value; -- int int_value; -- unsigned uint_value; -- long long long_long_value; -- unsigned long long ulong_long_value; -- int128_opt int128_value; -- uint128_opt uint128_value; -- bool bool_value; -- char_type char_value; -- float float_value; -- double double_value; -- long double long_double_value; -- const void* pointer; -- string_value string; -- custom_value custom; -- named_arg_value named_args; -- }; -- -- constexpr FMT_INLINE value() : no_value() {} -- constexpr FMT_INLINE value(int val) : int_value(val) {} -- constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} -- constexpr FMT_INLINE value(long long val) : long_long_value(val) {} -- constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} -- FMT_INLINE value(int128_opt val) : int128_value(val) {} -- FMT_INLINE value(uint128_opt val) : uint128_value(val) {} -- constexpr FMT_INLINE value(float val) : float_value(val) {} -- constexpr FMT_INLINE value(double val) : double_value(val) {} -- FMT_INLINE value(long double val) : long_double_value(val) {} -- constexpr FMT_INLINE value(bool val) : bool_value(val) {} -- constexpr FMT_INLINE value(char_type val) : char_value(val) {} -- FMT_CONSTEXPR FMT_INLINE value(const char_type* val) { -- string.data = val; -- if (is_constant_evaluated()) string.size = {}; -- } -- FMT_CONSTEXPR FMT_INLINE value(basic_string_view val) { -- string.data = val.data(); -- string.size = val.size(); -- } -- FMT_INLINE value(const void* val) : pointer(val) {} -- FMT_INLINE value(const named_arg_info* args, size_t size) -- : named_args{args, size} {} -- -- template FMT_CONSTEXPR FMT_INLINE value(T& val) { -- using value_type = remove_const_t; -- custom.value = const_cast(&val); -- // Get the formatter type through the context to allow different contexts -- // have different extension points, e.g. `formatter` for `format` and -- // `printf_formatter` for `printf`. -- custom.format = format_custom_arg< -- value_type, typename Context::template formatter_type>; -- } -- value(unformattable); -- value(unformattable_char); -- value(unformattable_pointer); -- -- private: -- // Formats an argument of a custom type, such as a user-defined class. -- template -- static void format_custom_arg(void* arg, -- typename Context::parse_context_type& parse_ctx, -- Context& ctx) { -- auto f = Formatter(); -- parse_ctx.advance_to(f.parse(parse_ctx)); -- using qualified_type = -- conditional_t(), const T, T>; -- ctx.advance_to(f.format(*static_cast(arg), ctx)); -- } --}; -- --// To minimize the number of types we need to deal with, long is translated --// either to int or to long long depending on its size. --enum { long_short = sizeof(long) == sizeof(int) }; --using long_type = conditional_t; --using ulong_type = conditional_t; -- --template struct format_as_result { -- template ::value || std::is_class::value)> -- static auto map(U*) -> decltype(format_as(std::declval())); -- static auto map(...) -> void; -- -- using type = decltype(map(static_cast(nullptr))); --}; --template using format_as_t = typename format_as_result::type; -- --template --struct has_format_as -- : bool_constant, void>::value> {}; -- --// Maps formatting arguments to core types. --// arg_mapper reports errors by returning unformattable instead of using --// static_assert because it's used in the is_formattable trait. --template struct arg_mapper { -- using char_type = typename Context::char_type; -- -- FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } -- FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { -- return val; -- } -- FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } -- FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { -- return val; -- } -- FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } -- FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } -- FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } -- FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { -- return val; -- } -- FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } -- FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) -- -> unsigned long long { -- return val; -- } -- FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { -- return val; -- } -- FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { -- return val; -- } -- FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } -- -- template ::value || -- std::is_same::value)> -- FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { -- return val; -- } -- template ::value || --#ifdef __cpp_char8_t -- std::is_same::value || --#endif -- std::is_same::value || -- std::is_same::value) && -- !std::is_same::value, -- int> = 0> -- FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { -- return {}; -- } -- -- FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } -- FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } -- FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { -- return val; -- } -- -- FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { -- return val; -- } -- FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { -- return val; -- } -- template ::value && !std::is_pointer::value && -- std::is_same>::value)> -- FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -- -> basic_string_view { -- return to_string_view(val); -- } -- template ::value && !std::is_pointer::value && -- !std::is_same>::value)> -- FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { -- return {}; -- } -- -- FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } -- FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { -- return val; -- } -- FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { -- return val; -- } -- -- // Use SFINAE instead of a const T* parameter to avoid a conflict with the -- // array overload. -- template < -- typename T, -- FMT_ENABLE_IF( -- std::is_pointer::value || std::is_member_pointer::value || -- std::is_function::type>::value || -- (std::is_convertible::value && -- !std::is_convertible::value && -- !has_formatter::value))> -- FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { -- return {}; -- } -- -- template ::value)> -- FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { -- return values; -- } -- -- // Only map owning types because mapping views can be unsafe. -- template , -- FMT_ENABLE_IF(std::is_arithmetic::value)> -- FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) { -- return map(format_as(val)); -- } -- -- template > -- struct formattable : bool_constant() || -- (has_formatter::value && -- !std::is_const::value)> {}; -- -- template ::value)> -- FMT_CONSTEXPR FMT_INLINE auto do_map(T& val) -> T& { -- return val; -- } -- template ::value)> -- FMT_CONSTEXPR FMT_INLINE auto do_map(T&) -> unformattable { -- return {}; -- } -- -- template , -- FMT_ENABLE_IF((std::is_class::value || std::is_enum::value || -- std::is_union::value) && -- !is_string::value && !is_char::value && -- !is_named_arg::value && -- !std::is_arithmetic>::value)> -- FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) { -- return do_map(val); -- } -- -- template ::value)> -- FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) -- -> decltype(this->map(named_arg.value)) { -- return map(named_arg.value); -- } -- -- auto map(...) -> unformattable { return {}; } --}; -- --// A type constant after applying arg_mapper. --template --using mapped_type_constant = -- type_constant().map(std::declval())), -- typename Context::char_type>; -- --enum { packed_arg_bits = 4 }; --// Maximum number of arguments with packed types. --enum { max_packed_args = 62 / packed_arg_bits }; --enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; --enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; -- --template --auto copy_str(InputIt begin, InputIt end, appender out) -> appender { -- get_container(out).append(begin, end); -- return out; --} -- --template --FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { -- return detail::copy_str(rng.begin(), rng.end(), out); --} -- --#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 --// A workaround for gcc 4.8 to make void_t work in a SFINAE context. --template struct void_t_impl { using type = void; }; --template using void_t = typename void_t_impl::type; --#else --template using void_t = void; --#endif -- --template --struct is_output_iterator : std::false_type {}; -- --template --struct is_output_iterator< -- It, T, -- void_t::iterator_category, -- decltype(*std::declval() = std::declval())>> -- : std::true_type {}; -- --template struct is_back_insert_iterator : std::false_type {}; --template --struct is_back_insert_iterator> -- : std::true_type {}; -- --// A type-erased reference to an std::locale to avoid a heavy include. --class locale_ref { -- private: -- const void* locale_; // A type-erased pointer to std::locale. -- -- public: -- constexpr FMT_INLINE locale_ref() : locale_(nullptr) {} -- template explicit locale_ref(const Locale& loc); -- -- explicit operator bool() const noexcept { return locale_ != nullptr; } -- -- template auto get() const -> Locale; --}; -- --template constexpr auto encode_types() -> unsigned long long { -- return 0; --} -- --template --constexpr auto encode_types() -> unsigned long long { -- return static_cast(mapped_type_constant::value) | -- (encode_types() << packed_arg_bits); --} -- --template --FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value { -- using arg_type = remove_cvref_t().map(val))>; -- -- constexpr bool formattable_char = -- !std::is_same::value; -- static_assert(formattable_char, "Mixing character types is disallowed."); -- -- // Formatting of arbitrary pointers is disallowed. If you want to format a -- // pointer cast it to `void*` or `const void*`. In particular, this forbids -- // formatting of `[const] volatile char*` printed as bool by iostreams. -- constexpr bool formattable_pointer = -- !std::is_same::value; -- static_assert(formattable_pointer, -- "Formatting of non-void pointers is disallowed."); -- -- constexpr bool formattable = !std::is_same::value; -- static_assert( -- formattable, -- "Cannot format an argument. To make type T formattable provide a " -- "formatter specialization: https://fmt.dev/latest/api.html#udt"); -- return {arg_mapper().map(val)}; --} -- --template --FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg { -- auto arg = basic_format_arg(); -- arg.type_ = mapped_type_constant::value; -- arg.value_ = make_arg(val); -- return arg; --} -- --template --FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg { -- return make_arg(val); --} --} // namespace detail --FMT_BEGIN_EXPORT -- --// A formatting argument. It is a trivially copyable/constructible type to --// allow storage in basic_memory_buffer. --template class basic_format_arg { -- private: -- detail::value value_; -- detail::type type_; -- -- template -- friend FMT_CONSTEXPR auto detail::make_arg(T& value) -- -> basic_format_arg; -- -- template -- friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, -- const basic_format_arg& arg) -- -> decltype(vis(0)); -- -- friend class basic_format_args; -- friend class dynamic_format_arg_store; -- -- using char_type = typename Context::char_type; -- -- template -- friend struct detail::arg_data; -- -- basic_format_arg(const detail::named_arg_info* args, size_t size) -- : value_(args, size) {} -- -- public: -- class handle { -- public: -- explicit handle(detail::custom_value custom) : custom_(custom) {} -- -- void format(typename Context::parse_context_type& parse_ctx, -- Context& ctx) const { -- custom_.format(custom_.value, parse_ctx, ctx); -- } -- -- private: -- detail::custom_value custom_; -- }; -- -- constexpr basic_format_arg() : type_(detail::type::none_type) {} -- -- constexpr explicit operator bool() const noexcept { -- return type_ != detail::type::none_type; -- } -- -- auto type() const -> detail::type { return type_; } -- -- auto is_integral() const -> bool { return detail::is_integral_type(type_); } -- auto is_arithmetic() const -> bool { -- return detail::is_arithmetic_type(type_); -- } --}; -- --/** -- \rst -- Visits an argument dispatching to the appropriate visit method based on -- the argument type. For example, if the argument type is ``double`` then -- ``vis(value)`` will be called with the value of type ``double``. -- \endrst -- */ --FMT_EXPORT --template --FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( -- Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { -- switch (arg.type_) { -- case detail::type::none_type: -- break; -- case detail::type::int_type: -- return vis(arg.value_.int_value); -- case detail::type::uint_type: -- return vis(arg.value_.uint_value); -- case detail::type::long_long_type: -- return vis(arg.value_.long_long_value); -- case detail::type::ulong_long_type: -- return vis(arg.value_.ulong_long_value); -- case detail::type::int128_type: -- return vis(detail::convert_for_visit(arg.value_.int128_value)); -- case detail::type::uint128_type: -- return vis(detail::convert_for_visit(arg.value_.uint128_value)); -- case detail::type::bool_type: -- return vis(arg.value_.bool_value); -- case detail::type::char_type: -- return vis(arg.value_.char_value); -- case detail::type::float_type: -- return vis(arg.value_.float_value); -- case detail::type::double_type: -- return vis(arg.value_.double_value); -- case detail::type::long_double_type: -- return vis(arg.value_.long_double_value); -- case detail::type::cstring_type: -- return vis(arg.value_.string.data); -- case detail::type::string_type: -- using sv = basic_string_view; -- return vis(sv(arg.value_.string.data, arg.value_.string.size)); -- case detail::type::pointer_type: -- return vis(arg.value_.pointer); -- case detail::type::custom_type: -- return vis(typename basic_format_arg::handle(arg.value_.custom)); -- } -- return vis(monostate()); --} -- --// Formatting context. --template class basic_format_context { -- private: -- OutputIt out_; -- basic_format_args args_; -- detail::locale_ref loc_; -- -- public: -- using iterator = OutputIt; -- using format_arg = basic_format_arg; -- using format_args = basic_format_args; -- using parse_context_type = basic_format_parse_context; -- template using formatter_type = formatter; -- -- /** The character type for the output. */ -- using char_type = Char; -- -- basic_format_context(basic_format_context&&) = default; -- basic_format_context(const basic_format_context&) = delete; -- void operator=(const basic_format_context&) = delete; -- /** -- Constructs a ``basic_format_context`` object. References to the arguments -- are stored in the object so make sure they have appropriate lifetimes. -- */ -- constexpr basic_format_context(OutputIt out, format_args ctx_args, -- detail::locale_ref loc = {}) -- : out_(out), args_(ctx_args), loc_(loc) {} -- -- constexpr auto arg(int id) const -> format_arg { return args_.get(id); } -- FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { -- return args_.get(name); -- } -- FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { -- return args_.get_id(name); -- } -- auto args() const -> const format_args& { return args_; } -- -- FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } -- void on_error(const char* message) { error_handler().on_error(message); } -- -- // Returns an iterator to the beginning of the output range. -- FMT_CONSTEXPR auto out() -> iterator { return out_; } -- -- // Advances the begin iterator to ``it``. -- void advance_to(iterator it) { -- if (!detail::is_back_insert_iterator()) out_ = it; -- } -- -- FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } --}; -- --template --using buffer_context = -- basic_format_context, Char>; --using format_context = buffer_context; -- --template --using is_formattable = bool_constant>() -- .map(std::declval()))>::value>; -- --/** -- \rst -- An array of references to arguments. It can be implicitly converted into -- `~fmt::basic_format_args` for passing into type-erased formatting functions -- such as `~fmt::vformat`. -- \endrst -- */ --template --class format_arg_store --#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -- // Workaround a GCC template argument substitution bug. -- : public basic_format_args --#endif --{ -- private: -- static const size_t num_args = sizeof...(Args); -- static constexpr size_t num_named_args = detail::count_named_args(); -- static const bool is_packed = num_args <= detail::max_packed_args; -- -- using value_type = conditional_t, -- basic_format_arg>; -- -- detail::arg_data -- data_; -- -- friend class basic_format_args; -- -- static constexpr unsigned long long desc = -- (is_packed ? detail::encode_types() -- : detail::is_unpacked_bit | num_args) | -- (num_named_args != 0 -- ? static_cast(detail::has_named_args_bit) -- : 0); -- -- public: -- template -- FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args) -- : --#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -- basic_format_args(*this), --#endif -- data_{detail::make_arg(args)...} { -- if (num_named_args != 0) -- detail::init_named_args(data_.named_args(), 0, 0, args...); -- } --}; -- --/** -- \rst -- Constructs a `~fmt::format_arg_store` object that contains references to -- arguments and can be implicitly converted to `~fmt::format_args`. `Context` -- can be omitted in which case it defaults to `~fmt::format_context`. -- See `~fmt::arg` for lifetime considerations. -- \endrst -- */ --// Arguments are taken by lvalue references to avoid some lifetime issues. --template --constexpr auto make_format_args(T&... args) -- -> format_arg_store...> { -- return {args...}; --} -- --/** -- \rst -- Returns a named argument to be used in a formatting function. -- It should only be used in a call to a formatting function or -- `dynamic_format_arg_store::push_back`. -- -- **Example**:: -- -- fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); -- \endrst -- */ --template --inline auto arg(const Char* name, const T& arg) -> detail::named_arg { -- static_assert(!detail::is_named_arg(), "nested named arguments"); -- return {name, arg}; --} --FMT_END_EXPORT -- --/** -- \rst -- A view of a collection of formatting arguments. To avoid lifetime issues it -- should only be used as a parameter type in type-erased functions such as -- ``vformat``:: -- -- void vlog(string_view format_str, format_args args); // OK -- format_args args = make_format_args(); // Error: dangling reference -- \endrst -- */ --template class basic_format_args { -- public: -- using size_type = int; -- using format_arg = basic_format_arg; -- -- private: -- // A descriptor that contains information about formatting arguments. -- // If the number of arguments is less or equal to max_packed_args then -- // argument types are passed in the descriptor. This reduces binary code size -- // per formatting function call. -- unsigned long long desc_; -- union { -- // If is_packed() returns true then argument values are stored in values_; -- // otherwise they are stored in args_. This is done to improve cache -- // locality and reduce compiled code size since storing larger objects -- // may require more code (at least on x86-64) even if the same amount of -- // data is actually copied to stack. It saves ~10% on the bloat test. -- const detail::value* values_; -- const format_arg* args_; -- }; -- -- constexpr auto is_packed() const -> bool { -- return (desc_ & detail::is_unpacked_bit) == 0; -- } -- auto has_named_args() const -> bool { -- return (desc_ & detail::has_named_args_bit) != 0; -- } -- -- FMT_CONSTEXPR auto type(int index) const -> detail::type { -- int shift = index * detail::packed_arg_bits; -- unsigned int mask = (1 << detail::packed_arg_bits) - 1; -- return static_cast((desc_ >> shift) & mask); -- } -- -- constexpr FMT_INLINE basic_format_args(unsigned long long desc, -- const detail::value* values) -- : desc_(desc), values_(values) {} -- constexpr basic_format_args(unsigned long long desc, const format_arg* args) -- : desc_(desc), args_(args) {} -- -- public: -- constexpr basic_format_args() : desc_(0), args_(nullptr) {} -- -- /** -- \rst -- Constructs a `basic_format_args` object from `~fmt::format_arg_store`. -- \endrst -- */ -- template -- constexpr FMT_INLINE basic_format_args( -- const format_arg_store& store) -- : basic_format_args(format_arg_store::desc, -- store.data_.args()) {} -- -- /** -- \rst -- Constructs a `basic_format_args` object from -- `~fmt::dynamic_format_arg_store`. -- \endrst -- */ -- constexpr FMT_INLINE basic_format_args( -- const dynamic_format_arg_store& store) -- : basic_format_args(store.get_types(), store.data()) {} -- -- /** -- \rst -- Constructs a `basic_format_args` object from a dynamic set of arguments. -- \endrst -- */ -- constexpr basic_format_args(const format_arg* args, int count) -- : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), -- args) {} -- -- /** Returns the argument with the specified id. */ -- FMT_CONSTEXPR auto get(int id) const -> format_arg { -- format_arg arg; -- if (!is_packed()) { -- if (id < max_size()) arg = args_[id]; -- return arg; -- } -- if (id >= detail::max_packed_args) return arg; -- arg.type_ = type(id); -- if (arg.type_ == detail::type::none_type) return arg; -- arg.value_ = values_[id]; -- return arg; -- } -- -- template -- auto get(basic_string_view name) const -> format_arg { -- int id = get_id(name); -- return id >= 0 ? get(id) : format_arg(); -- } -- -- template -- auto get_id(basic_string_view name) const -> int { -- if (!has_named_args()) return -1; -- const auto& named_args = -- (is_packed() ? values_[-1] : args_[-1].value_).named_args; -- for (size_t i = 0; i < named_args.size; ++i) { -- if (named_args.data[i].name == name) return named_args.data[i].id; -- } -- return -1; -- } -- -- auto max_size() const -> int { -- unsigned long long max_packed = detail::max_packed_args; -- return static_cast(is_packed() ? max_packed -- : desc_ & ~detail::is_unpacked_bit); -- } --}; -- --/** An alias to ``basic_format_args``. */ --// A separate type would result in shorter symbols but break ABI compatibility --// between clang and gcc on ARM (#1919). --FMT_EXPORT using format_args = basic_format_args; -- --// We cannot use enum classes as bit fields because of a gcc bug, so we put them --// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). --// Additionally, if an underlying type is specified, older gcc incorrectly warns --// that the type is too small. Both bugs are fixed in gcc 9.3. --#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 --# define FMT_ENUM_UNDERLYING_TYPE(type) --#else --# define FMT_ENUM_UNDERLYING_TYPE(type) : type --#endif --namespace align { --enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, -- numeric}; --} --using align_t = align::type; --namespace sign { --enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; --} --using sign_t = sign::type; -- --namespace detail { -- --// Workaround an array initialization issue in gcc 4.8. --template struct fill_t { -- private: -- enum { max_size = 4 }; -- Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; -- unsigned char size_ = 1; -- -- public: -- FMT_CONSTEXPR void operator=(basic_string_view s) { -- auto size = s.size(); -- FMT_ASSERT(size <= max_size, "invalid fill"); -- for (size_t i = 0; i < size; ++i) data_[i] = s[i]; -- size_ = static_cast(size); -- } -- -- constexpr auto size() const -> size_t { return size_; } -- constexpr auto data() const -> const Char* { return data_; } -- -- FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; } -- FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& { -- return data_[index]; -- } --}; --} // namespace detail -- --enum class presentation_type : unsigned char { -- none, -- dec, // 'd' -- oct, // 'o' -- hex_lower, // 'x' -- hex_upper, // 'X' -- bin_lower, // 'b' -- bin_upper, // 'B' -- hexfloat_lower, // 'a' -- hexfloat_upper, // 'A' -- exp_lower, // 'e' -- exp_upper, // 'E' -- fixed_lower, // 'f' -- fixed_upper, // 'F' -- general_lower, // 'g' -- general_upper, // 'G' -- chr, // 'c' -- string, // 's' -- pointer, // 'p' -- debug // '?' --}; -- --// Format specifiers for built-in and string types. --template struct format_specs { -- int width; -- int precision; -- presentation_type type; -- align_t align : 4; -- sign_t sign : 3; -- bool alt : 1; // Alternate form ('#'). -- bool localized : 1; -- detail::fill_t fill; -- -- constexpr format_specs() -- : width(0), -- precision(-1), -- type(presentation_type::none), -- align(align::none), -- sign(sign::none), -- alt(false), -- localized(false) {} --}; -- --namespace detail { -- --enum class arg_id_kind { none, index, name }; -- --// An argument reference. --template struct arg_ref { -- FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} -- -- FMT_CONSTEXPR explicit arg_ref(int index) -- : kind(arg_id_kind::index), val(index) {} -- FMT_CONSTEXPR explicit arg_ref(basic_string_view name) -- : kind(arg_id_kind::name), val(name) {} -- -- FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& { -- kind = arg_id_kind::index; -- val.index = idx; -- return *this; -- } -- -- arg_id_kind kind; -- union value { -- FMT_CONSTEXPR value(int idx = 0) : index(idx) {} -- FMT_CONSTEXPR value(basic_string_view n) : name(n) {} -- -- int index; -- basic_string_view name; -- } val; --}; -- --// Format specifiers with width and precision resolved at formatting rather --// than parsing time to allow reusing the same parsed specifiers with --// different sets of arguments (precompilation of format strings). --template --struct dynamic_format_specs : format_specs { -- arg_ref width_ref; -- arg_ref precision_ref; --}; -- --// Converts a character to ASCII. Returns '\0' on conversion failure. --template ::value)> --constexpr auto to_ascii(Char c) -> char { -- return c <= 0xff ? static_cast(c) : '\0'; --} --template ::value)> --constexpr auto to_ascii(Char c) -> char { -- return c <= 0xff ? static_cast(c) : '\0'; --} -- --// Returns the number of code units in a code point or 1 on error. --template --FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { -- if (const_check(sizeof(Char) != 1)) return 1; -- auto c = static_cast(*begin); -- return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1; --} -- --// Return the result via the out param to workaround gcc bug 77539. --template --FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { -- for (out = first; out != last; ++out) { -- if (*out == value) return true; -- } -- return false; --} -- --template <> --inline auto find(const char* first, const char* last, char value, -- const char*& out) -> bool { -- out = static_cast( -- std::memchr(first, value, to_unsigned(last - first))); -- return out != nullptr; --} -- --// Parses the range [begin, end) as an unsigned integer. This function assumes --// that the range is non-empty and the first character is a digit. --template --FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, -- int error_value) noexcept -> int { -- FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); -- unsigned value = 0, prev = 0; -- auto p = begin; -- do { -- prev = value; -- value = value * 10 + unsigned(*p - '0'); -- ++p; -- } while (p != end && '0' <= *p && *p <= '9'); -- auto num_digits = p - begin; -- begin = p; -- if (num_digits <= std::numeric_limits::digits10) -- return static_cast(value); -- // Check for overflow. -- const unsigned max = to_unsigned((std::numeric_limits::max)()); -- return num_digits == std::numeric_limits::digits10 + 1 && -- prev * 10ull + unsigned(p[-1] - '0') <= max -- ? static_cast(value) -- : error_value; --} -- --FMT_CONSTEXPR inline auto parse_align(char c) -> align_t { -- switch (c) { -- case '<': -- return align::left; -- case '>': -- return align::right; -- case '^': -- return align::center; -- } -- return align::none; --} -- --template constexpr auto is_name_start(Char c) -> bool { -- return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; --} -- --template --FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, -- Handler&& handler) -> const Char* { -- Char c = *begin; -- if (c >= '0' && c <= '9') { -- int index = 0; -- constexpr int max = (std::numeric_limits::max)(); -- if (c != '0') -- index = parse_nonnegative_int(begin, end, max); -- else -- ++begin; -- if (begin == end || (*begin != '}' && *begin != ':')) -- throw_format_error("invalid format string"); -- else -- handler.on_index(index); -- return begin; -- } -- if (!is_name_start(c)) { -- throw_format_error("invalid format string"); -- return begin; -- } -- auto it = begin; -- do { -- ++it; -- } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); -- handler.on_name({begin, to_unsigned(it - begin)}); -- return it; --} -- --template --FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, -- Handler&& handler) -> const Char* { -- FMT_ASSERT(begin != end, ""); -- Char c = *begin; -- if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); -- handler.on_auto(); -- return begin; --} -- --template struct dynamic_spec_id_handler { -- basic_format_parse_context& ctx; -- arg_ref& ref; -- -- FMT_CONSTEXPR void on_auto() { -- int id = ctx.next_arg_id(); -- ref = arg_ref(id); -- ctx.check_dynamic_spec(id); -- } -- FMT_CONSTEXPR void on_index(int id) { -- ref = arg_ref(id); -- ctx.check_arg_id(id); -- ctx.check_dynamic_spec(id); -- } -- FMT_CONSTEXPR void on_name(basic_string_view id) { -- ref = arg_ref(id); -- ctx.check_arg_id(id); -- } --}; -- --// Parses [integer | "{" [arg_id] "}"]. --template --FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, -- int& value, arg_ref& ref, -- basic_format_parse_context& ctx) -- -> const Char* { -- FMT_ASSERT(begin != end, ""); -- if ('0' <= *begin && *begin <= '9') { -- int val = parse_nonnegative_int(begin, end, -1); -- if (val != -1) -- value = val; -- else -- throw_format_error("number is too big"); -- } else if (*begin == '{') { -- ++begin; -- auto handler = dynamic_spec_id_handler{ctx, ref}; -- if (begin != end) begin = parse_arg_id(begin, end, handler); -- if (begin != end && *begin == '}') return ++begin; -- throw_format_error("invalid format string"); -- } -- return begin; --} -- --template --FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, -- int& value, arg_ref& ref, -- basic_format_parse_context& ctx) -- -> const Char* { -- ++begin; -- if (begin == end || *begin == '}') { -- throw_format_error("invalid precision"); -- return begin; -- } -- return parse_dynamic_spec(begin, end, value, ref, ctx); --} -- --enum class state { start, align, sign, hash, zero, width, precision, locale }; -- --// Parses standard format specifiers. --template --FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( -- const Char* begin, const Char* end, dynamic_format_specs& specs, -- basic_format_parse_context& ctx, type arg_type) -> const Char* { -- auto c = '\0'; -- if (end - begin > 1) { -- auto next = to_ascii(begin[1]); -- c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; -- } else { -- if (begin == end) return begin; -- c = to_ascii(*begin); -- } -- -- struct { -- state current_state = state::start; -- FMT_CONSTEXPR void operator()(state s, bool valid = true) { -- if (current_state >= s || !valid) -- throw_format_error("invalid format specifier"); -- current_state = s; -- } -- } enter_state; -- -- using pres = presentation_type; -- constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; -- struct { -- const Char*& begin; -- dynamic_format_specs& specs; -- type arg_type; -- -- FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* { -- if (!in(arg_type, set)) throw_format_error("invalid format specifier"); -- specs.type = type; -- return begin + 1; -- } -- } parse_presentation_type{begin, specs, arg_type}; -- -- for (;;) { -- switch (c) { -- case '<': -- case '>': -- case '^': -- enter_state(state::align); -- specs.align = parse_align(c); -- ++begin; -- break; -- case '+': -- case '-': -- case ' ': -- enter_state(state::sign, in(arg_type, sint_set | float_set)); -- switch (c) { -- case '+': -- specs.sign = sign::plus; -- break; -- case '-': -- specs.sign = sign::minus; -- break; -- case ' ': -- specs.sign = sign::space; -- break; -- } -- ++begin; -- break; -- case '#': -- enter_state(state::hash, is_arithmetic_type(arg_type)); -- specs.alt = true; -- ++begin; -- break; -- case '0': -- enter_state(state::zero); -- if (!is_arithmetic_type(arg_type)) -- throw_format_error("format specifier requires numeric argument"); -- if (specs.align == align::none) { -- // Ignore 0 if align is specified for compatibility with std::format. -- specs.align = align::numeric; -- specs.fill[0] = Char('0'); -- } -- ++begin; -- break; -- case '1': -- case '2': -- case '3': -- case '4': -- case '5': -- case '6': -- case '7': -- case '8': -- case '9': -- case '{': -- enter_state(state::width); -- begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); -- break; -- case '.': -- enter_state(state::precision, -- in(arg_type, float_set | string_set | cstring_set)); -- begin = parse_precision(begin, end, specs.precision, specs.precision_ref, -- ctx); -- break; -- case 'L': -- enter_state(state::locale, is_arithmetic_type(arg_type)); -- specs.localized = true; -- ++begin; -- break; -- case 'd': -- return parse_presentation_type(pres::dec, integral_set); -- case 'o': -- return parse_presentation_type(pres::oct, integral_set); -- case 'x': -- return parse_presentation_type(pres::hex_lower, integral_set); -- case 'X': -- return parse_presentation_type(pres::hex_upper, integral_set); -- case 'b': -- return parse_presentation_type(pres::bin_lower, integral_set); -- case 'B': -- return parse_presentation_type(pres::bin_upper, integral_set); -- case 'a': -- return parse_presentation_type(pres::hexfloat_lower, float_set); -- case 'A': -- return parse_presentation_type(pres::hexfloat_upper, float_set); -- case 'e': -- return parse_presentation_type(pres::exp_lower, float_set); -- case 'E': -- return parse_presentation_type(pres::exp_upper, float_set); -- case 'f': -- return parse_presentation_type(pres::fixed_lower, float_set); -- case 'F': -- return parse_presentation_type(pres::fixed_upper, float_set); -- case 'g': -- return parse_presentation_type(pres::general_lower, float_set); -- case 'G': -- return parse_presentation_type(pres::general_upper, float_set); -- case 'c': -- return parse_presentation_type(pres::chr, integral_set); -- case 's': -- return parse_presentation_type(pres::string, -- bool_set | string_set | cstring_set); -- case 'p': -- return parse_presentation_type(pres::pointer, pointer_set | cstring_set); -- case '?': -- return parse_presentation_type(pres::debug, -- char_set | string_set | cstring_set); -- case '}': -- return begin; -- default: { -- if (*begin == '}') return begin; -- // Parse fill and alignment. -- auto fill_end = begin + code_point_length(begin); -- if (end - fill_end <= 0) { -- throw_format_error("invalid format specifier"); -- return begin; -- } -- if (*begin == '{') { -- throw_format_error("invalid fill character '{'"); -- return begin; -- } -- auto align = parse_align(to_ascii(*fill_end)); -- enter_state(state::align, align != align::none); -- specs.fill = {begin, to_unsigned(fill_end - begin)}; -- specs.align = align; -- begin = fill_end + 1; -- } -- } -- if (begin == end) return begin; -- c = to_ascii(*begin); -- } --} -- --template --FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, -- Handler&& handler) -> const Char* { -- struct id_adapter { -- Handler& handler; -- int arg_id; -- -- FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); } -- FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } -- FMT_CONSTEXPR void on_name(basic_string_view id) { -- arg_id = handler.on_arg_id(id); -- } -- }; -- -- ++begin; -- if (begin == end) return handler.on_error("invalid format string"), end; -- if (*begin == '}') { -- handler.on_replacement_field(handler.on_arg_id(), begin); -- } else if (*begin == '{') { -- handler.on_text(begin, begin + 1); -- } else { -- auto adapter = id_adapter{handler, 0}; -- begin = parse_arg_id(begin, end, adapter); -- Char c = begin != end ? *begin : Char(); -- if (c == '}') { -- handler.on_replacement_field(adapter.arg_id, begin); -- } else if (c == ':') { -- begin = handler.on_format_specs(adapter.arg_id, begin + 1, end); -- if (begin == end || *begin != '}') -- return handler.on_error("unknown format specifier"), end; -- } else { -- return handler.on_error("missing '}' in format string"), end; -- } -- } -- return begin + 1; --} -- --template --FMT_CONSTEXPR FMT_INLINE void parse_format_string( -- basic_string_view format_str, Handler&& handler) { -- auto begin = format_str.data(); -- auto end = begin + format_str.size(); -- if (end - begin < 32) { -- // Use a simple loop instead of memchr for small strings. -- const Char* p = begin; -- while (p != end) { -- auto c = *p++; -- if (c == '{') { -- handler.on_text(begin, p - 1); -- begin = p = parse_replacement_field(p - 1, end, handler); -- } else if (c == '}') { -- if (p == end || *p != '}') -- return handler.on_error("unmatched '}' in format string"); -- handler.on_text(begin, p); -- begin = ++p; -- } -- } -- handler.on_text(begin, end); -- return; -- } -- struct writer { -- FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { -- if (from == to) return; -- for (;;) { -- const Char* p = nullptr; -- if (!find(from, to, Char('}'), p)) -- return handler_.on_text(from, to); -- ++p; -- if (p == to || *p != '}') -- return handler_.on_error("unmatched '}' in format string"); -- handler_.on_text(from, p); -- from = p + 1; -- } -- } -- Handler& handler_; -- } write = {handler}; -- while (begin != end) { -- // Doing two passes with memchr (one for '{' and another for '}') is up to -- // 2.5x faster than the naive one-pass implementation on big format strings. -- const Char* p = begin; -- if (*begin != '{' && !find(begin + 1, end, Char('{'), p)) -- return write(begin, end); -- write(begin, p); -- begin = parse_replacement_field(p, end, handler); -- } --} -- --template ::value> struct strip_named_arg { -- using type = T; --}; --template struct strip_named_arg { -- using type = remove_cvref_t; --}; -- --template --FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) -- -> decltype(ctx.begin()) { -- using char_type = typename ParseContext::char_type; -- using context = buffer_context; -- using mapped_type = conditional_t< -- mapped_type_constant::value != type::custom_type, -- decltype(arg_mapper().map(std::declval())), -- typename strip_named_arg::type>; -- return formatter().parse(ctx); --} -- --// Checks char specs and returns true iff the presentation type is char-like. --template --FMT_CONSTEXPR auto check_char_specs(const format_specs& specs) -> bool { -- if (specs.type != presentation_type::none && -- specs.type != presentation_type::chr && -- specs.type != presentation_type::debug) { -- return false; -- } -- if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) -- throw_format_error("invalid format specifier for char"); -- return true; --} -- --#if FMT_USE_NONTYPE_TEMPLATE_ARGS --template --constexpr auto get_arg_index_by_name(basic_string_view name) -> int { -- if constexpr (is_statically_named_arg()) { -- if (name == T::name) return N; -- } -- if constexpr (sizeof...(Args) > 0) -- return get_arg_index_by_name(name); -- (void)name; // Workaround an MSVC bug about "unused" parameter. -- return -1; --} --#endif -- --template --FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { --#if FMT_USE_NONTYPE_TEMPLATE_ARGS -- if constexpr (sizeof...(Args) > 0) -- return get_arg_index_by_name<0, Args...>(name); --#endif -- (void)name; -- return -1; --} -- --template class format_string_checker { -- private: -- using parse_context_type = compile_parse_context; -- static constexpr int num_args = sizeof...(Args); -- -- // Format specifier parsing function. -- // In the future basic_format_parse_context will replace compile_parse_context -- // here and will use is_constant_evaluated and downcasting to access the data -- // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. -- using parse_func = const Char* (*)(parse_context_type&); -- -- parse_context_type context_; -- parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; -- type types_[num_args > 0 ? static_cast(num_args) : 1]; -- -- public: -- explicit FMT_CONSTEXPR format_string_checker(basic_string_view fmt) -- : context_(fmt, num_args, types_), -- parse_funcs_{&parse_format_specs...}, -- types_{mapped_type_constant>::value...} {} -- -- FMT_CONSTEXPR void on_text(const Char*, const Char*) {} -- -- FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } -- FMT_CONSTEXPR auto on_arg_id(int id) -> int { -- return context_.check_arg_id(id), id; -- } -- FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { --#if FMT_USE_NONTYPE_TEMPLATE_ARGS -- auto index = get_arg_index_by_name(id); -- if (index < 0) on_error("named argument is not found"); -- return index; --#else -- (void)id; -- on_error("compile-time checks for named arguments require C++20 support"); -- return 0; --#endif -- } -- -- FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} -- -- FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) -- -> const Char* { -- context_.advance_to(begin); -- // id >= 0 check is a workaround for gcc 10 bug (#2065). -- return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; -- } -- -- FMT_CONSTEXPR void on_error(const char* message) { -- throw_format_error(message); -- } --}; -- --// Reports a compile-time error if S is not a valid format string. --template ::value)> --FMT_INLINE void check_format_string(const S&) { --#ifdef FMT_ENFORCE_COMPILE_STRING -- static_assert(is_compile_string::value, -- "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " -- "FMT_STRING."); --#endif --} --template ::value)> --void check_format_string(S format_str) { -- using char_t = typename S::char_type; -- FMT_CONSTEXPR auto s = basic_string_view(format_str); -- using checker = format_string_checker...>; -- FMT_CONSTEXPR bool error = (parse_format_string(s, checker(s)), true); -- ignore_unused(error); --} -- --template struct vformat_args { -- using type = basic_format_args< -- basic_format_context>, Char>>; --}; --template <> struct vformat_args { using type = format_args; }; -- --// Use vformat_args and avoid type_identity to keep symbols short. --template --void vformat_to(buffer& buf, basic_string_view fmt, -- typename vformat_args::type args, locale_ref loc = {}); -- --FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); --#ifndef _WIN32 --inline void vprint_mojibake(std::FILE*, string_view, format_args) {} --#endif --} // namespace detail -- --FMT_BEGIN_EXPORT -- --// A formatter specialization for natively supported types. --template --struct formatter::value != -- detail::type::custom_type>> { -- private: -- detail::dynamic_format_specs specs_; -- -- public: -- template -- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { -- auto type = detail::type_constant::value; -- auto end = -- detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type); -- if (type == detail::type::char_type) detail::check_char_specs(specs_); -- return end; -- } -- -- template ::value, -- FMT_ENABLE_IF(U == detail::type::string_type || -- U == detail::type::cstring_type || -- U == detail::type::char_type)> -- FMT_CONSTEXPR void set_debug_format(bool set = true) { -- specs_.type = set ? presentation_type::debug : presentation_type::none; -- } -- -- template -- FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const -- -> decltype(ctx.out()); --}; -- --#define FMT_FORMAT_AS(Type, Base) \ -- template \ -- struct formatter : formatter {} -- --FMT_FORMAT_AS(signed char, int); --FMT_FORMAT_AS(unsigned char, unsigned); --FMT_FORMAT_AS(short, int); --FMT_FORMAT_AS(unsigned short, unsigned); --FMT_FORMAT_AS(long, detail::long_type); --FMT_FORMAT_AS(unsigned long, detail::ulong_type); --FMT_FORMAT_AS(Char*, const Char*); --FMT_FORMAT_AS(std::basic_string, basic_string_view); --FMT_FORMAT_AS(std::nullptr_t, const void*); --FMT_FORMAT_AS(detail::std_string_view, basic_string_view); -- --template struct runtime_format_string { -- basic_string_view str; --}; -- --/** A compile-time format string. */ --template class basic_format_string { -- private: -- basic_string_view str_; -- -- public: -- template >::value)> -- FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { -- static_assert( -- detail::count< -- (std::is_base_of>::value && -- std::is_reference::value)...>() == 0, -- "passing views as lvalues is disallowed"); --#ifdef FMT_HAS_CONSTEVAL -- if constexpr (detail::count_named_args() == -- detail::count_statically_named_args()) { -- using checker = -- detail::format_string_checker...>; -- detail::parse_format_string(str_, checker(s)); -- } --#else -- detail::check_format_string(s); --#endif -- } -- basic_format_string(runtime_format_string fmt) : str_(fmt.str) {} -- -- FMT_INLINE operator basic_string_view() const { return str_; } -- FMT_INLINE auto get() const -> basic_string_view { return str_; } --}; -- --#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 --// Workaround broken conversion on older gcc. --template using format_string = string_view; --inline auto runtime(string_view s) -> string_view { return s; } --#else --template --using format_string = basic_format_string...>; --/** -- \rst -- Creates a runtime format string. -- -- **Example**:: -- -- // Check format string at runtime instead of compile-time. -- fmt::print(fmt::runtime("{:d}"), "I am not a number"); -- \endrst -- */ --inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } --#endif -- --FMT_API auto vformat(string_view fmt, format_args args) -> std::string; -- --/** -- \rst -- Formats ``args`` according to specifications in ``fmt`` and returns the result -- as a string. -- -- **Example**:: -- -- #include -- std::string message = fmt::format("The answer is {}.", 42); -- \endrst --*/ --template --FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) -- -> std::string { -- return vformat(fmt, fmt::make_format_args(args...)); --} -- --/** Formats a string and writes the output to ``out``. */ --template ::value)> --auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { -- auto&& buf = detail::get_buffer(out); -- detail::vformat_to(buf, fmt, args, {}); -- return detail::get_iterator(buf, out); --} -- --/** -- \rst -- Formats ``args`` according to specifications in ``fmt``, writes the result to -- the output iterator ``out`` and returns the iterator past the end of the output -- range. `format_to` does not append a terminating null character. -- -- **Example**:: -- -- auto out = std::vector(); -- fmt::format_to(std::back_inserter(out), "{}", 42); -- \endrst -- */ --template ::value)> --FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) -- -> OutputIt { -- return vformat_to(out, fmt, fmt::make_format_args(args...)); --} -- --template struct format_to_n_result { -- /** Iterator past the end of the output range. */ -- OutputIt out; -- /** Total (not truncated) output size. */ -- size_t size; --}; -- --template ::value)> --auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) -- -> format_to_n_result { -- using traits = detail::fixed_buffer_traits; -- auto buf = detail::iterator_buffer(out, n); -- detail::vformat_to(buf, fmt, args, {}); -- return {buf.out(), buf.count()}; --} -- --/** -- \rst -- Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` -- characters of the result to the output iterator ``out`` and returns the total -- (not truncated) output size and the iterator past the end of the output range. -- `format_to_n` does not append a terminating null character. -- \endrst -- */ --template ::value)> --FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, -- T&&... args) -> format_to_n_result { -- return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); --} -- --/** Returns the number of chars in the output of ``format(fmt, args...)``. */ --template --FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, -- T&&... args) -> size_t { -- auto buf = detail::counting_buffer<>(); -- detail::vformat_to(buf, fmt, fmt::make_format_args(args...), {}); -- return buf.count(); --} -- --FMT_API void vprint(string_view fmt, format_args args); --FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); -- --/** -- \rst -- Formats ``args`` according to specifications in ``fmt`` and writes the output -- to ``stdout``. -- -- **Example**:: -- -- fmt::print("Elapsed time: {0:.2f} seconds", 1.23); -- \endrst -- */ --template --FMT_INLINE void print(format_string fmt, T&&... args) { -- const auto& vargs = fmt::make_format_args(args...); -- return detail::is_utf8() ? vprint(fmt, vargs) -- : detail::vprint_mojibake(stdout, fmt, vargs); --} -- --/** -- \rst -- Formats ``args`` according to specifications in ``fmt`` and writes the -- output to the file ``f``. -- -- **Example**:: -- -- fmt::print(stderr, "Don't {}!", "panic"); -- \endrst -- */ --template --FMT_INLINE void print(std::FILE* f, format_string fmt, T&&... args) { -- const auto& vargs = fmt::make_format_args(args...); -- return detail::is_utf8() ? vprint(f, fmt, vargs) -- : detail::vprint_mojibake(f, fmt, vargs); --} -- --/** -- Formats ``args`` according to specifications in ``fmt`` and writes the -- output to the file ``f`` followed by a newline. -- */ --template --FMT_INLINE void println(std::FILE* f, format_string fmt, T&&... args) { -- return fmt::print(f, "{}\n", fmt::format(fmt, std::forward(args)...)); --} -- --/** -- Formats ``args`` according to specifications in ``fmt`` and writes the output -- to ``stdout`` followed by a newline. -- */ --template --FMT_INLINE void println(format_string fmt, T&&... args) { -- return fmt::println(stdout, fmt, std::forward(args)...); --} -- --FMT_END_EXPORT --FMT_GCC_PRAGMA("GCC pop_options") --FMT_END_NAMESPACE -- --#ifdef FMT_HEADER_ONLY --# include "format.h" --#endif --#endif // FMT_CORE_H_ -+#include "format.h" -diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h -index dac2d43..66ee747 100644 ---- a/include/fmt/format-inl.h -+++ b/include/fmt/format-inl.h -@@ -8,36 +8,36 @@ - #ifndef FMT_FORMAT_INL_H_ - #define FMT_FORMAT_INL_H_ - --#include --#include // errno --#include --#include --#include -- --#ifndef FMT_STATIC_THOUSANDS_SEPARATOR --# include -+#ifndef FMT_MODULE -+# include -+# include // errno -+# include -+# include -+# include - #endif - --#ifdef _WIN32 -+#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) - # include // _isatty - #endif - - #include "format.h" - -+#if FMT_USE_LOCALE -+# include -+#endif -+ -+#ifndef FMT_FUNC -+# define FMT_FUNC -+#endif -+ - FMT_BEGIN_NAMESPACE - namespace detail { - - FMT_FUNC void assert_fail(const char* file, int line, const char* message) { - // Use unchecked std::fprintf to avoid triggering another assertion when -- // writing to stderr fails -- std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); -- // Chosen instead of std::abort to satisfy Clang in CUDA mode during device -- // code pass. -- std::terminate(); --} -- --FMT_FUNC void throw_format_error(const char* message) { -- FMT_THROW(format_error(message)); -+ // writing to stderr fails. -+ fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); -+ abort(); - } - - FMT_FUNC void format_error_code(detail::buffer& out, int error_code, -@@ -56,112 +56,129 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, - ++error_code_size; - } - error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); -- auto it = buffer_appender(out); -+ auto it = appender(out); - if (message.size() <= inline_buffer_size - error_code_size) -- format_to(it, FMT_STRING("{}{}"), message, SEP); -- format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); -+ fmt::format_to(it, FMT_STRING("{}{}"), message, SEP); -+ fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); - FMT_ASSERT(out.size() <= inline_buffer_size, ""); - } - --FMT_FUNC void report_error(format_func func, int error_code, -- const char* message) noexcept { -+FMT_FUNC void do_report_error(format_func func, int error_code, -+ const char* message) noexcept { - memory_buffer full_message; - func(full_message, error_code, message); -- // Don't use fwrite_fully because the latter may throw. -+ // Don't use fwrite_all because the latter may throw. - if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) - std::fputc('\n', stderr); - } - - // A wrapper around fwrite that throws on error. --inline void fwrite_fully(const void* ptr, size_t size, size_t count, -- FILE* stream) { -- size_t written = std::fwrite(ptr, size, count, stream); -+inline void fwrite_all(const void* ptr, size_t count, FILE* stream) { -+ size_t written = std::fwrite(ptr, 1, count, stream); - if (written < count) - FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); - } - --#ifndef FMT_STATIC_THOUSANDS_SEPARATOR -+#if FMT_USE_LOCALE -+using std::locale; -+using std::numpunct; -+using std::use_facet; -+ - template - locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { -- static_assert(std::is_same::value, ""); -+ static_assert(std::is_same::value, ""); - } -+#else -+struct locale {}; -+template struct numpunct { -+ auto grouping() const -> std::string { return "\03"; } -+ auto thousands_sep() const -> Char { return ','; } -+ auto decimal_point() const -> Char { return '.'; } -+}; -+template Facet use_facet(locale) { return {}; } -+#endif // FMT_USE_LOCALE - --template Locale locale_ref::get() const { -- static_assert(std::is_same::value, ""); -- return locale_ ? *static_cast(locale_) : std::locale(); -+template auto locale_ref::get() const -> Locale { -+ static_assert(std::is_same::value, ""); -+#if FMT_USE_LOCALE -+ if (locale_) return *static_cast(locale_); -+#endif -+ return locale(); - } - - template - FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { -- auto& facet = std::use_facet>(loc.get()); -+ auto&& facet = use_facet>(loc.get()); - auto grouping = facet.grouping(); - auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); - return {std::move(grouping), thousands_sep}; - } --template FMT_FUNC Char decimal_point_impl(locale_ref loc) { -- return std::use_facet>(loc.get()) -- .decimal_point(); --} --#else - template --FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { -- return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; -+FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { -+ return use_facet>(loc.get()).decimal_point(); - } --template FMT_FUNC Char decimal_point_impl(locale_ref) { -- return '.'; --} --#endif - -+#if FMT_USE_LOCALE - FMT_FUNC auto write_loc(appender out, loc_value value, -- const format_specs<>& specs, locale_ref loc) -> bool { --#ifndef FMT_STATIC_THOUSANDS_SEPARATOR -+ const format_specs& specs, locale_ref loc) -> bool { - auto locale = loc.get(); - // We cannot use the num_put facet because it may produce output in - // a wrong encoding. - using facet = format_facet; - if (std::has_facet(locale)) -- return std::use_facet(locale).put(out, value, specs); -+ return use_facet(locale).put(out, value, specs); - return facet(locale).put(out, value, specs); --#endif -- return false; - } -+#endif - } // namespace detail - -+FMT_FUNC void report_error(const char* message) { -+#if FMT_USE_EXCEPTIONS -+ // Use FMT_THROW instead of throw to avoid bogus unreachable code warnings -+ // from MSVC. -+ FMT_THROW(format_error(message)); -+#else -+ fputs(message, stderr); -+ abort(); -+#endif -+} -+ - template typename Locale::id format_facet::id; - --#ifndef FMT_STATIC_THOUSANDS_SEPARATOR - template format_facet::format_facet(Locale& loc) { -- auto& numpunct = std::use_facet>(loc); -- grouping_ = numpunct.grouping(); -- if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); -+ auto& np = detail::use_facet>(loc); -+ grouping_ = np.grouping(); -+ if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep()); - } - -+#if FMT_USE_LOCALE - template <> - FMT_API FMT_FUNC auto format_facet::do_put( -- appender out, loc_value val, const format_specs<>& specs) const -> bool { -+ appender out, loc_value val, const format_specs& specs) const -> bool { - return val.visit( - detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); - } - #endif - --FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt, -- format_args args) { -+FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args) -+ -> std::system_error { - auto ec = std::error_code(error_code, std::generic_category()); - return std::system_error(ec, vformat(fmt, args)); - } - - namespace detail { - --template inline bool operator==(basic_fp x, basic_fp y) { -+template -+inline auto operator==(basic_fp x, basic_fp y) -> bool { - return x.f == y.f && x.e == y.e; - } - - // Compilers should be able to optimize this into the ror instruction. --FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { -+FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { - r &= 31; - return (n >> r) | (n << (32 - r)); - } --FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { -+FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { - r &= 63; - return (n >> r) | (n << (64 - r)); - } -@@ -170,14 +187,14 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { - namespace dragonbox { - // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a - // 64-bit unsigned integer. --inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { -+inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t { - return umul128_upper64(static_cast(x) << 32, y); - } - - // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a - // 128-bit unsigned integer. --inline uint128_fallback umul192_lower128(uint64_t x, -- uint128_fallback y) noexcept { -+inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept -+ -> uint128_fallback { - uint64_t high = x * y.high(); - uint128_fallback high_low = umul128(x, y.low()); - return {high + high_low.high(), high_low.low()}; -@@ -185,17 +202,17 @@ inline uint128_fallback umul192_lower128(uint64_t x, - - // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a - // 64-bit unsigned integer. --inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { -+inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t { - return x * y; - } - - // Various fast log computations. --inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { -+inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int { - FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); - return (e * 631305 - 261663) >> 21; - } - --FMT_INLINE_VARIABLE constexpr struct { -+FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct { - uint32_t divisor; - int shift_amount; - } div_small_pow10_infos[] = {{10, 16}, {100, 16}}; -@@ -204,7 +221,7 @@ FMT_INLINE_VARIABLE constexpr struct { - // divisible by pow(10, N). - // Precondition: n <= pow(10, N + 1). - template --bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { -+auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool { - // The numbers below are chosen such that: - // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, - // 2. nm mod 2^k < m if and only if n is divisible by d, -@@ -229,7 +246,7 @@ bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { - - // Computes floor(n / pow(10, N)) for small n and N. - // Precondition: n <= pow(10, N + 1). --template uint32_t small_division_by_pow10(uint32_t n) noexcept { -+template auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t { - constexpr auto info = div_small_pow10_infos[N - 1]; - FMT_ASSERT(n <= info.divisor * 10, "n is too large"); - constexpr uint32_t magic_number = -@@ -238,12 +255,12 @@ template uint32_t small_division_by_pow10(uint32_t n) noexcept { - } - - // Computes floor(n / 10^(kappa + 1)) (float) --inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { -+inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t { - // 1374389535 = ceil(2^37/100) - return static_cast((static_cast(n) * 1374389535) >> 37); - } - // Computes floor(n / 10^(kappa + 1)) (double) --inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { -+inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t { - // 2361183241434822607 = ceil(2^(64+7)/1000) - return umul128_upper64(n, 2361183241434822607ull) >> 7; - } -@@ -255,7 +272,7 @@ template <> struct cache_accessor { - using carrier_uint = float_info::carrier_uint; - using cache_entry_type = uint64_t; - -- static uint64_t get_cached_power(int k) noexcept { -+ static auto get_cached_power(int k) noexcept -> uint64_t { - FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, - "k is out of range"); - static constexpr const uint64_t pow10_significands[] = { -@@ -297,20 +314,23 @@ template <> struct cache_accessor { - bool is_integer; - }; - -- static compute_mul_result compute_mul( -- carrier_uint u, const cache_entry_type& cache) noexcept { -+ static auto compute_mul(carrier_uint u, -+ const cache_entry_type& cache) noexcept -+ -> compute_mul_result { - auto r = umul96_upper64(u, cache); - return {static_cast(r >> 32), - static_cast(r) == 0}; - } - -- static uint32_t compute_delta(const cache_entry_type& cache, -- int beta) noexcept { -+ static auto compute_delta(const cache_entry_type& cache, int beta) noexcept -+ -> uint32_t { - return static_cast(cache >> (64 - 1 - beta)); - } - -- static compute_mul_parity_result compute_mul_parity( -- carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { -+ static auto compute_mul_parity(carrier_uint two_f, -+ const cache_entry_type& cache, -+ int beta) noexcept -+ -> compute_mul_parity_result { - FMT_ASSERT(beta >= 1, ""); - FMT_ASSERT(beta < 64, ""); - -@@ -319,22 +339,22 @@ template <> struct cache_accessor { - static_cast(r >> (32 - beta)) == 0}; - } - -- static carrier_uint compute_left_endpoint_for_shorter_interval_case( -- const cache_entry_type& cache, int beta) noexcept { -+ static auto compute_left_endpoint_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint { - return static_cast( - (cache - (cache >> (num_significand_bits() + 2))) >> - (64 - num_significand_bits() - 1 - beta)); - } - -- static carrier_uint compute_right_endpoint_for_shorter_interval_case( -- const cache_entry_type& cache, int beta) noexcept { -+ static auto compute_right_endpoint_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint { - return static_cast( - (cache + (cache >> (num_significand_bits() + 1))) >> - (64 - num_significand_bits() - 1 - beta)); - } - -- static carrier_uint compute_round_up_for_shorter_interval_case( -- const cache_entry_type& cache, int beta) noexcept { -+ static auto compute_round_up_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint { - return (static_cast( - cache >> (64 - num_significand_bits() - 2 - beta)) + - 1) / -@@ -346,7 +366,7 @@ template <> struct cache_accessor { - using carrier_uint = float_info::carrier_uint; - using cache_entry_type = uint128_fallback; - -- static uint128_fallback get_cached_power(int k) noexcept { -+ static auto get_cached_power(int k) noexcept -> uint128_fallback { - FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, - "k is out of range"); - -@@ -985,8 +1005,7 @@ template <> struct cache_accessor { - {0xe0accfa875af45a7, 0x93eb1b80a33b8606}, - {0x8c6c01c9498d8b88, 0xbc72f130660533c4}, - {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, -- { 0xdb68c2ca82ed2a05, -- 0xa67398db9f6820e2 } -+ {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2}, - #else - {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, - {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, -@@ -1071,19 +1090,22 @@ template <> struct cache_accessor { - bool is_integer; - }; - -- static compute_mul_result compute_mul( -- carrier_uint u, const cache_entry_type& cache) noexcept { -+ static auto compute_mul(carrier_uint u, -+ const cache_entry_type& cache) noexcept -+ -> compute_mul_result { - auto r = umul192_upper128(u, cache); - return {r.high(), r.low() == 0}; - } - -- static uint32_t compute_delta(cache_entry_type const& cache, -- int beta) noexcept { -+ static auto compute_delta(cache_entry_type const& cache, int beta) noexcept -+ -> uint32_t { - return static_cast(cache.high() >> (64 - 1 - beta)); - } - -- static compute_mul_parity_result compute_mul_parity( -- carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { -+ static auto compute_mul_parity(carrier_uint two_f, -+ const cache_entry_type& cache, -+ int beta) noexcept -+ -> compute_mul_parity_result { - FMT_ASSERT(beta >= 1, ""); - FMT_ASSERT(beta < 64, ""); - -@@ -1092,35 +1114,35 @@ template <> struct cache_accessor { - ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; - } - -- static carrier_uint compute_left_endpoint_for_shorter_interval_case( -- const cache_entry_type& cache, int beta) noexcept { -+ static auto compute_left_endpoint_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint { - return (cache.high() - - (cache.high() >> (num_significand_bits() + 2))) >> - (64 - num_significand_bits() - 1 - beta); - } - -- static carrier_uint compute_right_endpoint_for_shorter_interval_case( -- const cache_entry_type& cache, int beta) noexcept { -+ static auto compute_right_endpoint_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint { - return (cache.high() + - (cache.high() >> (num_significand_bits() + 1))) >> - (64 - num_significand_bits() - 1 - beta); - } - -- static carrier_uint compute_round_up_for_shorter_interval_case( -- const cache_entry_type& cache, int beta) noexcept { -+ static auto compute_round_up_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint { - return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + - 1) / - 2; - } - }; - --FMT_FUNC uint128_fallback get_cached_power(int k) noexcept { -+FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback { - return cache_accessor::get_cached_power(k); - } - - // Various integer checks - template --bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { -+auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool { - const int case_shorter_interval_left_endpoint_lower_threshold = 2; - const int case_shorter_interval_left_endpoint_upper_threshold = 3; - return exponent >= case_shorter_interval_left_endpoint_lower_threshold && -@@ -1132,7 +1154,7 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { - FMT_ASSERT(n != 0, ""); - // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. - constexpr uint32_t mod_inv_5 = 0xcccccccd; -- constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5 -+ constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5 - - while (true) { - auto q = rotr(n * mod_inv_25, 2); -@@ -1168,7 +1190,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { - - // If n is not divisible by 10^8, work with n itself. - constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd; -- constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // = mod_inv_5 * mod_inv_5 -+ constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5 - - int s = 0; - while (true) { -@@ -1234,7 +1256,7 @@ FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { - return ret_value; - } - --template decimal_fp to_decimal(T x) noexcept { -+template auto to_decimal(T x) noexcept -> decimal_fp { - // Step 1: integer promotion & Schubfach multiplier calculation. - - using carrier_uint = typename float_info::carrier_uint; -@@ -1373,15 +1395,15 @@ template <> struct formatter { - for (auto i = n.bigits_.size(); i > 0; --i) { - auto value = n.bigits_[i - 1u]; - if (first) { -- out = format_to(out, FMT_STRING("{:x}"), value); -+ out = fmt::format_to(out, FMT_STRING("{:x}"), value); - first = false; - continue; - } -- out = format_to(out, FMT_STRING("{:08x}"), value); -+ out = fmt::format_to(out, FMT_STRING("{:08x}"), value); - } - if (n.exp_ > 0) -- out = format_to(out, FMT_STRING("p{}"), -- n.exp_ * detail::bigint::bigit_bits); -+ out = fmt::format_to(out, FMT_STRING("p{}"), -+ n.exp_ * detail::bigint::bigit_bits); - return out; - } - }; -@@ -1405,7 +1427,7 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, - const char* message) noexcept { - FMT_TRY { - auto ec = std::error_code(error_code, std::generic_category()); -- write(std::back_inserter(out), std::system_error(ec, message).what()); -+ detail::write(appender(out), std::system_error(ec, message).what()); - return; - } - FMT_CATCH(...) {} -@@ -1414,10 +1436,10 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, - - FMT_FUNC void report_system_error(int error_code, - const char* message) noexcept { -- report_error(format_system_error, error_code, message); -+ do_report_error(format_system_error, error_code, message); - } - --FMT_FUNC std::string vformat(string_view fmt, format_args args) { -+FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { - // Don't optimize the "{}" case to keep the binary size small and because it - // can be better optimized in fmt::format anyway. - auto buffer = memory_buffer(); -@@ -1426,39 +1448,304 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) { - } - - namespace detail { --#ifndef _WIN32 --FMT_FUNC bool write_console(std::FILE*, string_view) { return false; } -+ -+FMT_FUNC void vformat_to(buffer& buf, string_view fmt, format_args args, -+ locale_ref loc) { -+ auto out = appender(buf); -+ if (fmt.size() == 2 && equal2(fmt.data(), "{}")) -+ return args.get(0).visit(default_arg_formatter{out}); -+ parse_format_string( -+ fmt, format_handler{parse_context(fmt), {out, args, loc}}); -+} -+ -+template struct span { -+ T* data; -+ size_t size; -+}; -+ -+template auto flockfile(F* f) -> decltype(_lock_file(f)) { -+ _lock_file(f); -+} -+template auto funlockfile(F* f) -> decltype(_unlock_file(f)) { -+ _unlock_file(f); -+} -+ -+#ifndef getc_unlocked -+template auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) { -+ return _fgetc_nolock(f); -+} -+#endif -+ -+template -+struct has_flockfile : std::false_type {}; -+ -+template -+struct has_flockfile()))>> -+ : std::true_type {}; -+ -+// A FILE wrapper. F is FILE defined as a template parameter to make system API -+// detection work. -+template class file_base { -+ public: -+ F* file_; -+ -+ public: -+ file_base(F* file) : file_(file) {} -+ operator F*() const { return file_; } -+ -+ // Reads a code unit from the stream. -+ auto get() -> int { -+ int result = getc_unlocked(file_); -+ if (result == EOF && ferror(file_) != 0) -+ FMT_THROW(system_error(errno, FMT_STRING("getc failed"))); -+ return result; -+ } -+ -+ // Puts the code unit back into the stream buffer. -+ void unget(char c) { -+ if (ungetc(c, file_) == EOF) -+ FMT_THROW(system_error(errno, FMT_STRING("ungetc failed"))); -+ } -+ -+ void flush() { fflush(this->file_); } -+}; -+ -+// A FILE wrapper for glibc. -+template class glibc_file : public file_base { -+ private: -+ enum { -+ line_buffered = 0x200, // _IO_LINE_BUF -+ unbuffered = 2 // _IO_UNBUFFERED -+ }; -+ -+ public: -+ using file_base::file_base; -+ -+ auto is_buffered() const -> bool { -+ return (this->file_->_flags & unbuffered) == 0; -+ } -+ -+ void init_buffer() { -+ if (this->file_->_IO_write_ptr) return; -+ // Force buffer initialization by placing and removing a char in a buffer. -+ assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end); -+ putc_unlocked(0, this->file_); -+ --this->file_->_IO_write_ptr; -+ } -+ -+ // Returns the file's read buffer. -+ auto get_read_buffer() const -> span { -+ auto ptr = this->file_->_IO_read_ptr; -+ return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)}; -+ } -+ -+ // Returns the file's write buffer. -+ auto get_write_buffer() const -> span { -+ auto ptr = this->file_->_IO_write_ptr; -+ return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)}; -+ } -+ -+ void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } -+ -+ bool needs_flush() const { -+ if ((this->file_->_flags & line_buffered) == 0) return false; -+ char* end = this->file_->_IO_write_end; -+ return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end)); -+ } -+ -+ void flush() { fflush_unlocked(this->file_); } -+}; -+ -+// A FILE wrapper for Apple's libc. -+template class apple_file : public file_base { -+ private: -+ enum { -+ line_buffered = 1, // __SNBF -+ unbuffered = 2 // __SLBF -+ }; -+ -+ public: -+ using file_base::file_base; -+ -+ auto is_buffered() const -> bool { -+ return (this->file_->_flags & unbuffered) == 0; -+ } -+ -+ void init_buffer() { -+ if (this->file_->_p) return; -+ // Force buffer initialization by placing and removing a char in a buffer. -+ putc_unlocked(0, this->file_); -+ --this->file_->_p; -+ ++this->file_->_w; -+ } -+ -+ auto get_read_buffer() const -> span { -+ return {reinterpret_cast(this->file_->_p), -+ to_unsigned(this->file_->_r)}; -+ } -+ -+ auto get_write_buffer() const -> span { -+ return {reinterpret_cast(this->file_->_p), -+ to_unsigned(this->file_->_bf._base + this->file_->_bf._size - -+ this->file_->_p)}; -+ } -+ -+ void advance_write_buffer(size_t size) { -+ this->file_->_p += size; -+ this->file_->_w -= size; -+ } -+ -+ bool needs_flush() const { -+ if ((this->file_->_flags & line_buffered) == 0) return false; -+ return memchr(this->file_->_p + this->file_->_w, '\n', -+ to_unsigned(-this->file_->_w)); -+ } -+}; -+ -+// A fallback FILE wrapper. -+template class fallback_file : public file_base { -+ private: -+ char next_; // The next unconsumed character in the buffer. -+ bool has_next_ = false; -+ -+ public: -+ using file_base::file_base; -+ -+ auto is_buffered() const -> bool { return false; } -+ auto needs_flush() const -> bool { return false; } -+ void init_buffer() {} -+ -+ auto get_read_buffer() const -> span { -+ return {&next_, has_next_ ? 1u : 0u}; -+ } -+ -+ auto get_write_buffer() const -> span { return {nullptr, 0}; } -+ -+ void advance_write_buffer(size_t) {} -+ -+ auto get() -> int { -+ has_next_ = false; -+ return file_base::get(); -+ } -+ -+ void unget(char c) { -+ file_base::unget(c); -+ next_ = c; -+ has_next_ = true; -+ } -+}; -+ -+#ifndef FMT_USE_FALLBACK_FILE -+# define FMT_USE_FALLBACK_FILE 0 -+#endif -+ -+template -+auto get_file(F* f, int) -> apple_file { -+ return f; -+} -+template -+inline auto get_file(F* f, int) -> glibc_file { -+ return f; -+} -+ -+inline auto get_file(FILE* f, ...) -> fallback_file { return f; } -+ -+using file_ref = decltype(get_file(static_cast(nullptr), 0)); -+ -+template -+class file_print_buffer : public buffer { -+ public: -+ explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {} -+}; -+ -+template -+class file_print_buffer::value>> -+ : public buffer { -+ private: -+ file_ref file_; -+ -+ static void grow(buffer& base, size_t) { -+ auto& self = static_cast(base); -+ self.file_.advance_write_buffer(self.size()); -+ if (self.file_.get_write_buffer().size == 0) self.file_.flush(); -+ auto buf = self.file_.get_write_buffer(); -+ FMT_ASSERT(buf.size > 0, ""); -+ self.set(buf.data, buf.size); -+ self.clear(); -+ } -+ -+ public: -+ explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) { -+ flockfile(f); -+ file_.init_buffer(); -+ auto buf = file_.get_write_buffer(); -+ set(buf.data, buf.size); -+ } -+ ~file_print_buffer() { -+ file_.advance_write_buffer(size()); -+ bool flush = file_.needs_flush(); -+ F* f = file_; // Make funlockfile depend on the template parameter F -+ funlockfile(f); // for the system API detection to work. -+ if (flush) fflush(file_); -+ } -+}; -+ -+#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE) -+FMT_FUNC auto write_console(int, string_view) -> bool { return false; } - #else - using dword = conditional_t; - extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // - void*, const void*, dword, dword*, void*); - --FMT_FUNC bool write_console(std::FILE* f, string_view text) { -- auto fd = _fileno(f); -- if (!_isatty(fd)) return false; -+FMT_FUNC bool write_console(int fd, string_view text) { - auto u16 = utf8_to_utf16(text); -- auto written = dword(); - return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), -- static_cast(u16.size()), &written, nullptr) != 0; -+ static_cast(u16.size()), nullptr, nullptr) != 0; - } -+#endif - -+#ifdef _WIN32 - // Print assuming legacy (non-Unicode) encoding. --FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { -+FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args, -+ bool newline) { - auto buffer = memory_buffer(); -- detail::vformat_to(buffer, fmt, -- basic_format_args>(args)); -- fwrite_fully(buffer.data(), 1, buffer.size(), f); -+ detail::vformat_to(buffer, fmt, args); -+ if (newline) buffer.push_back('\n'); -+ fwrite_all(buffer.data(), buffer.size(), f); - } - #endif - - FMT_FUNC void print(std::FILE* f, string_view text) { -- if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f); -+#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) -+ int fd = _fileno(f); -+ if (_isatty(fd)) { -+ std::fflush(f); -+ if (write_console(fd, text)) return; -+ } -+#endif -+ fwrite_all(text.data(), text.size(), f); - } - } // namespace detail - -+FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) { -+ auto buffer = memory_buffer(); -+ detail::vformat_to(buffer, fmt, args); -+ detail::print(f, {buffer.data(), buffer.size()}); -+} -+ - FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { -+ if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>()) -+ return vprint_buffered(f, fmt, args); -+ auto&& buffer = detail::file_print_buffer<>(f); -+ return detail::vformat_to(buffer, fmt, args); -+} -+ -+FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) { - auto buffer = memory_buffer(); - detail::vformat_to(buffer, fmt, args); -+ buffer.push_back('\n'); - detail::print(f, {buffer.data(), buffer.size()}); - } - -diff --git a/include/fmt/format.h b/include/fmt/format.h -index a65d376..287e716 100644 ---- a/include/fmt/format.h -+++ b/include/fmt/format.h -@@ -33,24 +33,58 @@ - #ifndef FMT_FORMAT_H_ - #define FMT_FORMAT_H_ - --#include // std::signbit --#include // uint32_t --#include // std::memcpy --#include // std::initializer_list --#include // std::numeric_limits --#include // std::uninitialized_copy --#include // std::runtime_error --#include // std::system_error -- --#ifdef __cpp_lib_bit_cast --# include // std::bitcast -+#ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES -+# define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES -+# define FMT_REMOVE_TRANSITIVE_INCLUDES - #endif - --#include "core.h" -+#include "base.h" -+ -+#ifndef FMT_MODULE -+# include // std::signbit -+# include // std::byte -+# include // uint32_t -+# include // std::memcpy -+# include // std::numeric_limits -+# include // std::bad_alloc -+# if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI) -+// Workaround for pre gcc 5 libstdc++. -+# include // std::allocator_traits -+# endif -+# include // std::runtime_error -+# include // std::string -+# include // std::system_error -+ -+// Check FMT_CPLUSPLUS to avoid a warning in MSVC. -+# if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L -+# include // std::bit_cast -+# endif -+ -+// libc++ supports string_view in pre-c++17. -+# if FMT_HAS_INCLUDE() && \ -+ (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) -+# include -+# define FMT_USE_STRING_VIEW -+# endif - --#ifndef FMT_BEGIN_DETAIL_NAMESPACE --# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { --# define FMT_END_DETAIL_NAMESPACE } -+# if FMT_MSC_VERSION -+# include // _BitScanReverse[64], _umul128 -+# endif -+#endif // FMT_MODULE -+ -+#if defined(FMT_USE_NONTYPE_TEMPLATE_ARGS) -+// Use the provided definition. -+#elif defined(__NVCOMPILER) -+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 -+#elif FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L -+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 -+#elif defined(__cpp_nontype_template_args) && \ -+ __cpp_nontype_template_args >= 201911L -+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 -+#elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L -+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 -+#else -+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 - #endif - - #if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L -@@ -59,41 +93,22 @@ - # define FMT_INLINE_VARIABLE - #endif - --#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) --# define FMT_FALLTHROUGH [[fallthrough]] --#elif defined(__clang__) --# define FMT_FALLTHROUGH [[clang::fallthrough]] --#elif FMT_GCC_VERSION >= 700 && \ -- (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) --# define FMT_FALLTHROUGH [[gnu::fallthrough]] --#else --# define FMT_FALLTHROUGH --#endif -- --#ifndef FMT_DEPRECATED --# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 --# define FMT_DEPRECATED [[deprecated]] --# else --# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) --# define FMT_DEPRECATED __attribute__((deprecated)) --# elif FMT_MSC_VERSION --# define FMT_DEPRECATED __declspec(deprecated) --# else --# define FMT_DEPRECATED /* deprecated */ --# endif --# endif --#endif -- --#if FMT_GCC_VERSION || defined(__clang__) --# define FMT_VISIBILITY(value) __attribute__((visibility(value))) -+// Check if RTTI is disabled. -+#ifdef FMT_USE_RTTI -+// Use the provided definition. -+#elif defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \ -+ defined(__INTEL_RTTI__) || defined(__RTTI) -+// __RTTI is for EDG compilers. _CPPRTTI is for MSVC. -+# define FMT_USE_RTTI 1 - #else --# define FMT_VISIBILITY(value) -+# define FMT_USE_RTTI 0 - #endif - --#ifdef __has_builtin --# define FMT_HAS_BUILTIN(x) __has_builtin(x) -+// Visibility when compiled as a shared library/object. -+#if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) -+# define FMT_SO_VISIBILITY(value) FMT_VISIBILITY(value) - #else --# define FMT_HAS_BUILTIN(x) 0 -+# define FMT_SO_VISIBILITY(value) - #endif - - #if FMT_GCC_VERSION || FMT_CLANG_VERSION -@@ -102,8 +117,19 @@ - # define FMT_NOINLINE - #endif - -+namespace std { -+template struct iterator_traits> { -+ using iterator_category = output_iterator_tag; -+ using value_type = T; -+ using difference_type = -+ decltype(static_cast(nullptr) - static_cast(nullptr)); -+ using pointer = void; -+ using reference = void; -+}; -+} // namespace std -+ - #ifndef FMT_THROW --# if FMT_EXCEPTIONS -+# if FMT_USE_EXCEPTIONS - # if FMT_MSC_VERSION || defined(__NVCC__) - FMT_BEGIN_NAMESPACE - namespace detail { -@@ -122,35 +148,8 @@ FMT_END_NAMESPACE - # else - # define FMT_THROW(x) \ - ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what()) --# endif --#endif -- --#if FMT_EXCEPTIONS --# define FMT_TRY try --# define FMT_CATCH(x) catch (x) --#else --# define FMT_TRY if (true) --# define FMT_CATCH(x) if (false) --#endif -- --#ifndef FMT_MAYBE_UNUSED --# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) --# define FMT_MAYBE_UNUSED [[maybe_unused]] --# else --# define FMT_MAYBE_UNUSED --# endif --#endif -- --#ifndef FMT_USE_USER_DEFINED_LITERALS --// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. --# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ -- FMT_MSC_VERSION >= 1900) && \ -- (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) --# define FMT_USE_USER_DEFINED_LITERALS 1 --# else --# define FMT_USE_USER_DEFINED_LITERALS 0 --# endif --#endif -+# endif // FMT_USE_EXCEPTIONS -+#endif // FMT_THROW - - // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of - // integer formatter template instantiations to just one by only using the -@@ -160,7 +159,15 @@ FMT_END_NAMESPACE - # define FMT_REDUCE_INT_INSTANTIATIONS 0 - #endif - --// __builtin_clz is broken in clang with Microsoft CodeGen: -+FMT_BEGIN_NAMESPACE -+ -+template -+struct is_contiguous> -+ : std::true_type {}; -+ -+namespace detail { -+ -+// __builtin_clz is broken in clang with Microsoft codegen: - // https://github.com/fmtlib/fmt/issues/519. - #if !FMT_MSC_VERSION - # if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION -@@ -171,53 +178,30 @@ FMT_END_NAMESPACE - # endif - #endif - --// __builtin_ctz is broken in Intel Compiler Classic on Windows: --// https://github.com/fmtlib/fmt/issues/2510. --#ifndef __ICL --# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ -- defined(__NVCOMPILER) --# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) --# endif --# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ -- FMT_ICC_VERSION || defined(__NVCOMPILER) --# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) --# endif --#endif -- --#if FMT_MSC_VERSION --# include // _BitScanReverse[64], _BitScanForward[64], _umul128 --#endif -- --// Some compilers masquerade as both MSVC and GCC-likes or otherwise support -+// Some compilers masquerade as both MSVC and GCC but otherwise support - // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the - // MSVC intrinsics if the clz and clzll builtins are not available. --#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \ -- !defined(FMT_BUILTIN_CTZLL) --FMT_BEGIN_NAMESPACE --namespace detail { -+#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) - // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. --# if !defined(__clang__) --# pragma intrinsic(_BitScanForward) -+# ifndef __clang__ - # pragma intrinsic(_BitScanReverse) --# if defined(_WIN64) --# pragma intrinsic(_BitScanForward64) -+# ifdef _WIN64 - # pragma intrinsic(_BitScanReverse64) - # endif - # endif - - inline auto clz(uint32_t x) -> int { -+ FMT_ASSERT(x != 0, ""); -+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. - unsigned long r = 0; - _BitScanReverse(&r, x); -- FMT_ASSERT(x != 0, ""); -- // Static analysis complains about using uninitialized data -- // "r", but the only way that can happen is if "x" is 0, -- // which the callers guarantee to not happen. -- FMT_MSC_WARNING(suppress : 6102) - return 31 ^ static_cast(r); - } - # define FMT_BUILTIN_CLZ(n) detail::clz(n) - - inline auto clzll(uint64_t x) -> int { -+ FMT_ASSERT(x != 0, ""); -+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. - unsigned long r = 0; - # ifdef _WIN64 - _BitScanReverse64(&r, x); -@@ -228,56 +212,10 @@ inline auto clzll(uint64_t x) -> int { - // Scan the low 32 bits. - _BitScanReverse(&r, static_cast(x)); - # endif -- FMT_ASSERT(x != 0, ""); -- FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. - return 63 ^ static_cast(r); - } - # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) -- --inline auto ctz(uint32_t x) -> int { -- unsigned long r = 0; -- _BitScanForward(&r, x); -- FMT_ASSERT(x != 0, ""); -- FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. -- return static_cast(r); --} --# define FMT_BUILTIN_CTZ(n) detail::ctz(n) -- --inline auto ctzll(uint64_t x) -> int { -- unsigned long r = 0; -- FMT_ASSERT(x != 0, ""); -- FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. --# ifdef _WIN64 -- _BitScanForward64(&r, x); --# else -- // Scan the low 32 bits. -- if (_BitScanForward(&r, static_cast(x))) return static_cast(r); -- // Scan the high 32 bits. -- _BitScanForward(&r, static_cast(x >> 32)); -- r += 32; --# endif -- return static_cast(r); --} --# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) --} // namespace detail --FMT_END_NAMESPACE --#endif -- --FMT_BEGIN_NAMESPACE -- --template struct disjunction : std::false_type {}; --template struct disjunction

: P {}; --template --struct disjunction -- : conditional_t> {}; -- --template struct conjunction : std::true_type {}; --template struct conjunction

: P {}; --template --struct conjunction -- : conditional_t, P1> {}; -- --namespace detail { -+#endif // FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) - - FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { - ignore_unused(condition); -@@ -286,48 +224,24 @@ FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { - #endif - } - --template struct string_literal { -- static constexpr CharT value[sizeof...(C)] = {C...}; -- constexpr operator basic_string_view() const { -- return {value, sizeof...(C)}; -- } -+#if defined(FMT_USE_STRING_VIEW) -+template using std_string_view = std::basic_string_view; -+#else -+template struct std_string_view { -+ operator basic_string_view() const; - }; -- --#if FMT_CPLUSPLUS < 201703L --template --constexpr CharT string_literal::value[sizeof...(C)]; - #endif - --template class formatbuf : public Streambuf { -- private: -- using char_type = typename Streambuf::char_type; -- using streamsize = decltype(std::declval().sputn(nullptr, 0)); -- using int_type = typename Streambuf::int_type; -- using traits_type = typename Streambuf::traits_type; -- -- buffer& buffer_; -- -- public: -- explicit formatbuf(buffer& buf) : buffer_(buf) {} -- -- protected: -- // The put area is always empty. This makes the implementation simpler and has -- // the advantage that the streambuf and the buffer are always in sync and -- // sputc never writes into uninitialized memory. A disadvantage is that each -- // call to sputc always results in a (virtual) call to overflow. There is no -- // disadvantage here for sputn since this always results in a call to xsputn. -- -- auto overflow(int_type ch) -> int_type override { -- if (!traits_type::eq_int_type(ch, traits_type::eof())) -- buffer_.push_back(static_cast(ch)); -- return ch; -- } -- -- auto xsputn(const char_type* s, streamsize count) -> streamsize override { -- buffer_.append(s, s + count); -- return count; -+template struct string_literal { -+ static constexpr Char value[sizeof...(C)] = {C...}; -+ constexpr operator basic_string_view() const { -+ return {value, sizeof...(C)}; - } - }; -+#if FMT_CPLUSPLUS < 201703L -+template -+constexpr Char string_literal::value[sizeof...(C)]; -+#endif - - // Implementation of std::bit_cast for pre-C++20. - template -@@ -360,14 +274,12 @@ class uint128_fallback { - private: - uint64_t lo_, hi_; - -- friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept; -- - public: - constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} - constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} - -- constexpr uint64_t high() const noexcept { return hi_; } -- constexpr uint64_t low() const noexcept { return lo_; } -+ constexpr auto high() const noexcept -> uint64_t { return hi_; } -+ constexpr auto low() const noexcept -> uint64_t { return lo_; } - - template ::value)> - constexpr explicit operator T() const { -@@ -400,13 +312,14 @@ class uint128_fallback { - -> uint128_fallback { - return {~n.hi_, ~n.lo_}; - } -- friend auto operator+(const uint128_fallback& lhs, -- const uint128_fallback& rhs) -> uint128_fallback { -+ friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs, -+ const uint128_fallback& rhs) -+ -> uint128_fallback { - auto result = uint128_fallback(lhs); - result += rhs; - return result; - } -- friend auto operator*(const uint128_fallback& lhs, uint32_t rhs) -+ friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs) - -> uint128_fallback { - FMT_ASSERT(lhs.hi_ == 0, ""); - uint64_t hi = (lhs.lo_ >> 32) * rhs; -@@ -414,7 +327,7 @@ class uint128_fallback { - uint64_t new_lo = (hi << 32) + lo; - return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; - } -- friend auto operator-(const uint128_fallback& lhs, uint64_t rhs) -+ friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs) - -> uint128_fallback { - return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; - } -@@ -443,7 +356,7 @@ class uint128_fallback { - hi_ &= n.hi_; - } - -- FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept { -+ FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& { - if (is_constant_evaluated()) { - lo_ += n; - hi_ += (lo_ < n ? 1 : 0); -@@ -487,23 +400,24 @@ template constexpr auto num_bits() -> int { - } - // std::numeric_limits::digits may return 0 for 128-bit ints. - template <> constexpr auto num_bits() -> int { return 128; } --template <> constexpr auto num_bits() -> int { return 128; } -+template <> constexpr auto num_bits() -> int { return 128; } -+template <> constexpr auto num_bits() -> int { return 128; } - - // A heterogeneous bit_cast used for converting 96-bit long double to uint128_t - // and 128-bit pointers to uint128_fallback. - template sizeof(From))> - inline auto bit_cast(const From& from) -> To { -- constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned)); -+ constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned short)); - struct data_t { -- unsigned value[static_cast(size)]; -+ unsigned short value[static_cast(size)]; - } data = bit_cast(from); - auto result = To(); - if (const_check(is_big_endian())) { - for (int i = 0; i < size; ++i) -- result = (result << num_bits()) | data.value[i]; -+ result = (result << num_bits()) | data.value[i]; - } else { - for (int i = size - 1; i >= 0; --i) -- result = (result << num_bits()) | data.value[i]; -+ result = (result << num_bits()) | data.value[i]; - } - return result; - } -@@ -534,55 +448,30 @@ FMT_INLINE void assume(bool condition) { - (void)condition; - #if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION - __builtin_assume(condition); -+#elif FMT_GCC_VERSION -+ if (!condition) __builtin_unreachable(); - #endif - } - --// An approximation of iterator_t for pre-C++20 systems. --template --using iterator_t = decltype(std::begin(std::declval())); --template using sentinel_t = decltype(std::end(std::declval())); -- --// A workaround for std::string not having mutable data() until C++17. --template --inline auto get_data(std::basic_string& s) -> Char* { -- return &s[0]; --} --template --inline auto get_data(Container& c) -> typename Container::value_type* { -- return c.data(); --} -- --#if defined(_SECURE_SCL) && _SECURE_SCL --// Make a checked iterator to avoid MSVC warnings. --template using checked_ptr = stdext::checked_array_iterator; --template --constexpr auto make_checked(T* p, size_t size) -> checked_ptr { -- return {p, size}; --} --#else --template using checked_ptr = T*; --template constexpr auto make_checked(T* p, size_t) -> T* { -- return p; --} --#endif -- - // Attempts to reserve space for n extra characters in the output range. - // Returns a pointer to the reserved range or a reference to it. --template ::value)> -+template ::value&& -+ is_contiguous::value)> - #if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION - __attribute__((no_sanitize("undefined"))) - #endif --inline auto --reserve(std::back_insert_iterator it, size_t n) -- -> checked_ptr { -- Container& c = get_container(it); -+FMT_CONSTEXPR20 inline auto -+reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* { -+ auto& c = get_container(it); - size_t size = c.size(); - c.resize(size + n); -- return make_checked(get_data(c) + size, n); -+ return &c[size]; - } - - template --inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { -+FMT_CONSTEXPR20 inline auto reserve(basic_appender it, size_t n) -+ -> basic_appender { - buffer& buf = get_container(it); - buf.try_reserve(buf.size() + n); - return it; -@@ -601,18 +490,22 @@ template - constexpr auto to_pointer(OutputIt, size_t) -> T* { - return nullptr; - } --template auto to_pointer(buffer_appender it, size_t n) -> T* { -+template -+FMT_CONSTEXPR20 auto to_pointer(basic_appender it, size_t n) -> T* { - buffer& buf = get_container(it); -+ buf.try_reserve(buf.size() + n); - auto size = buf.size(); - if (buf.capacity() < size + n) return nullptr; - buf.try_resize(size + n); - return buf.data() + size; - } - --template ::value)> --inline auto base_iterator(std::back_insert_iterator& it, -- checked_ptr) -- -> std::back_insert_iterator { -+template ::value&& -+ is_contiguous::value)> -+inline auto base_iterator(OutputIt it, -+ typename OutputIt::container_type::value_type*) -+ -> OutputIt { - return it; - } - -@@ -631,23 +524,15 @@ FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) - } - template - FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { -- if (is_constant_evaluated()) { -- return fill_n(out, count, value); -- } -+ if (is_constant_evaluated()) return fill_n(out, count, value); - std::memset(out, value, to_unsigned(count)); - return out + count; - } - --#ifdef __cpp_char8_t --using char8_type = char8_t; --#else --enum char8_type : unsigned char {}; --#endif -- - template --FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, -- OutputIt out) -> OutputIt { -- return copy_str(begin, end, out); -+FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end, -+ OutputIt out) -> OutputIt { -+ return copy(begin, end, out); - } - - // A public domain branchless UTF-8 decoder by Christopher Wellons: -@@ -718,6 +603,7 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { - string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); - return result ? (error ? buf_ptr + 1 : end) : nullptr; - }; -+ - auto p = s.data(); - const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. - if (s.size() >= block_size) { -@@ -726,17 +612,20 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { - if (!p) return; - } - } -- if (auto num_chars_left = s.data() + s.size() - p) { -- char buf[2 * block_size - 1] = {}; -- copy_str(p, p + num_chars_left, buf); -- const char* buf_ptr = buf; -- do { -- auto end = decode(buf_ptr, p); -- if (!end) return; -- p += end - buf_ptr; -- buf_ptr = end; -- } while (buf_ptr - buf < num_chars_left); -- } -+ auto num_chars_left = to_unsigned(s.data() + s.size() - p); -+ if (num_chars_left == 0) return; -+ -+ // Suppress bogus -Wstringop-overflow. -+ if (FMT_GCC_VERSION) num_chars_left &= 3; -+ char buf[2 * block_size - 1] = {}; -+ copy(p, p + num_chars_left, buf); -+ const char* buf_ptr = buf; -+ do { -+ auto end = decode(buf_ptr, p); -+ if (!end) return; -+ p += end - buf_ptr; -+ buf_ptr = end; -+ } while (buf_ptr < buf + num_chars_left); - } - - template -@@ -745,13 +634,13 @@ inline auto compute_width(basic_string_view s) -> size_t { - } - - // Computes approximate display width of a UTF-8 string. --FMT_CONSTEXPR inline size_t compute_width(string_view s) { -+FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { - size_t num_code_points = 0; - // It is not a lambda for compatibility with C++14. - struct count_code_points { - size_t* count; - FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { -- *count += detail::to_unsigned( -+ *count += to_unsigned( - 1 + - (cp >= 0x1100 && - (cp <= 0x115f || // Hangul Jamo init. consonants -@@ -779,31 +668,24 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { - return num_code_points; - } - --inline auto compute_width(basic_string_view s) -> size_t { -- return compute_width( -- string_view(reinterpret_cast(s.data()), s.size())); --} -- - template - inline auto code_point_index(basic_string_view s, size_t n) -> size_t { -- size_t size = s.size(); -- return n < size ? n : size; -+ return min_of(n, s.size()); - } - - // Calculates the index of the nth code point in a UTF-8 string. - inline auto code_point_index(string_view s, size_t n) -> size_t { -- const char* data = s.data(); -- size_t num_code_points = 0; -- for (size_t i = 0, size = s.size(); i != size; ++i) { -- if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i; -- } -- return s.size(); --} -- --inline auto code_point_index(basic_string_view s, size_t n) -- -> size_t { -- return code_point_index( -- string_view(reinterpret_cast(s.data()), s.size()), n); -+ size_t result = s.size(); -+ const char* begin = s.begin(); -+ for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) { -+ if (n != 0) { -+ --n; -+ return true; -+ } -+ result = to_unsigned(sv.begin() - begin); -+ return false; -+ }); -+ return result; - } - - template struct is_integral : std::is_integral {}; -@@ -821,38 +703,22 @@ using is_integer = - !std::is_same::value && - !std::is_same::value>; - --#ifndef FMT_USE_FLOAT --# define FMT_USE_FLOAT 1 --#endif --#ifndef FMT_USE_DOUBLE --# define FMT_USE_DOUBLE 1 --#endif --#ifndef FMT_USE_LONG_DOUBLE --# define FMT_USE_LONG_DOUBLE 1 --#endif -- --#ifndef FMT_USE_FLOAT128 --# ifdef __clang__ --// Clang emulates GCC, so it has to appear early. --# if FMT_HAS_INCLUDE() --# define FMT_USE_FLOAT128 1 --# endif --# elif defined(__GNUC__) --// GNU C++: --# if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) --# define FMT_USE_FLOAT128 1 --# endif --# endif --# ifndef FMT_USE_FLOAT128 --# define FMT_USE_FLOAT128 0 --# endif -+#if defined(FMT_USE_FLOAT128) -+// Use the provided definition. -+#elif FMT_CLANG_VERSION && FMT_HAS_INCLUDE() -+# define FMT_USE_FLOAT128 1 -+#elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \ -+ !defined(__STRICT_ANSI__) -+# define FMT_USE_FLOAT128 1 -+#else -+# define FMT_USE_FLOAT128 0 - #endif -- - #if FMT_USE_FLOAT128 - using float128 = __float128; - #else --using float128 = void; -+struct float128 {}; - #endif -+ - template using is_float128 = std::is_same; - - template -@@ -871,24 +737,21 @@ using is_double_double = bool_constant::digits == 106>; - # define FMT_USE_FULL_CACHE_DRAGONBOX 0 - #endif - --template --template --void buffer::append(const U* begin, const U* end) { -- while (begin != end) { -- auto count = to_unsigned(end - begin); -- try_reserve(size_ + count); -- auto free_cap = capacity_ - size_; -- if (free_cap < count) count = free_cap; -- std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); -- size_ += count; -- begin += count; -+// An allocator that uses malloc/free to allow removing dependency on the C++ -+// standard libary runtime. -+template struct allocator { -+ using value_type = T; -+ -+ T* allocate(size_t n) { -+ FMT_ASSERT(n <= max_value() / sizeof(T), ""); -+ T* p = static_cast(malloc(n * sizeof(T))); -+ if (!p) FMT_THROW(std::bad_alloc()); -+ return p; - } --} - --template --struct is_locale : std::false_type {}; --template --struct is_locale> : std::true_type {}; -+ void deallocate(T* p, size_t) { free(p); } -+}; -+ - } // namespace detail - - FMT_BEGIN_EXPORT -@@ -898,34 +761,26 @@ FMT_BEGIN_EXPORT - enum { inline_buffer_size = 500 }; - - /** -- \rst -- A dynamically growing memory buffer for trivially copyable/constructible types -- with the first ``SIZE`` elements stored in the object itself. -- -- You can use the ``memory_buffer`` type alias for ``char`` instead. -- -- **Example**:: -- -- auto out = fmt::memory_buffer(); -- format_to(std::back_inserter(out), "The answer is {}.", 42); -- -- This will append the following output to the ``out`` object: -- -- .. code-block:: none -- -- The answer is 42. -- -- The output can be converted to an ``std::string`` with ``to_string(out)``. -- \endrst -+ * A dynamically growing memory buffer for trivially copyable/constructible -+ * types with the first `SIZE` elements stored in the object itself. Most -+ * commonly used via the `memory_buffer` alias for `char`. -+ * -+ * **Example**: -+ * -+ * auto out = fmt::memory_buffer(); -+ * fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); -+ * -+ * This will append "The answer is 42." to `out`. The buffer content can be -+ * converted to `std::string` with `to_string(out)`. - */ - template > --class basic_memory_buffer final : public detail::buffer { -+ typename Allocator = detail::allocator> -+class basic_memory_buffer : public detail::buffer { - private: - T store_[SIZE]; - -- // Don't inherit from Allocator avoid generating type_info for it. -- Allocator alloc_; -+ // Don't inherit from Allocator to avoid generating type_info for it. -+ FMT_NO_UNIQUE_ADDRESS Allocator alloc_; - - // Deallocate memory allocated by the buffer. - FMT_CONSTEXPR20 void deallocate() { -@@ -933,36 +788,37 @@ class basic_memory_buffer final : public detail::buffer { - if (data != store_) alloc_.deallocate(data, this->capacity()); - } - -- protected: -- FMT_CONSTEXPR20 void grow(size_t size) override { -+ static FMT_CONSTEXPR20 void grow(detail::buffer& buf, size_t size) { - detail::abort_fuzzing_if(size > 5000); -- const size_t max_size = std::allocator_traits::max_size(alloc_); -- size_t old_capacity = this->capacity(); -+ auto& self = static_cast(buf); -+ const size_t max_size = -+ std::allocator_traits::max_size(self.alloc_); -+ size_t old_capacity = buf.capacity(); - size_t new_capacity = old_capacity + old_capacity / 2; - if (size > new_capacity) - new_capacity = size; - else if (new_capacity > max_size) -- new_capacity = size > max_size ? size : max_size; -- T* old_data = this->data(); -- T* new_data = -- std::allocator_traits::allocate(alloc_, new_capacity); -+ new_capacity = max_of(size, max_size); -+ T* old_data = buf.data(); -+ T* new_data = self.alloc_.allocate(new_capacity); -+ // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481). -+ detail::assume(buf.size() <= new_capacity); - // The following code doesn't throw, so the raw pointer above doesn't leak. -- std::uninitialized_copy(old_data, old_data + this->size(), -- detail::make_checked(new_data, new_capacity)); -- this->set(new_data, new_capacity); -+ memcpy(new_data, old_data, buf.size() * sizeof(T)); -+ self.set(new_data, new_capacity); - // deallocate must not throw according to the standard, but even if it does, - // the buffer already uses the new storage and will deallocate it in - // destructor. -- if (old_data != store_) alloc_.deallocate(old_data, old_capacity); -+ if (old_data != self.store_) self.alloc_.deallocate(old_data, old_capacity); - } - - public: - using value_type = T; - using const_reference = const T&; - -- FMT_CONSTEXPR20 explicit basic_memory_buffer( -+ FMT_CONSTEXPR explicit basic_memory_buffer( - const Allocator& alloc = Allocator()) -- : alloc_(alloc) { -+ : detail::buffer(grow), alloc_(alloc) { - this->set(store_, SIZE); - if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); - } -@@ -976,8 +832,7 @@ class basic_memory_buffer final : public detail::buffer { - size_t size = other.size(), capacity = other.capacity(); - if (data == other.store_) { - this->set(store_, capacity); -- detail::copy_str(other.store_, other.store_ + size, -- detail::make_checked(store_, capacity)); -+ detail::copy(other.store_, other.store_ + size, store_); - } else { - this->set(data, capacity); - // Set pointer to the inline array so that delete is not called -@@ -989,21 +844,14 @@ class basic_memory_buffer final : public detail::buffer { - } - - public: -- /** -- \rst -- Constructs a :class:`fmt::basic_memory_buffer` object moving the content -- of the other object to it. -- \endrst -- */ -- FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { -+ /// Constructs a `basic_memory_buffer` object moving the content of the other -+ /// object to it. -+ FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept -+ : detail::buffer(grow) { - move(other); - } - -- /** -- \rst -- Moves the content of the other ``basic_memory_buffer`` object to this one. -- \endrst -- */ -+ /// Moves the content of the other `basic_memory_buffer` object to this one. - auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { - FMT_ASSERT(this != &other, ""); - deallocate(); -@@ -1014,119 +862,108 @@ class basic_memory_buffer final : public detail::buffer { - // Returns a copy of the allocator associated with this buffer. - auto get_allocator() const -> Allocator { return alloc_; } - -- /** -- Resizes the buffer to contain *count* elements. If T is a POD type new -- elements may not be initialized. -- */ -- FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } -+ /// Resizes the buffer to contain `count` elements. If T is a POD type new -+ /// elements may not be initialized. -+ FMT_CONSTEXPR void resize(size_t count) { this->try_resize(count); } - -- /** Increases the buffer capacity to *new_capacity*. */ -+ /// Increases the buffer capacity to `new_capacity`. - void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } - -- // Directly append data into the buffer - using detail::buffer::append; - template -- void append(const ContiguousRange& range) { -+ FMT_CONSTEXPR20 void append(const ContiguousRange& range) { - append(range.data(), range.data() + range.size()); - } - }; - - using memory_buffer = basic_memory_buffer; - -+template -+FMT_NODISCARD auto to_string(const basic_memory_buffer& buf) -+ -> std::string { -+ auto size = buf.size(); -+ detail::assume(size < std::string().max_size()); -+ return {buf.data(), size}; -+} -+ -+// A writer to a buffered stream. It doesn't own the underlying stream. -+class writer { -+ private: -+ detail::buffer* buf_; -+ -+ // We cannot create a file buffer in advance because any write to a FILE may -+ // invalidate it. -+ FILE* file_; -+ -+ public: -+ inline writer(FILE* f) : buf_(nullptr), file_(f) {} -+ inline writer(detail::buffer& buf) : buf_(&buf) {} -+ -+ /// Formats `args` according to specifications in `fmt` and writes the -+ /// output to the file. -+ template void print(format_string fmt, T&&... args) { -+ if (buf_) -+ fmt::format_to(appender(*buf_), fmt, std::forward(args)...); -+ else -+ fmt::print(file_, fmt, std::forward(args)...); -+ } -+}; -+ -+class string_buffer { -+ private: -+ std::string str_; -+ detail::container_buffer buf_; -+ -+ public: -+ inline string_buffer() : buf_(str_) {} -+ -+ inline operator writer() { return buf_; } -+ inline std::string& str() { return str_; } -+}; -+ - template - struct is_contiguous> : std::true_type { - }; - --FMT_END_EXPORT --namespace detail { --FMT_API bool write_console(std::FILE* f, string_view text); --FMT_API void print(std::FILE*, string_view); --} // namespace detail --FMT_BEGIN_EXPORT -- - // Suppress a misleading warning in older versions of clang. --#if FMT_CLANG_VERSION --# pragma clang diagnostic ignored "-Wweak-vtables" --#endif -+FMT_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables") - --/** An error reported from a formatting function. */ --class FMT_VISIBILITY("default") format_error : public std::runtime_error { -+/// An error reported from a formatting function. -+class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error { - public: - using std::runtime_error::runtime_error; - }; - --namespace detail_exported { --#if FMT_USE_NONTYPE_TEMPLATE_ARGS -+class loc_value; -+ -+FMT_END_EXPORT -+namespace detail { -+FMT_API auto write_console(int fd, string_view text) -> bool; -+FMT_API void print(FILE*, string_view); -+} // namespace detail -+ -+namespace detail { - template struct fixed_string { -- constexpr fixed_string(const Char (&str)[N]) { -- detail::copy_str(static_cast(str), -- str + N, data); -+ FMT_CONSTEXPR20 fixed_string(const Char (&s)[N]) { -+ detail::copy(static_cast(s), s + N, -+ data); - } - Char data[N] = {}; - }; --#endif - - // Converts a compile-time string to basic_string_view. --template -+FMT_EXPORT template - constexpr auto compile_string_to_view(const Char (&s)[N]) - -> basic_string_view { - // Remove trailing NUL character if needed. Won't be present if this is used - // with a raw character array (i.e. not defined as a string). - return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; - } --template --constexpr auto compile_string_to_view(detail::std_string_view s) -+FMT_EXPORT template -+constexpr auto compile_string_to_view(basic_string_view s) - -> basic_string_view { -- return {s.data(), s.size()}; -+ return s; - } --} // namespace detail_exported -- --class loc_value { -- private: -- basic_format_arg value_; -- -- public: -- template ::value)> -- loc_value(T value) : value_(detail::make_arg(value)) {} -- -- template ::value)> -- loc_value(T) {} -- -- template auto visit(Visitor&& vis) -> decltype(vis(0)) { -- return visit_format_arg(vis, value_); -- } --}; -- --// A locale facet that formats values in UTF-8. --// It is parameterized on the locale to avoid the heavy include. --template class format_facet : public Locale::facet { -- private: -- std::string separator_; -- std::string grouping_; -- std::string decimal_point_; -- -- protected: -- virtual auto do_put(appender out, loc_value val, -- const format_specs<>& specs) const -> bool; -- -- public: -- static FMT_API typename Locale::id id; -- -- explicit format_facet(Locale& loc); -- explicit format_facet(string_view sep = "", -- std::initializer_list g = {3}, -- std::string decimal_point = ".") -- : separator_(sep.data(), sep.size()), -- grouping_(g.begin(), g.end()), -- decimal_point_(decimal_point) {} -- -- auto put(appender out, loc_value val, const format_specs<>& specs) const -- -> bool { -- return do_put(out, val, specs); -- } --}; -- --FMT_BEGIN_DETAIL_NAMESPACE - - // Returns true if value is negative, false otherwise. - // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. -@@ -1139,14 +976,6 @@ constexpr auto is_negative(T) -> bool { - return false; - } - --template --FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { -- if (std::is_same()) return FMT_USE_FLOAT; -- if (std::is_same()) return FMT_USE_DOUBLE; -- if (std::is_same()) return FMT_USE_LONG_DOUBLE; -- return true; --} -- - // Smallest of uint32_t, uint64_t, uint128_t that is large enough to - // represent all values of an integral type T. - template -@@ -1157,27 +986,28 @@ using uint32_or_64_or_128_t = - template - using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; - --#define FMT_POWERS_OF_10(factor) \ -- factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ -- (factor)*1000000, (factor)*10000000, (factor)*100000000, \ -- (factor)*1000000000 -+#define FMT_POWERS_OF_10(factor) \ -+ factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \ -+ (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \ -+ (factor) * 100000000, (factor) * 1000000000 - - // Converts value in the range [0, 100) to a string. --constexpr const char* digits2(size_t value) { -- // GCC generates slightly better code when value is pointer-size. -- return &"0001020304050607080910111213141516171819" -- "2021222324252627282930313233343536373839" -- "4041424344454647484950515253545556575859" -- "6061626364656667686970717273747576777879" -- "8081828384858687888990919293949596979899"[value * 2]; --} -- --// Sign is a template parameter to workaround a bug in gcc 4.8. --template constexpr Char sign(Sign s) { --#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 -- static_assert(std::is_same::value, ""); --#endif -- return static_cast("\0-+ "[s]); -+// GCC generates slightly better code when value is pointer-size. -+inline auto digits2(size_t value) -> const char* { -+ // Align data since unaligned access may be slower when crossing a -+ // hardware-specific boundary. -+ alignas(2) static const char data[] = -+ "0001020304050607080910111213141516171819" -+ "2021222324252627282930313233343536373839" -+ "4041424344454647484950515253545556575859" -+ "6061626364656667686970717273747576777879" -+ "8081828384858687888990919293949596979899"; -+ return &data[value * 2]; -+} -+ -+template constexpr auto getsign(sign s) -> Char { -+ return static_cast(((' ' << 24) | ('+' << 16) | ('-' << 8)) >> -+ (static_cast(s) * 8)); - } - - template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { -@@ -1225,9 +1055,7 @@ inline auto do_count_digits(uint64_t n) -> int { - // except for n == 0 in which case count_digits returns 1. - FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { - #ifdef FMT_BUILTIN_CLZLL -- if (!is_constant_evaluated()) { -- return do_count_digits(n); -- } -+ if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); - #endif - return count_digits_fallback(n); - } -@@ -1255,7 +1083,7 @@ FMT_CONSTEXPR auto count_digits(UInt n) -> int { - FMT_INLINE auto do_count_digits(uint32_t n) -> int { - // An optimization by Kendall Willets from https://bit.ly/3uOIQrB. - // This increments the upper 32 bits (log10(T) - 1) when >= T is added. --# define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T) -+# define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T) - static constexpr uint64_t table[] = { - FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 - FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 -@@ -1277,9 +1105,7 @@ FMT_INLINE auto do_count_digits(uint32_t n) -> int { - // Optional version of count_digits for better performance on 32-bit platforms. - FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { - #ifdef FMT_BUILTIN_CLZ -- if (!is_constant_evaluated()) { -- return do_count_digits(n); -- } -+ if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); - #endif - return count_digits_fallback(n); - } -@@ -1316,91 +1142,118 @@ template <> inline auto decimal_point(locale_ref loc) -> wchar_t { - return decimal_point_impl(loc); - } - --// Compares two characters for equality. --template auto equal2(const Char* lhs, const char* rhs) -> bool { -- return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); --} --inline auto equal2(const char* lhs, const char* rhs) -> bool { -+#ifndef FMT_HEADER_ONLY -+FMT_BEGIN_EXPORT -+extern template FMT_API auto thousands_sep_impl(locale_ref) -+ -> thousands_sep_result; -+extern template FMT_API auto thousands_sep_impl(locale_ref) -+ -> thousands_sep_result; -+extern template FMT_API auto decimal_point_impl(locale_ref) -> char; -+extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; -+FMT_END_EXPORT -+#endif // FMT_HEADER_ONLY -+ -+// Compares two characters for equality. -+template auto equal2(const Char* lhs, const char* rhs) -> bool { -+ return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); -+} -+inline auto equal2(const char* lhs, const char* rhs) -> bool { - return memcmp(lhs, rhs, 2) == 0; - } - --// Copies two characters from src to dst. -+// Writes a two-digit value to out. - template --FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { -- if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { -- memcpy(dst, src, 2); -+FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) { -+ if (!is_constant_evaluated() && std::is_same::value && -+ !FMT_OPTIMIZE_SIZE) { -+ memcpy(out, digits2(value), 2); - return; - } -- *dst++ = static_cast(*src++); -- *dst = static_cast(*src); -+ *out++ = static_cast('0' + value / 10); -+ *out = static_cast('0' + value % 10); - } - --template struct format_decimal_result { -- Iterator begin; -- Iterator end; --}; -- --// Formats a decimal unsigned integer value writing into out pointing to a --// buffer of specified size. The caller must ensure that the buffer is large --// enough. -+// Formats a decimal unsigned integer value writing to out pointing to a buffer -+// of specified size. The caller must ensure that the buffer is large enough. - template --FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) -- -> format_decimal_result { -+FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size) -+ -> Char* { - FMT_ASSERT(size >= count_digits(value), "invalid digit count"); -- out += size; -- Char* end = out; -+ unsigned n = to_unsigned(size); - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. -- out -= 2; -- copy2(out, digits2(static_cast(value % 100))); -+ n -= 2; -+ write2digits(out + n, static_cast(value % 100)); - value /= 100; - } -- if (value < 10) { -- *--out = static_cast('0' + value); -- return {out, end}; -+ if (value >= 10) { -+ n -= 2; -+ write2digits(out + n, static_cast(value)); -+ } else { -+ out[--n] = static_cast('0' + value); - } -- out -= 2; -- copy2(out, digits2(static_cast(value))); -- return {out, end}; -+ return out + n; -+} -+ -+template -+FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value, -+ int num_digits) -> Char* { -+ do_format_decimal(out, value, num_digits); -+ return out + num_digits; - } - --template >::value)> --FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) -- -> format_decimal_result { -+template >::value)> -+FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits) -+ -> OutputIt { -+ if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { -+ do_format_decimal(ptr, value, num_digits); -+ return out; -+ } - // Buffer is large enough to hold all digits (digits10 + 1). -- Char buffer[digits10() + 1] = {}; -- auto end = format_decimal(buffer, value, size).end; -- return {out, detail::copy_str_noinline(buffer, end, out)}; -+ char buffer[digits10() + 1]; -+ if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); -+ do_format_decimal(buffer, value, num_digits); -+ return copy_noinline(buffer, buffer + num_digits, out); - } - --template --FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, -- bool upper = false) -> Char* { -- buffer += num_digits; -- Char* end = buffer; -+template -+FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, -+ int size, bool upper = false) -> Char* { -+ out += size; - do { - const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; -- unsigned digit = static_cast(value & ((1 << BASE_BITS) - 1)); -- *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) -- : digits[digit]); -- } while ((value >>= BASE_BITS) != 0); -- return end; -+ unsigned digit = static_cast(value & ((1 << base_bits) - 1)); -+ *--out = static_cast(base_bits < 4 ? static_cast('0' + digit) -+ : digits[digit]); -+ } while ((value >>= base_bits) != 0); -+ return out; -+} -+ -+// Formats an unsigned integer in the power of two base (binary, octal, hex). -+template -+FMT_CONSTEXPR auto format_base2e(int base_bits, Char* out, UInt value, -+ int num_digits, bool upper = false) -> Char* { -+ do_format_base2e(base_bits, out, value, num_digits, upper); -+ return out + num_digits; - } - --template --inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) -- -> It { -+template ::value)> -+FMT_CONSTEXPR inline auto format_base2e(int base_bits, OutputIt out, UInt value, -+ int num_digits, bool upper = false) -+ -> OutputIt { - if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { -- format_uint(ptr, value, num_digits, upper); -+ format_base2e(base_bits, ptr, value, num_digits, upper); - return out; - } -- // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). -- char buffer[num_bits() / BASE_BITS + 1]; -- format_uint(buffer, value, num_digits, upper); -- return detail::copy_str_noinline(buffer, buffer + num_digits, out); -+ // Make buffer large enough for any base. -+ char buffer[num_bits()]; -+ if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); -+ format_base2e(base_bits, buffer, value, num_digits, upper); -+ return detail::copy_noinline(buffer, buffer + num_digits, out); - } - - // A converter from UTF-8 to UTF-16. -@@ -1410,12 +1263,16 @@ class utf8_to_utf16 { - - public: - FMT_API explicit utf8_to_utf16(string_view s); -- operator basic_string_view() const { return {&buffer_[0], size()}; } -- auto size() const -> size_t { return buffer_.size() - 1; } -- auto c_str() const -> const wchar_t* { return &buffer_[0]; } -- auto str() const -> std::wstring { return {&buffer_[0], size()}; } -+ inline operator basic_string_view() const { -+ return {&buffer_[0], size()}; -+ } -+ inline auto size() const -> size_t { return buffer_.size() - 1; } -+ inline auto c_str() const -> const wchar_t* { return &buffer_[0]; } -+ inline auto str() const -> std::wstring { return {&buffer_[0], size()}; } - }; - -+enum class to_utf8_error_policy { abort, replace }; -+ - // A converter from UTF-16/UTF-32 (host endian) to UTF-8. - template class to_utf8 { - private: -@@ -1423,37 +1280,45 @@ template class to_utf8 { - - public: - to_utf8() {} -- explicit to_utf8(basic_string_view s) { -+ explicit to_utf8(basic_string_view s, -+ to_utf8_error_policy policy = to_utf8_error_policy::abort) { - static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, - "Expect utf16 or utf32"); -- -- if (!convert(s)) -+ if (!convert(s, policy)) - FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" - : "invalid utf32")); - } - operator string_view() const { return string_view(&buffer_[0], size()); } -- size_t size() const { return buffer_.size() - 1; } -- const char* c_str() const { return &buffer_[0]; } -- std::string str() const { return std::string(&buffer_[0], size()); } -+ auto size() const -> size_t { return buffer_.size() - 1; } -+ auto c_str() const -> const char* { return &buffer_[0]; } -+ auto str() const -> std::string { return std::string(&buffer_[0], size()); } - - // Performs conversion returning a bool instead of throwing exception on - // conversion error. This method may still throw in case of memory allocation - // error. -- bool convert(basic_string_view s) { -- if (!convert(buffer_, s)) return false; -+ auto convert(basic_string_view s, -+ to_utf8_error_policy policy = to_utf8_error_policy::abort) -+ -> bool { -+ if (!convert(buffer_, s, policy)) return false; - buffer_.push_back(0); - return true; - } -- static bool convert(Buffer& buf, basic_string_view s) { -+ static auto convert(Buffer& buf, basic_string_view s, -+ to_utf8_error_policy policy = to_utf8_error_policy::abort) -+ -> bool { - for (auto p = s.begin(); p != s.end(); ++p) { - uint32_t c = static_cast(*p); - if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { -- // surrogate pair -+ // Handle a surrogate pair. - ++p; - if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { -- return false; -+ if (policy == to_utf8_error_policy::abort) return false; -+ buf.append(string_view("\xEF\xBF\xBD")); -+ --p; -+ continue; -+ } else { -+ c = (c << 10) + static_cast(*p) - 0x35fdc00; - } -- c = (c << 10) + static_cast(*p) - 0x35fdc00; - } - if (c < 0x80) { - buf.push_back(static_cast(c)); -@@ -1478,14 +1343,14 @@ template class to_utf8 { - }; - - // Computes 128-bit result of multiplication of two 64-bit unsigned integers. --inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { -+inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { - #if FMT_USE_INT128 - auto p = static_cast(x) * static_cast(y); - return {static_cast(p >> 64), static_cast(p)}; - #elif defined(_MSC_VER) && defined(_M_X64) -- auto result = uint128_fallback(); -- result.lo_ = _umul128(x, y, &result.hi_); -- return result; -+ auto hi = uint64_t(); -+ auto lo = _umul128(x, y, &hi); -+ return {hi, lo}; - #else - const uint64_t mask = static_cast(max_value()); - -@@ -1509,19 +1374,19 @@ inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { - namespace dragonbox { - // Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from - // https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. --inline int floor_log10_pow2(int e) noexcept { -+inline auto floor_log10_pow2(int e) noexcept -> int { - FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); - static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); - return (e * 315653) >> 20; - } - --inline int floor_log2_pow10(int e) noexcept { -+inline auto floor_log2_pow10(int e) noexcept -> int { - FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); - return (e * 1741647) >> 19; - } - - // Computes upper 64 bits of multiplication of two 64-bit unsigned integers. --inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { -+inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t { - #if FMT_USE_INT128 - auto p = static_cast(x) * static_cast(y); - return static_cast(p >> 64); -@@ -1534,14 +1399,14 @@ inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { - - // Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a - // 128-bit unsigned integer. --inline uint128_fallback umul192_upper128(uint64_t x, -- uint128_fallback y) noexcept { -+inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept -+ -> uint128_fallback { - uint128_fallback r = umul128(x, y.high()); - r += umul128_upper64(x, y.low()); - return r; - } - --FMT_API uint128_fallback get_cached_power(int k) noexcept; -+FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback; - - // Type-specific information that Dragonbox uses. - template struct float_info; -@@ -1595,14 +1460,14 @@ template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; - } // namespace dragonbox - - // Returns true iff Float has the implicit bit which is not stored. --template constexpr bool has_implicit_bit() { -+template constexpr auto has_implicit_bit() -> bool { - // An 80-bit FP number has a 64-bit significand an no implicit bit. - return std::numeric_limits::digits != 64; - } - - // Returns the number of significand bits stored in Float. The implicit bit is - // not counted since it is not stored. --template constexpr int num_significand_bits() { -+template constexpr auto num_significand_bits() -> int { - // std::numeric_limits may not support __float128. - return is_float128() ? 112 - : (std::numeric_limits::digits - -@@ -1623,25 +1488,30 @@ template constexpr auto exponent_bias() -> int { - } - - // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. --template --FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { -+template -+FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { - FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); - if (exp < 0) { -- *it++ = static_cast('-'); -+ *out++ = static_cast('-'); - exp = -exp; - } else { -- *it++ = static_cast('+'); -+ *out++ = static_cast('+'); - } -- if (exp >= 100) { -- const char* top = digits2(to_unsigned(exp / 100)); -- if (exp >= 1000) *it++ = static_cast(top[0]); -- *it++ = static_cast(top[1]); -- exp %= 100; -- } -- const char* d = digits2(to_unsigned(exp)); -- *it++ = static_cast(d[0]); -- *it++ = static_cast(d[1]); -- return it; -+ auto uexp = static_cast(exp); -+ if (is_constant_evaluated()) { -+ if (uexp < 10) *out++ = '0'; -+ return format_decimal(out, uexp, count_digits(uexp)); -+ } -+ if (uexp >= 100u) { -+ const char* top = digits2(uexp / 100); -+ if (uexp >= 1000u) *out++ = static_cast(top[0]); -+ *out++ = static_cast(top[1]); -+ uexp %= 100; -+ } -+ const char* d = digits2(uexp); -+ *out++ = static_cast(d[0]); -+ *out++ = static_cast(d[1]); -+ return out; - } - - // A floating-point number f * pow(2, e) where F is an unsigned type. -@@ -1695,7 +1565,7 @@ using fp = basic_fp; - - // Normalizes the value converted from double and multiplied by (1 << SHIFT). - template --FMT_CONSTEXPR basic_fp normalize(basic_fp value) { -+FMT_CONSTEXPR auto normalize(basic_fp value) -> basic_fp { - // Handle subnormals. - const auto implicit_bit = F(1) << num_significand_bits(); - const auto shifted_implicit_bit = implicit_bit << SHIFT; -@@ -1712,7 +1582,7 @@ FMT_CONSTEXPR basic_fp normalize(basic_fp value) { - } - - // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. --FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { -+FMT_CONSTEXPR inline auto multiply(uint64_t lhs, uint64_t rhs) -> uint64_t { - #if FMT_USE_INT128 - auto product = static_cast<__uint128_t>(lhs) * rhs; - auto f = static_cast(product >> 64); -@@ -1729,191 +1599,82 @@ FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { - #endif - } - --FMT_CONSTEXPR inline fp operator*(fp x, fp y) { -+FMT_CONSTEXPR inline auto operator*(fp x, fp y) -> fp { - return {multiply(x.f, y.f), x.e + y.e + 64}; - } - --template struct basic_data { -- // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. -- // These are generated by support/compute-powers.py. -- static constexpr uint64_t pow10_significands[87] = { -- 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, -- 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, -- 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, -- 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, -- 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, -- 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, -- 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, -- 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, -- 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, -- 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, -- 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, -- 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, -- 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, -- 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, -- 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, -- 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, -- 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, -- 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, -- 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, -- 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, -- 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, -- 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, -- 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, -- 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, -- 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, -- 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, -- 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, -- 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, -- 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, -- }; -- --#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 --# pragma GCC diagnostic push --# pragma GCC diagnostic ignored "-Wnarrowing" --#endif -- // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding -- // to significands above. -- static constexpr int16_t pow10_exponents[87] = { -- -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -- -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, -- -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, -- -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, -- -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, -- 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, -- 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, -- 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; --#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 --# pragma GCC diagnostic pop --#endif -- -- static constexpr uint64_t power_of_10_64[20] = { -- 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), -- 10000000000000000000ULL}; -- -- // For checking rounding thresholds. -- // The kth entry is chosen to be the smallest integer such that the -- // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. -- static constexpr uint32_t fractional_part_rounding_thresholds[8] = { -- 2576980378, // ceil(2^31 + 2^32/10^1) -- 2190433321, // ceil(2^31 + 2^32/10^2) -- 2151778616, // ceil(2^31 + 2^32/10^3) -- 2147913145, // ceil(2^31 + 2^32/10^4) -- 2147526598, // ceil(2^31 + 2^32/10^5) -- 2147487943, // ceil(2^31 + 2^32/10^6) -- 2147484078, // ceil(2^31 + 2^32/10^7) -- 2147483691 // ceil(2^31 + 2^32/10^8) -- }; --}; -- --#if FMT_CPLUSPLUS < 201703L --template constexpr uint64_t basic_data::pow10_significands[]; --template constexpr int16_t basic_data::pow10_exponents[]; --template constexpr uint64_t basic_data::power_of_10_64[]; --template --constexpr uint32_t basic_data::fractional_part_rounding_thresholds[]; --#endif -- --// This is a struct rather than an alias to avoid shadowing warnings in gcc. --struct data : basic_data<> {}; -- --// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its --// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. --FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, -- int& pow10_exponent) { -- const int shift = 32; -- // log10(2) = 0x0.4d104d427de7fbcc... -- const int64_t significand = 0x4d104d427de7fbcc; -- int index = static_cast( -- ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + -- ((int64_t(1) << shift) - 1)) // ceil -- >> 32 // arithmetic shift -- ); -- // Decimal exponent of the first (smallest) cached power of 10. -- const int first_dec_exp = -348; -- // Difference between 2 consecutive decimal exponents in cached powers of 10. -- const int dec_exp_step = 8; -- index = (index - first_dec_exp - 1) / dec_exp_step + 1; -- pow10_exponent = first_dec_exp + index * dec_exp_step; -- // Using *(x + index) instead of x[index] avoids an issue with some compilers -- // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode). -- return {*(data::pow10_significands + index), -- *(data::pow10_exponents + index)}; --} -- --template -+template () == num_bits()> - using convert_float_result = -- conditional_t::value || -- std::numeric_limits::digits == -- std::numeric_limits::digits, -- double, T>; -+ conditional_t::value || doublish, double, T>; - - template - constexpr auto convert_float(T value) -> convert_float_result { - return static_cast>(value); - } - --template -+template - FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, -- const fill_t& fill) -> OutputIt { -- auto fill_size = fill.size(); -- if (fill_size == 1) return detail::fill_n(it, n, fill[0]); -- auto data = fill.data(); -- for (size_t i = 0; i < n; ++i) -- it = copy_str(data, data + fill_size, it); -+ const basic_specs& specs) -> OutputIt { -+ auto fill_size = specs.fill_size(); -+ if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit()); -+ if (const Char* data = specs.fill()) { -+ for (size_t i = 0; i < n; ++i) it = copy(data, data + fill_size, it); -+ } - return it; - } - - // Writes the output of f, padded according to format specifications in specs. - // size: output size in code units. - // width: output display width in (terminal) column positions. --template --FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, -+FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, - size_t size, size_t width, F&& f) -> OutputIt { -- static_assert(align == align::left || align == align::right, ""); -+ static_assert(default_align == align::left || default_align == align::right, -+ ""); - unsigned spec_width = to_unsigned(specs.width); - size_t padding = spec_width > width ? spec_width - width : 0; - // Shifts are encoded as string literals because static constexpr is not - // supported in constexpr functions. -- auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; -- size_t left_padding = padding >> shifts[specs.align]; -+ auto* shifts = -+ default_align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; -+ size_t left_padding = padding >> shifts[static_cast(specs.align())]; - size_t right_padding = padding - left_padding; -- auto it = reserve(out, size + padding * specs.fill.size()); -- if (left_padding != 0) it = fill(it, left_padding, specs.fill); -+ auto it = reserve(out, size + padding * specs.fill_size()); -+ if (left_padding != 0) it = fill(it, left_padding, specs); - it = f(it); -- if (right_padding != 0) it = fill(it, right_padding, specs.fill); -+ if (right_padding != 0) it = fill(it, right_padding, specs); - return base_iterator(out, it); - } - --template --constexpr auto write_padded(OutputIt out, const format_specs& specs, -+constexpr auto write_padded(OutputIt out, const format_specs& specs, - size_t size, F&& f) -> OutputIt { -- return write_padded(out, specs, size, size, f); -+ return write_padded(out, specs, size, size, f); - } - --template -+template - FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, -- const format_specs& specs) -> OutputIt { -- return write_padded( -+ const format_specs& specs = {}) -> OutputIt { -+ return write_padded( - out, specs, bytes.size(), [bytes](reserve_iterator it) { - const char* data = bytes.data(); -- return copy_str(data, data + bytes.size(), it); -+ return copy(data, data + bytes.size(), it); - }); - } - - template --auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) -+auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) - -> OutputIt { - int num_digits = count_digits<4>(value); - auto size = to_unsigned(num_digits) + size_t(2); - auto write = [=](reserve_iterator it) { - *it++ = static_cast('0'); - *it++ = static_cast('x'); -- return format_uint<4, Char>(it, value, num_digits); -+ return format_base2e(4, it, value, num_digits); - }; -- return specs ? write_padded(out, *specs, size, write) -+ return specs ? write_padded(out, *specs, size, write) - : base_iterator(out, write(reserve(out, size))); - } - -@@ -1921,8 +1682,9 @@ auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) - FMT_API auto is_printable(uint32_t cp) -> bool; - - inline auto needs_escape(uint32_t cp) -> bool { -- return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || -- !is_printable(cp); -+ if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true; -+ if (const_check(FMT_OPTIMIZE_SIZE > 1)) return false; -+ return !is_printable(cp); - } - - template struct find_escape_result { -@@ -1931,17 +1693,11 @@ template struct find_escape_result { - uint32_t cp; - }; - --template --using make_unsigned_char = -- typename conditional_t::value, -- std::make_unsigned, -- type_identity>::type; -- - template - auto find_escape(const Char* begin, const Char* end) - -> find_escape_result { - for (; begin != end; ++begin) { -- uint32_t cp = static_cast>(*begin); -+ uint32_t cp = static_cast>(*begin); - if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; - if (needs_escape(cp)) return {begin, begin + 1, cp}; - } -@@ -1950,7 +1706,7 @@ auto find_escape(const Char* begin, const Char* end) - - inline auto find_escape(const char* begin, const char* end) - -> find_escape_result { -- if (!is_utf8()) return find_escape(begin, end); -+ if (const_check(!use_utf8)) return find_escape(begin, end); - auto result = find_escape_result{end, nullptr, 0}; - for_each_codepoint(string_view(begin, to_unsigned(end - begin)), - [&](uint32_t cp, string_view sv) { -@@ -1963,40 +1719,14 @@ inline auto find_escape(const char* begin, const char* end) - return result; - } - --#define FMT_STRING_IMPL(s, base, explicit) \ -- [] { \ -- /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ -- /* Use a macro-like name to avoid shadowing warnings. */ \ -- struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ -- using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t; \ -- FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ -- operator fmt::basic_string_view() const { \ -- return fmt::detail_exported::compile_string_to_view(s); \ -- } \ -- }; \ -- return FMT_COMPILE_STRING(); \ -- }() -- --/** -- \rst -- Constructs a compile-time format string from a string literal *s*. -- -- **Example**:: -- -- // A compile-time error because 'd' is an invalid specifier for strings. -- std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); -- \endrst -- */ --#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) -- - template - auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { - *out++ = static_cast('\\'); - *out++ = static_cast(prefix); - Char buf[width]; - fill_n(buf, width, static_cast('0')); -- format_uint<4>(buf, cp, width); -- return copy_str(buf, buf + width, out); -+ format_base2e(4, buf, cp, width); -+ return copy(buf, buf + width, out); - } - - template -@@ -2016,23 +1746,15 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) - *out++ = static_cast('\\'); - c = static_cast('t'); - break; -- case '"': -- FMT_FALLTHROUGH; -- case '\'': -- FMT_FALLTHROUGH; -- case '\\': -- *out++ = static_cast('\\'); -- break; -+ case '"': FMT_FALLTHROUGH; -+ case '\'': FMT_FALLTHROUGH; -+ case '\\': *out++ = static_cast('\\'); break; - default: -- if (escape.cp < 0x100) { -- return write_codepoint<2, Char>(out, 'x', escape.cp); -- } -- if (escape.cp < 0x10000) { -+ if (escape.cp < 0x100) return write_codepoint<2, Char>(out, 'x', escape.cp); -+ if (escape.cp < 0x10000) - return write_codepoint<4, Char>(out, 'u', escape.cp); -- } -- if (escape.cp < 0x110000) { -+ if (escape.cp < 0x110000) - return write_codepoint<8, Char>(out, 'U', escape.cp); -- } - for (Char escape_char : basic_string_view( - escape.begin, to_unsigned(escape.end - escape.begin))) { - out = write_codepoint<2, Char>(out, 'x', -@@ -2051,7 +1773,7 @@ auto write_escaped_string(OutputIt out, basic_string_view str) - auto begin = str.begin(), end = str.end(); - do { - auto escape = find_escape(begin, end); -- out = copy_str(begin, escape.begin, out); -+ out = copy(begin, escape.begin, out); - begin = escape.end; - if (!begin) break; - out = write_escaped_cp(out, escape); -@@ -2062,11 +1784,13 @@ auto write_escaped_string(OutputIt out, basic_string_view str) - - template - auto write_escaped_char(OutputIt out, Char v) -> OutputIt { -+ Char v_array[1] = {v}; - *out++ = static_cast('\''); - if ((needs_escape(static_cast(v)) && v != static_cast('"')) || - v == static_cast('\'')) { -- out = write_escaped_cp( -- out, find_escape_result{&v, &v + 1, static_cast(v)}); -+ out = write_escaped_cp(out, -+ find_escape_result{v_array, v_array + 1, -+ static_cast(v)}); - } else { - *out++ = v; - } -@@ -2076,74 +1800,23 @@ auto write_escaped_char(OutputIt out, Char v) -> OutputIt { - - template - FMT_CONSTEXPR auto write_char(OutputIt out, Char value, -- const format_specs& specs) -> OutputIt { -- bool is_debug = specs.type == presentation_type::debug; -- return write_padded(out, specs, 1, [=](reserve_iterator it) { -+ const format_specs& specs) -> OutputIt { -+ bool is_debug = specs.type() == presentation_type::debug; -+ return write_padded(out, specs, 1, [=](reserve_iterator it) { - if (is_debug) return write_escaped_char(it, value); - *it++ = value; - return it; - }); - } - template --FMT_CONSTEXPR auto write(OutputIt out, Char value, -- const format_specs& specs, locale_ref loc = {}) -- -> OutputIt { -+FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, -+ locale_ref loc = {}) -> OutputIt { - // char is formatted as unsigned char for consistency across platforms. - using unsigned_type = - conditional_t::value, unsigned char, unsigned>; - return check_char_specs(specs) -- ? write_char(out, value, specs) -- : write(out, static_cast(value), specs, loc); --} -- --// Data for write_int that doesn't depend on output iterator type. It is used to --// avoid template code bloat. --template struct write_int_data { -- size_t size; -- size_t padding; -- -- FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, -- const format_specs& specs) -- : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { -- if (specs.align == align::numeric) { -- auto width = to_unsigned(specs.width); -- if (width > size) { -- padding = width - size; -- size = width; -- } -- } else if (specs.precision > num_digits) { -- size = (prefix >> 24) + to_unsigned(specs.precision); -- padding = to_unsigned(specs.precision - num_digits); -- } -- } --}; -- --// Writes an integer in the format --// --// where are written by write_digits(it). --// prefix contains chars in three lower bytes and the size in the fourth byte. --template --FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, -- unsigned prefix, -- const format_specs& specs, -- W write_digits) -> OutputIt { -- // Slightly faster check for specs.width == 0 && specs.precision == -1. -- if ((specs.width | (specs.precision + 1)) == 0) { -- auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); -- if (prefix != 0) { -- for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) -- *it++ = static_cast(p & 0xff); -- } -- return base_iterator(out, write_digits(it)); -- } -- auto data = write_int_data(num_digits, prefix, specs); -- return write_padded( -- out, specs, data.size, [=](reserve_iterator it) { -- for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) -- *it++ = static_cast(p & 0xff); -- it = detail::fill_n(it, data.padding, static_cast('0')); -- return write_digits(it); -- }); -+ ? write_char(out, value, specs) -+ : write(out, static_cast(value), specs, loc); - } - - template class digit_grouping { -@@ -2155,10 +1828,10 @@ template class digit_grouping { - std::string::const_iterator group; - int pos; - }; -- next_state initial_state() const { return {grouping_.begin(), 0}; } -+ auto initial_state() const -> next_state { return {grouping_.begin(), 0}; } - - // Returns the next digit group separator position. -- int next(next_state& state) const { -+ auto next(next_state& state) const -> int { - if (thousands_sep_.empty()) return max_value(); - if (state.group == grouping_.end()) return state.pos += grouping_.back(); - if (*state.group <= 0 || *state.group == max_value()) -@@ -2168,7 +1841,9 @@ template class digit_grouping { - } - - public: -- explicit digit_grouping(locale_ref loc, bool localized = true) { -+ template ::value)> -+ explicit digit_grouping(Locale loc, bool localized = true) { - if (!localized) return; - auto sep = thousands_sep(loc); - grouping_ = sep.grouping; -@@ -2177,9 +1852,9 @@ template class digit_grouping { - digit_grouping(std::string grouping, std::basic_string sep) - : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} - -- bool has_separator() const { return !thousands_sep_.empty(); } -+ auto has_separator() const -> bool { return !thousands_sep_.empty(); } - -- int count_separators(int num_digits) const { -+ auto count_separators(int num_digits) const -> int { - int count = 0; - auto state = initial_state(); - while (num_digits > next(state)) ++count; -@@ -2188,7 +1863,7 @@ template class digit_grouping { - - // Applies grouping to digits and write the output to out. - template -- Out apply(Out out, basic_string_view digits) const { -+ auto apply(Out out, basic_string_view digits) const -> Out { - auto num_digits = static_cast(digits.size()); - auto separators = basic_memory_buffer(); - separators.push_back(0); -@@ -2200,9 +1875,8 @@ template class digit_grouping { - for (int i = 0, sep_index = static_cast(separators.size() - 1); - i < num_digits; ++i) { - if (num_digits - i == separators[sep_index]) { -- out = -- copy_str(thousands_sep_.data(), -- thousands_sep_.data() + thousands_sep_.size(), out); -+ out = copy(thousands_sep_.data(), -+ thousands_sep_.data() + thousands_sep_.size(), out); - --sep_index; - } - *out++ = static_cast(digits[to_unsigned(i)]); -@@ -2211,48 +1885,78 @@ template class digit_grouping { - } - }; - -+FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { -+ prefix |= prefix != 0 ? value << 8 : value; -+ prefix += (1u + (value > 0xff ? 1 : 0)) << 24; -+} -+ - // Writes a decimal integer with digit grouping. - template - auto write_int(OutputIt out, UInt value, unsigned prefix, -- const format_specs& specs, -- const digit_grouping& grouping) -> OutputIt { -+ const format_specs& specs, const digit_grouping& grouping) -+ -> OutputIt { - static_assert(std::is_same, UInt>::value, ""); -- int num_digits = count_digits(value); -- char digits[40]; -- format_decimal(digits, value, num_digits); -- unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + -- grouping.count_separators(num_digits)); -- return write_padded( -+ int num_digits = 0; -+ auto buffer = memory_buffer(); -+ switch (specs.type()) { -+ default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; -+ case presentation_type::none: -+ case presentation_type::dec: -+ num_digits = count_digits(value); -+ format_decimal(appender(buffer), value, num_digits); -+ break; -+ case presentation_type::hex: -+ if (specs.alt()) -+ prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); -+ num_digits = count_digits<4>(value); -+ format_base2e(4, appender(buffer), value, num_digits, specs.upper()); -+ break; -+ case presentation_type::oct: -+ num_digits = count_digits<3>(value); -+ // Octal prefix '0' is counted as a digit, so only add it if precision -+ // is not greater than the number of digits. -+ if (specs.alt() && specs.precision <= num_digits && value != 0) -+ prefix_append(prefix, '0'); -+ format_base2e(3, appender(buffer), value, num_digits); -+ break; -+ case presentation_type::bin: -+ if (specs.alt()) -+ prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); -+ num_digits = count_digits<1>(value); -+ format_base2e(1, appender(buffer), value, num_digits); -+ break; -+ case presentation_type::chr: -+ return write_char(out, static_cast(value), specs); -+ } -+ -+ unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) + -+ to_unsigned(grouping.count_separators(num_digits)); -+ return write_padded( - out, specs, size, size, [&](reserve_iterator it) { -- if (prefix != 0) { -- char sign = static_cast(prefix); -- *it++ = static_cast(sign); -- } -- return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); -+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) -+ *it++ = static_cast(p & 0xff); -+ return grouping.apply(it, string_view(buffer.data(), buffer.size())); - }); - } - -+#if FMT_USE_LOCALE - // Writes a localized value. --FMT_API auto write_loc(appender out, loc_value value, -- const format_specs<>& specs, locale_ref loc) -> bool; --template --inline auto write_loc(OutputIt, loc_value, const format_specs&, -+FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, -+ locale_ref loc) -> bool; -+#endif -+template -+inline auto write_loc(OutputIt, const loc_value&, const format_specs&, - locale_ref) -> bool { - return false; - } - --FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { -- prefix |= prefix != 0 ? value << 8 : value; -- prefix += (1u + (value > 0xff ? 1 : 0)) << 24; --} -- - template struct write_int_arg { - UInt abs_value; - unsigned prefix; - }; - - template --FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) -+FMT_CONSTEXPR auto make_write_int_arg(T value, sign s) - -> write_int_arg> { - auto prefix = 0u; - auto abs_value = static_cast>(value); -@@ -2262,21 +1966,21 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) - } else { - constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; -- prefix = prefixes[sign]; -+ prefix = prefixes[static_cast(s)]; - } - return {abs_value, prefix}; - } - - template struct loc_writer { -- buffer_appender out; -- const format_specs& specs; -+ basic_appender out; -+ const format_specs& specs; - std::basic_string sep; - std::string grouping; - std::basic_string decimal_point; - - template ::value)> - auto operator()(T value) -> bool { -- auto arg = make_write_int_arg(value, specs.sign); -+ auto arg = make_write_int_arg(value, specs.sign()); - write_int(out, static_cast>(arg.abs_value), arg.prefix, - specs, digit_grouping(grouping, sep)); - return true; -@@ -2288,166 +1992,162 @@ template struct loc_writer { - } - }; - -+// Size and padding computation separate from write_int to avoid template bloat. -+struct size_padding { -+ unsigned size; -+ unsigned padding; -+ -+ FMT_CONSTEXPR size_padding(int num_digits, unsigned prefix, -+ const format_specs& specs) -+ : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { -+ if (specs.align() == align::numeric) { -+ auto width = to_unsigned(specs.width); -+ if (width > size) { -+ padding = width - size; -+ size = width; -+ } -+ } else if (specs.precision > num_digits) { -+ size = (prefix >> 24) + to_unsigned(specs.precision); -+ padding = to_unsigned(specs.precision - num_digits); -+ } -+ } -+}; -+ - template - FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, -- const format_specs& specs, -- locale_ref) -> OutputIt { -+ const format_specs& specs) -> OutputIt { - static_assert(std::is_same>::value, ""); -+ -+ constexpr int buffer_size = num_bits(); -+ char buffer[buffer_size]; -+ if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0'); -+ const char* begin = nullptr; -+ const char* end = buffer + buffer_size; -+ - auto abs_value = arg.abs_value; - auto prefix = arg.prefix; -- switch (specs.type) { -+ switch (specs.type()) { -+ default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; - case presentation_type::none: -- case presentation_type::dec: { -- auto num_digits = count_digits(abs_value); -- return write_int( -- out, num_digits, prefix, specs, [=](reserve_iterator it) { -- return format_decimal(it, abs_value, num_digits).end; -- }); -- } -- case presentation_type::hex_lower: -- case presentation_type::hex_upper: { -- bool upper = specs.type == presentation_type::hex_upper; -- if (specs.alt) -- prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); -- int num_digits = count_digits<4>(abs_value); -- return write_int( -- out, num_digits, prefix, specs, [=](reserve_iterator it) { -- return format_uint<4, Char>(it, abs_value, num_digits, upper); -- }); -- } -- case presentation_type::bin_lower: -- case presentation_type::bin_upper: { -- bool upper = specs.type == presentation_type::bin_upper; -- if (specs.alt) -- prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); -- int num_digits = count_digits<1>(abs_value); -- return write_int(out, num_digits, prefix, specs, -- [=](reserve_iterator it) { -- return format_uint<1, Char>(it, abs_value, num_digits); -- }); -- } -+ case presentation_type::dec: -+ begin = do_format_decimal(buffer, abs_value, buffer_size); -+ break; -+ case presentation_type::hex: -+ begin = do_format_base2e(4, buffer, abs_value, buffer_size, specs.upper()); -+ if (specs.alt()) -+ prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); -+ break; - case presentation_type::oct: { -- int num_digits = count_digits<3>(abs_value); -+ begin = do_format_base2e(3, buffer, abs_value, buffer_size); - // Octal prefix '0' is counted as a digit, so only add it if precision - // is not greater than the number of digits. -- if (specs.alt && specs.precision <= num_digits && abs_value != 0) -+ auto num_digits = end - begin; -+ if (specs.alt() && specs.precision <= num_digits && abs_value != 0) - prefix_append(prefix, '0'); -- return write_int(out, num_digits, prefix, specs, -- [=](reserve_iterator it) { -- return format_uint<3, Char>(it, abs_value, num_digits); -- }); -+ break; - } -+ case presentation_type::bin: -+ begin = do_format_base2e(1, buffer, abs_value, buffer_size); -+ if (specs.alt()) -+ prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); -+ break; - case presentation_type::chr: -- return write_char(out, static_cast(abs_value), specs); -- default: -- throw_format_error("invalid format specifier"); -+ return write_char(out, static_cast(abs_value), specs); - } -- return out; -+ -+ // Write an integer in the format -+ // -+ // prefix contains chars in three lower bytes and the size in the fourth byte. -+ int num_digits = static_cast(end - begin); -+ // Slightly faster check for specs.width == 0 && specs.precision == -1. -+ if ((specs.width | (specs.precision + 1)) == 0) { -+ auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); -+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) -+ *it++ = static_cast(p & 0xff); -+ return base_iterator(out, copy(begin, end, it)); -+ } -+ auto sp = size_padding(num_digits, prefix, specs); -+ unsigned padding = sp.padding; -+ return write_padded( -+ out, specs, sp.size, [=](reserve_iterator it) { -+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) -+ *it++ = static_cast(p & 0xff); -+ it = detail::fill_n(it, padding, static_cast('0')); -+ return copy(begin, end, it); -+ }); - } -+ - template --FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( -- OutputIt out, write_int_arg arg, const format_specs& specs, -- locale_ref loc) -> OutputIt { -- return write_int(out, arg, specs, loc); -+FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out, -+ write_int_arg arg, -+ const format_specs& specs) -+ -> OutputIt { -+ return write_int(out, arg, specs); - } --template ::value && - !std::is_same::value && -- std::is_same>::value)> --FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, -- const format_specs& specs, -- locale_ref loc) -> OutputIt { -- if (specs.localized && write_loc(out, value, specs, loc)) return out; -- return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, -- loc); -+ !std::is_same::value)> -+FMT_CONSTEXPR FMT_INLINE auto write(basic_appender out, T value, -+ const format_specs& specs, locale_ref loc) -+ -> basic_appender { -+ if (specs.localized() && write_loc(out, value, specs, loc)) return out; -+ return write_int_noinline(out, make_write_int_arg(value, specs.sign()), -+ specs); - } -+ - // An inlined version of write used in format string compilation. - template ::value && - !std::is_same::value && -- !std::is_same>::value)> -+ !std::is_same::value && -+ !std::is_same>::value)> - FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, -- const format_specs& specs, -- locale_ref loc) -> OutputIt { -- if (specs.localized && write_loc(out, value, specs, loc)) return out; -- return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); -+ const format_specs& specs, locale_ref loc) -+ -> OutputIt { -+ if (specs.localized() && write_loc(out, value, specs, loc)) return out; -+ return write_int(out, make_write_int_arg(value, specs.sign()), specs); - } - --// An output iterator that counts the number of objects written to it and --// discards them. --class counting_iterator { -- private: -- size_t count_; -- -- public: -- using iterator_category = std::output_iterator_tag; -- using difference_type = std::ptrdiff_t; -- using pointer = void; -- using reference = void; -- FMT_UNCHECKED_ITERATOR(counting_iterator); -- -- struct value_type { -- template FMT_CONSTEXPR void operator=(const T&) {} -- }; -- -- FMT_CONSTEXPR counting_iterator() : count_(0) {} -- -- FMT_CONSTEXPR size_t count() const { return count_; } -- -- FMT_CONSTEXPR counting_iterator& operator++() { -- ++count_; -- return *this; -- } -- FMT_CONSTEXPR counting_iterator operator++(int) { -- auto it = *this; -- ++*this; -- return it; -- } -- -- FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, -- difference_type n) { -- it.count_ += static_cast(n); -- return it; -- } -- -- FMT_CONSTEXPR value_type operator*() const { return {}; } --}; -- - template - FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, -- const format_specs& specs) -> OutputIt { -+ const format_specs& specs) -> OutputIt { - auto data = s.data(); - auto size = s.size(); - if (specs.precision >= 0 && to_unsigned(specs.precision) < size) - size = code_point_index(s, to_unsigned(specs.precision)); -- bool is_debug = specs.type == presentation_type::debug; -+ -+ bool is_debug = specs.type() == presentation_type::debug; -+ if (is_debug) { -+ auto buf = counting_buffer(); -+ write_escaped_string(basic_appender(buf), s); -+ size = buf.count(); -+ } -+ - size_t width = 0; - if (specs.width != 0) { -- if (is_debug) -- width = write_escaped_string(counting_iterator{}, s).count(); -- else -- width = compute_width(basic_string_view(data, size)); -+ width = -+ is_debug ? size : compute_width(basic_string_view(data, size)); - } -- return write_padded(out, specs, size, width, -- [=](reserve_iterator it) { -- if (is_debug) return write_escaped_string(it, s); -- return copy_str(data, data + size, it); -- }); -+ return write_padded( -+ out, specs, size, width, [=](reserve_iterator it) { -+ return is_debug ? write_escaped_string(it, s) -+ : copy(data, data + size, it); -+ }); - } - template --FMT_CONSTEXPR auto write(OutputIt out, -- basic_string_view> s, -- const format_specs& specs, locale_ref) -- -> OutputIt { -- return write(out, s, specs); -+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, -+ const format_specs& specs, locale_ref) -> OutputIt { -+ return write(out, s, specs); - } - template --FMT_CONSTEXPR auto write(OutputIt out, const Char* s, -- const format_specs& specs, locale_ref) -- -> OutputIt { -- return specs.type != presentation_type::pointer -- ? write(out, basic_string_view(s), specs, {}) -- : write_ptr(out, bit_cast(s), &specs); -+FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, -+ locale_ref) -> OutputIt { -+ if (specs.type() == presentation_type::pointer) -+ return write_ptr(out, bit_cast(s), &specs); -+ if (!s) report_error("string pointer is null"); -+ return write(out, basic_string_view(s), specs, {}); - } - - template OutputIt { - if (negative) abs_value = ~abs_value + 1; - int num_digits = count_digits(abs_value); - auto size = (negative ? 1 : 0) + static_cast(num_digits); -- auto it = reserve(out, size); -- if (auto ptr = to_pointer(it, size)) { -+ if (auto ptr = to_pointer(out, size)) { - if (negative) *ptr++ = static_cast('-'); - format_decimal(ptr, abs_value, num_digits); - return out; - } -- if (negative) *it++ = static_cast('-'); -- it = format_decimal(it, abs_value, num_digits).end; -- return base_iterator(out, it); -+ if (negative) *out++ = static_cast('-'); -+ return format_decimal(out, abs_value, num_digits); - } - --// A floating-point presentation format. --enum class float_format : unsigned char { -- general, // General: exponent notation or fixed point based on magnitude. -- exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. -- fixed, // Fixed point with the default precision of 6, e.g. 0.0012. -- hex --}; -- --struct float_specs { -- int precision; -- float_format format : 8; -- sign_t sign : 8; -- bool upper : 1; -- bool locale : 1; -- bool binary32 : 1; -- bool showpoint : 1; --}; -- --template --FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs, -- ErrorHandler&& eh = {}) -- -> float_specs { -- auto result = float_specs(); -- result.showpoint = specs.alt; -- result.locale = specs.localized; -- switch (specs.type) { -- case presentation_type::none: -- result.format = float_format::general; -- break; -- case presentation_type::general_upper: -- result.upper = true; -- FMT_FALLTHROUGH; -- case presentation_type::general_lower: -- result.format = float_format::general; -- break; -- case presentation_type::exp_upper: -- result.upper = true; -- FMT_FALLTHROUGH; -- case presentation_type::exp_lower: -- result.format = float_format::exp; -- result.showpoint |= specs.precision != 0; -- break; -- case presentation_type::fixed_upper: -- result.upper = true; -- FMT_FALLTHROUGH; -- case presentation_type::fixed_lower: -- result.format = float_format::fixed; -- result.showpoint |= specs.precision != 0; -- break; -- case presentation_type::hexfloat_upper: -- result.upper = true; -- FMT_FALLTHROUGH; -- case presentation_type::hexfloat_lower: -- result.format = float_format::hex; -- break; -- default: -- eh.on_error("invalid format specifier"); -- break; -+template -+FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, -+ format_specs& specs) -> const Char* { -+ FMT_ASSERT(begin != end, ""); -+ auto alignment = align::none; -+ auto p = begin + code_point_length(begin); -+ if (end - p <= 0) p = begin; -+ for (;;) { -+ switch (to_ascii(*p)) { -+ case '<': alignment = align::left; break; -+ case '>': alignment = align::right; break; -+ case '^': alignment = align::center; break; -+ } -+ if (alignment != align::none) { -+ if (p != begin) { -+ auto c = *begin; -+ if (c == '}') return begin; -+ if (c == '{') { -+ report_error("invalid fill character '{'"); -+ return begin; -+ } -+ specs.set_fill(basic_string_view(begin, to_unsigned(p - begin))); -+ begin = p + 1; -+ } else { -+ ++begin; -+ } -+ break; -+ } else if (p == begin) { -+ break; -+ } -+ p = begin; - } -- return result; -+ specs.set_align(alignment); -+ return begin; - } - - template - FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, -- format_specs specs, -- const float_specs& fspecs) -> OutputIt { -+ format_specs specs, sign s) -> OutputIt { - auto str = -- isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); -+ isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf"); - constexpr size_t str_size = 3; -- auto sign = fspecs.sign; -- auto size = str_size + (sign ? 1 : 0); -+ auto size = str_size + (s != sign::none ? 1 : 0); - // Replace '0'-padding with space for non-finite values. - const bool is_zero_fill = -- specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); -- if (is_zero_fill) specs.fill[0] = static_cast(' '); -- return write_padded(out, specs, size, [=](reserve_iterator it) { -- if (sign) *it++ = detail::sign(sign); -- return copy_str(str, str + str_size, it); -- }); -+ specs.fill_size() == 1 && specs.fill_unit() == '0'; -+ if (is_zero_fill) specs.set_fill(' '); -+ return write_padded(out, specs, size, -+ [=](reserve_iterator it) { -+ if (s != sign::none) -+ *it++ = detail::getsign(s); -+ return copy(str, str + str_size, it); -+ }); - } - - // A decimal floating-point number significand * pow(10, exp). -@@ -2571,12 +2243,12 @@ inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { - template - constexpr auto write_significand(OutputIt out, const char* significand, - int significand_size) -> OutputIt { -- return copy_str(significand, significand + significand_size, out); -+ return copy(significand, significand + significand_size, out); - } - template - inline auto write_significand(OutputIt out, UInt significand, - int significand_size) -> OutputIt { -- return format_decimal(out, significand, significand_size).end; -+ return format_decimal(out, significand, significand_size); - } - template - FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, -@@ -2596,14 +2268,13 @@ template ::value)> - inline auto write_significand(Char* out, UInt significand, int significand_size, - int integral_size, Char decimal_point) -> Char* { -- if (!decimal_point) -- return format_decimal(out, significand, significand_size).end; -+ if (!decimal_point) return format_decimal(out, significand, significand_size); - out += significand_size + 1; - Char* end = out; - int floating_size = significand_size - integral_size; - for (int i = floating_size / 2; i > 0; --i) { - out -= 2; -- copy2(out, digits2(static_cast(significand % 100))); -+ write2digits(out, static_cast(significand % 100)); - significand /= 100; - } - if (floating_size % 2 != 0) { -@@ -2624,19 +2295,19 @@ inline auto write_significand(OutputIt out, UInt significand, - Char buffer[digits10() + 2]; - auto end = write_significand(buffer, significand, significand_size, - integral_size, decimal_point); -- return detail::copy_str_noinline(buffer, end, out); -+ return detail::copy_noinline(buffer, end, out); - } - - template - FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, - int significand_size, int integral_size, - Char decimal_point) -> OutputIt { -- out = detail::copy_str_noinline(significand, -- significand + integral_size, out); -+ out = detail::copy_noinline(significand, significand + integral_size, -+ out); - if (!decimal_point) return out; - *out++ = decimal_point; -- return detail::copy_str_noinline(significand + integral_size, -- significand + significand_size, out); -+ return detail::copy_noinline(significand + integral_size, -+ significand + significand_size, out); - } - - template -@@ -2649,44 +2320,42 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, - decimal_point); - } - auto buffer = basic_memory_buffer(); -- write_significand(buffer_appender(buffer), significand, -- significand_size, integral_size, decimal_point); -+ write_significand(basic_appender(buffer), significand, significand_size, -+ integral_size, decimal_point); - grouping.apply( - out, basic_string_view(buffer.data(), to_unsigned(integral_size))); -- return detail::copy_str_noinline(buffer.data() + integral_size, -- buffer.end(), out); -+ return detail::copy_noinline(buffer.data() + integral_size, -+ buffer.end(), out); - } - --template > - FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, -- const format_specs& specs, -- float_specs fspecs, locale_ref loc) -- -> OutputIt { -+ const format_specs& specs, sign s, -+ int exp_upper, locale_ref loc) -> OutputIt { - auto significand = f.significand; - int significand_size = get_significand_size(f); - const Char zero = static_cast('0'); -- auto sign = fspecs.sign; -- size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); -+ size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0); - using iterator = reserve_iterator; - -- Char decimal_point = -- fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); -+ Char decimal_point = specs.localized() ? detail::decimal_point(loc) -+ : static_cast('.'); - - int output_exp = f.exponent + significand_size - 1; - auto use_exp_format = [=]() { -- if (fspecs.format == float_format::exp) return true; -- if (fspecs.format != float_format::general) return false; -+ if (specs.type() == presentation_type::exp) return true; -+ if (specs.type() == presentation_type::fixed) return false; - // Use the fixed notation if the exponent is in [exp_lower, exp_upper), - // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. -- const int exp_lower = -4, exp_upper = 16; -+ const int exp_lower = -4; - return output_exp < exp_lower || -- output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); -+ output_exp >= (specs.precision > 0 ? specs.precision : exp_upper); - }; - if (use_exp_format()) { - int num_zeros = 0; -- if (fspecs.showpoint) { -- num_zeros = fspecs.precision - significand_size; -+ if (specs.alt()) { -+ num_zeros = specs.precision - significand_size; - if (num_zeros < 0) num_zeros = 0; - size += to_unsigned(num_zeros); - } else if (significand_size == 1) { -@@ -2697,9 +2366,9 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; - - size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); -- char exp_char = fspecs.upper ? 'E' : 'e'; -+ char exp_char = specs.upper() ? 'E' : 'e'; - auto write = [=](iterator it) { -- if (sign) *it++ = detail::sign(sign); -+ if (s != sign::none) *it++ = detail::getsign(s); - // Insert a decimal point after the first digit and add an exponent. - it = write_significand(it, significand, significand_size, 1, - decimal_point); -@@ -2707,39 +2376,41 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - *it++ = static_cast(exp_char); - return write_exponent(output_exp, it); - }; -- return specs.width > 0 ? write_padded(out, specs, size, write) -- : base_iterator(out, write(reserve(out, size))); -+ return specs.width > 0 -+ ? write_padded(out, specs, size, write) -+ : base_iterator(out, write(reserve(out, size))); - } - - int exp = f.exponent + significand_size; - if (f.exponent >= 0) { - // 1234e5 -> 123400000[.0+] - size += to_unsigned(f.exponent); -- int num_zeros = fspecs.precision - exp; -+ int num_zeros = specs.precision - exp; - abort_fuzzing_if(num_zeros > 5000); -- if (fspecs.showpoint) { -+ if (specs.alt()) { - ++size; -- if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0; -+ if (num_zeros <= 0 && specs.type() != presentation_type::fixed) -+ num_zeros = 0; - if (num_zeros > 0) size += to_unsigned(num_zeros); - } -- auto grouping = Grouping(loc, fspecs.locale); -+ auto grouping = Grouping(loc, specs.localized()); - size += to_unsigned(grouping.count_separators(exp)); -- return write_padded(out, specs, size, [&](iterator it) { -- if (sign) *it++ = detail::sign(sign); -+ return write_padded(out, specs, size, [&](iterator it) { -+ if (s != sign::none) *it++ = detail::getsign(s); - it = write_significand(it, significand, significand_size, - f.exponent, grouping); -- if (!fspecs.showpoint) return it; -+ if (!specs.alt()) return it; - *it++ = decimal_point; - return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; - }); - } else if (exp > 0) { - // 1234e-2 -> 12.34[0+] -- int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; -- size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); -- auto grouping = Grouping(loc, fspecs.locale); -+ int num_zeros = specs.alt() ? specs.precision - significand_size : 0; -+ size += 1 + static_cast(max_of(num_zeros, 0)); -+ auto grouping = Grouping(loc, specs.localized()); - size += to_unsigned(grouping.count_separators(exp)); -- return write_padded(out, specs, size, [&](iterator it) { -- if (sign) *it++ = detail::sign(sign); -+ return write_padded(out, specs, size, [&](iterator it) { -+ if (s != sign::none) *it++ = detail::getsign(s); - it = write_significand(it, significand, significand_size, exp, - decimal_point, grouping); - return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; -@@ -2747,14 +2418,14 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - } - // 1234e-6 -> 0.001234 - int num_zeros = -exp; -- if (significand_size == 0 && fspecs.precision >= 0 && -- fspecs.precision < num_zeros) { -- num_zeros = fspecs.precision; -+ if (significand_size == 0 && specs.precision >= 0 && -+ specs.precision < num_zeros) { -+ num_zeros = specs.precision; - } -- bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; -+ bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt(); - size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); -- return write_padded(out, specs, size, [&](iterator it) { -- if (sign) *it++ = detail::sign(sign); -+ return write_padded(out, specs, size, [&](iterator it) { -+ if (s != sign::none) *it++ = detail::getsign(s); - *it++ = zero; - if (!pointy) return it; - *it++ = decimal_point; -@@ -2767,32 +2438,31 @@ template class fallback_digit_grouping { - public: - constexpr fallback_digit_grouping(locale_ref, bool) {} - -- constexpr bool has_separator() const { return false; } -+ constexpr auto has_separator() const -> bool { return false; } - -- constexpr int count_separators(int) const { return 0; } -+ constexpr auto count_separators(int) const -> int { return 0; } - - template -- constexpr Out apply(Out out, basic_string_view) const { -+ constexpr auto apply(Out out, basic_string_view) const -> Out { - return out; - } - }; - --template -+template - FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, -- const format_specs& specs, -- float_specs fspecs, locale_ref loc) -- -> OutputIt { -+ const format_specs& specs, sign s, -+ int exp_upper, locale_ref loc) -> OutputIt { - if (is_constant_evaluated()) { -- return do_write_float>(out, f, specs, fspecs, -- loc); -+ return do_write_float>(out, f, specs, s, -+ exp_upper, loc); - } else { -- return do_write_float(out, f, specs, fspecs, loc); -+ return do_write_float(out, f, specs, s, exp_upper, loc); - } - } - --template constexpr bool isnan(T value) { -- return !(value >= value); // std::isnan doesn't support __float128. -+template constexpr auto isnan(T value) -> bool { -+ return value != value; // std::isnan doesn't support __float128. - } - - template -@@ -2804,14 +2474,14 @@ struct has_isfinite> - - template ::value&& - has_isfinite::value)> --FMT_CONSTEXPR20 bool isfinite(T value) { -+FMT_CONSTEXPR20 auto isfinite(T value) -> bool { - constexpr T inf = T(std::numeric_limits::infinity()); - if (is_constant_evaluated()) - return !detail::isnan(value) && value < inf && value > -inf; - return std::isfinite(value); - } - template ::value)> --FMT_CONSTEXPR bool isfinite(T value) { -+FMT_CONSTEXPR auto isfinite(T value) -> bool { - T inf = T(std::numeric_limits::infinity()); - // std::isfinite doesn't support __float128. - return !detail::isnan(value) && value < inf && value > -inf; -@@ -2830,78 +2500,6 @@ FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { - return std::signbit(static_cast(value)); - } - --enum class round_direction { unknown, up, down }; -- --// Given the divisor (normally a power of 10), the remainder = v % divisor for --// some number v and the error, returns whether v should be rounded up, down, or --// whether the rounding direction can't be determined due to error. --// error should be less than divisor / 2. --FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, -- uint64_t remainder, -- uint64_t error) { -- FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. -- FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. -- FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. -- // Round down if (remainder + error) * 2 <= divisor. -- if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) -- return round_direction::down; -- // Round up if (remainder - error) * 2 >= divisor. -- if (remainder >= error && -- remainder - error >= divisor - (remainder - error)) { -- return round_direction::up; -- } -- return round_direction::unknown; --} -- --namespace digits { --enum result { -- more, // Generate more digits. -- done, // Done generating digits. -- error // Digit generation cancelled due to an error. --}; --} -- --struct gen_digits_handler { -- char* buf; -- int size; -- int precision; -- int exp10; -- bool fixed; -- -- FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, -- uint64_t remainder, uint64_t error, -- bool integral) { -- FMT_ASSERT(remainder < divisor, ""); -- buf[size++] = digit; -- if (!integral && error >= remainder) return digits::error; -- if (size < precision) return digits::more; -- if (!integral) { -- // Check if error * 2 < divisor with overflow prevention. -- // The check is not needed for the integral part because error = 1 -- // and divisor > (1 << 32) there. -- if (error >= divisor || error >= divisor - error) return digits::error; -- } else { -- FMT_ASSERT(error == 1 && divisor > 2, ""); -- } -- auto dir = get_round_direction(divisor, remainder, error); -- if (dir != round_direction::up) -- return dir == round_direction::down ? digits::done : digits::error; -- ++buf[size - 1]; -- for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { -- buf[i] = '0'; -- ++buf[i - 1]; -- } -- if (buf[0] > '9') { -- buf[0] = '1'; -- if (fixed) -- buf[size++] = '0'; -- else -- ++exp10; -- } -- return digits::done; -- } --}; -- - inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { - // Adjust fixed precision by exponent because it is relative to decimal - // point. -@@ -2910,149 +2508,50 @@ inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { - precision += exp10; - } - --// Generates output using the Grisu digit-gen algorithm. --// error: the size of the region (lower, upper) outside of which numbers --// definitely do not round to value (Delta in Grisu3). --FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error, -- int& exp, -- gen_digits_handler& handler) -- -> digits::result { -- const fp one(1ULL << -value.e, value.e); -- // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be -- // zero because it contains a product of two 64-bit numbers with MSB set (due -- // to normalization) - 1, shifted right by at most 60 bits. -- auto integral = static_cast(value.f >> -one.e); -- FMT_ASSERT(integral != 0, ""); -- FMT_ASSERT(integral == value.f >> -one.e, ""); -- // The fractional part of scaled value (p2 in Grisu) c = value % one. -- uint64_t fractional = value.f & (one.f - 1); -- exp = count_digits(integral); // kappa in Grisu. -- // Non-fixed formats require at least one digit and no precision adjustment. -- if (handler.fixed) { -- adjust_precision(handler.precision, exp + handler.exp10); -- // Check if precision is satisfied just by leading zeros, e.g. -- // format("{:.2f}", 0.001) gives "0.00" without generating any digits. -- if (handler.precision <= 0) { -- if (handler.precision < 0) return digits::done; -- // Divide by 10 to prevent overflow. -- uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e; -- auto dir = get_round_direction(divisor, value.f / 10, error * 10); -- if (dir == round_direction::unknown) return digits::error; -- handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; -- return digits::done; -- } -- } -- // Generate digits for the integral part. This can produce up to 10 digits. -- do { -- uint32_t digit = 0; -- auto divmod_integral = [&](uint32_t divisor) { -- digit = integral / divisor; -- integral %= divisor; -- }; -- // This optimization by Milo Yip reduces the number of integer divisions by -- // one per iteration. -- switch (exp) { -- case 10: -- divmod_integral(1000000000); -- break; -- case 9: -- divmod_integral(100000000); -- break; -- case 8: -- divmod_integral(10000000); -- break; -- case 7: -- divmod_integral(1000000); -- break; -- case 6: -- divmod_integral(100000); -- break; -- case 5: -- divmod_integral(10000); -- break; -- case 4: -- divmod_integral(1000); -- break; -- case 3: -- divmod_integral(100); -- break; -- case 2: -- divmod_integral(10); -- break; -- case 1: -- digit = integral; -- integral = 0; -- break; -- default: -- FMT_ASSERT(false, "invalid number of digits"); -- } -- --exp; -- auto remainder = (static_cast(integral) << -one.e) + fractional; -- auto result = handler.on_digit(static_cast('0' + digit), -- data::power_of_10_64[exp] << -one.e, -- remainder, error, true); -- if (result != digits::more) return result; -- } while (exp > 0); -- // Generate digits for the fractional part. -- for (;;) { -- fractional *= 10; -- error *= 10; -- char digit = static_cast('0' + (fractional >> -one.e)); -- fractional &= one.f - 1; -- --exp; -- auto result = handler.on_digit(digit, one.f, fractional, error, false); -- if (result != digits::more) return result; -- } --} -- - class bigint { - private: -- // A bigint is stored as an array of bigits (big digits), with bigit at index -- // 0 being the least significant one. -- using bigit = uint32_t; -+ // A bigint is a number in the form bigit_[N - 1] ... bigit_[0] * 32^exp_. -+ using bigit = uint32_t; // A big digit. - using double_bigit = uint64_t; -+ enum { bigit_bits = num_bits() }; - enum { bigits_capacity = 32 }; - basic_memory_buffer bigits_; - int exp_; - -- FMT_CONSTEXPR20 bigit operator[](int index) const { -- return bigits_[to_unsigned(index)]; -- } -- FMT_CONSTEXPR20 bigit& operator[](int index) { -- return bigits_[to_unsigned(index)]; -- } -- -- static constexpr const int bigit_bits = num_bits(); -- - friend struct formatter; - -- FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { -- auto result = static_cast((*this)[index]) - other - borrow; -- (*this)[index] = static_cast(result); -+ FMT_CONSTEXPR auto get_bigit(int i) const -> bigit { -+ return i >= exp_ && i < num_bigits() ? bigits_[i - exp_] : 0; -+ } -+ -+ FMT_CONSTEXPR void subtract_bigits(int index, bigit other, bigit& borrow) { -+ auto result = double_bigit(bigits_[index]) - other - borrow; -+ bigits_[index] = static_cast(result); - borrow = static_cast(result >> (bigit_bits * 2 - 1)); - } - -- FMT_CONSTEXPR20 void remove_leading_zeros() { -+ FMT_CONSTEXPR void remove_leading_zeros() { - int num_bigits = static_cast(bigits_.size()) - 1; -- while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; -+ while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits; - bigits_.resize(to_unsigned(num_bigits + 1)); - } - - // Computes *this -= other assuming aligned bigints and *this >= other. -- FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { -+ FMT_CONSTEXPR void subtract_aligned(const bigint& other) { - FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); - FMT_ASSERT(compare(*this, other) >= 0, ""); - bigit borrow = 0; - int i = other.exp_ - exp_; - for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) - subtract_bigits(i, other.bigits_[j], borrow); -- while (borrow > 0) subtract_bigits(i, 0, borrow); -+ if (borrow != 0) subtract_bigits(i, 0, borrow); -+ FMT_ASSERT(borrow == 0, ""); - remove_leading_zeros(); - } - -- FMT_CONSTEXPR20 void multiply(uint32_t value) { -- const double_bigit wide_value = value; -+ FMT_CONSTEXPR void multiply(uint32_t value) { - bigit carry = 0; -+ const double_bigit wide_value = value; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - double_bigit result = bigits_[i] * wide_value + carry; - bigits_[i] = static_cast(result); -@@ -3063,7 +2562,7 @@ class bigint { - - template ::value || - std::is_same::value)> -- FMT_CONSTEXPR20 void multiply(UInt value) { -+ FMT_CONSTEXPR void multiply(UInt value) { - using half_uint = - conditional_t::value, uint64_t, uint32_t>; - const int shift = num_bits() - bigit_bits; -@@ -3084,7 +2583,7 @@ class bigint { - - template ::value || - std::is_same::value)> -- FMT_CONSTEXPR20 void assign(UInt n) { -+ FMT_CONSTEXPR void assign(UInt n) { - size_t num_bigits = 0; - do { - bigits_[num_bigits++] = static_cast(n); -@@ -3095,30 +2594,30 @@ class bigint { - } - - public: -- FMT_CONSTEXPR20 bigint() : exp_(0) {} -+ FMT_CONSTEXPR bigint() : exp_(0) {} - explicit bigint(uint64_t n) { assign(n); } - - bigint(const bigint&) = delete; - void operator=(const bigint&) = delete; - -- FMT_CONSTEXPR20 void assign(const bigint& other) { -+ FMT_CONSTEXPR void assign(const bigint& other) { - auto size = other.bigits_.size(); - bigits_.resize(size); - auto data = other.bigits_.data(); -- std::copy(data, data + size, make_checked(bigits_.data(), size)); -+ copy(data, data + size, bigits_.data()); - exp_ = other.exp_; - } - -- template FMT_CONSTEXPR20 void operator=(Int n) { -+ template FMT_CONSTEXPR void operator=(Int n) { - FMT_ASSERT(n > 0, ""); - assign(uint64_or_128_t(n)); - } - -- FMT_CONSTEXPR20 int num_bigits() const { -+ FMT_CONSTEXPR auto num_bigits() const -> int { - return static_cast(bigits_.size()) + exp_; - } - -- FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { -+ FMT_CONSTEXPR auto operator<<=(int shift) -> bigint& { - FMT_ASSERT(shift >= 0, ""); - exp_ += shift / bigit_bits; - shift %= bigit_bits; -@@ -3133,46 +2632,39 @@ class bigint { - return *this; - } - -- template FMT_CONSTEXPR20 bigint& operator*=(Int value) { -+ template FMT_CONSTEXPR auto operator*=(Int value) -> bigint& { - FMT_ASSERT(value > 0, ""); - multiply(uint32_or_64_or_128_t(value)); - return *this; - } - -- friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { -- int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); -- if (num_lhs_bigits != num_rhs_bigits) -- return num_lhs_bigits > num_rhs_bigits ? 1 : -1; -- int i = static_cast(lhs.bigits_.size()) - 1; -- int j = static_cast(rhs.bigits_.size()) - 1; -+ friend FMT_CONSTEXPR auto compare(const bigint& b1, const bigint& b2) -> int { -+ int num_bigits1 = b1.num_bigits(), num_bigits2 = b2.num_bigits(); -+ if (num_bigits1 != num_bigits2) return num_bigits1 > num_bigits2 ? 1 : -1; -+ int i = static_cast(b1.bigits_.size()) - 1; -+ int j = static_cast(b2.bigits_.size()) - 1; - int end = i - j; - if (end < 0) end = 0; - for (; i >= end; --i, --j) { -- bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; -- if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; -+ bigit b1_bigit = b1.bigits_[i], b2_bigit = b2.bigits_[j]; -+ if (b1_bigit != b2_bigit) return b1_bigit > b2_bigit ? 1 : -1; - } - if (i != j) return i > j ? 1 : -1; - return 0; - } - - // Returns compare(lhs1 + lhs2, rhs). -- friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, -- const bigint& rhs) { -- auto minimum = [](int a, int b) { return a < b ? a : b; }; -- auto maximum = [](int a, int b) { return a > b ? a : b; }; -- int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits()); -+ friend FMT_CONSTEXPR auto add_compare(const bigint& lhs1, const bigint& lhs2, -+ const bigint& rhs) -> int { -+ int max_lhs_bigits = max_of(lhs1.num_bigits(), lhs2.num_bigits()); - int num_rhs_bigits = rhs.num_bigits(); - if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; - if (max_lhs_bigits > num_rhs_bigits) return 1; -- auto get_bigit = [](const bigint& n, int i) -> bigit { -- return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; -- }; - double_bigit borrow = 0; -- int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_); -+ int min_exp = min_of(min_of(lhs1.exp_, lhs2.exp_), rhs.exp_); - for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { -- double_bigit sum = -- static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); -- bigit rhs_bigit = get_bigit(rhs, i); -+ double_bigit sum = double_bigit(lhs1.get_bigit(i)) + lhs2.get_bigit(i); -+ bigit rhs_bigit = rhs.get_bigit(i); - if (sum > rhs_bigit + borrow) return 1; - borrow = rhs_bigit + borrow - sum; - if (borrow > 1) return -1; -@@ -3185,10 +2677,8 @@ class bigint { - FMT_CONSTEXPR20 void assign_pow10(int exp) { - FMT_ASSERT(exp >= 0, ""); - if (exp == 0) return *this = 1; -- // Find the top bit. -- int bitmask = 1; -- while (exp >= bitmask) bitmask <<= 1; -- bitmask >>= 1; -+ int bitmask = 1 << (num_bits() - -+ countl_zero(static_cast(exp)) - 1); - // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by - // repeated squaring and multiplication. - *this = 5; -@@ -3212,17 +2702,17 @@ class bigint { - // cross-product terms n[i] * n[j] such that i + j == bigit_index. - for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { - // Most terms are multiplied twice which can be optimized in the future. -- sum += static_cast(n[i]) * n[j]; -+ sum += double_bigit(n[i]) * n[j]; - } -- (*this)[bigit_index] = static_cast(sum); -+ bigits_[bigit_index] = static_cast(sum); - sum >>= num_bits(); // Compute the carry. - } - // Do the same for the top half. - for (int bigit_index = num_bigits; bigit_index < num_result_bigits; - ++bigit_index) { - for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) -- sum += static_cast(n[i++]) * n[j--]; -- (*this)[bigit_index] = static_cast(sum); -+ sum += double_bigit(n[i++]) * n[j--]; -+ bigits_[bigit_index] = static_cast(sum); - sum >>= num_bits(); - } - remove_leading_zeros(); -@@ -3231,20 +2721,20 @@ class bigint { - - // If this bigint has a bigger exponent than other, adds trailing zero to make - // exponents equal. This simplifies some operations such as subtraction. -- FMT_CONSTEXPR20 void align(const bigint& other) { -+ FMT_CONSTEXPR void align(const bigint& other) { - int exp_difference = exp_ - other.exp_; - if (exp_difference <= 0) return; - int num_bigits = static_cast(bigits_.size()); - bigits_.resize(to_unsigned(num_bigits + exp_difference)); - for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) - bigits_[j] = bigits_[i]; -- std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); -+ memset(bigits_.data(), 0, to_unsigned(exp_difference) * sizeof(bigit)); - exp_ -= exp_difference; - } - - // Divides this bignum by divisor, assigning the remainder to this and - // returning the quotient. -- FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { -+ FMT_CONSTEXPR auto divmod_assign(const bigint& divisor) -> int { - FMT_ASSERT(this != &divisor, ""); - if (compare(*this, divisor) < 0) return 0; - FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); -@@ -3319,6 +2809,7 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, - } - int even = static_cast((value.f & 1) == 0); - if (!upper) upper = &lower; -+ bool shortest = num_digits < 0; - if ((flags & dragon::fixup) != 0) { - if (add_compare(numerator, *upper, denominator) + even <= 0) { - --exp10; -@@ -3331,7 +2822,7 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, - if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1); - } - // Invariant: value == (numerator / denominator) * pow(10, exp10). -- if (num_digits < 0) { -+ if (shortest) { - // Generate the shortest representation. - num_digits = 0; - char* data = buf.data(); -@@ -3361,9 +2852,12 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, - } - // Generate the given number of digits. - exp10 -= num_digits - 1; -- if (num_digits == 0) { -- denominator *= 10; -- auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; -+ if (num_digits <= 0) { -+ auto digit = '0'; -+ if (num_digits == 0) { -+ denominator *= 10; -+ digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; -+ } - buf.push_back(digit); - return; - } -@@ -3386,7 +2880,10 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, - } - if (buf[0] == overflow) { - buf[0] = '1'; -- ++exp10; -+ if ((flags & dragon::fixed) != 0) -+ buf.push_back('0'); -+ else -+ ++exp10; - } - return; - } -@@ -3397,8 +2894,8 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, - - // Formats a floating-point number using the hexfloat format. - template ::value)> --FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, -- float_specs specs, buffer& buf) { -+FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, -+ buffer& buf) { - // float is passed as double to reduce the number of instantiations and to - // simplify implementation. - static_assert(!std::is_same::value, ""); -@@ -3408,26 +2905,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, - // Assume Float is in the format [sign][exponent][significand]. - using carrier_uint = typename info::carrier_uint; - -- constexpr auto num_float_significand_bits = -- detail::num_significand_bits(); -+ const auto num_float_significand_bits = detail::num_significand_bits(); - - basic_fp f(value); - f.e += num_float_significand_bits; - if (!has_implicit_bit()) --f.e; - -- constexpr auto num_fraction_bits = -+ const auto num_fraction_bits = - num_float_significand_bits + (has_implicit_bit() ? 1 : 0); -- constexpr auto num_xdigits = (num_fraction_bits + 3) / 4; -+ const auto num_xdigits = (num_fraction_bits + 3) / 4; - -- constexpr auto leading_shift = ((num_xdigits - 1) * 4); -+ const auto leading_shift = ((num_xdigits - 1) * 4); - const auto leading_mask = carrier_uint(0xF) << leading_shift; - const auto leading_xdigit = - static_cast((f.f & leading_mask) >> leading_shift); - if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1); - - int print_xdigits = num_xdigits - 1; -- if (precision >= 0 && print_xdigits > precision) { -- const int shift = ((print_xdigits - precision - 1) * 4); -+ if (specs.precision >= 0 && print_xdigits > specs.precision) { -+ const int shift = ((print_xdigits - specs.precision - 1) * 4); - const auto mask = carrier_uint(0xF) << shift; - const auto v = static_cast((f.f & mask) >> shift); - -@@ -3446,25 +2942,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, - } - } - -- print_xdigits = precision; -+ print_xdigits = specs.precision; - } - - char xdigits[num_bits() / 4]; - detail::fill_n(xdigits, sizeof(xdigits), '0'); -- format_uint<4>(xdigits, f.f, num_xdigits, specs.upper); -+ format_base2e(4, xdigits, f.f, num_xdigits, specs.upper()); - - // Remove zero tail - while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits; - - buf.push_back('0'); -- buf.push_back(specs.upper ? 'X' : 'x'); -+ buf.push_back(specs.upper() ? 'X' : 'x'); - buf.push_back(xdigits[0]); -- if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision) -+ if (specs.alt() || print_xdigits > 0 || print_xdigits < specs.precision) - buf.push_back('.'); - buf.append(xdigits + 1, xdigits + 1 + print_xdigits); -- for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0'); -+ for (; print_xdigits < specs.precision; ++print_xdigits) buf.push_back('0'); - -- buf.push_back(specs.upper ? 'P' : 'p'); -+ buf.push_back(specs.upper() ? 'P' : 'p'); - - uint32_t abs_e; - if (f.e < 0) { -@@ -3478,21 +2974,32 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, - } - - template ::value)> --FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, -- float_specs specs, buffer& buf) { -- format_hexfloat(static_cast(value), precision, specs, buf); -+FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, -+ buffer& buf) { -+ format_hexfloat(static_cast(value), specs, buf); -+} -+ -+constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { -+ // For checking rounding thresholds. -+ // The kth entry is chosen to be the smallest integer such that the -+ // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. -+ // It is equal to ceil(2^31 + 2^32/10^(k + 1)). -+ // These are stored in a string literal because we cannot have static arrays -+ // in constexpr functions and non-static ones are poorly optimized. -+ return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7" -+ U"\x800001ae\x8000002b"[index]; - } - - template --FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, -+FMT_CONSTEXPR20 auto format_float(Float value, int precision, -+ const format_specs& specs, bool binary32, - buffer& buf) -> int { - // float is passed as double to reduce the number of instantiations. - static_assert(!std::is_same::value, ""); -- FMT_ASSERT(value >= 0, "value is negative"); - auto converted_value = convert_float(value); - -- const bool fixed = specs.format == float_format::fixed; -- if (value <= 0) { // <= instead of == to silence a warning. -+ const bool fixed = specs.type() == presentation_type::fixed; -+ if (value == 0) { - if (precision <= 0 || !fixed) { - buf.push_back('0'); - return 0; -@@ -3505,7 +3012,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - int exp = 0; - bool use_dragon = true; - unsigned dragon_flags = 0; -- if (!is_fast_float()) { -+ if (!is_fast_float() || is_constant_evaluated()) { - const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) - using info = dragonbox::float_info; - const auto f = basic_fp(converted_value); -@@ -3513,38 +3020,10 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). - // This is based on log10(value) == log2(value) / log2(10) and approximation - // of log2(value) by e + num_fraction_bits idea from double-conversion. -- exp = static_cast( -- std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10)); -+ auto e = (f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10; -+ exp = static_cast(e); -+ if (e > exp) ++exp; // Compute ceil. - dragon_flags = dragon::fixup; -- } else if (!is_constant_evaluated() && precision < 0) { -- // Use Dragonbox for the shortest format. -- if (specs.binary32) { -- auto dec = dragonbox::to_decimal(static_cast(value)); -- write(buffer_appender(buf), dec.significand); -- return dec.exponent; -- } -- auto dec = dragonbox::to_decimal(static_cast(value)); -- write(buffer_appender(buf), dec.significand); -- return dec.exponent; -- } else if (is_constant_evaluated()) { -- // Use Grisu + Dragon4 for the given precision: -- // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. -- const int min_exp = -60; // alpha in Grisu. -- int cached_exp10 = 0; // K in Grisu. -- fp normalized = normalize(fp(converted_value)); -- const auto cached_pow = get_cached_power( -- min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); -- normalized = normalized * cached_pow; -- gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; -- if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && -- !is_constant_evaluated()) { -- exp += handler.exp10; -- buf.try_resize(to_unsigned(handler.size)); -- use_dragon = false; -- } else { -- exp += handler.size - cached_exp10 - 1; -- precision = handler.precision; -- } - } else { - // Extract significand bits and exponent bits. - using info = dragonbox::float_info; -@@ -3563,7 +3042,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - significand <<= 1; - } else { - // Normalize subnormal inputs. -- FMT_ASSERT(significand != 0, "zeros should not appear hear"); -+ FMT_ASSERT(significand != 0, "zeros should not appear here"); - int shift = countl_zero(significand); - FMT_ASSERT(shift >= num_bits() - num_significand_bits(), - ""); -@@ -3600,9 +3079,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - } - - // Compute the actual number of decimal digits to print. -- if (fixed) { -- adjust_precision(precision, exp + digits_in_the_first_segment); -- } -+ if (fixed) adjust_precision(precision, exp + digits_in_the_first_segment); - - // Use Dragon4 only when there might be not enough digits in the first - // segment. -@@ -3645,7 +3122,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - uint64_t prod; - uint32_t digits; - bool should_round_up; -- int number_of_digits_to_print = precision > 9 ? 9 : precision; -+ int number_of_digits_to_print = min_of(precision, 9); - - // Print a 9-digits subsegment, either the first or the second. - auto print_subsegment = [&](uint32_t subsegment, char* buffer) { -@@ -3673,7 +3150,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - // for details. - prod = ((subsegment * static_cast(450359963)) >> 20) + 1; - digits = static_cast(prod >> 32); -- copy2(buffer, digits2(digits)); -+ write2digits(buffer, digits); - number_of_digits_printed += 2; - } - -@@ -3681,7 +3158,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - while (number_of_digits_printed < number_of_digits_to_print) { - prod = static_cast(prod) * static_cast(100); - digits = static_cast(prod >> 32); -- copy2(buffer + number_of_digits_printed, digits2(digits)); -+ write2digits(buffer + number_of_digits_printed, digits); - number_of_digits_printed += 2; - } - }; -@@ -3707,12 +3184,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - // fractional part is strictly larger than 1/2. - if (precision < 9) { - uint32_t fractional_part = static_cast(prod); -- should_round_up = fractional_part >= -- data::fractional_part_rounding_thresholds -- [8 - number_of_digits_to_print] || -- ((fractional_part >> 31) & -- ((digits & 1) | (second_third_subsegments != 0) | -- has_more_segments)) != 0; -+ should_round_up = -+ fractional_part >= fractional_part_rounding_thresholds( -+ 8 - number_of_digits_to_print) || -+ ((fractional_part >> 31) & -+ ((digits & 1) | (second_third_subsegments != 0) | -+ has_more_segments)) != 0; - } - // Rounding at the subsegment boundary. - // In this case, the fractional part is at least 1/2 if and only if -@@ -3747,12 +3224,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - // of 19 digits, so in this case the third segment should be - // consisting of a genuine digit from the input. - uint32_t fractional_part = static_cast(prod); -- should_round_up = fractional_part >= -- data::fractional_part_rounding_thresholds -- [8 - number_of_digits_to_print] || -- ((fractional_part >> 31) & -- ((digits & 1) | (third_subsegment != 0) | -- has_more_segments)) != 0; -+ should_round_up = -+ fractional_part >= fractional_part_rounding_thresholds( -+ 8 - number_of_digits_to_print) || -+ ((fractional_part >> 31) & -+ ((digits & 1) | (third_subsegment != 0) | -+ has_more_segments)) != 0; - } - // Rounding at the subsegment boundary. - else { -@@ -3790,9 +3267,8 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - } - if (use_dragon) { - auto f = basic_fp(); -- bool is_predecessor_closer = specs.binary32 -- ? f.assign(static_cast(value)) -- : f.assign(converted_value); -+ bool is_predecessor_closer = binary32 ? f.assign(static_cast(value)) -+ : f.assign(converted_value); - if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; - if (fixed) dragon_flags |= dragon::fixed; - // Limit precision to the maximum possible number of significant digits in -@@ -3801,7 +3277,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - if (precision > max_double_digits) precision = max_double_digits; - format_dragon(f, dragon_flags, precision, buf, exp); - } -- if (!fixed && !specs.showpoint) { -+ if (!fixed && !specs.alt()) { - // Remove trailing zeros. - auto num_digits = buf.size(); - while (num_digits > 0 && buf[num_digits - 1] == '0') { -@@ -3812,97 +3288,106 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, - } - return exp; - } -+ -+// Numbers with exponents greater or equal to the returned value will use -+// the exponential notation. -+template constexpr auto exp_upper() -> int { -+ return std::numeric_limits::digits10 != 0 -+ ? min_of(16, std::numeric_limits::digits10 + 1) -+ : 16; -+} -+ - template --FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, -- format_specs specs, locale_ref loc) -- -> OutputIt { -- float_specs fspecs = parse_float_type_spec(specs); -- fspecs.sign = specs.sign; -- if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. -- fspecs.sign = sign::minus; -- value = -value; -- } else if (fspecs.sign == sign::minus) { -- fspecs.sign = sign::none; -- } -+FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, -+ locale_ref loc) -> OutputIt { -+ // Use signbit because value < 0 is false for NaN. -+ sign s = detail::signbit(value) ? sign::minus : specs.sign(); - - if (!detail::isfinite(value)) -- return write_nonfinite(out, detail::isnan(value), specs, fspecs); -+ return write_nonfinite(out, detail::isnan(value), specs, s); - -- if (specs.align == align::numeric && fspecs.sign) { -- auto it = reserve(out, 1); -- *it++ = detail::sign(fspecs.sign); -- out = base_iterator(out, it); -- fspecs.sign = sign::none; -+ if (specs.align() == align::numeric && s != sign::none) { -+ *out++ = detail::getsign(s); -+ s = sign::none; - if (specs.width != 0) --specs.width; - } - -+ constexpr int exp_upper = detail::exp_upper(); -+ int precision = specs.precision; -+ if (precision < 0) { -+ if (specs.type() != presentation_type::none) { -+ precision = 6; -+ } else if (is_fast_float::value && !is_constant_evaluated()) { -+ // Use Dragonbox for the shortest format. -+ using floaty = conditional_t= sizeof(double), double, float>; -+ auto dec = dragonbox::to_decimal(static_cast(value)); -+ return write_float(out, dec, specs, s, exp_upper, loc); -+ } -+ } -+ - memory_buffer buffer; -- if (fspecs.format == float_format::hex) { -- if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); -- format_hexfloat(convert_float(value), specs.precision, fspecs, buffer); -- return write_bytes(out, {buffer.data(), buffer.size()}, -- specs); -- } -- int precision = specs.precision >= 0 || specs.type == presentation_type::none -- ? specs.precision -- : 6; -- if (fspecs.format == float_format::exp) { -+ if (specs.type() == presentation_type::hexfloat) { -+ if (s != sign::none) buffer.push_back(detail::getsign(s)); -+ format_hexfloat(convert_float(value), specs, buffer); -+ return write_bytes(out, {buffer.data(), buffer.size()}, -+ specs); -+ } -+ -+ if (specs.type() == presentation_type::exp) { - if (precision == max_value()) -- throw_format_error("number is too big"); -+ report_error("number is too big"); - else - ++precision; -- } else if (fspecs.format != float_format::fixed && precision == 0) { -+ if (specs.precision != 0) specs.set_alt(); -+ } else if (specs.type() == presentation_type::fixed) { -+ if (specs.precision != 0) specs.set_alt(); -+ } else if (precision == 0) { - precision = 1; - } -- if (const_check(std::is_same())) fspecs.binary32 = true; -- int exp = format_float(convert_float(value), precision, fspecs, buffer); -- fspecs.precision = precision; -+ int exp = format_float(convert_float(value), precision, specs, -+ std::is_same(), buffer); -+ -+ specs.precision = precision; - auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; -- return write_float(out, f, specs, fspecs, loc); -+ return write_float(out, f, specs, s, exp_upper, loc); - } - - template ::value)> --FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, -+FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, - locale_ref loc = {}) -> OutputIt { -- if (const_check(!is_supported_floating_point(value))) return out; -- return specs.localized && write_loc(out, value, specs, loc) -+ return specs.localized() && write_loc(out, value, specs, loc) - ? out -- : write_float(out, value, specs, loc); -+ : write_float(out, value, specs, loc); - } - - template ::value)> - FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { -- if (is_constant_evaluated()) return write(out, value, format_specs()); -- if (const_check(!is_supported_floating_point(value))) return out; -+ if (is_constant_evaluated()) return write(out, value, format_specs()); - -- auto fspecs = float_specs(); -- if (detail::signbit(value)) { -- fspecs.sign = sign::minus; -- value = -value; -- } -+ auto s = detail::signbit(value) ? sign::minus : sign::none; - -- constexpr auto specs = format_specs(); -- using floaty = conditional_t::value, double, T>; -+ constexpr auto specs = format_specs(); -+ using floaty = conditional_t= sizeof(double), double, float>; - using floaty_uint = typename dragonbox::float_info::carrier_uint; - floaty_uint mask = exponent_mask(); - if ((bit_cast(value) & mask) == mask) -- return write_nonfinite(out, std::isnan(value), specs, fspecs); -+ return write_nonfinite(out, std::isnan(value), specs, s); - - auto dec = dragonbox::to_decimal(static_cast(value)); -- return write_float(out, dec, specs, fspecs, {}); -+ return write_float(out, dec, specs, s, exp_upper(), {}); - } - - template ::value && - !is_fast_float::value)> - inline auto write(OutputIt out, T value) -> OutputIt { -- return write(out, value, format_specs()); -+ return write(out, value, format_specs()); - } - - template --auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) -+auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) - -> OutputIt { - FMT_ASSERT(false, ""); - return out; -@@ -3911,13 +3396,11 @@ auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) - template - FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) - -> OutputIt { -- auto it = reserve(out, value.size()); -- it = copy_str_noinline(value.begin(), value.end(), it); -- return base_iterator(out, it); -+ return copy_noinline(value.begin(), value.end(), out); - } - - template ::value)> -+ FMT_ENABLE_IF(has_to_string_view::value)> - constexpr auto write(OutputIt out, const T& value) -> OutputIt { - return write(out, to_string_view(value)); - } -@@ -3925,10 +3408,8 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt { - // FMT_ENABLE_IF() condition separated to workaround an MSVC bug. - template < - typename Char, typename OutputIt, typename T, -- bool check = -- std::is_enum::value && !std::is_same::value && -- mapped_type_constant>::value != -- type::custom_type, -+ bool check = std::is_enum::value && !std::is_same::value && -+ mapped_type_constant::value != type::custom_type, - FMT_ENABLE_IF(check)> - FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { - return write(out, static_cast>(value)); -@@ -3936,13 +3417,12 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { - - template ::value)> --FMT_CONSTEXPR auto write(OutputIt out, T value, -- const format_specs& specs = {}, locale_ref = {}) -- -> OutputIt { -- return specs.type != presentation_type::none && -- specs.type != presentation_type::string -- ? write(out, value ? 1 : 0, specs, {}) -- : write_bytes(out, value ? "true" : "false", specs); -+FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {}, -+ locale_ref = {}) -> OutputIt { -+ return specs.type() != presentation_type::none && -+ specs.type() != presentation_type::string -+ ? write(out, value ? 1 : 0, specs, {}) -+ : write_bytes(out, value ? "true" : "false", specs); - } - - template -@@ -3953,202 +3433,150 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { - } - - template --FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) -- -> OutputIt { -+FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt { - if (value) return write(out, basic_string_view(value)); -- throw_format_error("string pointer is null"); -+ report_error("string pointer is null"); - return out; - } - - template ::value)> --auto write(OutputIt out, const T* value, const format_specs& specs = {}, -+auto write(OutputIt out, const T* value, const format_specs& specs = {}, - locale_ref = {}) -> OutputIt { - return write_ptr(out, bit_cast(value), &specs); - } - --// A write overload that handles implicit conversions. - template > --FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< -- std::is_class::value && !is_string::value && -- !is_floating_point::value && !std::is_same::value && -- !std::is_same().map( -- value))>>::value, -- OutputIt> { -- return write(out, arg_mapper().map(value)); -+ FMT_ENABLE_IF(mapped_type_constant::value == -+ type::custom_type && -+ !std::is_fundamental::value)> -+FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> OutputIt { -+ auto f = formatter(); -+ auto parse_ctx = parse_context({}); -+ f.parse(parse_ctx); -+ auto ctx = basic_format_context(out, {}, {}); -+ return f.format(value, ctx); - } - --template > --FMT_CONSTEXPR auto write(OutputIt out, const T& value) -- -> enable_if_t::value == type::custom_type, -- OutputIt> { -- auto ctx = Context(out, {}, {}); -- return typename Context::template formatter_type().format(value, ctx); --} -+template -+using is_builtin = -+ bool_constant::value || FMT_BUILTIN_TYPES>; - - // An argument visitor that formats the argument and writes it via the output - // iterator. It's a class and not a generic lambda for compatibility with C++11. - template struct default_arg_formatter { -- using iterator = buffer_appender; -- using context = buffer_context; -+ using context = buffered_context; - -- iterator out; -- basic_format_args args; -- locale_ref loc; -+ basic_appender out; - -- template auto operator()(T value) -> iterator { -- return write(out, value); -- } -- auto operator()(typename basic_format_arg::handle h) -> iterator { -- basic_format_parse_context parse_ctx({}); -- context format_ctx(out, args, loc); -- h.format(parse_ctx, format_ctx); -- return format_ctx.out(); -- } --}; -- --template struct arg_formatter { -- using iterator = buffer_appender; -- using context = buffer_context; -+ void operator()(monostate) { report_error("argument not found"); } - -- iterator out; -- const format_specs& specs; -- locale_ref locale; -- -- template -- FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { -- return detail::write(out, value, specs, locale); -- } -- auto operator()(typename basic_format_arg::handle) -> iterator { -- // User-defined types are handled separately because they require access -- // to the parse context. -- return out; -+ template ::value)> -+ void operator()(T value) { -+ write(out, value); - } --}; - --template struct custom_formatter { -- basic_format_parse_context& parse_ctx; -- buffer_context& ctx; -+ template ::value)> -+ void operator()(T) { -+ FMT_ASSERT(false, ""); -+ } - -- void operator()( -- typename basic_format_arg>::handle h) const { -- h.format(parse_ctx, ctx); -+ void operator()(typename basic_format_arg::handle h) { -+ // Use a null locale since the default format must be unlocalized. -+ auto parse_ctx = parse_context({}); -+ auto format_ctx = context(out, {}, {}); -+ h.format(parse_ctx, format_ctx); - } -- template void operator()(T) const {} - }; - --template class width_checker { -- public: -- explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} -+template struct arg_formatter { -+ basic_appender out; -+ const format_specs& specs; -+ FMT_NO_UNIQUE_ADDRESS locale_ref locale; - -- template ::value)> -- FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { -- if (is_negative(value)) handler_.on_error("negative width"); -- return static_cast(value); -+ template ::value)> -+ FMT_CONSTEXPR FMT_INLINE void operator()(T value) { -+ detail::write(out, value, specs, locale); - } - -- template ::value)> -- FMT_CONSTEXPR auto operator()(T) -> unsigned long long { -- handler_.on_error("width is not integer"); -- return 0; -+ template ::value)> -+ void operator()(T) { -+ FMT_ASSERT(false, ""); - } - -- private: -- ErrorHandler& handler_; -+ void operator()(typename basic_format_arg>::handle) { -+ // User-defined types are handled separately because they require access -+ // to the parse context. -+ } - }; - --template class precision_checker { -- public: -- explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} -- -+struct dynamic_spec_getter { - template ::value)> - FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { -- if (is_negative(value)) handler_.on_error("negative precision"); -- return static_cast(value); -+ return is_negative(value) ? ~0ull : static_cast(value); - } - - template ::value)> - FMT_CONSTEXPR auto operator()(T) -> unsigned long long { -- handler_.on_error("precision is not integer"); -+ report_error("width/precision is not integer"); - return 0; - } -- -- private: -- ErrorHandler& handler_; - }; - --template