From 088e5a4a257b616c0c6beb795ad59070fa5428bc Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Fri, 31 Oct 2025 20:45:44 +0000 Subject: Update the vendored libraries. --- vendor/README.md | 6 +- vendor/fmt/base.h | 209 ++-- vendor/fmt/color.h | 36 +- vendor/fmt/compile.h | 203 ++-- vendor/fmt/format-inl.h | 122 +-- vendor/fmt/format.cc | 17 +- vendor/fmt/format.h | 815 +++++++++------- vendor/lyra/lyra.hpp | 2329 +++++++++++++++++++++++++++++---------------- vendor/utfcpp/core.h | 56 +- vendor/utfcpp/unchecked.h | 23 +- 10 files changed, 2380 insertions(+), 1436 deletions(-) diff --git a/vendor/README.md b/vendor/README.md index d21d0a4c4..abaea1eee 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -18,7 +18,7 @@ This directory contains vendored dependencies that are shipped with InspIRCd to **License** — MIT License -**Version** — 11.2.0 +**Version** — 12.1.0 **Website** — [https://github.com/fmtlib/fmt/](https://github.com/fmtlib/fmt/) @@ -38,7 +38,7 @@ This directory contains vendored dependencies that are shipped with InspIRCd to **License** — Boost Software License -**Version** — 1.6.1 +**Version** — 1.7.0 **Website** — [https://github.com/bfgroup/Lyra](https://github.com/bfgroup/Lyra) @@ -68,6 +68,6 @@ This directory contains vendored dependencies that are shipped with InspIRCd to **License** — Boost Software License -**Version** — v4.0.6 +**Version** — v4.0.8 **Website** — [https://github.com/nemtrif/utfcpp](https://github.com/nemtrif/utfcpp) diff --git a/vendor/fmt/base.h b/vendor/fmt/base.h index 87b3fd7cb..0d5786778 100644 --- a/vendor/fmt/base.h +++ b/vendor/fmt/base.h @@ -21,7 +21,7 @@ #endif // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 110200 +#define FMT_VERSION 120100 // Detect compiler versions. #if defined(__clang__) && !defined(__ibmxl__) @@ -114,7 +114,9 @@ #endif // Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. -#if !defined(__cpp_lib_is_constant_evaluated) +#ifdef FMT_USE_CONSTEVAL +// Use the provided definition. +#elif !defined(__cpp_lib_is_constant_evaluated) # define FMT_USE_CONSTEVAL 0 #elif FMT_CPLUSPLUS < 201709L # define FMT_USE_CONSTEVAL 0 @@ -201,14 +203,6 @@ # 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 - #if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_VISIBILITY(value) __attribute__((visibility(value))) #else @@ -242,6 +236,7 @@ FMT_PRAGMA_GCC(optimize("Og")) # define FMT_GCC_OPTIMIZED #endif FMT_PRAGMA_CLANG(diagnostic push) +FMT_PRAGMA_GCC(diagnostic push) #ifdef FMT_ALWAYS_INLINE // Use the provided definition. @@ -260,7 +255,7 @@ FMT_PRAGMA_CLANG(diagnostic push) #ifndef FMT_BEGIN_NAMESPACE # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - inline namespace v11 { + inline namespace v12 { # define FMT_END_NAMESPACE \ } \ } @@ -356,6 +351,9 @@ template constexpr auto max_of(T a, T b) -> T { return a > b ? a : b; } +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + namespace detail { // Suppresses "unused variable" warnings with the method described in // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. @@ -396,7 +394,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, # define FMT_ASSERT(condition, message) \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ ? (void)0 \ - : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) + : ::fmt::assert_fail(__FILE__, __LINE__, (message))) #endif #ifdef FMT_USE_INT128 @@ -419,8 +417,12 @@ 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) +#ifdef FMT_USE_BITINT +// Use the provided definition. +#elif FMT_CLANG_VERSION >= 1500 && !defined(__CUDACC__) +# define FMT_USE_BITINT 1 +#else +# define FMT_USE_BITINT 0 #endif #if FMT_USE_BITINT @@ -463,12 +465,13 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled }; 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 constexpr auto narrow(T*) -> char* { return nullptr; } +constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* { + return s; +} template -FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) - -> int { +FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, 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; @@ -540,7 +543,7 @@ template class basic_string_view { 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 && !detail::is_constant_evaluated()) { - size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr. + size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr. return; } #endif @@ -616,19 +619,6 @@ template class basic_string_view { using string_view = basic_string_view; -// DEPRECATED! Will be merged with is_char and moved to detail. -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 - -// Specifies if `T` is a character (code unit) type. -template struct is_char : is_xchar {}; -template <> struct is_char : std::true_type {}; - template class basic_appender; using appender = basic_appender; @@ -781,7 +771,7 @@ class basic_specs { (static_cast(p) << precision_shift); } - constexpr bool dynamic() const { + constexpr auto dynamic() const -> bool { return (data_ & (width_mask | precision_mask)) != 0; } @@ -921,14 +911,50 @@ template class parse_context { FMT_CONSTEXPR void check_dynamic_spec(int arg_id); }; +#ifndef FMT_USE_LOCALE +# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) +#endif + +// A type-erased reference to std::locale to avoid the 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) : locale_(&loc) { + // Check if std::isalpha is found via ADL to reduce the chance of misuse. + isalpha('x', loc); + } + + inline explicit operator bool() const noexcept { return locale_ != nullptr; } +#endif // FMT_USE_LOCALE + + public: + template auto get() const -> Locale; +}; + FMT_END_EXPORT namespace detail { +// Specifies if `T` is a code unit type. +template struct is_code_unit : std::false_type {}; +template <> struct is_code_unit : std::true_type {}; +template <> struct is_code_unit : std::true_type {}; +template <> struct is_code_unit : std::true_type {}; +template <> struct is_code_unit : std::true_type {}; +#ifdef __cpp_char8_t +template <> struct is_code_unit : bool_constant {}; +#endif + // 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)> +template ::value)> constexpr auto to_string_view(const Char* s) -> basic_string_view { return s; } @@ -1057,11 +1083,11 @@ template constexpr auto count() -> int { return (B1 ? 1 : 0) + count(); } -template constexpr auto count_named_args() -> int { - return count::value...>(); +template constexpr auto count_named_args() -> int { + return count::value...>(); } -template constexpr auto count_static_named_args() -> int { - return count::value...>(); +template constexpr auto count_static_named_args() -> int { + return count::value...>(); } template struct named_arg_info { @@ -1069,7 +1095,7 @@ template struct named_arg_info { int id; }; -// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13. +// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13. template FMT_CONSTEXPR void check_for_duplicate(named_arg_info* named_args, int named_arg_index, @@ -1173,7 +1199,7 @@ template struct type_mapper { static auto map(ubitint) -> conditional_t; - template ::value)> + template ::value)> static auto map(T) -> conditional_t< std::is_same::value || std::is_same::value, Char, void>; @@ -1679,12 +1705,12 @@ 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)]; + 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)]; + parse_func parse_funcs_[max_of(1, NUM_ARGS)]; public: template @@ -1828,12 +1854,17 @@ template class buffer { void append(const U* begin, const U* end) { while (begin != end) { + auto size = size_; + auto free_cap = capacity_ - size; auto count = to_unsigned(end - begin); - try_reserve(size_ + count); - auto free_cap = capacity_ - size_; - if (free_cap < count) count = free_cap; + if (free_cap < count) { + grow_(*this, size + count); + size = size_; + free_cap = capacity_ - size; + count = count < free_cap ? count : free_cap; + } // A loop is faster than memcpy on small sizes. - T* out = ptr_ + size_; + T* out = ptr_ + size; for (size_t i = 0; i < count; ++i) out[i] = begin[i]; size_ += count; begin += count; @@ -2033,6 +2064,17 @@ struct has_back_insert_iterator_container_append< .append(std::declval(), std::declval()))>> : std::true_type {}; +template +struct has_back_insert_iterator_container_insert_at_end : std::false_type {}; + +template +struct has_back_insert_iterator_container_insert_at_end< + OutputIt, InputIt, + void_t()) + .insert(get_container(std::declval()).end(), + std::declval(), + std::declval()))>> : std::true_type {}; + // An optimized version of std::copy with the output value type (T). template ::value&& @@ -2047,6 +2089,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) template ::value && !has_back_insert_iterator_container_append< + OutputIt, InputIt>::value && + has_back_insert_iterator_container_insert_at_end< OutputIt, InputIt>::value)> FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { @@ -2056,7 +2100,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) } template ::value)> + FMT_ENABLE_IF(!(is_back_insert_iterator::value && + (has_back_insert_iterator_container_append< + OutputIt, InputIt>::value || + has_back_insert_iterator_container_insert_at_end< + OutputIt, InputIt>::value)))> FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { while (begin != end) *out++ = static_cast(*begin++); return out; @@ -2176,7 +2224,7 @@ template class value { static_assert(N <= 64, "unsupported _BitInt"); } - template ::value)> + template ::value)> constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { static_assert( std::is_same::value || std::is_same::value, @@ -2252,7 +2300,7 @@ template class value { custom.value = const_cast(&x); #endif } - custom.format = format_custom>; + custom.format = format_custom; } template ())> @@ -2263,10 +2311,10 @@ template class value { } // Formats an argument of a custom type, such as a user-defined class. - template + template static void format_custom(void* arg, parse_context& parse_ctx, Context& ctx) { - auto f = Formatter(); + auto f = formatter(); parse_ctx.advance_to(f.parse(parse_ctx)); using qualified_type = conditional_t(), const T, T>; @@ -2293,35 +2341,14 @@ struct is_output_iterator< 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 +template constexpr auto encode_types() -> unsigned long long { - return static_cast(stored_type_constant::value) | - (encode_types() << packed_arg_bits); + return static_cast(stored_type_constant::value) | + (encode_types() << packed_arg_bits); } template @@ -2338,8 +2365,9 @@ 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]; + arg_t args[1u + NUM_ARGS]; + named_arg_info + named_args[static_cast(NUM_NAMED_ARGS)]; template FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) @@ -2358,8 +2386,8 @@ struct named_arg_store { } 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; + auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete; + auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete; operator const arg_t*() const { return args + 1; } }; @@ -2372,7 +2400,7 @@ 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)], + arg_t[max_of(1, NUM_ARGS)], named_arg_store>; type args; }; @@ -2656,22 +2684,17 @@ class context { private: appender out_; format_args args_; - FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; + FMT_NO_UNIQUE_ADDRESS locale_ref loc_; public: - /// The character type for the output. - using char_type = char; - + using char_type = char; ///< The character type for the output. 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 = {}) + FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {}) : out_(out), args_(args), loc_(loc) {} context(context&&) = default; context(const context&) = delete; @@ -2692,7 +2715,7 @@ class context { // Advances the begin iterator to `it`. FMT_CONSTEXPR void advance_to(iterator) {} - FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; } + FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; } }; template struct runtime_format_string { @@ -2779,9 +2802,6 @@ 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 fmt, T&&... args) { return fmt::println(stdout, fmt, static_cast(args)...); } -FMT_END_EXPORT +FMT_PRAGMA_GCC(diagnostic pop) FMT_PRAGMA_CLANG(diagnostic pop) FMT_PRAGMA_GCC(pop_options) +FMT_END_EXPORT FMT_END_NAMESPACE #ifdef FMT_HEADER_ONLY diff --git a/vendor/fmt/color.h b/vendor/fmt/color.h index 638f15b43..2cbc53ca3 100644 --- a/vendor/fmt/color.h +++ b/vendor/fmt/color.h @@ -375,19 +375,17 @@ template struct ansi_color_escape { // 10 more. if (is_background) value += 10u; - size_t index = 0; - buffer[index++] = static_cast('\x1b'); - buffer[index++] = static_cast('['); + buffer[size++] = static_cast('\x1b'); + buffer[size++] = static_cast('['); if (value >= 100u) { - buffer[index++] = static_cast('1'); + buffer[size++] = static_cast('1'); value %= 100u; } - buffer[index++] = static_cast('0' + value / 10u); - buffer[index++] = static_cast('0' + value % 10u); + buffer[size++] = static_cast('0' + value / 10u); + buffer[size++] = static_cast('0' + value % 10u); - buffer[index++] = static_cast('m'); - buffer[index++] = static_cast('\0'); + buffer[size++] = static_cast('m'); return; } @@ -398,7 +396,7 @@ template struct ansi_color_escape { to_esc(color.r, buffer + 7, ';'); to_esc(color.g, buffer + 11, ';'); to_esc(color.b, buffer + 15, 'm'); - buffer[19] = static_cast(0); + size = 19; } FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { uint8_t em_codes[num_emphases] = {}; @@ -411,26 +409,28 @@ template struct ansi_color_escape { if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; - size_t index = 0; + buffer[size++] = static_cast('\x1b'); + buffer[size++] = static_cast('['); + for (size_t i = 0; i < num_emphases; ++i) { if (!em_codes[i]) continue; - buffer[index++] = static_cast('\x1b'); - buffer[index++] = static_cast('['); - buffer[index++] = static_cast('0' + em_codes[i]); - buffer[index++] = static_cast('m'); + buffer[size++] = static_cast('0' + em_codes[i]); + buffer[size++] = static_cast(';'); } - buffer[index++] = static_cast(0); + + buffer[size - 1] = static_cast('m'); } FMT_CONSTEXPR operator const Char*() const noexcept { return 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(); + FMT_CONSTEXPR auto end() const noexcept -> const Char* { + return buffer + size; } private: static constexpr size_t num_emphases = 8; - Char buffer[7u + 3u * num_emphases + 1u]; + Char buffer[7u + 4u * num_emphases] = {}; + size_t size = 0; static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, char delimiter) noexcept { diff --git a/vendor/fmt/compile.h b/vendor/fmt/compile.h index 08d9427ff..64eb7a20d 100644 --- a/vendor/fmt/compile.h +++ b/vendor/fmt/compile.h @@ -15,15 +15,14 @@ #include "format.h" FMT_BEGIN_NAMESPACE +FMT_BEGIN_EXPORT // A compile-time string which is compiled into fast formatting code. -FMT_EXPORT class compiled_string {}; +class compiled_string {}; template struct is_compiled_string : std::is_base_of {}; -namespace detail { - /** * 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 @@ -41,18 +40,42 @@ namespace detail { # define FMT_COMPILE(s) FMT_STRING(s) #endif +/** + * Converts a string literal into a format string that will be parsed at + * compile time and converted into efficient formatting code. Requires support + * for class types in constant template parameters (a C++20 feature). + * + * **Example**: + * + * // Converts 42 into std::string using the most efficient method and no + * // runtime format string processing. + * using namespace fmt::literals; + * std::string s = fmt::format("{}"_cf, 42); + */ +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +inline namespace literals { +template constexpr auto operator""_cf() { + return FMT_COMPILE(Str.data); +} +} // namespace literals +#endif + +FMT_END_EXPORT + +namespace detail { + template -auto first(const T& value, const Tail&...) -> const T& { +constexpr auto first(const T& value, const Tail&...) -> const T& { return value; } #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -template struct type_list {}; +template struct type_list {}; // Returns a reference to the argument at index N from [first, rest...]. template -constexpr const auto& get([[maybe_unused]] const T& first, - [[maybe_unused]] const Args&... rest) { +constexpr auto get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) -> const auto& { static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); if constexpr (N == 0) return first; @@ -84,8 +107,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { } template -constexpr int get_arg_index_by_name(basic_string_view name, - type_list) { +constexpr auto get_arg_index_by_name(basic_string_view name, + type_list) -> int { return get_arg_index_by_name(name); } @@ -105,8 +128,8 @@ template struct text { basic_string_view data; using char_type = Char; - template - constexpr OutputIt format(OutputIt out, const Args&...) const { + template + constexpr auto format(OutputIt out, const T&...) const -> OutputIt { return write(out, data); } }; @@ -115,8 +138,8 @@ template struct is_compiled_format> : std::true_type {}; template -constexpr text make_text(basic_string_view s, size_t pos, - size_t size) { +constexpr auto make_text(basic_string_view s, size_t pos, size_t size) + -> text { return {{&s[pos], size}}; } @@ -124,8 +147,8 @@ template struct code_unit { Char value; using char_type = Char; - template - constexpr OutputIt format(OutputIt out, const Args&...) const { + template + constexpr auto format(OutputIt out, const T&...) const -> OutputIt { *out++ = value; return out; } @@ -133,7 +156,7 @@ template struct code_unit { // This ensures that the argument type is convertible to `const T&`. template -constexpr const T& get_arg_checked(const Args&... args) { +constexpr auto get_arg_checked(const Args&... args) -> const T& { const auto& arg = detail::get(args...); if constexpr (detail::is_named_arg>()) { return arg.value; @@ -146,13 +169,13 @@ template struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument N. -template struct field { +template struct field { using char_type = Char; - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { - const T& arg = get_arg_checked(args...); - if constexpr (std::is_convertible>::value) { + template + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { + const V& 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 { @@ -170,10 +193,10 @@ template struct runtime_named_field { basic_string_view name; template - constexpr static bool try_format_argument( + constexpr static auto try_format_argument( OutputIt& out, // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 - [[maybe_unused]] basic_string_view arg_name, const T& arg) { + [[maybe_unused]] basic_string_view arg_name, const T& arg) -> bool { if constexpr (is_named_arg::type>::value) { if (arg_name == arg.name) { out = write(out, arg.value); @@ -183,8 +206,8 @@ template struct runtime_named_field { return false; } - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { + template + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { bool found = (try_format_argument(out, name, args) || ...); if (!found) { FMT_THROW(format_error("argument with specified name is not found")); @@ -197,17 +220,17 @@ template struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument N and has format specifiers. -template struct spec_field { +template struct spec_field { using char_type = Char; - formatter fmt; + formatter fmt; - template - constexpr FMT_INLINE OutputIt format(OutputIt out, - const Args&... args) const { + template + constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const + -> OutputIt { const auto& vargs = fmt::make_format_args>(args...); basic_format_context ctx(out, vargs); - return fmt.format(get_arg_checked(args...), ctx); + return fmt.format(get_arg_checked(args...), ctx); } }; @@ -219,8 +242,8 @@ template struct concat { R rhs; using char_type = typename L::char_type; - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { + template + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { out = lhs.format(out, args...); return rhs.format(out, args...); } @@ -230,14 +253,14 @@ template struct is_compiled_format> : std::true_type {}; template -constexpr concat make_concat(L lhs, R rhs) { +constexpr auto make_concat(L lhs, R rhs) -> concat { return {lhs, rhs}; } struct unknown_format {}; template -constexpr size_t parse_text(basic_string_view str, size_t pos) { +constexpr auto parse_text(basic_string_view str, size_t pos) -> size_t { for (size_t size = str.size(); pos != size; ++pos) { if (str[pos] == '{' || str[pos] == '}') break; } @@ -270,8 +293,8 @@ template struct parse_specs_result { enum { manual_indexing_id = -1 }; template -constexpr parse_specs_result parse_specs(basic_string_view str, - size_t pos, int next_arg_id) { +constexpr auto parse_specs(basic_string_view str, size_t pos, + int next_arg_id) -> parse_specs_result { str.remove_prefix(pos); auto ctx = compile_parse_context(str, max_value(), nullptr, next_arg_id); @@ -285,16 +308,16 @@ template struct arg_id_handler { arg_id_kind kind; arg_ref arg_id; - constexpr int on_auto() { + constexpr auto on_auto() -> int { FMT_ASSERT(false, "handler cannot be used with automatic indexing"); return 0; } - constexpr int on_index(int id) { + constexpr auto on_index(int id) -> int { kind = arg_id_kind::index; arg_id = arg_ref(id); return 0; } - constexpr int on_name(basic_string_view id) { + constexpr auto on_name(basic_string_view id) -> int { kind = arg_id_kind::name; arg_id = arg_ref(id); return 0; @@ -433,27 +456,28 @@ FMT_BEGIN_EXPORT #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -template ::value)> -FMT_INLINE std::basic_string format(const CompiledFormat& cf, - const Args&... args) { +FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf, + const T&... args) + -> std::basic_string { auto s = std::basic_string(); cf.format(std::back_inserter(s), args...); return s; } -template ::value)> -constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, - const Args&... args) { +constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf, + const T&... args) -> OutputIt { return cf.format(out, args...); } -template ::value)> -FMT_INLINE std::basic_string format(const S&, - Args&&... args) { +FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args) + -> std::basic_string { if constexpr (std::is_same::value) { constexpr auto str = basic_string_view(S()); if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { @@ -466,72 +490,97 @@ FMT_INLINE std::basic_string format(const S&, } } } - constexpr auto compiled = detail::compile(S()); + constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { return fmt::format( static_cast>(S()), - std::forward(args)...); + std::forward(args)...); } else { - return fmt::format(compiled, std::forward(args)...); + return fmt::format(compiled, std::forward(args)...); } } -template ::value)> -FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { - constexpr auto compiled = detail::compile(S()); +FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt { + constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { return fmt::format_to( out, static_cast>(S()), - std::forward(args)...); + std::forward(args)...); } else { - return fmt::format_to(out, compiled, std::forward(args)...); + return fmt::format_to(out, compiled, std::forward(args)...); } } #endif -template ::value)> -auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) +auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... 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)...); + fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); return {buf.out(), buf.count()}; } -template ::value)> -FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) - -> size_t { +FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); - fmt::format_to(appender(buf), fmt, args...); + fmt::format_to(appender(buf), fmt, std::forward(args)...); return buf.count(); } -template ::value)> -void print(std::FILE* f, const S& fmt, const Args&... args) { +void print(std::FILE* f, const S& fmt, T&&... args) { auto buf = memory_buffer(); - fmt::format_to(appender(buf), fmt, args...); + fmt::format_to(appender(buf), fmt, std::forward(args)...); detail::print(f, {buf.data(), buf.size()}); } -template ::value)> -void print(const S& fmt, const Args&... args) { - print(stdout, fmt, args...); +void print(const S& fmt, T&&... args) { + print(stdout, fmt, std::forward(args)...); } -#if FMT_USE_NONTYPE_TEMPLATE_ARGS -inline namespace literals { -template constexpr auto operator""_cf() { - return FMT_COMPILE(Str.data); -} -} // namespace literals -#endif +template class static_format_result { + private: + char data[N]; + + public: + template ::value)> + explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) { + *fmt::format_to(data, fmt, std::forward(args)...) = '\0'; + } + + auto str() const -> fmt::string_view { return {data, N - 1}; } + auto c_str() const -> const char* { return data; } +}; + +/** + * Formats arguments according to the format string `fmt_str` and produces + * a string of the exact required size at compile time. Both the format string + * and the arguments must be compile-time expressions. + * + * The resulting string can be accessed as a C string via `c_str()` or as + * a `fmt::string_view` via `str()`. + * + * **Example**: + * + * // Produces the static string "42" at compile time. + * static constexpr auto result = FMT_STATIC_FORMAT("{}", 42); + * const char* s = result.c_str(); + */ +#define FMT_STATIC_FORMAT(fmt_str, ...) \ + fmt::static_format_result< \ + fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \ + FMT_COMPILE(fmt_str), __VA_ARGS__) FMT_END_EXPORT FMT_END_NAMESPACE diff --git a/vendor/fmt/format-inl.h b/vendor/fmt/format-inl.h index a1e016611..945cb912a 100644 --- a/vendor/fmt/format-inl.h +++ b/vendor/fmt/format-inl.h @@ -22,7 +22,7 @@ #include "format.h" -#if FMT_USE_LOCALE +#if FMT_USE_LOCALE && !defined(FMT_MODULE) # include #endif @@ -31,14 +31,44 @@ #endif FMT_BEGIN_NAMESPACE -namespace detail { +#ifndef FMT_CUSTOM_ASSERT_FAIL 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. - fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); abort(); } +#endif + +#if FMT_USE_LOCALE +namespace detail { +using std::locale; +using std::numpunct; +using std::use_facet; +} // namespace detail +#else +namespace detail { +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 {}; } +} // namespace detail +#endif // FMT_USE_LOCALE + +template auto locale_ref::get() const -> Locale { + using namespace detail; + static_assert(std::is_same::value, ""); +#if FMT_USE_LOCALE + if (locale_) return *static_cast(locale_); +#endif + return locale(); +} + +namespace detail { FMT_FUNC void format_error_code(detail::buffer& out, int error_code, string_view message) noexcept { @@ -79,33 +109,6 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) { FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } -#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, ""); -} -#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 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 = use_facet>(loc.get()); @@ -133,14 +136,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value, } // 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(); +#if FMT_MSC_VERSION || defined(__NVCC__) + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (!b) return; #endif + FMT_THROW(format_error(message)); } template typename Locale::id format_facet::id; @@ -174,11 +176,11 @@ inline auto operator==(basic_fp x, basic_fp y) -> bool { } // Compilers should be able to optimize this into the ror instruction. -FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { +FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { r &= 31; return (n >> r) | (n << (32 - r)); } -FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { +FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { r &= 63; return (n >> r) | (n << (64 - r)); } @@ -275,7 +277,7 @@ template <> struct cache_accessor { 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[] = { + static constexpr uint64_t pow10_significands[] = { 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, @@ -370,7 +372,7 @@ template <> struct cache_accessor { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); - static constexpr const uint128_fallback pow10_significands[] = { + static constexpr uint128_fallback pow10_significands[] = { #if FMT_USE_FULL_CACHE_DRAGONBOX {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0x9faacf3df73609b1, 0x77b191618c54e9ad}, @@ -1037,7 +1039,7 @@ template <> struct cache_accessor { #if FMT_USE_FULL_CACHE_DRAGONBOX return pow10_significands[k - float_info::min_k]; #else - static constexpr const uint64_t powers_of_5_64[] = { + static constexpr uint64_t powers_of_5_64[] = { 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, @@ -1149,8 +1151,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool { exponent <= case_shorter_interval_left_endpoint_upper_threshold; } -// Remove trailing zeros from n and return the number of zeros removed (float) -FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { +// Remove trailing zeros from n and return the number of zeros removed (float). +FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int { 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; @@ -1170,22 +1172,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { return s; } -// Removes trailing zeros and returns the number of zeros removed (double) -FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { +// Removes trailing zeros and returns the number of zeros removed (double). +FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int { FMT_ASSERT(n != 0, ""); - // This magic number is ceil(2^90 / 10^8). - constexpr uint64_t magic_number = 12379400392853802749ull; - auto nm = umul128(n, magic_number); - // Is n is divisible by 10^8? - if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { + constexpr uint32_t ten_pow_8 = 100000000u; + if ((n % ten_pow_8) == 0) { // If yes, work with the quotient... - auto n32 = static_cast(nm.high() >> (90 - 64)); + auto n32 = static_cast(n / ten_pow_8); // ... and use the 32 bit variant of the function - int s = remove_trailing_zeros(n32, 8); + int num_zeros = remove_trailing_zeros(n32, 8); n = n32; - return s; + return num_zeros; } // If n is not divisible by 10^8, work with n itself. @@ -1210,7 +1209,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { // The main algorithm for shorter interval case template -FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { +FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); @@ -1454,8 +1453,8 @@ FMT_FUNC void vformat_to(buffer& buf, string_view fmt, format_args args, 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}}); + parse_format_string(fmt, + format_handler<>{parse_context<>(fmt), {out, args, loc}}); } template struct span { @@ -1546,10 +1545,11 @@ template class glibc_file : public file_base { void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } - bool needs_flush() const { + auto needs_flush() const -> bool { 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)); + auto size = max_of(this->file_->_IO_write_ptr - end, 0); + return memchr(end, '\n', static_cast(size)); } void flush() { fflush_unlocked(this->file_); } @@ -1573,7 +1573,7 @@ template class apple_file : public file_base { 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_); + if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_); --this->file_->_p; ++this->file_->_w; } @@ -1594,7 +1594,7 @@ template class apple_file : public file_base { this->file_->_w -= size; } - bool needs_flush() const { + auto needs_flush() const -> bool { if ((this->file_->_flags & line_buffered) == 0) return false; return memchr(this->file_->_p + this->file_->_w, '\n', to_unsigned(-this->file_->_w)); diff --git a/vendor/fmt/format.cc b/vendor/fmt/format.cc index 3ccd80684..526082e34 100644 --- a/vendor/fmt/format.cc +++ b/vendor/fmt/format.cc @@ -8,6 +8,12 @@ #include "fmt/format-inl.h" FMT_BEGIN_NAMESPACE + +#if FMT_USE_LOCALE +template FMT_API locale_ref::locale_ref(const std::locale& loc); // DEPRECATED! +template FMT_API auto locale_ref::get() const -> std::locale; +#endif + namespace detail { template FMT_API auto dragonbox::to_decimal(float x) noexcept @@ -15,12 +21,6 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp; -#if FMT_USE_LOCALE -// DEPRECATED! locale_ref in the detail namespace -template FMT_API locale_ref::locale_ref(const std::locale& loc); -template FMT_API auto locale_ref::get() const -> std::locale; -#endif - // Explicit instantiations for char. template FMT_API auto thousands_sep_impl(locale_ref) @@ -30,16 +30,13 @@ template FMT_API auto decimal_point_impl(locale_ref) -> char; // DEPRECATED! template FMT_API void buffer::append(const char*, const char*); -// DEPRECATED! -template FMT_API void vformat_to(buffer&, string_view, - typename vformat_args<>::type, locale_ref); - // Explicit instantiations for wchar_t. template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +// DEPRECATED! template FMT_API void buffer::append(const wchar_t*, const wchar_t*); } // namespace detail diff --git a/vendor/fmt/format.h b/vendor/fmt/format.h index 50e571442..4a6530072 100644 --- a/vendor/fmt/format.h +++ b/vendor/fmt/format.h @@ -40,7 +40,15 @@ #include "base.h" +// libc++ supports string_view in pre-c++17. +#if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# define FMT_USE_STRING_VIEW +#endif + #ifndef FMT_MODULE +# include // malloc, free + # include // std::signbit # include // std::byte # include // uint32_t @@ -60,11 +68,8 @@ # include // std::bit_cast # endif -// libc++ supports string_view in pre-c++17. -# if FMT_HAS_INCLUDE() && \ - (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# if defined(FMT_USE_STRING_VIEW) # include -# define FMT_USE_STRING_VIEW # endif # if FMT_MSC_VERSION @@ -117,6 +122,41 @@ # define FMT_NOINLINE #endif +#ifdef FMT_DEPRECATED +// Use the provided definition. +#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) +# define FMT_DEPRECATED [[deprecated]] +#else +# define FMT_DEPRECATED /* deprecated */ +#endif + +// Detect constexpr std::string. +#if !FMT_USE_CONSTEVAL +# define FMT_USE_CONSTEXPR_STRING 0 +#elif defined(__cpp_lib_constexpr_string) && \ + __cpp_lib_constexpr_string >= 201907L +# if FMT_CLANG_VERSION && FMT_GLIBCXX_RELEASE +// clang + libstdc++ are able to work only starting with gcc13.3 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113294 +# if FMT_GLIBCXX_RELEASE < 13 +# define FMT_USE_CONSTEXPR_STRING 0 +# elif FMT_GLIBCXX_RELEASE == 13 && __GLIBCXX__ < 20240521 +# define FMT_USE_CONSTEXPR_STRING 0 +# else +# define FMT_USE_CONSTEXPR_STRING 1 +# endif +# else +# define FMT_USE_CONSTEXPR_STRING 1 +# endif +#else +# define FMT_USE_CONSTEXPR_STRING 0 +#endif +#if FMT_USE_CONSTEXPR_STRING +# define FMT_CONSTEXPR_STRING constexpr +#else +# define FMT_CONSTEXPR_STRING +#endif + // GCC 4.9 doesn't support qualified names in specializations. namespace std { template struct iterator_traits> { @@ -129,28 +169,19 @@ template struct iterator_traits> { }; } // namespace std -#ifndef FMT_THROW -# if FMT_USE_EXCEPTIONS -# if FMT_MSC_VERSION || defined(__NVCC__) -FMT_BEGIN_NAMESPACE -namespace detail { -template inline void do_throw(const Exception& x) { - // Silence unreachable code warnings in MSVC and NVCC because these - // are nearly impossible to fix in a generic code. - volatile bool b = true; - if (b) throw x; -} -} // namespace detail -FMT_END_NAMESPACE -# define FMT_THROW(x) detail::do_throw(x) -# else -# define FMT_THROW(x) throw x -# endif -# else -# define FMT_THROW(x) \ - ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what()) -# endif // FMT_USE_EXCEPTIONS -#endif // FMT_THROW +#ifdef FMT_THROW +// Use the provided definition. +#elif FMT_USE_EXCEPTIONS +# define FMT_THROW(x) throw x +#else +# define FMT_THROW(x) ::fmt::assert_fail(__FILE__, __LINE__, (x).what()) +#endif + +#ifdef __clang_analyzer__ +# define FMT_CLANG_ANALYZER 1 +#else +# define FMT_CLANG_ANALYZER 0 +#endif // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of // integer formatter template instantiations to just one by only using the @@ -491,6 +522,11 @@ template constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } +template FMT_CONSTEXPR auto to_pointer(T*& ptr, size_t n) -> T* { + T* begin = ptr; + ptr += n; + return begin; +} template FMT_CONSTEXPR20 auto to_pointer(basic_appender it, size_t n) -> T* { buffer& buf = get_container(it); @@ -526,6 +562,8 @@ 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); + static_assert(sizeof(T) == 1, + "sizeof(T) must be 1 to use char for initialization"); std::memset(out, value, to_unsigned(count)); return out + count; } @@ -555,10 +593,10 @@ FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end, */ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) -> const char* { - constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; - constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; - constexpr const int shiftc[] = {0, 18, 12, 6, 0}; - constexpr const int shifte[] = {0, 6, 4, 2, 0}; + constexpr int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + constexpr uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + constexpr int shiftc[] = {0, 18, 12, 6, 0}; + constexpr int shifte[] = {0, 6, 4, 2, 0}; int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" [static_cast(*s) >> 3]; @@ -629,21 +667,9 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { } while (buf_ptr < buf + num_chars_left); } -template -inline auto compute_width(basic_string_view s) -> size_t { - return s.size(); -} - -// Computes approximate display width of a UTF-8 string. -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 += to_unsigned( - 1 + - (cp >= 0x1100 && +FMT_CONSTEXPR inline auto display_width_of(uint32_t cp) noexcept -> size_t { + return to_unsigned( + 1 + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET @@ -661,32 +687,6 @@ FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { (cp >= 0x1f300 && cp <= 0x1f64f) || // Supplemental Symbols and Pictographs: (cp >= 0x1f900 && cp <= 0x1f9ff)))); - return true; - } - }; - // We could avoid branches by using utf8_decode directly. - for_each_codepoint(s, count_code_points{&num_code_points}); - return num_code_points; -} - -template -inline auto code_point_index(basic_string_view s, size_t n) -> size_t { - 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 { - 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 {}; @@ -730,6 +730,9 @@ struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; template struct is_fast_float : std::false_type {}; +template +using fast_float_t = conditional_t; + template using is_double_double = bool_constant::digits == 106>; @@ -738,11 +741,12 @@ using is_double_double = bool_constant::digits == 106>; #endif // An allocator that uses malloc/free to allow removing dependency on the C++ -// standard libary runtime. -template struct allocator { +// standard libary runtime. std::decay is used for back_inserter to be found by +// ADL when applied to memory_buffer. +template struct allocator : private std::decay { using value_type = T; - T* allocate(size_t n) { + auto allocate(size_t n) -> T* { FMT_ASSERT(n <= max_value() / sizeof(T), ""); T* p = static_cast(malloc(n * sizeof(T))); if (!p) FMT_THROW(std::bad_alloc()); @@ -750,8 +754,23 @@ template struct allocator { } void deallocate(T* p, size_t) { free(p); } + + constexpr friend auto operator==(allocator, allocator) noexcept -> bool { + return true; // All instances of this allocator are equivalent. + } + constexpr friend auto operator!=(allocator, allocator) noexcept -> bool { + return false; + } }; +template +FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) + -> decltype(f.set_debug_format(set)) { + f.set_debug_format(set); +} +template +FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} + } // namespace detail FMT_BEGIN_EXPORT @@ -825,11 +844,32 @@ class basic_memory_buffer : public detail::buffer { FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } private: + template :: + propagate_on_container_move_assignment::value)> + FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { + alloc_ = std::move(other.alloc_); + return true; + } + // If the allocator does not propagate then copy the data from other. + template :: + propagate_on_container_move_assignment::value)> + FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { + T* data = other.data(); + if (alloc_ == other.alloc_ || data == other.store_) return true; + size_t size = other.size(); + // Perform copy operation, allocators are different. + this->resize(size); + detail::copy(data, data + size, this->data()); + return false; + } + // Move data from other to this buffer. FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { - alloc_ = std::move(other.alloc_); T* data = other.data(); size_t size = other.size(), capacity = other.capacity(); + if (!move_alloc(other)) return; if (data == other.store_) { this->set(store_, capacity); detail::copy(other.store_, other.store_ + size, store_); @@ -918,7 +958,7 @@ class string_buffer { inline string_buffer() : buf_(str_) {} inline operator writer() { return buf_; } - inline std::string& str() { return str_; } + inline auto str() -> std::string& { return str_; } }; template @@ -1044,7 +1084,7 @@ inline auto do_count_digits(uint64_t n) -> int { 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; - static constexpr const uint64_t zero_or_powers_of_10[] = { + static constexpr uint64_t zero_or_powers_of_10[] = { 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL}; return t - (n < zero_or_powers_of_10[t]); @@ -1225,7 +1265,7 @@ FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, out += size; do { const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = static_cast(value & ((1 << base_bits) - 1)); + unsigned digit = static_cast(value & ((1u << base_bits) - 1)); *--out = static_cast(base_bits < 4 ? static_cast('0' + digit) : digits[digit]); } while ((value >>= base_bits) != 0); @@ -1283,10 +1323,11 @@ template class to_utf8 { 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, policy)) + "expected utf16 or utf32"); + 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()); } auto size() const -> size_t { return buffer_.size() - 1; } @@ -1316,9 +1357,8 @@ template class to_utf8 { 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)); @@ -1343,7 +1383,7 @@ template class to_utf8 { }; // Computes 128-bit result of multiplication of two 64-bit unsigned integers. -inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { +FMT_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)}; @@ -1487,6 +1527,13 @@ template constexpr auto exponent_bias() -> int { : std::numeric_limits::max_exponent - 1; } +FMT_CONSTEXPR inline auto compute_exp_size(int exp) -> int { + auto prefix_size = 2; // sign + 'e' + auto abs_exp = exp >= 0 ? exp : -exp; + if (abs_exp < 100) return prefix_size + 2; + return prefix_size + (abs_exp >= 1000 ? 4 : 3); +} + // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. template FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { @@ -1519,7 +1566,7 @@ template struct basic_fp { F f; int e; - static constexpr const int num_significand_bits = + static constexpr int num_significand_bits = static_cast(sizeof(F) * num_bits()); constexpr basic_fp() : f(0), e(0) {} @@ -1612,6 +1659,15 @@ constexpr auto convert_float(T value) -> convert_float_result { return static_cast>(value); } +template +auto select(T true_value, F) -> T { + return true_value; +} +template +auto select(T, F false_value) -> F { + return false_value; +} + template FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n, const basic_specs& specs) -> OutputIt { @@ -1808,16 +1864,6 @@ FMT_CONSTEXPR auto write_char(OutputIt out, Char value, return it; }); } -template -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); -} template class digit_grouping { private: @@ -1841,9 +1887,7 @@ template class digit_grouping { } public: - template ::value)> - explicit digit_grouping(Locale loc, bool localized = true) { + explicit digit_grouping(locale_ref loc, bool localized = true) { if (!localized) return; auto sep = thousands_sep(loc); grouping_ = sep.grouping; @@ -1861,7 +1905,7 @@ template class digit_grouping { return count; } - // Applies grouping to digits and write the output to out. + // Applies grouping to digits and writes the output to out. template auto apply(Out out, basic_string_view digits) const -> Out { auto num_digits = static_cast(digits.size()); @@ -1943,6 +1987,8 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, // Writes a localized value. FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool; +auto write_loc(basic_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&, @@ -1964,8 +2010,7 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign s) prefix = 0x01000000 | '-'; abs_value = 0 - abs_value; } else { - constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; + constexpr unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '}; prefix = prefixes[static_cast(s)]; } return {abs_value, prefix}; @@ -2018,7 +2063,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, const format_specs& specs) -> OutputIt { static_assert(std::is_same>::value, ""); - constexpr int buffer_size = num_bits(); + constexpr size_t buffer_size = num_bits(); char buffer[buffer_size]; if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0'); const char* begin = nullptr; @@ -2111,12 +2156,108 @@ FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, } template +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); +} + +template ::value)> +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const format_specs& specs) -> OutputIt { + bool is_debug = specs.type() == presentation_type::debug; + if (specs.precision < 0 && specs.width == 0) { + auto&& it = reserve(out, s.size()); + return is_debug ? write_escaped_string(it, s) : copy(s, it); + } + + size_t display_width_limit = + specs.precision < 0 ? SIZE_MAX : to_unsigned(specs.precision); + size_t display_width = + !is_debug || specs.precision == 0 ? 0 : 1; // Account for opening '"'. + size_t size = !is_debug || specs.precision == 0 ? 0 : 1; + for_each_codepoint(s, [&](uint32_t cp, string_view sv) { + if (is_debug && needs_escape(cp)) { + counting_buffer buf; + write_escaped_cp(basic_appender(buf), + find_escape_result{sv.begin(), sv.end(), cp}); + // We're reinterpreting bytes as display width. That's okay + // because write_escaped_cp() only writes ASCII characters. + size_t cp_width = buf.count(); + if (display_width + cp_width <= display_width_limit) { + display_width += cp_width; + size += cp_width; + // If this is the end of the string, account for closing '"'. + if (display_width < display_width_limit && sv.end() == s.end()) { + ++display_width; + ++size; + } + return true; + } + + size += display_width_limit - display_width; + display_width = display_width_limit; + return false; + } + + size_t cp_width = display_width_of(cp); + if (cp_width + display_width <= display_width_limit) { + display_width += cp_width; + size += sv.size(); + // If this is the end of the string, account for closing '"'. + if (is_debug && display_width < display_width_limit && + sv.end() == s.end()) { + ++display_width; + ++size; + } + return true; + } + + return false; + }); + + struct bounded_output_iterator { + reserve_iterator underlying_iterator; + size_t bound; + + FMT_CONSTEXPR auto operator*() -> bounded_output_iterator& { return *this; } + FMT_CONSTEXPR auto operator++() -> bounded_output_iterator& { + return *this; + } + FMT_CONSTEXPR auto operator++(int) -> bounded_output_iterator& { + return *this; + } + FMT_CONSTEXPR auto operator=(char c) -> bounded_output_iterator& { + if (bound > 0) { + *underlying_iterator++ = c; + --bound; + } + return *this; + } + }; + + return write_padded( + out, specs, size, display_width, [=](reserve_iterator it) { + return is_debug + ? write_escaped_string(bounded_output_iterator{it, size}, s) + .underlying_iterator + : copy(s.data(), s.data() + size, it); + }); +} + +template ::value)> FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, 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)); + size = to_unsigned(specs.precision); bool is_debug = specs.type() == presentation_type::debug; if (is_debug) { @@ -2125,22 +2266,19 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, size = buf.count(); } - size_t width = 0; - if (specs.width != 0) { - width = - is_debug ? size : compute_width(basic_string_view(data, size)); - } return write_padded( - out, specs, size, width, [=](reserve_iterator it) { + out, specs, size, [=](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); } + template FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, locale_ref) -> OutputIt { @@ -2274,7 +2412,7 @@ inline auto write_significand(Char* out, UInt significand, int significand_size, int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; - write2digits(out, static_cast(significand % 100)); + write2digits(out, static_cast(significand % 100)); significand /= 100; } if (floating_size % 2 != 0) { @@ -2328,93 +2466,80 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, buffer.end(), out); } -template > -FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - 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'); - size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0); - using iterator = reserve_iterator; +// Numbers with exponents greater or equal to the returned value will use +// the exponential notation. +template FMT_CONSTEVAL auto exp_upper() -> int { + return std::numeric_limits::digits10 != 0 + ? min_of(16, std::numeric_limits::digits10 + 1) + : 16; +} - Char decimal_point = specs.localized() ? detail::decimal_point(loc) - : static_cast('.'); - - int output_exp = f.exponent + significand_size - 1; - auto use_exp_format = [=]() { - 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; - return output_exp < exp_lower || - output_exp >= (specs.precision > 0 ? specs.precision : exp_upper); - }; - if (use_exp_format()) { - int num_zeros = 0; - 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) { - decimal_point = Char(); - } - auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; - int exp_digits = 2; - 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 = specs.upper() ? 'E' : 'e'; - auto write = [=](iterator it) { - 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); - if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); - *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))); +// Use the fixed notation if the exponent is in [-4, exp_upper), +// e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. +constexpr auto use_fixed(int exp, int exp_upper) -> bool { + return exp >= -4 && exp < exp_upper; +} + +template class fallback_digit_grouping { + public: + constexpr fallback_digit_grouping(locale_ref, bool) {} + + constexpr auto has_separator() const -> bool { return false; } + + constexpr auto count_separators(int) const -> int { return 0; } + + template + constexpr auto apply(Out out, basic_string_view) const -> Out { + return out; } +}; + +template +FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f, + int significand_size, Char decimal_point, + const format_specs& specs, sign s, + locale_ref loc = {}) -> OutputIt { + using iterator = reserve_iterator; int exp = f.exponent + significand_size; + long long size = significand_size + (s != sign::none ? 1 : 0); if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] - size += to_unsigned(f.exponent); + size += f.exponent; int num_zeros = specs.precision - exp; abort_fuzzing_if(num_zeros > 5000); if (specs.alt()) { ++size; if (num_zeros <= 0 && specs.type() != presentation_type::fixed) num_zeros = 0; - if (num_zeros > 0) size += to_unsigned(num_zeros); + if (num_zeros > 0) size += num_zeros; } auto grouping = Grouping(loc, specs.localized()); - size += to_unsigned(grouping.count_separators(exp)); - 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 (!specs.alt()) return it; - *it++ = decimal_point; - return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; - }); - } else if (exp > 0) { + size += grouping.count_separators(exp); + return write_padded( + out, specs, static_cast(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + it = write_significand(it, f.significand, significand_size, + f.exponent, grouping); + if (!specs.alt()) return it; + *it++ = decimal_point; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; + }); + } + if (exp > 0) { // 1234e-2 -> 12.34[0+] int num_zeros = specs.alt() ? specs.precision - significand_size : 0; - size += 1 + static_cast(max_of(num_zeros, 0)); + size += 1 + 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 (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; - }); + size += grouping.count_separators(exp); + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + it = write_significand(it, f.significand, significand_size, exp, + decimal_point, grouping); + return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; + }); } // 1234e-6 -> 0.001234 int num_zeros = -exp; @@ -2423,41 +2548,68 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, num_zeros = specs.precision; } 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 (s != sign::none) *it++ = detail::getsign(s); - *it++ = zero; - if (!pointy) return it; - *it++ = decimal_point; - it = detail::fill_n(it, num_zeros, zero); - return write_significand(it, significand, significand_size); - }); + size += 1 + (pointy ? 1 : 0) + num_zeros; + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + *it++ = Char('0'); + if (!pointy) return it; + *it++ = decimal_point; + it = detail::fill_n(it, num_zeros, Char('0')); + return write_significand(it, f.significand, significand_size); + }); } -template class fallback_digit_grouping { - public: - constexpr fallback_digit_grouping(locale_ref, bool) {} - - constexpr auto has_separator() const -> bool { return false; } - - constexpr auto count_separators(int) const -> int { return 0; } - - template - constexpr auto apply(Out out, basic_string_view) const -> Out { - return out; - } -}; +template +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { + Char point = specs.localized() ? detail::decimal_point(loc) : Char('.'); + int significand_size = get_significand_size(f); + int exp = f.exponent + significand_size - 1; + if (specs.type() == presentation_type::fixed || + (specs.type() != presentation_type::exp && + use_fixed(exp, specs.precision > 0 ? specs.precision : exp_upper))) { + return write_fixed(out, f, significand_size, point, specs, + s, loc); + } + + // Write value in the exponential format. + int num_zeros = 0; + long long size = significand_size + (s != sign::none ? 1 : 0); + if (specs.alt()) { + num_zeros = max_of(specs.precision - significand_size, 0); + size += num_zeros; + } else if (significand_size == 1) { + point = Char(); + } + size += (point ? 1 : 0) + compute_exp_size(exp); + char exp_char = specs.upper() ? 'E' : 'e'; + auto write = [=](reserve_iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, f.significand, significand_size, 1, point); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, Char('0')); + *it++ = Char(exp_char); + return write_exponent(exp, it); + }; + auto usize = to_unsigned(size); + return specs.width > 0 + ? write_padded(out, specs, usize, write) + : base_iterator(out, write(reserve(out, usize))); +} template FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, const format_specs& specs, sign s, int exp_upper, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { - return do_write_float>(out, f, specs, s, - exp_upper, loc); + return do_write_float>(out, f, specs, s, + exp_upper, loc); } else { - return do_write_float(out, f, specs, s, exp_upper, loc); + return do_write_float>(out, f, specs, s, + exp_upper, loc); } } @@ -2488,7 +2640,7 @@ FMT_CONSTEXPR auto isfinite(T value) -> bool { } template ::value)> -FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { +FMT_INLINE FMT_CONSTEXPR auto signbit(T value) -> bool { if (is_constant_evaluated()) { #ifdef __cpp_if_constexpr if constexpr (std::numeric_limits::is_iec559) { @@ -2728,7 +2880,7 @@ class bigint { 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]; - memset(bigits_.data(), 0, to_unsigned(exp_difference) * sizeof(bigit)); + fill_n(bigits_.data(), to_unsigned(exp_difference), 0U); exp_ -= exp_difference; } @@ -3289,17 +3441,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, 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 ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, + locale_ref loc = {}) -> OutputIt { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; -template -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(); @@ -3312,15 +3459,14 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, if (specs.width != 0) --specs.width; } - constexpr int exp_upper = detail::exp_upper(); + const 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)); + auto dec = dragonbox::to_decimal(static_cast>(value)); return write_float(out, dec, specs, s, exp_upper, loc); } } @@ -3352,38 +3498,77 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, return write_float(out, f, specs, s, exp_upper, loc); } -template ::value)> -FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, - locale_ref loc = {}) -> OutputIt { - return specs.localized() && write_loc(out, value, specs, loc) - ? out - : 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()); auto s = detail::signbit(value) ? sign::minus : sign::none; - - 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, s); - - auto dec = dragonbox::to_decimal(static_cast(value)); - return write_float(out, dec, specs, s, exp_upper(), {}); + auto mask = exponent_mask>(); + if ((bit_cast(value) & mask) == mask) + return write_nonfinite(out, std::isnan(value), {}, s); + + auto dec = dragonbox::to_decimal(static_cast>(value)); + auto significand = dec.significand; + int significand_size = count_digits(significand); + int exponent = dec.exponent + significand_size - 1; + if (use_fixed(exponent, detail::exp_upper())) { + return write_fixed>( + out, dec, significand_size, Char('.'), {}, s); + } + + // Write value in the exponential format. + const char* prefix = "e+"; + int abs_exponent = exponent; + if (exponent < 0) { + abs_exponent = -exponent; + prefix = "e-"; + } + auto has_decimal_point = significand_size != 1; + size_t size = std::is_pointer::value + ? 0u + : to_unsigned((s != sign::none ? 1 : 0) + significand_size + + (has_decimal_point ? 1 : 0) + + (abs_exponent >= 100 ? 5 : 4)); + if (auto ptr = to_pointer(out, size)) { + if (s != sign::none) *ptr++ = Char('-'); + if (has_decimal_point) { + auto begin = ptr; + ptr = format_decimal(ptr, significand, significand_size + 1); + *begin = begin[1]; + begin[1] = '.'; + } else { + *ptr++ = static_cast('0' + significand); + } + if (std::is_same::value) { + memcpy(ptr, prefix, 2); + ptr += 2; + } else { + *ptr++ = prefix[0]; + *ptr++ = prefix[1]; + } + if (abs_exponent >= 100) { + *ptr++ = static_cast('0' + abs_exponent / 100); + abs_exponent %= 100; + } + write2digits(ptr, static_cast(abs_exponent)); + return select::value>(ptr + 2, out); + } + auto it = reserve(out, size); + if (s != sign::none) *it++ = Char('-'); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + has_decimal_point ? Char('.') : Char()); + *it++ = Char('e'); + it = write_exponent(exponent, it); + return base_iterator(out, it); } template ::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { - return write(out, value, format_specs()); + return write(out, value, {}); } template @@ -3523,32 +3708,18 @@ struct dynamic_spec_getter { } }; -template -FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg { - auto arg = ctx.arg(id); - if (!arg) report_error("argument not found"); - return arg; -} - template -FMT_CONSTEXPR int get_dynamic_spec( - arg_id_kind kind, const arg_ref& ref, - Context& ctx) { - FMT_ASSERT(kind != arg_id_kind::none, ""); +FMT_CONSTEXPR void handle_dynamic_spec( + arg_id_kind kind, int& value, + const arg_ref& ref, Context& ctx) { + if (kind == arg_id_kind::none) return; auto arg = kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name); if (!arg) report_error("argument not found"); - unsigned long long value = arg.visit(dynamic_spec_getter()); - if (value > to_unsigned(max_value())) + unsigned long long result = arg.visit(dynamic_spec_getter()); + if (result > to_unsigned(max_value())) report_error("width/precision is out of range"); - return static_cast(value); -} - -template -FMT_CONSTEXPR void handle_dynamic_spec( - arg_id_kind kind, int& value, - const arg_ref& ref, Context& ctx) { - if (kind != arg_id_kind::none) value = get_dynamic_spec(kind, ref, ctx); + value = static_cast(result); } #if FMT_USE_NONTYPE_TEMPLATE_ARGS @@ -3586,7 +3757,7 @@ template struct udl_arg { }; #endif // FMT_USE_NONTYPE_TEMPLATE_ARGS -template struct format_handler { +template struct format_handler { parse_context parse_ctx; buffered_context ctx; @@ -3612,7 +3783,8 @@ template struct format_handler { auto on_format_specs(int id, const Char* begin, const Char* end) -> const Char* { - auto arg = get_arg(ctx, id); + auto arg = ctx.arg(id); + if (!arg) report_error("argument not found"); // Not using a visitor for custom types gives better codegen. if (arg.format_custom(begin, parse_ctx, ctx)) return parse_ctx.begin(); @@ -3632,6 +3804,7 @@ template struct format_handler { FMT_NORETURN void on_error(const char* message) { report_error(message); } }; +// It is used in format-inl.h and os.cc. using format_func = void (*)(detail::buffer&, int, const char*); FMT_API void do_report_error(format_func func, int error_code, const char* message) noexcept; @@ -3652,28 +3825,6 @@ FMT_CONSTEXPR auto native_formatter::format( specs_.precision_ref, ctx); return write(ctx.out(), val, specs, ctx.locale()); } - -// DEPRECATED! https://github.com/fmtlib/fmt/issues/4292. -template -struct is_locale : std::false_type {}; -template -struct is_locale> : std::true_type {}; - -// DEPRECATED! -template struct vformat_args { - using type = basic_format_args>; -}; -template <> struct vformat_args { - using type = format_args; -}; - -template -void vformat_to(buffer& buf, basic_string_view fmt, - typename vformat_args::type args, locale_ref loc = {}) { - auto out = basic_appender(buf); - parse_format_string( - fmt, format_handler{parse_context(fmt), {out, args, loc}}); -} } // namespace detail FMT_BEGIN_EXPORT @@ -3685,19 +3836,16 @@ template class generic_context { private: OutputIt out_; basic_format_args args_; - detail::locale_ref loc_; + locale_ref loc_; public: using char_type = Char; using iterator = OutputIt; - using parse_context_type FMT_DEPRECATED = parse_context; - template - using formatter_type FMT_DEPRECATED = formatter; enum { builtin_types = FMT_BUILTIN_TYPES }; constexpr generic_context(OutputIt out, basic_format_args args, - detail::locale_ref loc = {}) + locale_ref loc = {}) : out_(out), args_(args), loc_(loc) {} generic_context(generic_context&&) = default; generic_context(const generic_context&) = delete; @@ -3720,7 +3868,7 @@ template class generic_context { if (!detail::is_back_insert_iterator()) out_ = it; } - constexpr auto locale() const -> detail::locale_ref { return loc_; } + constexpr auto locale() const -> locale_ref { return loc_; } }; class loc_value { @@ -3825,7 +3973,7 @@ struct formatter>> * auto s = fmt::format("{}", fmt::ptr(p)); */ template auto ptr(T p) -> const void* { - static_assert(std::is_pointer::value, ""); + static_assert(std::is_pointer::value, "fmt::ptr used with non-pointer"); return detail::bit_cast(p); } @@ -3850,13 +3998,14 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t { } // namespace enums #ifdef __cpp_lib_byte -template <> struct formatter : formatter { +template +struct formatter : formatter { static auto format_as(std::byte b) -> unsigned char { return static_cast(b); } template auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) { - return formatter::format(format_as(b), ctx); + return formatter::format(format_as(b), ctx); } }; #endif @@ -4070,21 +4219,26 @@ class format_int { inline auto str() const -> std::string { return {str_, size()}; } }; -#define FMT_STRING_IMPL(s, base) \ - [] { \ - /* 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::remove_cvref_t; \ - constexpr explicit operator fmt::basic_string_view() const { \ - return fmt::detail::compile_string_to_view(s); \ - } \ - }; \ - using FMT_STRING_VIEW = \ - fmt::basic_string_view; \ - fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ - return FMT_COMPILE_STRING(); \ - }() +#if FMT_CLANG_ANALYZER +# define FMT_STRING_IMPL(s, base) s +#else +# define FMT_STRING_IMPL(s, base) \ + [] { \ + /* 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::remove_cvref_t; \ + constexpr explicit operator fmt::basic_string_view() \ + const { \ + return fmt::detail::compile_string_to_view(s); \ + } \ + }; \ + using FMT_STRING_VIEW = \ + fmt::basic_string_view; \ + fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ + return FMT_COMPILE_STRING(); \ + }() +#endif // FMT_CLANG_ANALYZER /** * Constructs a legacy compile-time format string from a string literal `s`. @@ -4140,46 +4294,41 @@ FMT_API void format_system_error(detail::buffer& out, int error_code, // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, const char* message) noexcept; -template ::value)> -inline auto vformat(const Locale& loc, string_view fmt, format_args args) +inline auto vformat(locale_ref loc, string_view fmt, format_args args) -> std::string { auto buf = memory_buffer(); - detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + detail::vformat_to(buf, fmt, args, loc); return {buf.data(), buf.size()}; } -template ::value)> -FMT_INLINE auto format(const Locale& loc, format_string fmt, T&&... args) +template +FMT_INLINE auto format(locale_ref loc, format_string fmt, T&&... args) -> std::string { return vformat(loc, fmt.str, vargs{{args...}}); } -template ::value)> -auto vformat_to(OutputIt out, const Locale& loc, string_view fmt, - format_args args) -> OutputIt { +auto vformat_to(OutputIt out, locale_ref loc, string_view fmt, format_args args) + -> OutputIt { auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + detail::vformat_to(buf, fmt, args, loc); return detail::get_iterator(buf, out); } -template ::value&& - detail::is_locale::value)> -FMT_INLINE auto format_to(OutputIt out, const Locale& loc, - format_string fmt, T&&... args) -> OutputIt { +template ::value)> +FMT_INLINE auto format_to(OutputIt out, locale_ref loc, format_string fmt, + T&&... args) -> OutputIt { return fmt::vformat_to(out, loc, fmt.str, vargs{{args...}}); } -template ::value)> -FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc, +template +FMT_NODISCARD FMT_INLINE auto formatted_size(locale_ref loc, format_string fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); - detail::vformat_to(buf, fmt.str, vargs{{args...}}, - detail::locale_ref(loc)); + detail::vformat_to(buf, fmt.str, vargs{{args...}}, loc); return buf.count(); } @@ -4208,7 +4357,7 @@ FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) * std::string answer = fmt::to_string(42); */ template ::value)> -FMT_NODISCARD auto to_string(T value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(T value) -> std::string { // The buffer should be large enough to store the number including the sign // or "false" for bool. char buffer[max_of(detail::digits10() + 2, 5)]; @@ -4216,13 +4365,15 @@ FMT_NODISCARD auto to_string(T value) -> std::string { } template ::value)> -FMT_NODISCARD auto to_string(const T& value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) + -> std::string { return to_string(format_as(value)); } template ::value && !detail::use_format_as::value)> -FMT_NODISCARD auto to_string(const T& value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) + -> std::string { auto buffer = memory_buffer(); detail::write(appender(buffer), value); return {buffer.data(), buffer.size()}; diff --git a/vendor/lyra/lyra.hpp b/vendor/lyra/lyra.hpp index d97c511f1..4e8470f4d 100644 --- a/vendor/lyra/lyra.hpp +++ b/vendor/lyra/lyra.hpp @@ -1,4 +1,4 @@ -// Copyright 2018-2022 René Ferdinand Rivera Morell +// Copyright René Ferdinand Rivera Morell // Copyright 2021 Max Ferger // Copyright 2017 Two Blue Cubes Ltd. All rights reserved. // @@ -13,8 +13,8 @@ #define LYRA_VERSION_HPP #define LYRA_VERSION_MAJOR 1 -#define LYRA_VERSION_MINOR 6 -#define LYRA_VERSION_PATCH 1 +#define LYRA_VERSION_MINOR 7 +#define LYRA_VERSION_PATCH 0 #define LYRA_VERSION \ (((LYRA_VERSION_MAJOR)*10000000) + ((LYRA_VERSION_MINOR)*100000) \ @@ -27,143 +27,6 @@ #define LYRA_ARG_HPP -#ifndef LYRA_DETAIL_PRINT_HPP -#define LYRA_DETAIL_PRINT_HPP - -#include -#include - -#ifndef LYRA_DEBUG -# define LYRA_DEBUG 0 -#endif - -namespace lyra { namespace detail { - -constexpr bool is_debug = LYRA_DEBUG; - -template -std::string to_string(T && t) -{ - return std::string(std::move(t)); -} - -using std::to_string; - -struct print -{ - print(const char * scope_name = nullptr) - : scope(scope_name) - { - if (is_debug) print::depth() += 1; - if (scope) debug(scope, "..."); - } - - ~print() - { - if (scope) debug("...", scope); - if (is_debug) print::depth() -= 1; - } - - template - void debug(A... arg) - { - if (is_debug) - { - std::cerr << "[DEBUG]" - << std::string((print::depth() - 1) * 2, ' '); - std::string args[] = { to_string(arg)... }; - for (auto & arg_string : args) - { - std::cerr << " " << arg_string; - } - std::cerr << "\n"; - } - } - - private: - const char * scope; - - static std::size_t & depth() - { - static std::size_t d = 0; - return d; - } -}; - -}} // namespace lyra::detail - -#if LYRA_DEBUG -# define LYRA_PRINT_SCOPE ::lyra::detail::print lyra_print_scope -# define LYRA_PRINT_DEBUG lyra_print_scope.debug -#else -# define LYRA_PRINT_SCOPE(...) while (false) -# define LYRA_PRINT_DEBUG(...) while (false) -#endif - -#endif - -#ifndef LYRA_PARSER_HPP -#define LYRA_PARSER_HPP - - -#ifndef LYRA_ARGS_HPP -#define LYRA_ARGS_HPP - -#include -#include -#include - -namespace lyra { - -/* tag::reference[] - -[#lyra_args] -= `lyra::args` - -Transport for raw args (copied from main args, supplied via init list, or from -a pair of iterators). - -*/ // end::reference[] -class args -{ - public: - args(int argc, char const * const * argv) - : m_exeName(argv[0]) - , m_args(argv + 1, argv + argc) - {} - - args(std::initializer_list args_list) - : m_exeName(*args_list.begin()) - , m_args(args_list.begin() + 1, args_list.end()) - {} - - template - args(const It & start, const It & end) - : m_exeName(*start) - , m_args(start + 1, end) - {} - - std::string exe_name() const { return m_exeName; } - - std::vector::const_iterator begin() const - { - return m_args.begin(); - } - - std::vector::const_iterator end() const - { - return m_args.end(); - } - - private: - std::string m_exeName; - std::vector m_args; -}; - -} // namespace lyra - -#endif - #ifndef LYRA_DETAIL_BOUND_HPP #define LYRA_DETAIL_BOUND_HPP @@ -190,15 +53,14 @@ struct is_callable template static auto test(...) -> decltype(std::false_type()); - static constexpr bool value = decltype(test(0))::value; + static constexpr bool value = decltype(test(nullptr))::value; }; template struct remove_cvref { - typedef - typename std::remove_cv::type>::type - type; + using type = + typename std::remove_cv::type>::type; }; template @@ -212,7 +74,7 @@ struct is_invocable static auto test(...) -> decltype(std::false_type()); static constexpr bool value - = decltype(test::type>(0))::value; + = decltype(test::type>(nullptr))::value; }; template @@ -230,21 +92,40 @@ template