diff --git a/engine/action/dot.hpp b/engine/action/dot.hpp
index cd0e5fb4854..ff260d998c6 100644
--- a/engine/action/dot.hpp
+++ b/engine/action/dot.hpp
@@ -98,7 +98,7 @@ struct dot_t : private noncopyable
void last_tick();
bool channel_interrupt();
- friend void format_to( const dot_t&, fmt::format_context::iterator );
+ friend void sc_format_to( const dot_t&, fmt::format_context::iterator );
void reschedule_tick();
private:
diff --git a/engine/action/sc_action.cpp b/engine/action/sc_action.cpp
index 68834245361..ffe14859883 100644
--- a/engine/action/sc_action.cpp
+++ b/engine/action/sc_action.cpp
@@ -4497,7 +4497,7 @@ double action_t::last_tick_factor(const dot_t* /* d */, timespan_t time_to_tick,
return std::min(1.0, duration / time_to_tick);
}
-void format_to( const action_t& action, fmt::format_context::iterator out )
+void sc_format_to( const action_t& action, fmt::format_context::iterator out )
{
if ( action.sim->log_spell_id )
{
diff --git a/engine/action/sc_action.hpp b/engine/action/sc_action.hpp
index 799214040fa..e1de66525f3 100644
--- a/engine/action/sc_action.hpp
+++ b/engine/action/sc_action.hpp
@@ -1012,7 +1012,7 @@ struct action_t : private noncopyable
return( r == BLOCK_RESULT_BLOCKED || r == BLOCK_RESULT_CRIT_BLOCKED );
}
- friend void format_to( const action_t&, fmt::format_context::iterator );
+ friend void sc_format_to( const action_t&, fmt::format_context::iterator );
};
struct call_action_list_t : public action_t
diff --git a/engine/action/sc_dot.cpp b/engine/action/sc_dot.cpp
index a1079cb02de..1c2e8d864aa 100644
--- a/engine/action/sc_dot.cpp
+++ b/engine/action/sc_dot.cpp
@@ -1078,7 +1078,7 @@ void dot_t::dot_end_event_t::execute()
dot->last_tick();
}
-void format_to( const dot_t& dot, fmt::format_context::iterator out )
+void sc_format_to( const dot_t& dot, fmt::format_context::iterator out )
{
fmt::format_to( out, "Dot {}", dot.name_str );
}
diff --git a/engine/buff/sc_buff.cpp b/engine/buff/sc_buff.cpp
index c53ef07a26c..8687350d0ec 100644
--- a/engine/buff/sc_buff.cpp
+++ b/engine/buff/sc_buff.cpp
@@ -2803,7 +2803,7 @@ void buff_t::update_stack_uptime_array( timespan_t current_time, int old_stacks
uptime_array.add( end_time, end_partial.total_seconds() * mul );
}
-void format_to( const buff_t& buff, fmt::format_context::iterator out )
+void sc_format_to( const buff_t& buff, fmt::format_context::iterator out )
{
if ( buff.sim->log_spell_id )
{
diff --git a/engine/buff/sc_buff.hpp b/engine/buff/sc_buff.hpp
index 5e94c746100..7075140f217 100644
--- a/engine/buff/sc_buff.hpp
+++ b/engine/buff/sc_buff.hpp
@@ -364,7 +364,7 @@ struct buff_t : private noncopyable
virtual buff_t* apply_affecting_conduit( const conduit_data_t& conduit, int effect_num = 1 );
virtual buff_t* apply_affecting_conduit_effect( const conduit_data_t& conduit, size_t effect_num );
- friend void format_to( const buff_t&, fmt::format_context::iterator );
+ friend void sc_format_to( const buff_t&, fmt::format_context::iterator );
private:
void update_trigger_calculations();
void adjust_haste();
diff --git a/engine/class_modules/sc_hunter.cpp b/engine/class_modules/sc_hunter.cpp
index da05e6110c1..9c08005ac42 100644
--- a/engine/class_modules/sc_hunter.cpp
+++ b/engine/class_modules/sc_hunter.cpp
@@ -43,12 +43,12 @@ static void print_affected_by( const action_t* a, const spelleffect_data_t& effe
const spell_data_t& spell = *effect.spell();
const auto& spell_text = a->player->dbc->spell_text( spell.id() );
- fmt::format_to( out, "{} {} is affected by {}", *a->player, *a, spell.name_cstr() );
+ fmt::format_to( std::back_inserter(out), "{} {} is affected by {}", *a->player, *a, spell.name_cstr() );
if ( spell_text.rank() )
- fmt::format_to( out, " (desc={})", spell_text.rank() );
- fmt::format_to( out, " (id={}) effect#{}", spell.id(), effect.spell_effect_num() + 1 );
+ fmt::format_to( std::back_inserter(out), " (desc={})", spell_text.rank() );
+ fmt::format_to( std::back_inserter(out), " (id={}) effect#{}", spell.id(), effect.spell_effect_num() + 1 );
if ( !label.empty() )
- fmt::format_to( out, ": {}", label );
+ fmt::format_to( std::back_inserter(out), ": {}", label );
a -> sim -> print_debug( "{}", util::string_view( out.data(), out.size() ) );
}
diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp
index beceb52cc5c..c8c08ec29ae 100644
--- a/engine/class_modules/sc_mage.cpp
+++ b/engine/class_modules/sc_mage.cpp
@@ -7450,7 +7450,7 @@ class mage_report_t : public player_report_extension_t
if ( !data->active() )
continue;
- auto nonzero = [] ( const char* fmt, double d ) { return d != 0.0 ? fmt::format( fmt, d ) : ""; };
+ auto nonzero = [] ( const char* fmt, double d ) { return d != 0.0 ? fmt::format( fmt::runtime(fmt), d ) : ""; };
auto cells = [ &, total = data->count_total() ] ( double mean, bool util = false )
{
std::string format_str = "
{} | {} | ";
diff --git a/engine/dbc/sc_item_data.cpp b/engine/dbc/sc_item_data.cpp
index cdd64275207..52b679609de 100644
--- a/engine/dbc/sc_item_data.cpp
+++ b/engine/dbc/sc_item_data.cpp
@@ -1273,10 +1273,10 @@ static std::string get_bonus_mod_stat( util::span entr
if ( b.size() > 0 )
{
- fmt::format_to( b, ", " );
+ fmt::format_to( std::back_inserter( b ), ", " );
}
- fmt::format_to( b, "{} ({})",
+ fmt::format_to( std::back_inserter( b ), "{} ({})",
util::stat_type_abbrev( util::translate_item_mod( entries[ i ].value_1 ) ),
entries[ i ].value_2 );
@@ -1407,7 +1407,7 @@ std::string dbc::bonus_ids_str( const dbc_t& dbc )
fields.emplace_back( fmt::format( "mod_to_stat={{ {} }}", item_mod_stat ) );
}
- fmt::format_to( s, "{}\n", fmt::join( fields, ", " ) );
+ fmt::format_to( std::back_inserter(s), "{}\n", fmt::join( fields, ", " ) );
}
return to_string( s );
diff --git a/engine/dbc/sc_spell_info.cpp b/engine/dbc/sc_spell_info.cpp
index 1e66889887a..b7bc8137764 100644
--- a/engine/dbc/sc_spell_info.cpp
+++ b/engine/dbc/sc_spell_info.cpp
@@ -132,24 +132,24 @@ void print_hotfixes( fmt::memory_buffer& buf,
for ( const auto& hotfix : hotfixes )
{
if ( buf.size() > 0 )
- fmt::format_to( buf, ", " );
+ fmt::format_to( std::back_inserter(buf), ", " );
auto entry = map.find( hotfix.field_id );
if ( entry == map.end() )
- fmt::format_to( buf, "Unknown({})", hotfix.field_id );
+ fmt::format_to( std::back_inserter(buf), "Unknown({})", hotfix.field_id );
else
- fmt::format_to( buf, "{}", entry -> second );
+ fmt::format_to( std::back_inserter(buf), "{}", entry -> second );
switch ( hotfix.field_type )
{
case hotfix::UINT:
- fmt::format_to( buf, " ({} -> {})", hotfix.orig_value.u, hotfix.hotfixed_value.u );
+ fmt::format_to( std::back_inserter(buf), " ({} -> {})", hotfix.orig_value.u, hotfix.hotfixed_value.u );
break;
case hotfix::INT:
- fmt::format_to( buf, " ({} -> {})", hotfix.orig_value.i, hotfix.hotfixed_value.i );
+ fmt::format_to( std::back_inserter(buf), " ({} -> {})", hotfix.orig_value.i, hotfix.hotfixed_value.i );
break;
case hotfix::FLOAT:
- fmt::format_to( buf, " ({} -> {})", hotfix.orig_value.f, hotfix.hotfixed_value.f );
+ fmt::format_to( std::back_inserter(buf), " ({} -> {})", hotfix.orig_value.f, hotfix.hotfixed_value.f );
break;
// Don't print out the changed string for now, seems pointless
case hotfix::STRING:
@@ -2123,10 +2123,10 @@ std::string spell_info::to_str( const dbc_t& dbc, const spell_data_t* spell, int
return fmt::format( "{} ({} effect#{})", spell->name_cstr(), spell->id(), effects.front()->index() + 1 );
fmt::memory_buffer s;
- fmt::format_to( s, "{} ({} effects: ", spell->name_cstr(), spell->id() );
+ fmt::format_to( std::back_inserter(s), "{} ({} effects: ", spell->name_cstr(), spell->id() );
for ( size_t i = 0; i < effects.size(); i++ )
- fmt::format_to( s, "{}#{}", i == 0 ? "" : ", ", effects[ i ]->index() + 1 );
- fmt::format_to( s, ")" );
+ fmt::format_to( std::back_inserter(s), "{}#{}", i == 0 ? "" : ", ", effects[ i ]->index() + 1 );
+ fmt::format_to( std::back_inserter(s), ")" );
return to_string( s );
};
diff --git a/engine/interfaces/bcp_api.cpp b/engine/interfaces/bcp_api.cpp
index 4c46542599f..aced545e3bd 100644
--- a/engine/interfaces/bcp_api.cpp
+++ b/engine/interfaces/bcp_api.cpp
@@ -148,11 +148,11 @@ void authorize( sim_t* sim, const std::string& region )
std::string oauth_endpoint;
if ( util::str_compare_ci( region, "eu" ) || util::str_compare_ci( region, "us" ) )
{
- oauth_endpoint = fmt::format( GLOBAL_OAUTH_ENDPOINT_URI, region );
+ oauth_endpoint = fmt::format( fmt::runtime(GLOBAL_OAUTH_ENDPOINT_URI), region );
}
else if ( util::str_compare_ci( region, "kr" ) || util::str_compare_ci( region, "tw" ) )
{
- oauth_endpoint = fmt::format( GLOBAL_OAUTH_ENDPOINT_URI, "apac" );
+ oauth_endpoint = fmt::format( fmt::runtime(GLOBAL_OAUTH_ENDPOINT_URI), "apac" );
}
else if ( util::str_compare_ci( region, "cn" ) )
{
@@ -341,11 +341,11 @@ void download_item( sim_t* sim,
if ( !util::str_compare_ci( region, "cn" ) )
{
- url = fmt::format( GLOBAL_ITEM_ENDPOINT_URI, region, item_id, LOCALES[ region ][ 0 ] );
+ url = fmt::format( fmt::runtime(GLOBAL_ITEM_ENDPOINT_URI), region, item_id, LOCALES[ region ][ 0 ] );
}
else
{
- url = fmt::format( CHINA_ITEM_ENDPOINT_URI, item_id );
+ url = fmt::format( fmt::runtime(CHINA_ITEM_ENDPOINT_URI), item_id );
}
download( sim, d, region, url, caching );
@@ -1077,11 +1077,11 @@ void download_roster( rapidjson::Document& d,
std::string url;
if ( !util::str_compare_ci( region, "cn" ) )
{
- url = fmt::format( GLOBAL_GUILD_ENDPOINT_URI, region, server, name, LOCALES[ region ][ 0 ] );
+ url = fmt::format( fmt::runtime(GLOBAL_GUILD_ENDPOINT_URI), region, server, name, LOCALES[ region ][ 0 ] );
}
else
{
- url = fmt::format( CHINA_GUILD_ENDPOINT_URI, server, name );
+ url = fmt::format( fmt::runtime(CHINA_GUILD_ENDPOINT_URI), server, name );
}
download( sim, d, region, url, caching );
@@ -1162,13 +1162,13 @@ player_t* bcp_api::download_player( sim_t* sim, const std::string& region, const
if (!util::str_compare_ci(region, "cn"))
{
- player.url = fmt::format(GLOBAL_PLAYER_ENDPOINT_URI, region, normalized_server, normalized_name, region, LOCALES[region][0]);
- player.origin = fmt::format(GLOBAL_ORIGIN_URI, LOCALES[region][1], normalized_server, normalized_name);
+ player.url = fmt::format(fmt::runtime(GLOBAL_PLAYER_ENDPOINT_URI), region, normalized_server, normalized_name, region, LOCALES[region][0]);
+ player.origin = fmt::format(fmt::runtime(GLOBAL_ORIGIN_URI), LOCALES[region][1], normalized_server, normalized_name);
}
else
{
- player.url = fmt::format(CHINA_PLAYER_ENDPOINT_URI, normalized_server, normalized_name);
- player.origin = fmt::format(CHINA_ORIGIN_URI, normalized_server, normalized_name);
+ player.url = fmt::format(fmt::runtime(CHINA_PLAYER_ENDPOINT_URI), normalized_server, normalized_name);
+ player.origin = fmt::format(fmt::runtime(CHINA_ORIGIN_URI), normalized_server, normalized_name);
}
#ifdef SC_DEFAULT_APIKEY
diff --git a/engine/item/enchants.cpp b/engine/item/enchants.cpp
index e344db5f81b..b8c7cf7da91 100644
--- a/engine/item/enchants.cpp
+++ b/engine/item/enchants.cpp
@@ -259,7 +259,7 @@ void enchant::initialize_item_enchant( item_t& item, std::vector& s
{
if ( item.player->profession[ profession ] < static_cast( enchant.req_skill_value ) )
{
- item.sim->error( "Player {} attempting to use {) '{}' without {} skill level of {} (has {}), disabling enchant.",
+ item.sim->error( "Player {} attempting to use {} '{}' without {} skill level of {} (has {}), disabling enchant.",
item.player->name(), util::special_effect_source_string( source ), enchant.name,
util::profession_type_string( profession ), enchant.req_skill_value,
item.player->profession[ profession ] );
diff --git a/engine/item/enchants.hpp b/engine/item/enchants.hpp
index 69b5ea20754..fb0efe2092d 100644
--- a/engine/item/enchants.hpp
+++ b/engine/item/enchants.hpp
@@ -12,6 +12,7 @@
#include "util/string_view.hpp"
#include
+#include
class dbc_t;
struct gem_property_data_t;
diff --git a/engine/item/item.cpp b/engine/item/item.cpp
index e280bfc9686..8d91d05c48c 100644
--- a/engine/item/item.cpp
+++ b/engine/item/item.cpp
@@ -367,7 +367,7 @@ std::string item_t::socket_bonus_stats_str() const
return str;
}
-void format_to( const item_t& item, fmt::format_context::iterator out )
+void sc_format_to( const item_t& item, fmt::format_context::iterator out )
{
fmt::format_to( out, "name={} id={}", item.name_str, item.parsed.data.id );
if ( item.slot != SLOT_INVALID )
diff --git a/engine/item/item.hpp b/engine/item/item.hpp
index 2144d36969b..e7a00c0918b 100644
--- a/engine/item/item.hpp
+++ b/engine/item/item.hpp
@@ -217,5 +217,5 @@ struct item_t
const special_effect_t* special_effect( special_effect_source_e source = SPECIAL_EFFECT_SOURCE_NONE, special_effect_e type = SPECIAL_EFFECT_NONE ) const;
const special_effect_t* special_effect_with_name( util::string_view name, special_effect_source_e source = SPECIAL_EFFECT_SOURCE_NONE, special_effect_e type = SPECIAL_EFFECT_NONE ) const;
- friend void format_to( const item_t&, fmt::format_context::iterator );
+ friend void sc_format_to( const item_t&, fmt::format_context::iterator );
};
diff --git a/engine/item/special_effect.cpp b/engine/item/special_effect.cpp
index d95d468d78b..fd446dfc886 100644
--- a/engine/item/special_effect.cpp
+++ b/engine/item/special_effect.cpp
@@ -857,7 +857,7 @@ std::string special_effect_t::name() const
return n;
}
-void format_to( const special_effect_t& se, fmt::format_context::iterator out )
+void sc_format_to( const special_effect_t& se, fmt::format_context::iterator out )
{
fmt::format_to( out, "{}", se.name() );
fmt::format_to( out, " type={}", se.type );
diff --git a/engine/item/special_effect.hpp b/engine/item/special_effect.hpp
index 2fc10197c38..907cefa2ddd 100644
--- a/engine/item/special_effect.hpp
+++ b/engine/item/special_effect.hpp
@@ -156,5 +156,5 @@ struct special_effect_t
timespan_t duration() const;
timespan_t tick_time() const;
- friend void format_to( const special_effect_t&, fmt::format_context::iterator );
+ friend void sc_format_to( const special_effect_t&, fmt::format_context::iterator );
};
diff --git a/engine/lib/fmt/README.md b/engine/lib/fmt/README.md
index 3d544cefe17..e032854bd1b 100644
--- a/engine/lib/fmt/README.md
+++ b/engine/lib/fmt/README.md
@@ -14,5 +14,5 @@ std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
SimC Integration:
-----------------
-* Release 7.1.3 from https://github.com/fmtlib/fmt/releases/tag/7.1.3
+* Release 8.0.0 from https://github.com/fmtlib/fmt/releases/tag/8.0.0
* rename .cc files to .cpp for simpler integration into our build systems.
\ No newline at end of file
diff --git a/engine/lib/fmt/args.h b/engine/lib/fmt/args.h
new file mode 100644
index 00000000000..562e8ab1118
--- /dev/null
+++ b/engine/lib/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/engine/lib/fmt/chrono.h b/engine/lib/fmt/chrono.h
index 1a3b8d5e5cd..c024fd710c0 100644
--- a/engine/lib/fmt/chrono.h
+++ b/engine/lib/fmt/chrono.h
@@ -8,13 +8,13 @@
#ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_
+#include
#include
#include
#include
#include
#include "format.h"
-#include "locale.h"
FMT_BEGIN_NAMESPACE
@@ -282,13 +282,89 @@ To safe_duration_cast(std::chrono::duration from,
#define FMT_NOMACRO
namespace detail {
+template struct null {};
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
+
+inline auto do_write(const std::tm& time, const std::locale& loc, char format,
+ char modifier) -> std::string {
+ auto&& os = std::ostringstream();
+ os.imbue(loc);
+ using iterator = std::ostreambuf_iterator;
+ const auto& facet = std::use_facet>(loc);
+ auto end = facet.put(os, os, ' ', &time, format, modifier);
+ if (end.failed()) FMT_THROW(format_error("failed to format time"));
+ auto str = os.str();
+ if (!detail::is_utf8() || loc == std::locale::classic()) return str;
+ // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
+ // gcc-4.
+#if FMT_MSC_VER != 0 || \
+ (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
+ // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
+ // and newer.
+ using code_unit = wchar_t;
+#else
+ using code_unit = char32_t;
+#endif
+ auto& f = std::use_facet>(loc);
+ auto mb = std::mbstate_t();
+ const char* from_next = nullptr;
+ code_unit* to_next = nullptr;
+ constexpr size_t buf_size = 32;
+ code_unit buf[buf_size] = {};
+ auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf,
+ buf + buf_size, to_next);
+ if (result != std::codecvt_base::ok)
+ FMT_THROW(format_error("failed to format time"));
+ str.clear();
+ for (code_unit* p = buf; p != to_next; ++p) {
+ uint32_t c = static_cast(*p);
+ if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
+ // surrogate pair
+ ++p;
+ if (p == to_next || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
+ FMT_THROW(format_error("failed to format time"));
+ }
+ c = (c << 10) + static_cast(*p) - 0x35fdc00;
+ }
+ if (c < 0x80) {
+ str.push_back(static_cast(c));
+ } else if (c < 0x800) {
+ str.push_back(static_cast(0xc0 | (c >> 6)));
+ str.push_back(static_cast(0x80 | (c & 0x3f)));
+ } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
+ str.push_back(static_cast(0xe0 | (c >> 12)));
+ str.push_back(static_cast(0x80 | ((c & 0xfff) >> 6)));
+ str.push_back(static_cast(0x80 | (c & 0x3f)));
+ } else if (c >= 0x10000 && c <= 0x10ffff) {
+ str.push_back(static_cast(0xf0 | (c >> 18)));
+ str.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12)));
+ str.push_back(static_cast(0x80 | ((c & 0xfff) >> 6)));
+ str.push_back(static_cast(0x80 | (c & 0x3f)));
+ } else {
+ FMT_THROW(format_error("failed to format time"));
+ }
+ }
+ return str;
+}
+
+template
+auto write(OutputIt out, const std::tm& time, const std::locale& loc,
+ char format, char modifier = 0) -> OutputIt {
+ auto str = do_write(time, loc, format, modifier);
+ return std::copy(str.begin(), str.end(), out);
+}
} // namespace detail
-// Thread-safe replacement for std::localtime
+FMT_MODULE_EXPORT_BEGIN
+
+/**
+ 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 +406,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_;
@@ -371,44 +451,84 @@ inline std::tm gmtime(
return gmtime(std::chrono::system_clock::to_time_t(time_point));
}
-namespace detail {
+FMT_BEGIN_DETAIL_NAMESPACE
+
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
-template
-struct formatter, Char>
- : formatter {
+FMT_END_DETAIL_NAMESPACE
+
+template
+struct formatter,
+ Char> : formatter {
+ FMT_CONSTEXPR formatter() {
+ this->specs = {default_specs, sizeof(default_specs) / sizeof(Char)};
+ }
+
+ template
+ 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;
+ if (end != it) this->specs = {it, detail::to_unsigned(end - it)};
+ return end;
+ }
+
template
auto format(std::chrono::time_point val,
FormatContext& ctx) -> decltype(ctx.out()) {
std::tm time = localtime(val);
return formatter::format(time, ctx);
}
+
+ static constexpr Char default_specs[] = {'%', 'Y', '-', '%', 'm', '-',
+ '%', 'd', ' ', '%', 'H', ':',
+ '%', 'M', ':', '%', 'S'};
};
+template
+constexpr Char
+ formatter,
+ Char>::default_specs[];
+
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());
+ // By appending an extra space we can distinguish an empty result that
+ // indicates insufficient buffer size from a guaranteed non-empty result
+ // https://github.com/fmtlib/fmt/issues/2238
+ tm_format.push_back(' ');
+ tm_format.push_back('\0');
basic_memory_buffer buf;
size_t start = buf.size();
for (;;) {
@@ -418,49 +538,40 @@ template struct formatter {
buf.resize(start + count);
break;
}
- if (size >= tm_format.size() * 256) {
- // If the buffer is 256 times larger than the format string, assume
- // that `strftime` gives an empty result. There doesn't seem to be a
- // better way to distinguish the two cases:
- // https://github.com/fmtlib/fmt/issues/367
- break;
- }
const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
- return std::copy(buf.begin(), buf.end(), ctx.out());
+ // Remove the extra space.
+ return std::copy(buf.begin(), buf.end() - 1, ctx.out());
}
- basic_memory_buffer tm_format;
+ basic_string_view specs;
};
-namespace detail {
-template FMT_CONSTEXPR const char* get_units() {
+FMT_BEGIN_DETAIL_NAMESPACE
+
+template FMT_CONSTEXPR inline const char* get_units() {
+ if (std::is_same::value) return "as";
+ if (std::is_same::value) return "fs";
+ if (std::is_same::value) return "ps";
+ if (std::is_same::value) return "ns";
+ if (std::is_same::value) return "µs";
+ if (std::is_same::value) return "ms";
+ if (std::is_same::value) return "cs";
+ if (std::is_same::value) return "ds";
+ if (std::is_same>::value) return "s";
+ if (std::is_same::value) return "das";
+ if (std::is_same::value) return "hs";
+ if (std::is_same::value) return "ks";
+ if (std::is_same::value) return "Ms";
+ if (std::is_same::value) return "Gs";
+ if (std::is_same::value) return "Ts";
+ if (std::is_same::value) return "Ps";
+ if (std::is_same::value) return "Es";
+ if (std::is_same>::value) return "m";
+ if (std::is_same>::value) return "h";
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>() {
- return "m";
-}
-template <> FMT_CONSTEXPR const char* get_units>() {
- return "h";
-}
enum class numeric_system {
standard,
@@ -626,33 +737,50 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
return ptr;
}
-struct chrono_format_checker {
- FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
-
- template 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_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_NORETURN void on_utc_offset() { report_no_date(); }
- FMT_NORETURN void on_tz_name() { report_no_date(); }
+template struct null_chrono_spec_handler {
+ FMT_CONSTEXPR void unsupported() {
+ static_cast(this)->unsupported();
+ }
+ FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
+ FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
+ FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
+ FMT_CONSTEXPR void on_full_month() { unsupported(); }
+ FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_us_date() { unsupported(); }
+ FMT_CONSTEXPR void on_iso_date() { unsupported(); }
+ FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
+ FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
+ FMT_CONSTEXPR void on_iso_time() { unsupported(); }
+ FMT_CONSTEXPR void on_am_pm() { unsupported(); }
+ FMT_CONSTEXPR void on_duration_value() { unsupported(); }
+ FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
+ FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
+ FMT_CONSTEXPR void on_tz_name() { unsupported(); }
+};
+
+struct chrono_format_checker : null_chrono_spec_handler {
+ FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
+
+ template
+ FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+ 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_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() {}
};
template ::value)>
@@ -676,7 +804,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 +883,21 @@ inline std::chrono::duration get_milliseconds(
return std::chrono::duration(static_cast(ms));
}
-template
+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) {
- 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);
+ 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 +915,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 ::value && sizeof(Rep) < sizeof(int),
@@ -886,13 +1027,9 @@ struct chrono_formatter {
void format_localized(const tm& time, char format, char modifier = 0) {
if (isnan(val)) return write_nan();
- auto locale = context.locale().template get();
- auto& facet = std::use_facet>(locale);
- std::basic_ostringstream os;
- os.imbue(locale);
- facet.put(os, os, ' ', &time, format, modifier);
- auto str = os.str();
- std::copy(str.begin(), str.end(), out);
+ const auto& loc = localized ? context.locale().template get()
+ : std::locale::classic();
+ out = detail::write(out, time, loc, format, modifier);
}
void on_text(const char_type* begin, const char_type* end) {
@@ -1005,17 +1142,59 @@ struct chrono_formatter {
out = format_duration_unit(out);
}
};
-} // namespace detail
+
+FMT_END_DETAIL_NAMESPACE
+
+#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
+using weekday = std::chrono::weekday;
+#else
+// A fallback version of weekday.
+class weekday {
+ private:
+ unsigned char value;
+
+ public:
+ weekday() = default;
+ explicit constexpr weekday(unsigned wd) noexcept
+ : value(static_cast(wd != 7 ? wd : 0)) {}
+ constexpr unsigned c_encoding() const noexcept { return value; }
+};
+#endif
+
+// A rudimentary weekday formatter.
+template <> struct formatter {
+ private:
+ bool localized = false;
+
+ public:
+ FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
+ auto begin = ctx.begin(), end = ctx.end();
+ if (begin != end && *begin == 'L') {
+ ++begin;
+ localized = true;
+ }
+ return begin;
+ }
+
+ auto format(weekday wd, format_context& ctx) -> decltype(ctx.out()) {
+ auto time = std::tm();
+ time.tm_wday = static_cast(wd.c_encoding());
+ const auto& loc = localized ? ctx.locale().template get()
+ : std::locale::classic();
+ return detail::write(ctx.out(), time, loc, 'a');
+ }
+};
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;
+ bool localized = false;
+ basic_string_view format_str;
using duration = std::chrono::duration;
struct spec_handler {
@@ -1038,17 +1217,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);
}
};
@@ -1073,13 +1256,15 @@ struct formatter, Char> {
else
handler.on_error("precision not allowed for this argument type");
}
+ if (begin != end && *begin == 'L') {
+ ++begin;
+ localized = true;
+ }
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end};
}
public:
- formatter() : precision(-1) {}
-
FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
@@ -1089,30 +1274,35 @@ 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;
- parse_chrono_format(begin, end, f);
+ f.precision = precision_copy;
+ f.localized = localized;
+ detail::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);
}
};
+FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_CHRONO_H_
diff --git a/engine/lib/fmt/color.h b/engine/lib/fmt/color.h
index 94e3419d1df..3d5490e87f4 100644
--- a/engine/lib/fmt/color.h
+++ b/engine/lib/fmt/color.h
@@ -10,7 +10,15 @@
#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
+FMT_MODULE_EXPORT_BEGIN
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
@@ -198,7 +206,7 @@ struct rgb {
uint8_t b;
};
-namespace detail {
+FMT_BEGIN_DETAIL_NAMESPACE
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@@ -221,9 +229,10 @@ struct color_type {
uint32_t rgb_color;
} value;
};
-} // namespace detail
-// Experimental text formatting support.
+FMT_END_DETAIL_NAMESPACE
+
+/** 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 +269,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 +316,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,19 +354,22 @@ 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;
}
-namespace detail {
+FMT_BEGIN_DETAIL_NAMESPACE
template struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@@ -358,7 +377,7 @@ template struct ansi_color_escape {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
- bool is_background = esc == detail::data::background_color;
+ bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
@@ -411,7 +430,7 @@ template struct ansi_color_escape {
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
- FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
+ FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
return buffer + std::char_traits::length(buffer);
}
@@ -430,13 +449,13 @@ template struct ansi_color_escape {
template
FMT_CONSTEXPR ansi_color_escape make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT {
- return ansi_color_escape(foreground, detail::data::foreground_color);
+ return ansi_color_escape(foreground, "\x1b[38;2;");
}
template
FMT_CONSTEXPR ansi_color_escape make_background_color(
detail::color_type background) FMT_NOEXCEPT {
- return ansi_color_escape(background, detail::data::background_color);
+ return ansi_color_escape(background, "\x1b[48;2;");
}
template
@@ -455,18 +474,17 @@ inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
}
template inline void reset_color(FILE* stream) FMT_NOEXCEPT {
- fputs(detail::data::reset_color, stream);
+ fputs("\x1b[0m", stream);
}
template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
- fputs(detail::data::wreset_color, stream);
+ fputs(L"\x1b[0m", stream);
}
template
inline void reset_color(buffer& buffer) FMT_NOEXCEPT {
- const char* begin = data::reset_color;
- const char* end = begin + sizeof(data::reset_color) - 1;
- buffer.append(begin, end);
+ auto reset_color = string_view("\x1b[0m");
+ buffer.append(reset_color.begin(), reset_color.end());
}
template
@@ -489,10 +507,11 @@ void vformat_to(buffer& buf, const text_style& ts,
auto background = detail::make_background_color(ts.get_background());
buf.append(background.begin(), background.end());
}
- detail::vformat_to(buf, format_str, args);
+ detail::vformat_to(buf, format_str, args, {});
if (has_style) detail::reset_color(buf);
}
-} // namespace detail
+
+FMT_END_DETAIL_NAMESPACE
template >
void vprint(std::FILE* f, const text_style& ts, const S& format,
@@ -523,11 +542,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)>
@@ -559,8 +582,8 @@ inline std::basic_string vformat(
template >
inline std::basic_string format(const text_style& ts, const S& format_str,
const Args&... args) {
- return vformat(ts, to_string_view(format_str),
- fmt::make_args_checked(format_str, args...));
+ return fmt::vformat(ts, to_string_view(format_str),
+ fmt::make_args_checked(format_str, args...));
}
/**
@@ -571,7 +594,7 @@ template format_str,
basic_format_args>> args) {
- decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out));
+ auto&& buf = detail::get_buffer(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
@@ -598,6 +621,7 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
fmt::make_args_checked(format_str, args...));
}
+FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_
diff --git a/engine/lib/fmt/compile.h b/engine/lib/fmt/compile.h
index 3a33b02014c..00000c92e30 100644
--- a/engine/lib/fmt/compile.h
+++ b/engine/lib/fmt/compile.h
@@ -8,360 +8,176 @@
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
-#include
-
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
-// A compile-time string which is compiled into fast formatting code.
-class compiled_string {};
-
-template