From eea5dd7743cda4247ec9a361bf57f4cec54e6a2d Mon Sep 17 00:00:00 2001 From: Sandu Liviu Catalin Date: Tue, 30 Mar 2021 20:32:25 +0300 Subject: [PATCH] Update fmt library. --- vendor/Fmt/include/fmt/args.h | 232 +++++ vendor/Fmt/include/fmt/chrono.h | 214 +++-- vendor/Fmt/include/fmt/color.h | 85 +- vendor/Fmt/include/fmt/compile.h | 363 ++++++- vendor/Fmt/include/fmt/core.h | 485 ++++------ vendor/Fmt/include/fmt/format-inl.h | 160 +--- vendor/Fmt/include/fmt/format.h | 1380 ++++++++++++++------------- vendor/Fmt/include/fmt/locale.h | 4 +- vendor/Fmt/include/fmt/os.h | 40 +- vendor/Fmt/include/fmt/ostream.h | 2 + vendor/Fmt/include/fmt/printf.h | 103 +- vendor/Fmt/include/fmt/ranges.h | 199 ++-- 12 files changed, 1875 insertions(+), 1392 deletions(-) create mode 100644 vendor/Fmt/include/fmt/args.h diff --git a/vendor/Fmt/include/fmt/args.h b/vendor/Fmt/include/fmt/args.h new file mode 100644 index 00000000..562e8ab1 --- /dev/null +++ b/vendor/Fmt/include/fmt/args.h @@ -0,0 +1,232 @@ +// Formatting library for C++ - dynamic format arguments +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_ARGS_H_ +#define FMT_ARGS_H_ + +#include // std::reference_wrapper +#include // std::unique_ptr +#include + +#include "core.h" + +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) { + 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; + }; + + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template const T& push(const Arg& arg) { + auto new_node = std::unique_ptr>(new typed_node(arg)); + auto& value = new_node->value; + new_node->next = std::move(head_); + head_ = std::move(new_node); + return value; + } +}; +} // 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 + */ +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 +{ + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr detail::type mapped_type = + detail::mapped_type_constant::value; + + enum { + value = !(detail::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::value || + (mapped_type != detail::type::cstring_type && + mapped_type != detail::type::string_type && + mapped_type != detail::type::custom_type)) + }; + }; + + template + using stored_type = conditional_t::value && + !has_formatter::value && + !detail::is_reference_wrapper::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> data_; + std::vector> named_info_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + detail::dynamic_arg_list dynamic_args_; + + 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 { + return named_info_.empty() ? data_.data() : data_.data() + 1; + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(detail::make_arg(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))); + 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()}; + guard.release(); + } + + public: + /** + \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 + */ + template void push_back(const T& arg) { + if (detail::const_check(need_copy::value)) + 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 + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + need_copy::value, + "objects of built-in types and string views are always copied"); + emplace_arg(arg.get()); + } + + /** + 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))); + } else { + emplace_arg(fmt::arg(arg_name, arg.value)); + } + } + + /** Erase all elements from the store */ + void clear() { + data_.clear(); + named_info_.clear(); + dynamic_args_ = detail::dynamic_arg_list(); + } + + /** + \rst + Reserves space to store at least *new_cap* arguments including + *new_cap_named* named arguments. + \endrst + */ + 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"); + data_.reserve(new_cap); + named_info_.reserve(new_cap_named); + } +}; + +FMT_END_NAMESPACE + +#endif // FMT_ARGS_H_ diff --git a/vendor/Fmt/include/fmt/chrono.h b/vendor/Fmt/include/fmt/chrono.h index 1a3b8d5e..645b06c3 100644 --- a/vendor/Fmt/include/fmt/chrono.h +++ b/vendor/Fmt/include/fmt/chrono.h @@ -8,6 +8,7 @@ #ifndef FMT_CHRONO_H_ #define FMT_CHRONO_H_ +#include #include #include #include @@ -288,7 +289,11 @@ inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); } } // namespace detail -// Thread-safe replacement for std::localtime +/** + 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) { struct dispatcher { std::time_t time_; @@ -330,7 +335,11 @@ inline std::tm localtime( return localtime(std::chrono::system_clock::to_time_t(time_point)); } -// Thread-safe replacement for std::gmtime +/** + 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) { struct dispatcher { std::time_t time_; @@ -374,12 +383,21 @@ inline std::tm gmtime( namespace detail { inline size_t strftime(char* str, size_t count, const char* format, const std::tm* time) { - return std::strftime(str, count, format, time); + // Assign to a pointer to suppress GCCs -Wformat-nonliteral + // First assign the nullptr to suppress -Wsuggest-attribute=format + std::size_t (*strftime)(char*, std::size_t, const char*, const std::tm*) = + nullptr; + strftime = std::strftime; + return strftime(str, count, format, time); } inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format, const std::tm* time) { - return std::wcsftime(str, count, format, time); + // See above + std::size_t (*wcsftime)(wchar_t*, std::size_t, const wchar_t*, + const std::tm*) = nullptr; + wcsftime = std::wcsftime; + return wcsftime(str, count, format, time); } } // namespace detail @@ -396,19 +414,21 @@ struct formatter, Char> template struct formatter { template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); if (it != ctx.end() && *it == ':') ++it; auto end = it; while (end != ctx.end() && *end != '}') ++end; - tm_format.reserve(detail::to_unsigned(end - it + 1)); - tm_format.append(it, end); - tm_format.push_back('\0'); + specs = {it, detail::to_unsigned(end - it)}; return end; } template - auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { + auto format(const std::tm& tm, FormatContext& ctx) const + -> decltype(ctx.out()) { + basic_memory_buffer tm_format; + tm_format.append(specs.begin(), specs.end()); + tm_format.push_back('\0'); basic_memory_buffer buf; size_t start = buf.size(); for (;;) { @@ -431,34 +451,68 @@ template struct formatter { return std::copy(buf.begin(), buf.end(), ctx.out()); } - basic_memory_buffer tm_format; + basic_string_view specs; }; namespace detail { template FMT_CONSTEXPR const char* get_units() { return nullptr; } -template <> FMT_CONSTEXPR const char* get_units() { return "as"; } -template <> FMT_CONSTEXPR const char* get_units() { return "fs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ps"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ns"; } -template <> FMT_CONSTEXPR const char* get_units() { return "µs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ms"; } -template <> FMT_CONSTEXPR const char* get_units() { return "cs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ds"; } -template <> FMT_CONSTEXPR const char* get_units>() { return "s"; } -template <> FMT_CONSTEXPR const char* get_units() { return "das"; } -template <> FMT_CONSTEXPR const char* get_units() { return "hs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ks"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Ms"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Gs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Ts"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Ps"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Es"; } -template <> FMT_CONSTEXPR const char* get_units>() { +template <> FMT_CONSTEXPR inline const char* get_units() { + return "as"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "fs"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "ps"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "ns"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "µs"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "ms"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "cs"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "ds"; +} +template <> FMT_CONSTEXPR inline const char* get_units>() { + return "s"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "das"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "hs"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "ks"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "Ms"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "Gs"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "Ts"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "Ps"; +} +template <> FMT_CONSTEXPR inline const char* get_units() { + return "Es"; +} +template <> FMT_CONSTEXPR inline const char* get_units>() { return "m"; } -template <> FMT_CONSTEXPR const char* get_units>() { +template <> FMT_CONSTEXPR inline const char* get_units>() { return "h"; } @@ -629,28 +683,29 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, struct chrono_format_checker { FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); } - template void on_text(const Char*, const Char*) {} + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} FMT_NORETURN void on_abbr_weekday() { report_no_date(); } FMT_NORETURN void on_full_weekday() { report_no_date(); } FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); } FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); } FMT_NORETURN void on_abbr_month() { report_no_date(); } FMT_NORETURN void on_full_month() { report_no_date(); } - void on_24_hour(numeric_system) {} - void on_12_hour(numeric_system) {} - void on_minute(numeric_system) {} - void on_second(numeric_system) {} + FMT_CONSTEXPR void on_24_hour(numeric_system) {} + FMT_CONSTEXPR void on_12_hour(numeric_system) {} + FMT_CONSTEXPR void on_minute(numeric_system) {} + FMT_CONSTEXPR void on_second(numeric_system) {} FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); } FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); } FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); } FMT_NORETURN void on_us_date() { report_no_date(); } FMT_NORETURN void on_iso_date() { report_no_date(); } - void on_12_hour_time() {} - void on_24_hour_time() {} - void on_iso_time() {} - void on_am_pm() {} - void on_duration_value() {} - void on_duration_unit() {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_duration_value() {} + FMT_CONSTEXPR void on_duration_unit() {} FMT_NORETURN void on_utc_offset() { report_no_date(); } FMT_NORETURN void on_tz_name() { report_no_date(); } }; @@ -676,7 +731,8 @@ inline bool isfinite(T value) { // 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(value >= 0 && value <= upper, "invalid value"); + FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper), + "invalid value"); (void)upper; return static_cast(value); } @@ -754,15 +810,21 @@ inline std::chrono::duration get_milliseconds( return std::chrono::duration(static_cast(ms)); } -template -OutputIt format_duration_value(OutputIt out, Rep val, int precision) { - const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; - if (precision >= 0) return format_to(out, pr_f, val, precision); - const Char fp_f[] = {'{', ':', 'g', '}', 0}; - const Char format[] = {'{', '}', 0}; - return format_to(out, std::is_floating_point::value ? fp_f : format, - val); +template ::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int) { + return write(out, val); } + +template ::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int precision) { + auto specs = basic_format_specs(); + specs.precision = precision; + specs.type = precision > 0 ? 'f' : 'g'; + return write(out, val, specs); +} + template OutputIt copy_unit(string_view unit, OutputIt out, Char) { return std::copy(unit.begin(), unit.end(), out); @@ -780,10 +842,15 @@ template OutputIt format_duration_unit(OutputIt out) { if (const char* unit = get_units()) return copy_unit(string_view(unit), out, Char()); - const Char num_f[] = {'[', '{', '}', ']', 's', 0}; - if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num); - const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; - return format_to(out, num_def_f, Period::num, Period::den); + *out++ = '['; + out = write(out, Period::num); + if (const_check(Period::den != 1)) { + *out++ = '/'; + out = write(out, Period::den); + } + *out++ = ']'; + *out++ = 's'; + return out; } template struct formatter, Char> { private: basic_format_specs specs; - int precision; + int precision = -1; using arg_ref_type = detail::arg_ref; arg_ref_type width_ref; arg_ref_type precision_ref; - mutable basic_string_view format_str; + basic_string_view format_str; using duration = std::chrono::duration; struct spec_handler { @@ -1038,17 +1105,21 @@ struct formatter, Char> { } void on_error(const char* msg) { FMT_THROW(format_error(msg)); } - void on_fill(basic_string_view fill) { f.specs.fill = fill; } - void on_align(align_t align) { f.specs.align = align; } - void on_width(int width) { f.specs.width = width; } - void on_precision(int _precision) { f.precision = _precision; } - void end_precision() {} + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + f.specs.fill = fill; + } + FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; } + FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; } + FMT_CONSTEXPR void on_precision(int _precision) { + f.precision = _precision; + } + FMT_CONSTEXPR void end_precision() {} - template void on_dynamic_width(Id arg_id) { + template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { f.width_ref = make_arg_ref(arg_id); } - template void on_dynamic_precision(Id arg_id) { + template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { f.precision_ref = make_arg_ref(arg_id); } }; @@ -1078,8 +1149,6 @@ struct formatter, Char> { } public: - formatter() : precision(-1) {} - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) -> decltype(ctx.begin()) { auto range = do_parse(ctx); @@ -1089,27 +1158,30 @@ struct formatter, Char> { } template - auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) { + auto format(const 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(); // 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.width, width_ref, - ctx); - detail::handle_dynamic_spec(precision, + detail::handle_dynamic_spec(specs_copy.width, + width_ref, ctx); + detail::handle_dynamic_spec(precision_copy, precision_ref, ctx); if (begin == end || *begin == '}') { - out = detail::format_duration_value(out, d.count(), precision); + out = detail::format_duration_value(out, d.count(), precision_copy); detail::format_duration_unit(out); } else { detail::chrono_formatter f( ctx, out, d); - f.precision = precision; + f.precision = precision_copy; parse_chrono_format(begin, end, f); } return detail::write( - ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); } }; diff --git a/vendor/Fmt/include/fmt/color.h b/vendor/Fmt/include/fmt/color.h index 94e3419d..24ccedac 100644 --- a/vendor/Fmt/include/fmt/color.h +++ b/vendor/Fmt/include/fmt/color.h @@ -10,6 +10,13 @@ #include "format.h" +// __declspec(deprecated) is broken in some MSVC versions. +#if FMT_MSC_VER +# define FMT_DEPRECATED_NONMSVC +#else +# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED +#endif + FMT_BEGIN_NAMESPACE enum class color : uint32_t { @@ -223,7 +230,7 @@ struct color_type { }; } // namespace detail -// Experimental text formatting support. +/** A text style consisting of foreground and background colors and emphasis. */ class text_style { public: FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT @@ -260,33 +267,14 @@ class text_style { return lhs |= rhs; } - 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 AND a terminal color")); - foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; - } - - 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 AND a terminal color")); - background_color.value.rgb_color &= rhs.background_color.value.rgb_color; - } - - ems = static_cast(static_cast(ems) & - static_cast(rhs.ems)); - return *this; + FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=( + const text_style& rhs) { + return and_assign(rhs); } - friend FMT_CONSTEXPR text_style operator&(text_style lhs, - const text_style& rhs) { - return lhs &= rhs; + FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style + operator&(text_style lhs, const text_style& rhs) { + return lhs.and_assign(rhs); } FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { @@ -326,8 +314,34 @@ class text_style { } } + // DEPRECATED! + FMT_CONSTEXPR text_style& and_assign(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 AND a terminal color")); + foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; + } + + 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 AND a terminal color")); + background_color.value.rgb_color &= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) & + static_cast(rhs.ems)); + return *this; + } + friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) FMT_NOEXCEPT; + friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) FMT_NOEXCEPT; @@ -338,15 +352,18 @@ class text_style { emphasis ems; }; -FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT { - return text_style(/*is_foreground=*/true, foreground); +/** Creates a text style from the foreground (text) color. */ +FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT { + return text_style(true, foreground); } -FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT { - return text_style(/*is_foreground=*/false, background); +/** Creates a text style from the background color. */ +FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT { + return text_style(false, background); } -FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { +FMT_CONSTEXPR inline text_style operator|(emphasis lhs, + emphasis rhs) FMT_NOEXCEPT { return text_style(lhs) | rhs; } @@ -523,11 +540,15 @@ void print(std::FILE* f, const text_style& ts, const S& format_str, } /** + \rst Formats a string and prints it to stdout using ANSI escape sequences to specify text formatting. - Example: + + **Example**:: + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), "Elapsed time: {0:.2f} seconds", 1.23); + \endrst */ template ::value)> diff --git a/vendor/Fmt/include/fmt/compile.h b/vendor/Fmt/include/fmt/compile.h index 3a33b020..580cacde 100644 --- a/vendor/Fmt/include/fmt/compile.h +++ b/vendor/Fmt/include/fmt/compile.h @@ -8,13 +8,102 @@ #ifndef FMT_COMPILE_H_ #define FMT_COMPILE_H_ +#include #include #include "format.h" +#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +# if defined(__cpp_nontype_template_parameter_class) && \ + (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 903) +# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1 +# else +# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 +# endif +#endif + FMT_BEGIN_NAMESPACE namespace detail { +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; + using _Unchecked_type = + truncating_iterator_base; // Mark iterator as checked. + + 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 {}; @@ -36,6 +125,24 @@ struct is_compiled_string : std::is_base_of {}; */ #define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +template struct fixed_string { + constexpr fixed_string(const Char (&str)[N]) { + copy_str(static_cast(str), str + N, + data); + } + Char data[N]{}; +}; + +template Str> +struct udl_compiled_string : compiled_string { + using char_type = Char; + constexpr operator basic_string_view() const { + return {Str.data, N - 1}; + } +}; +#endif + template const T& first(const T& value, const Tail&...) { return value; @@ -175,9 +282,9 @@ class format_string_compiler : public error_handler { repl.arg_id = part_.part_kind == part::kind::arg_index ? arg_ref(part_.val.arg_index) : arg_ref(part_.val.str); - auto part = part::make_replacement(repl); - part.arg_id_end = begin; - handler_(part); + auto replacement_part = part::make_replacement(repl); + replacement_part.arg_id_end = begin; + handler_(replacement_part); return it; } }; @@ -195,9 +302,15 @@ template void format_arg( basic_format_parse_context& parse_ctx, Context& ctx, Id arg_id) { - ctx.advance_to(visit_format_arg( - arg_formatter(ctx, &parse_ctx), - ctx.arg(arg_id))); + auto arg = ctx.arg(arg_id); + if (arg.type() == type::custom_type) { + visit_format_arg(custom_formatter(parse_ctx, ctx), arg); + } else { + ctx.advance_to(visit_format_arg( + default_arg_formatter{ + ctx.out(), ctx.args(), ctx.locale()}, + arg)); + } } // vformat_to is defined in a subnamespace to prevent ADL. @@ -257,10 +370,9 @@ auto vformat_to(OutputIt out, CompiledFormat& cf, if (specs.precision >= 0) checker.check_precision(); advance_to(parse_ctx, part.arg_id_end); - ctx.advance_to( - visit_format_arg(arg_formatter( - ctx, nullptr, &specs), - arg)); + ctx.advance_to(visit_format_arg( + arg_formatter(ctx, specs), + arg)); break; } } @@ -393,7 +505,7 @@ template struct text { using char_type = Char; template - OutputIt format(OutputIt out, const Args&...) const { + constexpr OutputIt format(OutputIt out, const Args&...) const { return write(out, data); } }; @@ -412,7 +524,7 @@ template struct code_unit { using char_type = Char; template - OutputIt format(OutputIt out, const Args&...) const { + constexpr OutputIt format(OutputIt out, const Args&...) const { return write(out, value); } }; @@ -425,23 +537,60 @@ template struct field { using char_type = Char; template - OutputIt format(OutputIt out, const Args&... args) const { - // This ensures that the argument type is convertile to `const T&`. - const T& arg = get(args...); - return write(out, arg); + constexpr OutputIt format(OutputIt out, const Args&... args) const { + if constexpr (is_named_arg::type>::value) { + const auto& arg = get(args...).value; + return write(out, arg); + } else { + // This ensures that the argument type is convertile to `const T&`. + const T& arg = get(args...); + return write(out, arg); + } } }; template struct is_compiled_format> : std::true_type {}; +// A replacement field that refers to argument with name. +template struct runtime_named_field { + using char_type = Char; + basic_string_view name; + + template + constexpr static bool 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) { + if constexpr (is_named_arg::type>::value) { + if (arg_name == arg.name) { + out = write(out, arg.value); + return true; + } + } + return false; + } + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + bool found = (try_format_argument(out, name, args) || ...); + if (!found) { + throw format_error("argument with specified name is not found"); + } + return out; + } +}; + +template +struct is_compiled_format> : std::true_type {}; + // A replacement field that refers to argument N and has format specifiers. template struct spec_field { using char_type = Char; - mutable formatter fmt; + formatter fmt; template - OutputIt format(OutputIt out, const Args&... args) const { + constexpr OutputIt format(OutputIt out, const Args&... args) const { // This ensures that the argument type is convertile to `const T&`. const T& arg = get(args...); const auto& vargs = @@ -460,7 +609,7 @@ template struct concat { using char_type = typename L::char_type; template - OutputIt format(OutputIt out, const Args&... args) const { + constexpr OutputIt format(OutputIt out, const Args&... args) const { out = lhs.format(out, args...); return rhs.format(out, args...); } @@ -508,14 +657,51 @@ template struct parse_specs_result { int next_arg_id; }; +constexpr int manual_indexing_id = -1; + template constexpr parse_specs_result parse_specs(basic_string_view str, - size_t pos, int arg_id) { + size_t pos, int next_arg_id) { str.remove_prefix(pos); - auto ctx = basic_format_parse_context(str, {}, arg_id + 1); + auto ctx = basic_format_parse_context(str, {}, next_arg_id); auto f = formatter(); auto end = f.parse(ctx); - return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()}; + return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, + next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; +} + +template struct arg_id_handler { + constexpr void on_error(const char* message) { throw format_error(message); } + + constexpr int on_arg_id() { + FMT_ASSERT(false, "handler cannot be used with automatic indexing"); + return 0; + } + + constexpr int on_arg_id(int id) { + arg_id = arg_ref(id); + return 0; + } + + constexpr int on_arg_id(basic_string_view id) { + arg_id = arg_ref(id); + return 0; + } + + arg_ref arg_id; +}; + +template struct parse_arg_id_result { + 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 adapter = id_adapter, Char>{handler, 0}; + auto arg_id_end = parse_arg_id(begin, end, adapter); + return parse_arg_id_result{handler.arg_id, arg_id_end}; } // Compiles a non-empty format string and returns the compiled representation @@ -525,24 +711,59 @@ constexpr auto compile_format_string(S format_str) { using char_type = typename S::char_type; constexpr basic_string_view str = format_str; if constexpr (str[POS] == '{') { - if (POS + 1 == str.size()) + if constexpr (POS + 1 == str.size()) throw format_error("unmatched '{' in format string"); if constexpr (str[POS + 1] == '{') { return parse_tail(make_text(str, POS, 1), format_str); - } else if constexpr (str[POS + 1] == '}') { - using type = get_type; - return parse_tail(field(), - format_str); - } else if constexpr (str[POS + 1] == ':') { - using type = get_type; - constexpr auto result = parse_specs(str, POS + 2, ID); - return parse_tail( - spec_field{result.fmt}, format_str); + } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { + static_assert(ID != manual_indexing_id, + "cannot switch from manual to automatic argument indexing"); + using id_type = get_type; + if constexpr (str[POS + 1] == '}') { + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_tail( + field(), format_str); + } else { + constexpr auto result = parse_specs(str, POS + 2, ID + 1); + return parse_tail( + spec_field{result.fmt}, format_str); + } } else { - return unknown_format(); + constexpr auto arg_id_result = + parse_arg_id(str.data() + POS + 1, str.data() + str.size()); + constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); + 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) { + 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; + using id_type = get_type; + if constexpr (c == '}') { + return parse_tail( + field(), format_str); + } else if constexpr (c == ':') { + constexpr auto result = + parse_specs(str, arg_id_end_pos + 1, 0); + return parse_tail( + spec_field{result.fmt}, + format_str); + } + } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { + if constexpr (c == '}') { + return parse_tail( + runtime_named_field{arg_id_result.arg_id.val.name}, + format_str); + } else if constexpr (c == ':') { + return unknown_format(); // no type info for specs parsing + } + } } } else if constexpr (str[POS] == '}') { - if (POS + 1 == str.size()) + if constexpr (POS + 1 == str.size()) throw format_error("unmatched '}' in format string"); return parse_tail(make_text(str, POS, 1), format_str); } else { @@ -568,12 +789,7 @@ constexpr auto compile(S format_str) { constexpr auto result = detail::compile_format_string, 0, 0>( format_str); - if constexpr (std::is_same, - detail::unknown_format>()) { - return detail::compiled_format(to_string_view(format_str)); - } else { - return result; - } + return result; } } #else @@ -615,8 +831,8 @@ FMT_INLINE std::basic_string format(const CompiledFormat& cf, template ::value)> -OutputIt format_to(OutputIt out, const CompiledFormat& cf, - const Args&... args) { +constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { return cf.format(out, args...); } # endif // __cpp_if_constexpr @@ -641,19 +857,36 @@ FMT_INLINE std::basic_string format(const S&, #ifdef __cpp_if_constexpr if constexpr (std::is_same::value) { constexpr basic_string_view str = S(); - if (str.size() == 2 && str[0] == '{' && str[1] == '}') - return fmt::to_string(detail::first(args...)); + if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { + const auto& first = detail::first(args...); + if constexpr (detail::is_named_arg< + remove_cvref_t>::value) { + return fmt::to_string(first.value); + } else { + return fmt::to_string(first); + } + } } #endif constexpr auto compiled = detail::compile(S()); +#ifdef __cpp_if_constexpr + if constexpr (std::is_same, + detail::unknown_format>()) { + return format(static_cast>(S()), + std::forward(args)...); + } else { + return format(compiled, std::forward(args)...); + } +#else return format(compiled, std::forward(args)...); +#endif } template ::value)> -OutputIt format_to(OutputIt out, const CompiledFormat& cf, - const Args&... args) { +constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { using char_type = typename CompiledFormat::char_type; using context = format_context_t; return detail::cf::vformat_to(out, cf, @@ -662,9 +895,20 @@ OutputIt format_to(OutputIt out, const CompiledFormat& cf, template ::value)> -OutputIt format_to(OutputIt out, const S&, const Args&... args) { +FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { constexpr auto compiled = detail::compile(S()); - return format_to(out, compiled, args...); +#ifdef __cpp_if_constexpr + if constexpr (std::is_same, + detail::unknown_format>()) { + return format_to(out, + static_cast>(S()), + std::forward(args)...); + } else { + return format_to(out, compiled, std::forward(args)...); + } +#else + return format_to(out, compiled, std::forward(args)...); +#endif } template @@ -684,18 +928,31 @@ auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf, template ::value)> format_to_n_result format_to_n(OutputIt out, size_t n, const S&, - const Args&... args) { - constexpr auto compiled = detail::compile(S()); - auto it = format_to(detail::truncating_iterator(out, n), compiled, - args...); + Args&&... args) { + auto it = format_to(detail::truncating_iterator(out, n), S(), + std::forward(args)...); return {it.base(), it.count()}; } -template +template ::value || + detail::is_compiled_string::value)> size_t formatted_size(const CompiledFormat& cf, const Args&... args) { return format_to(detail::counting_iterator(), cf, args...).count(); } +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +inline namespace literals { +template +constexpr detail::udl_compiled_string, + sizeof(Str.data), Str> +operator""_cf() { + return {}; +} +} // namespace literals +#endif + FMT_END_NAMESPACE #endif // FMT_COMPILE_H_ diff --git a/vendor/Fmt/include/fmt/core.h b/vendor/Fmt/include/fmt/core.h index 0a81e0cc..449ed042 100644 --- a/vendor/Fmt/include/fmt/core.h +++ b/vendor/Fmt/include/fmt/core.h @@ -10,12 +10,9 @@ #include // std::FILE #include -#include #include -#include #include #include -#include // The fmt library version in the form major * 10000 + minor * 100 + patch. #define FMT_VERSION 70103 @@ -28,8 +25,10 @@ #if defined(__GNUC__) && !defined(__clang__) # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +# define FMT_GCC_PRAGMA(arg) _Pragma(arg) #else # define FMT_GCC_VERSION 0 +# define FMT_GCC_PRAGMA(arg) #endif #if defined(__INTEL_COMPILER) @@ -52,10 +51,10 @@ #ifdef _MSC_VER # define FMT_MSC_VER _MSC_VER -# define FMT_SUPPRESS_MSC_WARNING(n) __pragma(warning(suppress : n)) +# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) #else # define FMT_MSC_VER 0 -# define FMT_SUPPRESS_MSC_WARNING(n) +# define FMT_MSC_WARNING(...) #endif #ifdef __has_feature @@ -95,7 +94,7 @@ # define FMT_CONSTEXPR constexpr # define FMT_CONSTEXPR_DECL constexpr #else -# define FMT_CONSTEXPR inline +# define FMT_CONSTEXPR # define FMT_CONSTEXPR_DECL #endif @@ -180,7 +179,7 @@ #ifndef FMT_USE_INLINE_NAMESPACES # if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ - (FMT_MSC_VER >= 1900 && !_MANAGED) + (FMT_MSC_VER >= 1900 && (!defined(_MANAGED) || !_MANAGED)) # define FMT_USE_INLINE_NAMESPACES 1 # else # define FMT_USE_INLINE_NAMESPACES 0 @@ -206,7 +205,7 @@ #endif #if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# define FMT_CLASS_API FMT_SUPPRESS_MSC_WARNING(4275) +# define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275) # ifdef FMT_EXPORT # define FMT_API __declspec(dllexport) # define FMT_EXTERN_TEMPLATE_API FMT_API @@ -248,8 +247,15 @@ #ifndef FMT_UNICODE # define FMT_UNICODE !FMT_MSC_VER #endif -#if FMT_UNICODE && FMT_MSC_VER -# pragma execution_character_set("utf-8") + +#ifndef FMT_COMPILE_TIME_CHECKS +# define FMT_COMPILE_TIME_CHECKS 0 +#endif + +// Enable minimal optimizations for more compact code in debug mode. +FMT_GCC_PRAGMA("GCC push_options") +#ifndef __OPTIMIZE__ +FMT_GCC_PRAGMA("GCC optimize(\"Og\")") #endif FMT_BEGIN_NAMESPACE @@ -274,10 +280,22 @@ struct 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). -#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 +#ifdef FMT_DOC +# define FMT_ENABLE_IF(...) +#else +# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 +#endif namespace detail { +constexpr bool is_constant_evaluated() FMT_NOEXCEPT { +#ifdef __cpp_lib_is_constant_evaluated + return std::is_constant_evaluated(); +#else + return false; +#endif +} + // A helper function to suppress "conditional expression is constant" warnings. template constexpr T const_check(T value) { return value; } @@ -327,7 +345,7 @@ FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { return static_cast::type>(value); } -FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5"; +FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; template constexpr bool is_unicode() { return FMT_UNICODE || sizeof(Char) != 1 || @@ -377,8 +395,12 @@ template class basic_string_view { #if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr. FMT_CONSTEXPR #endif - basic_string_view(const Char* s) - : data_(s), size_(std::char_traits::length(s)) {} + FMT_INLINE basic_string_view(const Char* s) : data_(s) { + if (std::is_same::value) + size_ = std::strlen(reinterpret_cast(s)); + else + size_ = std::char_traits::length(s); + } /** Constructs a string reference from a ``std::basic_string`` object. */ template @@ -465,7 +487,7 @@ template <> struct is_char : std::true_type {}; \endrst */ template ::value)> -inline basic_string_view to_string_view(const Char* s) { +FMT_INLINE basic_string_view to_string_view(const Char* s) { return s; } @@ -476,7 +498,7 @@ inline basic_string_view to_string_view( } template -inline basic_string_view to_string_view(basic_string_view s) { +constexpr basic_string_view to_string_view(basic_string_view s) { return s; } @@ -670,7 +692,7 @@ template class buffer { protected: // Don't initialize ptr_ since it is not accessed to save a few cycles. - FMT_SUPPRESS_MSC_WARNING(26495) + FMT_MSC_WARNING(suppress : 26495) buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT @@ -783,9 +805,7 @@ class iterator_buffer final : public Traits, public buffer { public: explicit iterator_buffer(OutputIt out, size_t n = buffer_size) - : Traits(n), - buffer(data_, 0, buffer_size), - out_(out) {} + : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} ~iterator_buffer() { flush(); } OutputIt out() { @@ -935,9 +955,9 @@ struct arg_data { T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; template - FMT_INLINE arg_data(const U&... init) : args_{init...} {} - FMT_INLINE const T* args() const { return args_; } - FMT_INLINE std::nullptr_t named_args() { return nullptr; } + FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} + FMT_CONSTEXPR FMT_INLINE const T* args() const { return args_; } + FMT_CONSTEXPR FMT_INLINE std::nullptr_t named_args() { return nullptr; } }; template @@ -958,7 +978,8 @@ void init_named_args(named_arg_info* named_args, int arg_count, } template -FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {} +FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, + const Args&...) {} template struct is_named_arg : std::false_type {}; @@ -1070,17 +1091,20 @@ template class value { constexpr FMT_INLINE value(int val = 0) : int_value(val) {} constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} - FMT_INLINE value(long long val) : long_long_value(val) {} - FMT_INLINE value(unsigned long long val) : ulong_long_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_t val) : int128_value(val) {} FMT_INLINE value(uint128_t val) : uint128_value(val) {} FMT_INLINE value(float val) : float_value(val) {} FMT_INLINE value(double val) : double_value(val) {} FMT_INLINE value(long double val) : long_double_value(val) {} - FMT_INLINE value(bool val) : bool_value(val) {} - FMT_INLINE value(char_type val) : char_value(val) {} - FMT_INLINE value(const char_type* val) { string.data = val; } - FMT_INLINE value(basic_string_view 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(); } @@ -1126,36 +1150,40 @@ struct unformattable {}; template struct arg_mapper { using char_type = typename Context::char_type; - FMT_CONSTEXPR int map(signed char val) { return val; } - FMT_CONSTEXPR unsigned map(unsigned char val) { return val; } - FMT_CONSTEXPR int map(short val) { return val; } - FMT_CONSTEXPR unsigned map(unsigned short val) { return val; } - FMT_CONSTEXPR int map(int val) { return val; } - FMT_CONSTEXPR unsigned map(unsigned val) { return val; } - FMT_CONSTEXPR long_type map(long val) { return val; } - FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; } - FMT_CONSTEXPR long long map(long long val) { return val; } - FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; } - FMT_CONSTEXPR int128_t map(int128_t val) { return val; } - FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; } - FMT_CONSTEXPR bool map(bool val) { return val; } + FMT_CONSTEXPR FMT_INLINE int map(signed char val) { return val; } + FMT_CONSTEXPR FMT_INLINE unsigned map(unsigned char val) { return val; } + FMT_CONSTEXPR FMT_INLINE int map(short val) { return val; } + FMT_CONSTEXPR FMT_INLINE unsigned map(unsigned short val) { return val; } + FMT_CONSTEXPR FMT_INLINE int map(int val) { return val; } + FMT_CONSTEXPR FMT_INLINE unsigned map(unsigned val) { return val; } + FMT_CONSTEXPR FMT_INLINE long_type map(long val) { return val; } + FMT_CONSTEXPR FMT_INLINE ulong_type map(unsigned long val) { return val; } + FMT_CONSTEXPR FMT_INLINE long long map(long long val) { return val; } + FMT_CONSTEXPR FMT_INLINE unsigned long long map(unsigned long long val) { + return val; + } + FMT_CONSTEXPR FMT_INLINE int128_t map(int128_t val) { return val; } + FMT_CONSTEXPR FMT_INLINE uint128_t map(uint128_t val) { return val; } + FMT_CONSTEXPR FMT_INLINE bool map(bool val) { return val; } template ::value)> - FMT_CONSTEXPR char_type map(T val) { + FMT_CONSTEXPR FMT_INLINE char_type map(T val) { static_assert( std::is_same::value || std::is_same::value, "mixing character types is disallowed"); return val; } - FMT_CONSTEXPR float map(float val) { return val; } - FMT_CONSTEXPR double map(double val) { return val; } - FMT_CONSTEXPR long double map(long double val) { return val; } + FMT_CONSTEXPR FMT_INLINE float map(float val) { return val; } + FMT_CONSTEXPR FMT_INLINE double map(double val) { return val; } + FMT_CONSTEXPR FMT_INLINE long double map(long double val) { return val; } - FMT_CONSTEXPR const char_type* map(char_type* val) { return val; } - FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; } + FMT_CONSTEXPR FMT_INLINE const char_type* map(char_type* val) { return val; } + FMT_CONSTEXPR FMT_INLINE const char_type* map(const char_type* val) { + return val; + } template ::value)> - FMT_CONSTEXPR basic_string_view map(const T& val) { + FMT_CONSTEXPR FMT_INLINE basic_string_view map(const T& val) { static_assert(std::is_same>::value, "mixing character types is disallowed"); return to_string_view(val); @@ -1165,7 +1193,7 @@ template struct arg_mapper { std::is_constructible, T>::value && !is_string::value && !has_formatter::value && !has_fallback_formatter::value)> - FMT_CONSTEXPR basic_string_view map(const T& val) { + FMT_CONSTEXPR FMT_INLINE basic_string_view map(const T& val) { return basic_string_view(val); } template < @@ -1175,30 +1203,34 @@ template struct arg_mapper { !std::is_constructible, T>::value && !is_string::value && !has_formatter::value && !has_fallback_formatter::value)> - FMT_CONSTEXPR basic_string_view map(const T& val) { + FMT_CONSTEXPR FMT_INLINE basic_string_view map(const T& val) { return std_string_view(val); } - FMT_CONSTEXPR const char* map(const signed char* val) { + FMT_CONSTEXPR FMT_INLINE const char* map(const signed char* val) { static_assert(std::is_same::value, "invalid string type"); return reinterpret_cast(val); } - FMT_CONSTEXPR const char* map(const unsigned char* val) { + FMT_CONSTEXPR FMT_INLINE const char* map(const unsigned char* val) { static_assert(std::is_same::value, "invalid string type"); return reinterpret_cast(val); } - FMT_CONSTEXPR const char* map(signed char* val) { + FMT_CONSTEXPR FMT_INLINE const char* map(signed char* val) { const auto* const_val = val; return map(const_val); } - FMT_CONSTEXPR const char* map(unsigned char* val) { + FMT_CONSTEXPR FMT_INLINE const char* map(unsigned char* val) { const auto* const_val = val; return map(const_val); } - FMT_CONSTEXPR const void* map(void* val) { return val; } - FMT_CONSTEXPR const void* map(const void* val) { return val; } - FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; } - template FMT_CONSTEXPR int map(const T*) { + FMT_CONSTEXPR FMT_INLINE const void* map(void* val) { return val; } + FMT_CONSTEXPR FMT_INLINE const void* map(const void* val) { return val; } + FMT_CONSTEXPR FMT_INLINE const void* map(std::nullptr_t val) { return val; } + + // We use SFINAE instead of a const T* parameter to avoid conflicting with + // the C array overload. + template + FMT_CONSTEXPR auto map(T) -> enable_if_t::value, int> { // Formatting of arbitrary pointers is disallowed. If you want to output // a pointer cast it to "void *" or "const void *". In particular, this // forbids formatting of "[const] volatile char *" which is printed as bool @@ -1207,11 +1239,16 @@ template struct arg_mapper { return 0; } + template + FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { + return values; + } + template ::value && !has_formatter::value && !has_fallback_formatter::value)> - FMT_CONSTEXPR auto map(const T& val) + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(std::declval().map( static_cast::type>(val))) { return map(static_cast::type>(val)); @@ -1220,12 +1257,12 @@ template struct arg_mapper { FMT_ENABLE_IF(!is_string::value && !is_char::value && (has_formatter::value || has_fallback_formatter::value))> - FMT_CONSTEXPR const T& map(const T& val) { + FMT_CONSTEXPR FMT_INLINE const T& map(const T& val) { return val; } template - FMT_CONSTEXPR auto map(const named_arg& val) + FMT_CONSTEXPR FMT_INLINE auto map(const named_arg& val) -> decltype(std::declval().map(val.value)) { return map(val.value); } @@ -1354,14 +1391,16 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( return vis(monostate()); } -template struct formattable : std::false_type {}; - namespace detail { +#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 detail::void_t_impl::type; +#else +template using void_t = void; +#endif template struct is_output_iterator : std::false_type {}; @@ -1394,7 +1433,7 @@ class locale_ref { const void* locale_; // A type-erased pointer to std::locale. public: - locale_ref() : locale_(nullptr) {} + constexpr locale_ref() : locale_(nullptr) {} template explicit locale_ref(const Locale& loc); explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } @@ -1418,24 +1457,18 @@ FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { return arg; } -template int check(unformattable) { - static_assert( - formattable(), - "Cannot format an argument. To make type T formattable provide a " - "formatter specialization: https://fmt.dev/latest/api.html#udt"); - return 0; -} -template inline const U& check(const U& val) { - return val; -} - // The type template parameter is there to avoid an ODR violation when using // a fallback formatter in one translation unit and an implicit conversion in // another (not recommended). template -inline value make_arg(const T& val) { - return check(arg_mapper().map(val)); +FMT_CONSTEXPR FMT_INLINE value make_arg(const T& val) { + const auto& arg = arg_mapper().map(val); + static_assert( + !std::is_same::value, + "Cannot format an argument. To make type T formattable provide a " + "formatter specialization: https://fmt.dev/latest/api.html#udt"); + return arg; } template make_arg(const T& value) { return make_arg(value); } - -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) { - 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; - }; - - template struct typed_node : node<> { - T value; - - template - FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} - - template - FMT_CONSTEXPR typed_node(const basic_string_view& arg) - : value(arg.data(), arg.size()) {} - }; - - std::unique_ptr> head_; - - public: - template const T& push(const Arg& arg) { - auto new_node = std::unique_ptr>(new typed_node(arg)); - auto& value = new_node->value; - new_node->next = std::move(head_); - head_ = std::move(new_node); - return value; - } -}; } // namespace detail // Formatting context. @@ -1509,28 +1501,30 @@ template class basic_format_context { Constructs a ``basic_format_context`` object. References to the arguments are stored in the object so make sure they have appropriate lifetimes. */ - basic_format_context(OutputIt out, - basic_format_args ctx_args, - detail::locale_ref loc = detail::locale_ref()) + constexpr basic_format_context( + OutputIt out, basic_format_args ctx_args, + detail::locale_ref loc = detail::locale_ref()) : out_(out), args_(ctx_args), loc_(loc) {} - format_arg arg(int id) const { return args_.get(id); } - format_arg arg(basic_string_view name) { return args_.get(name); } + constexpr format_arg arg(int id) const { return args_.get(id); } + FMT_CONSTEXPR format_arg arg(basic_string_view name) { + return args_.get(name); + } int arg_id(basic_string_view name) { return args_.get_id(name); } const basic_format_args& args() const { return args_; } - detail::error_handler error_handler() { return {}; } + FMT_CONSTEXPR detail::error_handler error_handler() { return {}; } void on_error(const char* message) { error_handler().on_error(message); } // Returns an iterator to the beginning of the output range. - iterator out() { return out_; } + FMT_CONSTEXPR iterator out() { return out_; } // Advances the begin iterator to ``it``. void advance_to(iterator it) { if (!detail::is_back_insert_iterator()) out_ = it; } - detail::locale_ref locale() { return loc_; } + FMT_CONSTEXPR detail::locale_ref locale() { return loc_; } }; template @@ -1543,6 +1537,11 @@ using wformat_context = buffer_context; #define FMT_BUFFER_CONTEXT(Char) \ basic_format_context, Char> +template +using is_formattable = bool_constant>().map(std::declval())), + detail::unformattable>::value>; + /** \rst An array of references to arguments. It can be implicitly converted into @@ -1579,7 +1578,7 @@ class format_arg_store : 0); public: - format_arg_store(const Args&... args) + FMT_CONSTEXPR FMT_INLINE format_arg_store(const Args&... args) : #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 basic_format_args(*this), @@ -1600,7 +1599,7 @@ class format_arg_store \endrst */ template -inline format_arg_store make_format_args( +constexpr format_arg_store make_format_args( const Args&... args) { return {args...}; } @@ -1614,8 +1613,8 @@ inline format_arg_store make_format_args( \endrst */ template > -inline auto make_args_checked(const S& format_str, - const remove_reference_t&... args) +FMT_INLINE auto make_args_checked(const S& format_str, + const remove_reference_t&... args) -> format_arg_store, remove_reference_t...> { static_assert( detail::count<( @@ -1628,8 +1627,9 @@ inline auto make_args_checked(const S& format_str, /** \rst - Returns a named argument to be used in a formatting function. It should only - be used in a call to a formatting function. + 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**:: @@ -1642,179 +1642,6 @@ inline detail::named_arg arg(const Char* name, const T& arg) { return {name, arg}; } -/** - \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 - */ -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 -{ - private: - using char_type = typename Context::char_type; - - template struct need_copy { - static constexpr detail::type mapped_type = - detail::mapped_type_constant::value; - - enum { - value = !(detail::is_reference_wrapper::value || - std::is_same>::value || - std::is_same>::value || - (mapped_type != detail::type::cstring_type && - mapped_type != detail::type::string_type && - mapped_type != detail::type::custom_type)) - }; - }; - - template - using stored_type = conditional_t::value, - std::basic_string, T>; - - // Storage of basic_format_arg must be contiguous. - std::vector> data_; - std::vector> named_info_; - - // Storage of arguments not fitting into basic_format_arg must grow - // without relocation because items in data_ refer to it. - detail::dynamic_arg_list dynamic_args_; - - 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 { - return named_info_.empty() ? data_.data() : data_.data() + 1; - } - - template void emplace_arg(const T& arg) { - data_.emplace_back(detail::make_arg(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))); - 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()}; - guard.release(); - } - - public: - /** - \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 - */ - template void push_back(const T& arg) { - if (detail::const_check(need_copy::value)) - 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. Supports named arguments wrapped in - ``std::reference_wrapper`` via ``std::ref()``/``std::cref()``. - - **Example**:: - - fmt::dynamic_format_arg_store store; - char str[] = "1234567890"; - store.push_back(std::cref(str)); - int a1_val{42}; - auto a1 = fmt::arg("a1_", a1_val); - store.push_back(std::cref(a1)); - - // Changing str affects the output but only for string and custom types. - str[0] = 'X'; - - std::string result = fmt::vformat("{} and {a1_}"); - assert(result == "X234567890 and 42"); - \endrst - */ - template void push_back(std::reference_wrapper arg) { - static_assert( - detail::is_named_arg::type>::value || - need_copy::value, - "objects of built-in types and string views are always copied"); - emplace_arg(arg.get()); - } - - /** - 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. - */ - 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))); - } else { - emplace_arg(fmt::arg(arg_name, arg.value)); - } - } - - /** Erase all elements from the store */ - void clear() { - data_.clear(); - named_info_.clear(); - dynamic_args_ = detail::dynamic_arg_list(); - } - - /** - \rst - Reserves space to store at least *new_cap* arguments including - *new_cap_named* named arguments. - \endrst - */ - 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"); - data_.reserve(new_cap); - named_info_.reserve(new_cap_named); - } -}; - /** \rst A view of a collection of formatting arguments. To avoid lifetime issues it @@ -1846,25 +1673,27 @@ template class basic_format_args { const format_arg* args_; }; - bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; } + constexpr bool is_packed() const { + return (desc_ & detail::is_unpacked_bit) == 0; + } bool has_named_args() const { return (desc_ & detail::has_named_args_bit) != 0; } - detail::type type(int index) const { + FMT_CONSTEXPR detail::type type(int index) const { int shift = index * detail::packed_arg_bits; unsigned int mask = (1 << detail::packed_arg_bits) - 1; return static_cast((desc_ >> shift) & mask); } - basic_format_args(unsigned long long desc, - const detail::value* values) + constexpr FMT_INLINE basic_format_args(unsigned long long desc, + const detail::value* values) : desc_(desc), values_(values) {} - basic_format_args(unsigned long long desc, const format_arg* args) + constexpr basic_format_args(unsigned long long desc, const format_arg* args) : desc_(desc), args_(args) {} public: - basic_format_args() : desc_(0) {} + constexpr basic_format_args() : desc_(0), args_(nullptr) {} /** \rst @@ -1872,8 +1701,10 @@ template class basic_format_args { \endrst */ template - FMT_INLINE basic_format_args(const format_arg_store& store) - : basic_format_args(store.desc, store.data_.args()) {} + constexpr FMT_INLINE basic_format_args( + const format_arg_store& store) + : basic_format_args(format_arg_store::desc, + store.data_.args()) {} /** \rst @@ -1881,7 +1712,8 @@ template class basic_format_args { `~fmt::dynamic_format_arg_store`. \endrst */ - FMT_INLINE basic_format_args(const dynamic_format_arg_store& store) + constexpr FMT_INLINE basic_format_args( + const dynamic_format_arg_store& store) : basic_format_args(store.get_types(), store.data()) {} /** @@ -1889,12 +1721,12 @@ template class basic_format_args { Constructs a `basic_format_args` object from a dynamic set of arguments. \endrst */ - basic_format_args(const format_arg* args, int count) + 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. */ - format_arg get(int id) const { + FMT_CONSTEXPR format_arg get(int id) const { format_arg arg; if (!is_packed()) { if (id < max_size()) arg = args_[id]; @@ -2043,11 +1875,11 @@ inline auto format_to_n(OutputIt out, size_t n, const S& format_str, Returns the number of characters in the output of ``format(format_str, args...)``. */ -template -inline size_t formatted_size(string_view format_str, Args&&... args) { +template > +inline size_t formatted_size(const S& format_str, Args&&... args) { const auto& vargs = fmt::make_args_checked(format_str, args...); detail::counting_buffer<> buf; - detail::vformat_to(buf, format_str, vargs); + detail::vformat_to(buf, to_string_view(format_str), vargs); return buf.count(); } @@ -2070,7 +1902,9 @@ FMT_INLINE std::basic_string vformat( */ // Pass char_t as a default template parameter instead of using // std::basic_string> to reduce the symbol size. -template > +template , + FMT_ENABLE_IF(!FMT_COMPILE_TIME_CHECKS || + !std::is_same::value)> FMT_INLINE std::basic_string format(const S& format_str, Args&&... args) { const auto& vargs = fmt::make_args_checked(format_str, args...); return detail::vformat(to_string_view(format_str), vargs); @@ -2117,6 +1951,13 @@ inline void print(const S& format_str, Args&&... args) { : detail::vprint_mojibake(stdout, to_string_view(format_str), vargs); } +FMT_GCC_PRAGMA("GCC pop_options") FMT_END_NAMESPACE #endif // FMT_CORE_H_ + +// Define FMT_DYNAMIC_ARGS to make core.h provide dynamic_format_arg_store +// DEPRECATED! Include fmt/args.h directly instead. +#ifdef FMT_DYNAMIC_ARGS +#include "args.h" +#endif diff --git a/vendor/Fmt/include/fmt/format-inl.h b/vendor/Fmt/include/fmt/format-inl.h index 8f2fe735..6722fe8c 100644 --- a/vendor/Fmt/include/fmt/format-inl.h +++ b/vendor/Fmt/include/fmt/format-inl.h @@ -8,7 +8,7 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -#include +#include #include #include #include @@ -145,9 +145,9 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); auto it = buffer_appender(out); if (message.size() <= inline_buffer_size - error_code_size) - format_to(it, "{}{}", message, SEP); - format_to(it, "{}{}", ERROR_STR, error_code); - assert(out.size() <= inline_buffer_size); + format_to(it, FMT_STRING("{}{}"), message, SEP); + 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, @@ -165,11 +165,8 @@ inline void fwrite_fully(const void* ptr, size_t size, size_t count, size_t written = std::fwrite(ptr, size, count, stream); if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); } -} // namespace detail - -#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) -namespace detail { +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR template locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { static_assert(std::is_same::value, ""); @@ -191,19 +188,18 @@ template FMT_FUNC Char decimal_point_impl(locale_ref loc) { return std::use_facet>(loc.get()) .decimal_point(); } -} // namespace detail #else -template -FMT_FUNC std::string detail::grouping_impl(locale_ref) { +template FMT_FUNC std::string grouping_impl(locale_ref) { return "\03"; } -template FMT_FUNC Char detail::thousands_sep_impl(locale_ref) { +template FMT_FUNC Char thousands_sep_impl(locale_ref) { return FMT_STATIC_THOUSANDS_SEPARATOR; } -template FMT_FUNC Char detail::decimal_point_impl(locale_ref) { +template FMT_FUNC Char decimal_point_impl(locale_ref) { return '.'; } #endif +} // namespace detail FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default; @@ -247,9 +243,6 @@ const typename basic_data::digit_pair basic_data::digits[] = { {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; -template -const char basic_data::hex_digits[] = "0123456789abcdef"; - #define FMT_POWERS_OF_10(factor) \ factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ (factor)*1000000, (factor)*10000000, (factor)*100000000, \ @@ -1070,10 +1063,14 @@ const char basic_data::background_color[] = "\x1b[48;2;"; template const char basic_data::reset_color[] = "\x1b[0m"; template const wchar_t basic_data::wreset_color[] = L"\x1b[0m"; template const char basic_data::signs[] = {0, '-', '+', ' '}; + +#if __cplusplus < 201703L +template constexpr const char basic_data::hex_digits[]; +template constexpr const unsigned basic_data::prefixes[]; +template constexpr const char basic_data::left_padding_shifts[]; template -const char basic_data::left_padding_shifts[] = {31, 31, 0, 1, 0}; -template -const char basic_data::right_padding_shifts[] = {0, 31, 0, 1, 0}; +constexpr const char basic_data::right_padding_shifts[]; +#endif template struct bits { static FMT_CONSTEXPR_DECL const int value = @@ -1228,7 +1225,7 @@ struct accumulator { if (lower < n) ++upper; } void operator>>=(int shift) { - assert(shift == 32); + FMT_ASSERT(shift == 32, ""); (void)shift; lower = (upper << 32) | (lower >> 32); upper >>= 32; @@ -1307,7 +1304,7 @@ class bigint { public: bigint() : exp_(0) {} explicit bigint(uint64_t n) { assign(n); } - ~bigint() { assert(bigits_.capacity() <= bigits_capacity); } + ~bigint() { FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); } bigint(const bigint&) = delete; void operator=(const bigint&) = delete; @@ -1333,7 +1330,7 @@ class bigint { int num_bigits() const { return static_cast(bigits_.size()) + exp_; } FMT_NOINLINE bigint& operator<<=(int shift) { - assert(shift >= 0); + FMT_ASSERT(shift >= 0, ""); exp_ += shift / bigit_bits; shift %= bigit_bits; if (shift == 0) return *this; @@ -1395,7 +1392,7 @@ class bigint { // Assigns pow(10, exp) to this bigint. void assign_pow10(int exp) { - assert(exp >= 0); + FMT_ASSERT(exp >= 0, ""); if (exp == 0) return assign(1); // Find the top bit. int bitmask = 1; @@ -1646,8 +1643,7 @@ struct fixed_handler { // Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. namespace dragonbox { // Computes 128-bit result of multiplication of two 64-bit unsigned integers. -FMT_SAFEBUFFERS inline uint128_wrapper umul128(uint64_t x, - uint64_t y) FMT_NOEXCEPT { +inline uint128_wrapper umul128(uint64_t x, uint64_t y) FMT_NOEXCEPT { #if FMT_USE_INT128 return static_cast(x) * static_cast(y); #elif defined(_MSC_VER) && defined(_M_X64) @@ -1675,8 +1671,7 @@ FMT_SAFEBUFFERS inline uint128_wrapper umul128(uint64_t x, } // Computes upper 64 bits of multiplication of two 64-bit unsigned integers. -FMT_SAFEBUFFERS inline uint64_t umul128_upper64(uint64_t x, - uint64_t y) FMT_NOEXCEPT { +inline uint64_t umul128_upper64(uint64_t x, uint64_t y) FMT_NOEXCEPT { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return static_cast(p >> 64); @@ -1689,8 +1684,7 @@ FMT_SAFEBUFFERS inline uint64_t umul128_upper64(uint64_t x, // Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -FMT_SAFEBUFFERS inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) - FMT_NOEXCEPT { +inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT { uint128_wrapper g0 = umul128(x, y.high()); g0 += umul128_upper64(x, y.low()); return g0.high(); @@ -1704,8 +1698,7 @@ inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT { // Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -FMT_SAFEBUFFERS inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) - FMT_NOEXCEPT { +inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT { uint64_t g01 = x * y.high(); uint64_t g10 = umul128_upper64(x, y.low()); return g01 + g10; @@ -2124,8 +2117,8 @@ FMT_ALWAYS_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { // The main algorithm for shorter interval case template -FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decimal_fp shorter_interval_case( - int exponent) FMT_NOEXCEPT { +FMT_ALWAYS_INLINE decimal_fp shorter_interval_case(int exponent) + FMT_NOEXCEPT { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); @@ -2171,8 +2164,7 @@ FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decimal_fp shorter_interval_case( return ret_value; } -template -FMT_SAFEBUFFERS decimal_fp to_decimal(T x) FMT_NOEXCEPT { +template decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Step 1: integer promotion & Schubfach multiplier calculation. using carrier_uint = typename float_info::carrier_uint; @@ -2571,11 +2563,11 @@ int snprintf_float(T value, int precision, float_specs specs, --exp_pos; } while (*exp_pos != 'e'); char sign = exp_pos[1]; - assert(sign == '+' || sign == '-'); + FMT_ASSERT(sign == '+' || sign == '-', ""); int exp = 0; auto p = exp_pos + 2; // Skip 'e' and sign. do { - assert(is_digit(*p)); + FMT_ASSERT(is_digit(*p), ""); exp = exp * 10 + (*p++ - '0'); } while (p != end); if (sign == '-') exp = -exp; @@ -2593,54 +2585,6 @@ int snprintf_float(T value, int precision, float_specs specs, } } -// A public domain branchless UTF-8 decoder by Christopher Wellons: -// https://github.com/skeeto/branchless-utf8 -/* Decode the next character, c, from buf, reporting errors in e. - * - * Since this is a branchless decoder, four bytes will be read from the - * buffer regardless of the actual length of the next character. This - * means the buffer _must_ have at least three bytes of zero padding - * following the end of the data stream. - * - * Errors are reported in e, which will be non-zero if the parsed - * character was somehow invalid: invalid byte sequence, non-canonical - * encoding, or a surrogate half. - * - * The function returns a pointer to the next character. When an error - * occurs, this pointer will be a guess that depends on the particular - * error, but it will always advance at least one byte. - */ -inline const char* utf8_decode(const char* buf, uint32_t* c, int* e) { - static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; - static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; - static const int shiftc[] = {0, 18, 12, 6, 0}; - static const int shifte[] = {0, 6, 4, 2, 0}; - - int len = code_point_length(buf); - const char* next = buf + len; - - // Assume a four-byte character and load four bytes. Unused bits are - // shifted out. - auto s = reinterpret_cast(buf); - *c = uint32_t(s[0] & masks[len]) << 18; - *c |= uint32_t(s[1] & 0x3f) << 12; - *c |= uint32_t(s[2] & 0x3f) << 6; - *c |= uint32_t(s[3] & 0x3f) << 0; - *c >>= shiftc[len]; - - // Accumulate the various error conditions. - *e = (*c < mins[len]) << 6; // non-canonical encoding - *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? - *e |= (*c > 0x10FFFF) << 8; // out of range? - *e |= (s[1] & 0xc0) >> 2; - *e |= (s[2] & 0xc0) >> 4; - *e |= (s[3]) >> 6; - *e ^= 0x2a; // top two bits of each tail byte correct? - *e >>= shifte[len]; - - return next; -} - struct stringifier { template FMT_INLINE std::string operator()(T value) const { return to_string(value); @@ -2656,7 +2600,8 @@ struct stringifier { } // namespace detail template <> struct formatter { - format_parse_context::iterator parse(format_parse_context& ctx) { + FMT_CONSTEXPR format_parse_context::iterator parse( + format_parse_context& ctx) { return ctx.begin(); } @@ -2667,23 +2612,21 @@ template <> struct formatter { for (auto i = n.bigits_.size(); i > 0; --i) { auto value = n.bigits_[i - 1u]; if (first) { - out = format_to(out, "{:x}", value); + out = format_to(out, FMT_STRING("{:x}"), value); first = false; continue; } - out = format_to(out, "{:08x}", value); + out = format_to(out, FMT_STRING("{:08x}"), value); } if (n.exp_ > 0) - out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits); + out = format_to(out, FMT_STRING("p{}"), + n.exp_ * detail::bigint::bigit_bits); return out; } }; FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { - auto transcode = [this](const char* p) { - auto cp = uint32_t(); - auto error = 0; - p = utf8_decode(p, &cp, &error); + for_each_codepoint(s, [this](uint32_t cp, int error) { if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); if (cp <= 0xFFFF) { buffer_.push_back(static_cast(cp)); @@ -2692,21 +2635,7 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { buffer_.push_back(static_cast(0xD800 + (cp >> 10))); buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); } - return p; - }; - auto p = s.data(); - const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. - if (s.size() >= block_size) { - for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p); - } - if (auto num_chars_left = s.data() + s.size() - p) { - char buf[2 * block_size - 1] = {}; - memcpy(buf, p, to_unsigned(num_chars_left)); - p = buf; - do { - p = transcode(p); - } while (p - buf < num_chars_left); - } + }); buffer_.push_back(0); } @@ -2720,8 +2649,8 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, int result = detail::safe_strerror(error_code, system_message, buf.size()); if (result == 0) { - format_to(detail::buffer_appender(out), "{}: {}", message, - system_message); + format_to(detail::buffer_appender(out), FMT_STRING("{}: {}"), + message, system_message); return; } if (result != ERANGE) @@ -2770,12 +2699,13 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { if (_isatty(fd)) { detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); auto written = detail::dword(); - if (!detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), - u16.c_str(), static_cast(u16.size()), - &written, nullptr)) { - FMT_THROW(format_error("failed to write to console")); + if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), + u16.c_str(), static_cast(u16.size()), + &written, nullptr)) { + return; } - return; + // Fallback to fwrite on failure. It can happen if the output has been + // redirected to NUL. } #endif detail::fwrite_fully(buffer.data(), 1, buffer.size(), f); diff --git a/vendor/Fmt/include/fmt/format.h b/vendor/Fmt/include/fmt/format.h index 1a037b02..f7d7efb0 100644 --- a/vendor/Fmt/include/fmt/format.h +++ b/vendor/Fmt/include/fmt/format.h @@ -33,13 +33,15 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include #include #include +#include // std::byte #include +#include #include #include #include +#include // std::swap #include "core.h" @@ -69,6 +71,12 @@ # define FMT_NOINLINE #endif +#if FMT_GCC_VERSION +# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) +#else +# define FMT_GCC_VISIBILITY_HIDDEN +#endif + #if __cplusplus == 201103L || __cplusplus == 201402L # if defined(__INTEL_COMPILER) || defined(__PGI) # define FMT_FALLTHROUGH @@ -113,10 +121,9 @@ FMT_END_NAMESPACE # define FMT_THROW(x) throw x # endif # else -# define FMT_THROW(x) \ - do { \ - static_cast(sizeof(x)); \ - FMT_ASSERT(false, ""); \ +# define FMT_THROW(x) \ + do { \ + FMT_ASSERT(false, (x).what()); \ } while (false) # endif #endif @@ -140,20 +147,6 @@ FMT_END_NAMESPACE # endif #endif -#ifndef FMT_USE_UDL_TEMPLATE -// EDG frontend based compilers (icc, nvcc, PGI, etc) and GCC < 6.4 do not -// properly support UDL templates and GCC >= 9 warns about them. -# if FMT_USE_USER_DEFINED_LITERALS && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 501) && \ - ((FMT_GCC_VERSION >= 604 && __cplusplus >= 201402L) || \ - FMT_CLANG_VERSION >= 304) && \ - !defined(__PGI) && !defined(__NVCC__) -# define FMT_USE_UDL_TEMPLATE 1 -# else -# define FMT_USE_UDL_TEMPLATE 0 -# endif -#endif - #ifndef FMT_USE_FLOAT # define FMT_USE_FLOAT 1 #endif @@ -167,9 +160,9 @@ FMT_END_NAMESPACE #endif // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of -// int_writer template instances to just one by only using the largest integer -// type. This results in a reduction in binary size but will cause a decrease in -// integer formatting performance. +// integer formatter template instantiations to just one by only using the +// largest integer type. This results in a reduction in binary size but will +// cause a decrease in integer formatting performance. #if !defined(FMT_REDUCE_INT_INSTANTIATIONS) # define FMT_REDUCE_INT_INSTANTIATIONS 0 #endif @@ -196,18 +189,18 @@ FMT_END_NAMESPACE // Some compilers masquerade as both MSVC and GCC-likes or 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_VER && !defined(FMT_BUILTIN_CLZLL) && \ - !defined(FMT_BUILTIN_CTZLL) && !defined(_MANAGED) +#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(FMT_BUILTIN_CTZLL) FMT_BEGIN_NAMESPACE namespace detail { // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. -# ifndef __clang__ +# if !defined(__clang__) +# pragma managed(push, off) # pragma intrinsic(_BitScanForward) # pragma intrinsic(_BitScanReverse) -# endif -# if defined(_WIN64) && !defined(__clang__) -# pragma intrinsic(_BitScanForward64) -# pragma intrinsic(_BitScanReverse64) +# if defined(_WIN64) +# pragma intrinsic(_BitScanForward64) +# pragma intrinsic(_BitScanReverse64) +# endif # endif inline int clz(uint32_t x) { @@ -217,7 +210,7 @@ inline int clz(uint32_t x) { // 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_SUPPRESS_MSC_WARNING(6102) + FMT_MSC_WARNING(suppress : 6102) return 31 ^ static_cast(r); } # define FMT_BUILTIN_CLZ(n) detail::clz(n) @@ -233,7 +226,7 @@ inline int clzll(uint64_t x) { _BitScanReverse(&r, static_cast(x)); # endif FMT_ASSERT(x != 0, ""); - FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. return 63 ^ static_cast(r); } # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) @@ -242,7 +235,7 @@ inline int ctz(uint32_t x) { unsigned long r = 0; _BitScanForward(&r, x); FMT_ASSERT(x != 0, ""); - FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. return static_cast(r); } # define FMT_BUILTIN_CTZ(n) detail::ctz(n) @@ -250,7 +243,7 @@ inline int ctz(uint32_t x) { inline int ctzll(uint64_t x) { unsigned long r = 0; FMT_ASSERT(x != 0, ""); - FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. # ifdef _WIN64 _BitScanForward64(&r, x); # else @@ -263,6 +256,9 @@ inline int ctzll(uint64_t x) { return static_cast(r); } # define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) +# if !defined(__clang__) +# pragma managed(pop) +# endif } // namespace detail FMT_END_NAMESPACE #endif @@ -275,6 +271,13 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE namespace detail { +#if __cplusplus >= 202002L || \ + (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEXPR20 +#endif + // An equivalent of `*reinterpret_cast(&source)` that doesn't have // undefined behavior (e.g. due to type aliasing). // Example: uint64_t d = bit_cast(2.718); @@ -366,7 +369,7 @@ template inline T* make_checked(T* p, size_t) { return p; } #endif template ::value)> -#if FMT_CLANG_VERSION +#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif inline checked_ptr @@ -384,10 +387,14 @@ inline buffer_appender reserve(buffer_appender it, size_t n) { return it; } -template inline Iterator& reserve(Iterator& it, size_t) { +template constexpr Iterator& reserve(Iterator& it, size_t) { return it; } +template +using reserve_iterator = + remove_reference_t(), 0))>; + template constexpr T* to_pointer(OutputIt, size_t) { return nullptr; @@ -408,7 +415,7 @@ inline std::back_insert_iterator base_iterator( } template -inline Iterator base_iterator(Iterator, Iterator it) { +constexpr Iterator base_iterator(Iterator, Iterator it) { return it; } @@ -451,96 +458,191 @@ class counting_iterator { value_type operator*() const { return {}; } }; -template class truncating_iterator_base { - protected: - OutputIt out_; - size_t limit_; - size_t count_; - - truncating_iterator_base(OutputIt out, size_t limit) - : out_(out), limit_(limit), count_(0) {} - - public: - using iterator_category = std::output_iterator_tag; - using value_type = typename std::iterator_traits::value_type; - using difference_type = void; - using pointer = void; - using reference = void; - using _Unchecked_type = - truncating_iterator_base; // Mark iterator as checked. - - 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(OutputIt out, size_t limit) - : truncating_iterator_base(out, limit) {} - - truncating_iterator& operator++() { - if (this->count_++ < this->limit_) ++this->out_; - return *this; +// is spectacularly slow to compile in C++20 so use a simple fill_n +// instead (#1998). +template +FMT_CONSTEXPR OutputIt fill_n(OutputIt out, Size count, const T& value) { + for (Size i = 0; i < count; ++i) *out++ = value; + return out; +} +template +FMT_CONSTEXPR20 T* fill_n(T* out, Size count, char value) { + if (is_constant_evaluated()) { + return fill_n(out, count, value); } + std::memset(out, value, to_unsigned(count)); + return out + count; +} - truncating_iterator operator++(int) { - auto it = *this; - ++*this; - return it; +template +using needs_conversion = bool_constant< + std::is_same::value_type, + char>::value && + std::is_same::value>; + +template ::value)> +FMT_CONSTEXPR OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + while (begin != end) *it++ = *begin++; + return it; +} + +template ::value)> +FMT_CONSTEXPR20 OutChar* copy_str(InputIt begin, InputIt end, OutChar* out) { + if (is_constant_evaluated()) { + return copy_str(begin, end, out); } + return std::uninitialized_copy(begin, end, out); +} - value_type& operator*() const { - return this->count_ < this->limit_ ? *this->out_ : blackhole_; - } -}; +template ::value)> +OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + while (begin != end) *it++ = static_cast(*begin++); + return it; +} -template -class truncating_iterator - : public truncating_iterator_base { - public: - truncating_iterator(OutputIt out, size_t limit) - : truncating_iterator_base(out, limit) {} +template ::value)> +buffer_appender copy_str(InputIt begin, InputIt end, + buffer_appender out) { + get_container(out).append(begin, end); + return out; +} - 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; } -}; +template +inline counting_iterator copy_str(InputIt begin, InputIt end, + counting_iterator it) { + return it + (end - begin); +} template -inline size_t count_code_points(basic_string_view s) { +FMT_CONSTEXPR int code_point_length(const Char* begin) { + if (const_check(sizeof(Char) != 1)) return 1; + constexpr char lengths[] = {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, 0}; + int len = lengths[static_cast(*begin) >> 3]; + + // Compute the pointer to the next character early so that the next + // iteration can start working on the next character. Neither Clang + // nor GCC figure out this reordering on their own. + return len + !len; +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from s, reporting errors in e. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in e, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +FMT_CONSTEXPR inline const char* utf8_decode(const char* s, uint32_t* c, + int* e) { + 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}; + + int len = code_point_length(s); + const char* next = s + len; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + *c = uint32_t(s[0] & masks[len]) << 18; + *c |= uint32_t(s[1] & 0x3f) << 12; + *c |= uint32_t(s[2] & 0x3f) << 6; + *c |= uint32_t(s[3] & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + using uchar = unsigned char; + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (uchar(s[1]) & 0xc0) >> 2; + *e |= (uchar(s[2]) & 0xc0) >> 4; + *e |= uchar(s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} + +template +FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { + auto decode = [f](const char* p) { + auto cp = uint32_t(); + auto error = 0; + p = utf8_decode(p, &cp, &error); + f(cp, error); + return p; + }; + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) p = decode(p); + } + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + copy_str(p, p + num_chars_left, buf); + p = buf; + do { + p = decode(p); + } while (p - buf < num_chars_left); + } +} + +template +inline size_t compute_width(basic_string_view s) { return s.size(); } -// Counts the number of code points in a UTF-8 string. -inline size_t count_code_points(basic_string_view s) { - const char* data = s.data(); +// Computes approximate display width of a UTF-8 string. +FMT_CONSTEXPR inline size_t compute_width(string_view s) { 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; - } + // It is not a lambda for compatibility with C++14. + struct count_code_points { + size_t* count; + FMT_CONSTEXPR void operator()(uint32_t cp, int error) const { + *count += + 1 + + (error == 0 && cp >= 0x1100 && + (cp <= 0x115f || // Hangul Jamo init. consonants + cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET〈 + cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET 〉 + // CJK ... Yi except Unicode Character “〿”: + (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || + (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables + (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs + (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms + (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms + (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms + (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms + (cp >= 0x20000 && cp <= 0x2fffd) || // CJK + (cp >= 0x30000 && cp <= 0x3fffd) || + // Miscellaneous Symbols and Pictographs + Emoticons: + (cp >= 0x1f300 && cp <= 0x1f64f) || + // Supplemental Symbols and Pictographs: + (cp >= 0x1f900 && cp <= 0x1f9ff))); + } + }; + for_each_codepoint(s, count_code_points{&num_code_points}); return num_code_points; } -inline size_t count_code_points(basic_string_view s) { - return count_code_points(basic_string_view( +inline size_t compute_width(basic_string_view s) { + return compute_width(basic_string_view( reinterpret_cast(s.data()), s.size())); } @@ -555,38 +657,11 @@ inline size_t code_point_index(basic_string_view s, size_t n) { const char8_type* 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; - } + if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i; } return s.size(); } -template -using needs_conversion = bool_constant< - std::is_same::value_type, - char>::value && - std::is_same::value>; - -template ::value)> -OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - return std::copy(begin, end, it); -} - -template ::value)> -OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - return std::transform(begin, end, it, - [](char c) { return static_cast(c); }); -} - -template -inline counting_iterator copy_str(InputIt begin, InputIt end, - counting_iterator it) { - return it + (end - begin); -} - template using is_fast_float = bool_constant::is_iec559 && sizeof(T) <= sizeof(double)>; @@ -598,7 +673,7 @@ using is_fast_float = bool_constant::is_iec559 && template template void buffer::append(const U* begin, const U* end) { - do { + while (begin != end) { auto count = to_unsigned(end - begin); try_reserve(size_ + count); auto free_cap = capacity_ - size_; @@ -606,13 +681,14 @@ void buffer::append(const U* begin, const U* end) { std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); size_ += count; begin += count; - } while (begin != end); + } } template void iterator_buffer::flush() { - out_ = std::copy_n(data_, this->limit(this->size()), out_); + auto size = this->size(); this->clear(); + out_ = copy_str(data_, data_ + this->limit(size), out_); } } // namespace detail @@ -742,9 +818,13 @@ void basic_memory_buffer::grow(size_t size) { #ifdef FMT_FUZZ if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); #endif + const size_t max_size = std::allocator_traits::max_size(alloc_); size_t old_capacity = this->capacity(); size_t new_capacity = old_capacity + old_capacity / 2; - if (size > new_capacity) new_capacity = size; + 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); @@ -811,6 +891,8 @@ using uint32_or_64_or_128_t = conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, uint32_t, conditional_t() <= 64, uint64_t, uint128_t>>; +template +using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; // 128-bit integer type used internally struct FMT_EXTERN_TEMPLATE_API uint128_wrapper { @@ -883,14 +965,16 @@ template struct FMT_EXTERN_TEMPLATE_API basic_data { // GCC generates slightly better code for pairs than chars. using digit_pair = char[2]; static const digit_pair digits[]; - static const char hex_digits[]; + static constexpr const char hex_digits[] = "0123456789abcdef"; static const char foreground_color[]; static const char background_color[]; static const char reset_color[5]; static const wchar_t wreset_color[5]; static const char signs[]; - static const char left_padding_shifts[5]; - static const char right_padding_shifts[5]; + static constexpr const unsigned prefixes[] = {0, 0, 0x1000000u | '+', + 0x1000000u | ' '}; + static constexpr const char left_padding_shifts[] = {31, 31, 0, 1, 0}; + static constexpr const char right_padding_shifts[] = {0, 31, 0, 1, 0}; // DEPRECATED! These are for ABI compatibility. static const uint32_t zero_or_powers_of_10_32[]; @@ -915,17 +999,7 @@ FMT_EXTERN template struct basic_data; // This is a struct rather than an alias to avoid shadowing warnings in gcc. struct data : basic_data<> {}; -#ifdef FMT_BUILTIN_CLZLL -// Returns the number of decimal digits in n. Leading zeros are not counted -// except for n == 0 in which case count_digits returns 1. -inline int count_digits(uint64_t n) { - // https://github.com/fmtlib/format-benchmark/blob/master/digits10 - auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63); - return t - (n < data::zero_or_powers_of_10_64_new[t]); -} -#else -// Fallback version of count_digits used when __builtin_clz is not available. -inline int count_digits(uint64_t n) { +template FMT_CONSTEXPR int count_digits_fallback(T n) { int count = 1; for (;;) { // Integer division is slow so do it for a group of four digits instead @@ -939,27 +1013,33 @@ inline int count_digits(uint64_t n) { count += 4; } } -#endif - #if FMT_USE_INT128 -inline int count_digits(uint128_t n) { - int count = 1; - for (;;) { - // Integer division is slow so do it for a group of four 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. - if (n < 10) return count; - if (n < 100) return count + 1; - if (n < 1000) return count + 2; - if (n < 10000) return count + 3; - n /= 10000U; - count += 4; - } +FMT_CONSTEXPR inline int count_digits(uint128_t n) { + return count_digits_fallback(n); } #endif +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +FMT_CONSTEXPR20 inline int count_digits(uint64_t n) { + if (is_constant_evaluated()) { + return count_digits_fallback(n); + } +#ifdef FMT_BUILTIN_CLZLL + // https://github.com/fmtlib/format-benchmark/blob/master/digits10 + auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63); + return t - (n < data::zero_or_powers_of_10_64_new[t]); +#else + return count_digits_fallback(n); +#endif +} + // Counts the number of digits in n. BITS = log2(radix). -template inline int count_digits(UInt n) { +template FMT_CONSTEXPR int count_digits(UInt n) { +#ifdef FMT_BUILTIN_CLZ + if (num_bits() == 32) + return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; +#endif int num_digits = 0; do { ++num_digits; @@ -977,16 +1057,12 @@ template <> int count_digits<4>(detail::fallback_uintptr n); # define FMT_ALWAYS_INLINE inline #endif -// To suppress unnecessary security cookie checks -#if FMT_MSC_VER && !FMT_CLANG_VERSION -# define FMT_SAFEBUFFERS __declspec(safebuffers) -#else -# define FMT_SAFEBUFFERS -#endif - #ifdef FMT_BUILTIN_CLZ // Optional version of count_digits for better performance on 32-bit platforms. -inline int count_digits(uint32_t n) { +FMT_CONSTEXPR20 inline int count_digits(uint32_t n) { + if (is_constant_evaluated()) { + return count_digits_fallback(n); + } auto t = bsr2log10(FMT_BUILTIN_CLZ(n | 1) ^ 31); return t - (n < data::zero_or_powers_of_10_32_new[t]); } @@ -998,6 +1074,7 @@ template constexpr int digits10() FMT_NOEXCEPT { template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } +// DEPRECATED! grouping will be merged into thousands_sep. template FMT_API std::string grouping_impl(locale_ref loc); template inline std::string grouping(locale_ref loc) { return grouping_impl(loc); @@ -1046,11 +1123,20 @@ template struct format_decimal_result { // buffer of specified size. The caller must ensure that the buffer is large // enough. template -inline format_decimal_result format_decimal(Char* out, UInt value, - int size) { +FMT_CONSTEXPR20 format_decimal_result format_decimal(Char* out, + UInt value, + int size) { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); out += size; Char* end = out; + if (is_constant_evaluated()) { + while (value >= 10) { + *--out = static_cast('0' + value % 10); + value /= 10; + } + *--out = static_cast('0' + value); + return {out, end}; + } 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 @@ -1079,8 +1165,8 @@ inline format_decimal_result format_decimal(Iterator out, UInt value, } template -inline Char* format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) { +FMT_CONSTEXPR Char* format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) { buffer += num_digits; Char* end = buffer; do { @@ -1159,8 +1245,8 @@ template struct fill_t { size_ = static_cast(size); } - size_t size() const { return size_; } - const Char* data() const { return data_; } + constexpr size_t size() const { return size_; } + constexpr const Char* data() const { return data_; } FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } FMT_CONSTEXPR const Char& operator[](size_t index) const { @@ -1189,6 +1275,7 @@ template struct basic_format_specs { align_t align : 4; sign_t sign : 3; bool alt : 1; // Alternate form ('#'). + bool localized : 1; detail::fill_t fill; constexpr basic_format_specs() @@ -1197,7 +1284,8 @@ template struct basic_format_specs { type(0), align(align::none), sign(sign::none), - alt(false) {} + alt(false), + localized(false) {} }; using format_specs = basic_format_specs; @@ -1327,47 +1415,15 @@ int snprintf_float(T value, int precision, float_specs specs, template T promote_float(T value) { return value; } inline double promote_float(float value) { return static_cast(value); } -template -FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { - switch (spec) { - case 0: - case 'd': - handler.on_dec(); - break; - case 'x': - case 'X': - handler.on_hex(); - break; - case 'b': - case 'B': - handler.on_bin(); - break; - case 'o': - handler.on_oct(); - break; -#ifdef FMT_DEPRECATED_N_SPECIFIER - case 'n': -#endif - case 'L': - handler.on_num(); - break; - case 'c': - handler.on_chr(); - break; - default: - handler.on_error(); - } -} - template FMT_CONSTEXPR float_specs parse_float_type_spec( const basic_format_specs& specs, ErrorHandler&& eh = {}) { auto result = float_specs(); result.showpoint = specs.alt; + result.locale = specs.localized; switch (specs.type) { case 0: result.format = float_format::general; - result.showpoint |= specs.precision > 0; break; case 'G': result.upper = true; @@ -1397,10 +1453,9 @@ FMT_CONSTEXPR float_specs parse_float_type_spec( break; #ifdef FMT_DEPRECATED_N_SPECIFIER case 'n': -#endif - case 'L': result.locale = true; break; +#endif default: eh.on_error("invalid type specifier"); break; @@ -1408,12 +1463,32 @@ FMT_CONSTEXPR float_specs parse_float_type_spec( return result; } +template +FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) { + switch (spec) { + case 0: + case 'd': + case 'x': + case 'X': + case 'b': + case 'B': + case 'o': +#ifdef FMT_DEPRECATED_N_SPECIFIER + case 'n': +#endif + case 'c': + break; + default: + eh.on_error("invalid type specifier"); + break; + } +} + template -FMT_CONSTEXPR void handle_char_specs(const basic_format_specs* specs, +FMT_CONSTEXPR void handle_char_specs(const basic_format_specs& specs, Handler&& handler) { - if (!specs) return handler.on_char(); - if (specs->type && specs->type != 'c') return handler.on_int(); - if (specs->align == align::numeric || specs->sign != sign::none || specs->alt) + if (specs.type && specs.type != 'c') return handler.on_int(); + if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) handler.on_error("invalid format specifier for char"); handler.on_char(); } @@ -1438,22 +1513,6 @@ FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); } -template class int_type_checker : private ErrorHandler { - public: - FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {} - - FMT_CONSTEXPR void on_dec() {} - FMT_CONSTEXPR void on_hex() {} - FMT_CONSTEXPR void on_bin() {} - FMT_CONSTEXPR void on_oct() {} - FMT_CONSTEXPR void on_num() {} - FMT_CONSTEXPR void on_chr() {} - - FMT_CONSTEXPR void on_error() { - ErrorHandler::on_error("invalid type specifier"); - } -}; - template class char_specs_checker : public ErrorHandler { private: @@ -1463,9 +1522,7 @@ class char_specs_checker : public ErrorHandler { FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh) : ErrorHandler(eh), type_(type) {} - FMT_CONSTEXPR void on_int() { - handle_int_type_spec(type_, int_type_checker(*this)); - } + FMT_CONSTEXPR void on_int() { check_int_type_spec(type_, *this); } FMT_CONSTEXPR void on_char() {} }; @@ -1480,10 +1537,13 @@ class cstring_type_checker : public ErrorHandler { }; template -FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { +FMT_NOINLINE FMT_CONSTEXPR OutputIt fill(OutputIt it, size_t n, + const fill_t& fill) { auto fill_size = fill.size(); - if (fill_size == 1) return std::fill_n(it, n, fill[0]); - for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it); + 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); return it; } @@ -1492,37 +1552,47 @@ FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { // width: output display width in (terminal) column positions. template -inline OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, size_t size, - size_t width, F&& f) { +FMT_CONSTEXPR OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, + size_t size, size_t width, F&& f) { static_assert(align == align::left || align == align::right, ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; auto* shifts = align == align::left ? data::left_padding_shifts : data::right_padding_shifts; size_t left_padding = padding >> shifts[specs.align]; + size_t right_padding = padding - left_padding; auto it = reserve(out, size + padding * specs.fill.size()); - it = fill(it, left_padding, specs.fill); + if (left_padding != 0) it = fill(it, left_padding, specs.fill); it = f(it); - it = fill(it, padding - left_padding, specs.fill); + if (right_padding != 0) it = fill(it, right_padding, specs.fill); return base_iterator(out, it); } template -inline OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, size_t size, - F&& f) { +constexpr OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, + size_t size, F&& f) { return write_padded(out, specs, size, size, f); } template OutputIt write_bytes(OutputIt out, string_view bytes, const basic_format_specs& specs) { - using iterator = remove_reference_t; - return write_padded(out, specs, bytes.size(), [bytes](iterator it) { - const char* data = bytes.data(); - return copy_str(data, data + bytes.size(), it); + return write_padded(out, specs, bytes.size(), + [bytes](reserve_iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); +} + +template +constexpr OutputIt write_char(OutputIt out, Char value, + const basic_format_specs& specs) { + return write_padded(out, specs, 1, [=](reserve_iterator it) { + *it++ = value; + return it; }); } @@ -1532,9 +1602,9 @@ template struct write_int_data { size_t size; size_t padding; - write_int_data(int num_digits, string_view prefix, - const basic_format_specs& specs) - : size(prefix.size() + to_unsigned(num_digits)), padding(0) { + FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, + const basic_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) { @@ -1542,7 +1612,7 @@ template struct write_int_data { size = width; } } else if (specs.precision > num_digits) { - size = prefix.size() + to_unsigned(specs.precision); + size = (prefix >> 24) + to_unsigned(specs.precision); padding = to_unsigned(specs.precision - num_digits); } } @@ -1550,171 +1620,176 @@ template struct write_int_data { // Writes an integer in the format // -// where are written by f(it). -template -OutputIt write_int(OutputIt out, int num_digits, string_view prefix, - const basic_format_specs& specs, F f) { +// 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 OutputIt +write_int(OutputIt out, int num_digits, unsigned prefix, + const basic_format_specs& specs, W write_digits) { + // 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); - using iterator = remove_reference_t; - return write_padded(out, specs, data.size, [=](iterator it) { - if (prefix.size() != 0) - it = copy_str(prefix.begin(), prefix.end(), it); - it = std::fill_n(it, data.padding, static_cast('0')); - return f(it); - }); + 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); + }); } -template -OutputIt write(OutputIt out, basic_string_view s, - const basic_format_specs& specs) { +template +bool write_int_localized(OutputIt& out, UInt value, unsigned prefix, + const basic_format_specs& specs, + locale_ref loc) { + static_assert(std::is_same, UInt>::value, ""); + const auto sep_size = 1; + std::string groups = grouping(loc); + if (groups.empty()) return false; + auto sep = thousands_sep(loc); + if (!sep) return false; + int num_digits = count_digits(value); + int size = num_digits, n = num_digits; + std::string::const_iterator group = groups.cbegin(); + while (group != groups.cend() && n > *group && *group > 0 && + *group != max_value()) { + size += sep_size; + n -= *group; + ++group; + } + if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); + char digits[40]; + format_decimal(digits, value, num_digits); + basic_memory_buffer buffer; + if (prefix != 0) ++size; + const auto usize = to_unsigned(size); + buffer.resize(usize); + basic_string_view s(&sep, sep_size); + // Index of a decimal digit with the least significant digit having index 0. + int digit_index = 0; + group = groups.cbegin(); + auto p = buffer.data() + size - 1; + for (int i = num_digits - 1; i > 0; --i) { + *p-- = static_cast(digits[i]); + if (*group <= 0 || ++digit_index % *group != 0 || + *group == max_value()) + continue; + if (group + 1 != groups.cend()) { + digit_index = 0; + ++group; + } + std::uninitialized_copy(s.data(), s.data() + s.size(), + make_checked(p, s.size())); + p -= s.size(); + } + *p-- = static_cast(*digits); + if (prefix != 0) *p = static_cast(prefix); + auto data = buffer.data(); + out = write_padded( + out, specs, usize, usize, [=](reserve_iterator it) { + return copy_str(data, data + size, it); + }); + return true; +} + +FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { + prefix |= prefix != 0 ? value << 8 : value; + prefix += (1u + (value > 0xff ? 1 : 0)) << 24; +} + +template +FMT_CONSTEXPR OutputIt write_int(OutputIt out, T value, + const basic_format_specs& specs, + locale_ref loc) { + auto prefix = 0u; + auto abs_value = static_cast>(value); + if (is_negative(value)) { + prefix = 0x01000000 | '-'; + abs_value = 0 - abs_value; + } else { + prefix = data::prefixes[specs.sign]; + } + auto utype = static_cast(specs.type); + switch (specs.type) { + case 0: + case 'd': { + if (specs.localized && + write_int_localized(out, static_cast>(abs_value), + prefix, specs, loc)) { + return out; + } + 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 'x': + case 'X': { + if (specs.alt) prefix_append(prefix, (utype << 8) | '0'); + bool upper = specs.type != 'x'; + 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 'b': + case 'B': { + if (specs.alt) prefix_append(prefix, (utype << 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 'o': { + int num_digits = count_digits<3>(abs_value); + if (specs.alt && specs.precision <= num_digits && abs_value != 0) { + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + 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); + }); + } +#ifdef FMT_DEPRECATED_N_SPECIFIER + case 'n': + return write_int_localized(out, abs_value, prefix, specs, loc); +#endif + case 'c': + return write_char(out, static_cast(abs_value), specs); + default: + FMT_THROW(format_error("invalid type specifier")); + } + return out; +} + +template +FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view s, + const basic_format_specs& specs) { 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)); auto width = specs.width != 0 - ? count_code_points(basic_string_view(data, size)) + ? compute_width(basic_string_view(data, size)) : 0; - using iterator = remove_reference_t; - return write_padded(out, specs, size, width, [=](iterator it) { - return copy_str(data, data + size, it); - }); + return write_padded(out, specs, size, width, + [=](reserve_iterator it) { + return copy_str(data, data + size, it); + }); } -// The handle_int_type_spec handler that writes an integer. -template struct int_writer { - OutputIt out; - locale_ref locale; - const basic_format_specs& specs; - UInt abs_value; - char prefix[4]; - unsigned prefix_size; - - using iterator = - remove_reference_t(), 0))>; - - string_view get_prefix() const { return string_view(prefix, prefix_size); } - - template - int_writer(OutputIt output, locale_ref loc, Int value, - const basic_format_specs& s) - : out(output), - locale(loc), - specs(s), - abs_value(static_cast(value)), - prefix_size(0) { - static_assert(std::is_same, UInt>::value, ""); - if (is_negative(value)) { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } else if (specs.sign != sign::none && specs.sign != sign::minus) { - prefix[0] = specs.sign == sign::plus ? '+' : ' '; - ++prefix_size; - } - } - - void on_dec() { - auto num_digits = count_digits(abs_value); - out = write_int( - out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) { - return format_decimal(it, abs_value, num_digits).end; - }); - } - - void on_hex() { - if (specs.alt) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = specs.type; - } - int num_digits = count_digits<4>(abs_value); - out = write_int(out, num_digits, get_prefix(), specs, - [this, num_digits](iterator it) { - return format_uint<4, Char>(it, abs_value, num_digits, - specs.type != 'x'); - }); - } - - void on_bin() { - if (specs.alt) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = static_cast(specs.type); - } - int num_digits = count_digits<1>(abs_value); - out = write_int(out, num_digits, get_prefix(), specs, - [this, num_digits](iterator it) { - return format_uint<1, Char>(it, abs_value, num_digits); - }); - } - - void on_oct() { - int num_digits = count_digits<3>(abs_value); - if (specs.alt && specs.precision <= num_digits && abs_value != 0) { - // Octal prefix '0' is counted as a digit, so only add it if precision - // is not greater than the number of digits. - prefix[prefix_size++] = '0'; - } - out = write_int(out, num_digits, get_prefix(), specs, - [this, num_digits](iterator it) { - return format_uint<3, Char>(it, abs_value, num_digits); - }); - } - - enum { sep_size = 1 }; - - void on_num() { - std::string groups = grouping(locale); - if (groups.empty()) return on_dec(); - auto sep = thousands_sep(locale); - if (!sep) return on_dec(); - int num_digits = count_digits(abs_value); - int size = num_digits, n = num_digits; - std::string::const_iterator group = groups.cbegin(); - while (group != groups.cend() && n > *group && *group > 0 && - *group != max_value()) { - size += sep_size; - n -= *group; - ++group; - } - if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); - char digits[40]; - format_decimal(digits, abs_value, num_digits); - basic_memory_buffer buffer; - size += static_cast(prefix_size); - const auto usize = to_unsigned(size); - buffer.resize(usize); - basic_string_view s(&sep, sep_size); - // Index of a decimal digit with the least significant digit having index 0. - int digit_index = 0; - group = groups.cbegin(); - auto p = buffer.data() + size - 1; - for (int i = num_digits - 1; i > 0; --i) { - *p-- = static_cast(digits[i]); - if (*group <= 0 || ++digit_index % *group != 0 || - *group == max_value()) - continue; - if (group + 1 != groups.cend()) { - digit_index = 0; - ++group; - } - std::uninitialized_copy(s.data(), s.data() + s.size(), - make_checked(p, s.size())); - p -= s.size(); - } - *p-- = static_cast(*digits); - if (prefix_size != 0) *p = static_cast('-'); - auto data = buffer.data(); - out = write_padded( - out, specs, usize, usize, - [=](iterator it) { return copy_str(data, data + size, it); }); - } - - void on_chr() { *out++ = static_cast(abs_value); } - - FMT_NORETURN void on_error() { - FMT_THROW(format_error("invalid type specifier")); - } -}; - template OutputIt write_nonfinite(OutputIt out, bool isinf, const basic_format_specs& specs, @@ -1724,8 +1799,7 @@ OutputIt write_nonfinite(OutputIt out, bool isinf, constexpr size_t str_size = 3; auto sign = fspecs.sign; auto size = str_size + (sign ? 1 : 0); - using iterator = remove_reference_t; - return write_padded(out, specs, size, [=](iterator it) { + return write_padded(out, specs, size, [=](reserve_iterator it) { if (sign) *it++ = static_cast(data::signs[sign]); return copy_str(str, str + str_size, it); }); @@ -1768,7 +1842,7 @@ inline Char* write_significand(Char* out, UInt significand, if (integral_size == 1) out[0] = out[1]; else - std::copy_n(out + 1, integral_size, out); + std::uninitialized_copy_n(out + 1, integral_size, out); out[integral_size] = decimal_point; return end; } @@ -1805,7 +1879,7 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, static const Char zero = static_cast('0'); auto sign = fspecs.sign; size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); - using iterator = remove_reference_t; + using iterator = reserve_iterator; int output_exp = fp.exponent + significand_size - 1; auto use_exp_format = [=]() { @@ -1820,7 +1894,8 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, if (use_exp_format()) { int num_zeros = 0; if (fspecs.showpoint) { - num_zeros = (std::max)(fspecs.precision - significand_size, 0); + num_zeros = fspecs.precision - significand_size; + if (num_zeros < 0) num_zeros = 0; size += to_unsigned(num_zeros); } else if (significand_size == 1) { decimal_point = Char(); @@ -1836,7 +1911,7 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, // 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 = std::fill_n(it, num_zeros, zero); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); *it++ = static_cast(exp_char); return write_exponent(output_exp, it); }; @@ -1855,15 +1930,15 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, #endif if (fspecs.showpoint) { if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; - if (num_zeros > 0) size += to_unsigned(num_zeros); + if (num_zeros > 0) size += to_unsigned(num_zeros) + 1; } return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = static_cast(data::signs[sign]); it = write_significand(it, significand, significand_size); - it = std::fill_n(it, fp.exponent, zero); + it = detail::fill_n(it, fp.exponent, zero); if (!fspecs.showpoint) return it; *it++ = decimal_point; - return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; }); } else if (exp > 0) { // 1234e-2 -> 12.34[0+] @@ -1873,7 +1948,7 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, if (sign) *it++ = static_cast(data::signs[sign]); it = write_significand(it, significand, significand_size, exp, decimal_point); - return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; }); } // 1234e-6 -> 0.001234 @@ -1882,13 +1957,14 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, fspecs.precision < num_zeros) { num_zeros = fspecs.precision; } - size += 2 + to_unsigned(num_zeros); + bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; + size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = static_cast(data::signs[sign]); *it++ = zero; - if (num_zeros == 0 && significand_size == 0 && !fspecs.showpoint) return it; + if (!pointy) return it; *it++ = decimal_point; - it = std::fill_n(it, num_zeros, zero); + it = detail::fill_n(it, num_zeros, zero); return write_significand(it, significand, significand_size); }); } @@ -1973,23 +2049,12 @@ inline OutputIt write(OutputIt out, T value) { return write(out, value, basic_format_specs()); } -template -OutputIt write_char(OutputIt out, Char value, - const basic_format_specs& specs) { - using iterator = remove_reference_t; - return write_padded(out, specs, 1, [=](iterator it) { - *it++ = value; - return it; - }); -} - template OutputIt write_ptr(OutputIt out, UIntPtr value, const basic_format_specs* specs) { int num_digits = count_digits<4>(value); auto size = to_unsigned(num_digits) + size_t(2); - using iterator = remove_reference_t; - auto write = [=](iterator it) { + auto write = [=](reserve_iterator it) { *it++ = static_cast('0'); *it++ = static_cast('x'); return format_uint<4, Char>(it, value, num_digits); @@ -2017,24 +2082,23 @@ OutputIt write(OutputIt out, string_view value) { } template -OutputIt write(OutputIt out, basic_string_view value) { +FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view value) { auto it = reserve(out, value.size()); - it = std::copy(value.begin(), value.end(), it); + it = copy_str(value.begin(), value.end(), it); return base_iterator(out, it); } -template -buffer_appender write(buffer_appender out, - basic_string_view value) { - get_container(out).append(value.begin(), value.end()); - return out; +template ::value)> +constexpr OutputIt write(OutputIt out, const T& value) { + return write(out, to_string_view(value)); } template ::value && !std::is_same::value && !std::is_same::value)> -OutputIt write(OutputIt out, T value) { +FMT_CONSTEXPR OutputIt write(OutputIt out, T value) { auto abs_value = static_cast>(value); bool negative = is_negative(value); // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. @@ -2052,20 +2116,33 @@ OutputIt write(OutputIt out, T value) { return base_iterator(out, it); } +// FMT_ENABLE_IF() condition separated to workaround 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, + FMT_ENABLE_IF(check)> +FMT_CONSTEXPR OutputIt write(OutputIt out, T value) { + return write( + out, static_cast::type>(value)); +} + template -OutputIt write(OutputIt out, bool value) { +constexpr OutputIt write(OutputIt out, bool value) { return write(out, string_view(value ? "true" : "false")); } template -OutputIt write(OutputIt out, Char value) { +FMT_CONSTEXPR OutputIt write(OutputIt out, Char value) { auto it = reserve(out, 1); *it++ = value; return base_iterator(out, it); } template -OutputIt write(OutputIt out, const Char* value) { +FMT_CONSTEXPR OutputIt write(OutputIt out, const Char* value) { if (!value) { FMT_THROW(format_error("string pointer is null")); } else { @@ -2125,8 +2202,8 @@ class arg_formatter_base { private: iterator out_; + const format_specs& specs_; locale_ref locale_; - format_specs* specs_; // Attempts to reserve space for n extra characters in the output range. // Returns a pointer to the reserved range or a reference to out_. @@ -2134,16 +2211,6 @@ class arg_formatter_base { return detail::reserve(out_, n); } - using reserve_iterator = remove_reference_t(), 0))>; - - template void write_int(T value, const format_specs& spec) { - using uint_type = uint32_or_64_or_128_t; - int_writer w(out_, locale_, value, spec); - handle_int_type_spec(spec.type, w); - out_ = w.out; - } - void write(char value) { auto&& it = reserve(1); *it++ = value; @@ -2161,44 +2228,44 @@ class arg_formatter_base { void write(wstring_view value) { static_assert(std::is_same::value, ""); auto&& it = reserve(value.size()); - it = std::copy(value.begin(), value.end(), it); + it = copy_str(value.begin(), value.end(), it); } template void write(const Ch* s, size_t size, const format_specs& specs) { - auto width = specs.width != 0 - ? count_code_points(basic_string_view(s, size)) - : 0; - out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) { - return copy_str(s, s + size, it); - }); + auto width = + specs.width != 0 ? compute_width(basic_string_view(s, size)) : 0; + out_ = write_padded(out_, specs, size, width, + [=](reserve_iterator it) { + return copy_str(s, s + size, it); + }); } template - void write(basic_string_view s, const format_specs& specs = {}) { + FMT_CONSTEXPR void write(basic_string_view s, + const format_specs& specs = {}) { out_ = detail::write(out_, s, specs); } void write_pointer(const void* p) { - out_ = write_ptr(out_, to_uintptr(p), specs_); + out_ = write_ptr(out_, to_uintptr(p), &specs_); } struct char_spec_handler : ErrorHandler { arg_formatter_base& formatter; Char value; - char_spec_handler(arg_formatter_base& f, Char val) + constexpr char_spec_handler(arg_formatter_base& f, Char val) : formatter(f), value(val) {} - void on_int() { + FMT_CONSTEXPR void on_int() { // char is only formatted as int if there are specs. - formatter.write_int(static_cast(value), *formatter.specs_); + formatter.out_ = + detail::write_int(formatter.out_, static_cast(value), + formatter.specs_, formatter.locale_); } - void on_char() { - if (formatter.specs_) - formatter.out_ = write_char(formatter.out_, value, *formatter.specs_); - else - formatter.write(value); + FMT_CONSTEXPR void on_char() { + formatter.out_ = write_char(formatter.out_, value, formatter.specs_); } }; @@ -2215,28 +2282,22 @@ class arg_formatter_base { protected: iterator out() { return out_; } - format_specs* specs() { return specs_; } + const format_specs& specs() { return specs_; } - void write(bool value) { - if (specs_) - write(string_view(value ? "true" : "false"), *specs_); - else - out_ = detail::write(out_, value); + FMT_CONSTEXPR void write(bool value) { + write(string_view(value ? "true" : "false"), specs_); } void write(const Char* value) { - if (!value) { + if (value) + write(basic_string_view(value), specs_); + else FMT_THROW(format_error("string pointer is null")); - } else { - auto length = std::char_traits::length(value); - basic_string_view sv(value, length); - specs_ ? write(sv, *specs_) : write(sv); - } } public: - arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc) - : out_(out), locale_(loc), specs_(s) {} + constexpr arg_formatter_base(OutputIt out, const format_specs& s, locale_ref loc) + : out_(out), specs_(s), locale_(loc) {} iterator operator()(monostate) { FMT_ASSERT(false, "invalid argument type"); @@ -2244,54 +2305,44 @@ class arg_formatter_base { } template ::value)> - FMT_INLINE iterator operator()(T value) { - if (specs_) - write_int(value, *specs_); - else - out_ = detail::write(out_, value); - return out_; + FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) { + return out_ = detail::write_int(out_, value, specs_, locale_); } - iterator operator()(Char value) { + FMT_CONSTEXPR iterator operator()(Char value) { handle_char_specs(specs_, char_spec_handler(*this, static_cast(value))); return out_; } - iterator operator()(bool value) { - if (specs_ && specs_->type) return (*this)(value ? 1 : 0); + FMT_CONSTEXPR iterator operator()(bool value) { + if (specs_.type && specs_.type != 's') return (*this)(value ? 1 : 0); write(value != 0); return out_; } template ::value)> iterator operator()(T value) { - auto specs = specs_ ? *specs_ : format_specs(); if (const_check(is_supported_floating_point(value))) - out_ = detail::write(out_, value, specs, locale_); + out_ = detail::write(out_, value, specs_, locale_); else FMT_ASSERT(false, "unsupported float argument type"); return out_; } iterator operator()(const Char* value) { - if (!specs_) return write(value), out_; - handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value)); + handle_cstring_type_spec(specs_.type, cstring_spec_handler(*this, value)); return out_; } - iterator operator()(basic_string_view value) { - if (specs_) { - check_string_type_spec(specs_->type, error_handler()); - write(value, *specs_); - } else { - write(value); - } + FMT_CONSTEXPR iterator operator()(basic_string_view value) { + check_string_type_spec(specs_.type, error_handler()); + write(value, specs_); return out_; } iterator operator()(const void* value) { - if (specs_) check_pointer_type_spec(specs_->type, error_handler()); + check_pointer_type_spec(specs_.type, error_handler()); write_pointer(value); return out_; } @@ -2306,8 +2357,6 @@ class arg_formatter : public arg_formatter_base { using context_type = basic_format_context; context_type& ctx_; - basic_format_parse_context* parse_ctx_; - const Char* ptr_; public: using iterator = typename base::iterator; @@ -2320,21 +2369,14 @@ class arg_formatter : public arg_formatter_base { *specs* contains format specifier information for standard argument types. \endrst */ - explicit arg_formatter( - context_type& ctx, - basic_format_parse_context* parse_ctx = nullptr, - format_specs* specs = nullptr, const Char* ptr = nullptr) - : base(ctx.out(), specs, ctx.locale()), - ctx_(ctx), - parse_ctx_(parse_ctx), - ptr_(ptr) {} + constexpr explicit arg_formatter(context_type& ctx, const format_specs& specs) + : base(ctx.out(), specs, ctx.locale()), ctx_(ctx) {} using base::operator(); - /** Formats an argument of a user-defined type. */ - iterator operator()(typename basic_format_arg::handle handle) { - if (ptr_) advance_to(*parse_ctx_, ptr_); - handle.format(*parse_ctx_, ctx_); + iterator operator()(typename basic_format_arg::handle) { + // User-defined types are handled separately because they require access to + // the parse context. return ctx_.out(); } }; @@ -2448,6 +2490,7 @@ template class specs_setter { FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } FMT_CONSTEXPR void on_hash() { specs_.alt = true; } + FMT_CONSTEXPR void on_localized() { specs_.localized = true; } FMT_CONSTEXPR void on_zero() { specs_.align = align::numeric; @@ -2537,6 +2580,11 @@ template class specs_checker : public Handler { Handler::on_hash(); } + FMT_CONSTEXPR void on_localized() { + checker_.require_numeric_argument(); + Handler::on_localized(); + } + FMT_CONSTEXPR void on_zero() { checker_.require_numeric_argument(); Handler::on_zero(); @@ -2697,14 +2745,10 @@ class dynamic_specs_handler }; template -FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end, - IDHandler&& handler) { +FMT_CONSTEXPR const Char* do_parse_arg_id(const Char* begin, const Char* end, + IDHandler&& handler) { FMT_ASSERT(begin != end, ""); Char c = *begin; - if (c == '}' || c == ':') { - handler(); - return begin; - } if (c >= '0' && c <= '9') { int index = 0; if (c != '0') @@ -2729,6 +2773,16 @@ FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end, return it; } +template +FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_arg_id(const Char* begin, + const Char* end, + IDHandler&& handler) { + Char c = *begin; + if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); + handler(); + return begin; +} + // Adapts SpecHandler to IDHandler API for dynamic width. template struct width_adapter { explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {} @@ -2763,19 +2817,6 @@ template struct precision_adapter { SpecHandler& handler; }; -template -FMT_CONSTEXPR int code_point_length(const Char* begin) { - if (const_check(sizeof(Char) != 1)) return 1; - constexpr char lengths[] = {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, 0}; - int len = lengths[static_cast(*begin) >> 3]; - - // Compute the pointer to the next character early so that the next - // iteration can start working on the next character. Neither Clang - // nor GCC figure out this reordering on their own. - return len + !len; -} - template constexpr bool is_ascii_letter(Char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } @@ -2814,6 +2855,8 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, case '^': align = align::center; break; + default: + break; } if (align != align::none) { if (p != begin) { @@ -2876,8 +2919,14 @@ FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end, // Parses standard format specifiers and sends notifications about parsed // components to handler. template -FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end, - SpecHandler&& handler) { +FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_format_specs( + const Char* begin, const Char* end, SpecHandler&& handler) { + if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) && + *begin != 'L') { + handler.on_type(*begin++); + return begin; + } + if (begin == end) return begin; begin = parse_align(begin, end, handler); @@ -2897,6 +2946,8 @@ FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end, handler.on_space(); ++begin; break; + default: + break; } if (begin == end) return begin; @@ -2917,6 +2968,12 @@ FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end, // Parse precision. if (*begin == '.') { begin = parse_precision(begin, end, handler); + if (begin == end) return begin; + } + + if (*begin == 'L') { + handler.on_localized(); + ++begin; } // Parse type. @@ -3037,10 +3094,9 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs( ParseContext& ctx) { using char_type = typename ParseContext::char_type; using context = buffer_context; - using mapped_type = - conditional_t::value != - type::custom_type, - decltype(arg_mapper().map(std::declval())), T>; + using mapped_type = conditional_t< + detail::mapped_type_constant::value != type::custom_type, + decltype(arg_mapper().map(std::declval())), T>; auto f = conditional_t::value, formatter, detail::fallback_formatter>(); @@ -3057,11 +3113,8 @@ struct format_handler : detail::error_handler { : parse_context(str), context(out, format_args, loc) {} void on_text(const Char* begin, const Char* end) { - auto size = to_unsigned(end - begin); - auto out = context.out(); - auto&& it = reserve(out, size); - it = std::copy_n(begin, size, it); - context.advance_to(out); + auto text = basic_string_view(begin, to_unsigned(end - begin)); + context.advance_to(write(context.out(), text)); } int on_arg_id() { return parse_context.next_arg_id(); } @@ -3088,20 +3141,14 @@ struct format_handler : detail::error_handler { return parse_context.begin(); } auto specs = basic_format_specs(); - if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin)) { - specs.type = static_cast(*begin++); - } else { - using parse_context_t = basic_format_parse_context; - specs_checker> handler( - specs_handler(specs, parse_context, - context), - arg.type()); - begin = parse_format_specs(begin, end, handler); - if (begin == end || *begin != '}') - on_error("missing '}' in format string"); - } - context.advance_to(visit_format_arg( - arg_formatter(context, &parse_context, &specs), arg)); + using parse_context_t = basic_format_parse_context; + specs_checker> handler( + specs_handler(specs, parse_context, context), + arg.type()); + begin = parse_format_specs(begin, end, handler); + if (begin == end || *begin != '}') on_error("missing '}' in format string"); + context.advance_to( + visit_format_arg(arg_formatter(context, specs), arg)); return begin; } }; @@ -3158,7 +3205,8 @@ class format_string_checker { FMT_CONSTEXPR const Char* on_format_specs(int id, const Char* begin, const Char*) { advance_to(context_, begin); - return id < num_args ? parse_funcs_[id](context_) : 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) { @@ -3193,17 +3241,18 @@ FMT_CONSTEXPR basic_string_view compile_string_to_view( return {s.data(), s.size()}; } -#define FMT_STRING_IMPL(s, base) \ - [] { \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_COMPILE_STRING : base { \ - using char_type = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR \ - operator fmt::basic_string_view() const { \ - return fmt::detail::compile_string_to_view(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ +#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_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ + using char_type = fmt::remove_cvref_t; \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR \ + operator fmt::basic_string_view() const { \ + return fmt::detail::compile_string_to_view(s); \ + } \ + }; \ + return FMT_COMPILE_STRING(); \ }() /** @@ -3230,8 +3279,9 @@ void check_format_string(S format_str) { } template