diff --git a/python-tweedledum/.SRCINFO b/python-tweedledum/.SRCINFO index c2c960b..162766e 100644 --- a/python-tweedledum/.SRCINFO +++ b/python-tweedledum/.SRCINFO @@ -1,7 +1,7 @@ pkgbase = python-tweedledum pkgdesc = A library for synthesizing and manipulating quantum circuits pkgver = 1.1.1 - pkgrel = 3 + pkgrel = 6 url = https://github.com/boschmitt/tweedledum arch = x86_64 license = MIT @@ -12,8 +12,10 @@ pkgbase = python-tweedledum makedepends = python-setuptools depends = gcc-libs source = tweedledum-1.1.1.tar.gz::https://files.pythonhosted.org/packages/source/t/tweedledum/tweedledum-1.1.1.tar.gz - source = fix-build.patch + source = fix-build.patch::https://github.com/boschmitt/tweedledum/pull/170.patch + source = pybind11.tar.gz::https://github.com/pybind/pybind11/archive/refs/tags/v2.10.4.tar.gz b2sums = 99da09829a70a316fdc582929bfe8ca5d805f0a7a6f049da3951c57c5e4bec24343a1021020e8d00791683ab5c140647d78ee0dde5dac95370b648e0eee44b04 b2sums = 24cb2303a6d3fdac0967c6c925bb49113cc317aa03524b024e9131180bdcd7f6dd812d0e2ebfe157f0331e366b0284c972cc1b2b780960af38365c7d70fa078c + b2sums = 7b2d86e8262581b2cc6dd720b83336206e242ef8ca99b257b01a11141ed8b127d7f35d7d573bc763dd36f2fe8c8ac91766089deb63a76e9c10029c34eec2d6d3 pkgname = python-tweedledum diff --git a/python-tweedledum/PKGBUILD b/python-tweedledum/PKGBUILD index e216667..b9c27ee 100644 --- a/python-tweedledum/PKGBUILD +++ b/python-tweedledum/PKGBUILD @@ -2,7 +2,7 @@ _pkgname=tweedledum pkgname=python-${_pkgname} pkgver=1.1.1 -pkgrel=3 +pkgrel=6 pkgdesc="A library for synthesizing and manipulating quantum circuits" arch=('x86_64') url="https://github.com/boschmitt/tweedledum" @@ -17,17 +17,30 @@ makedepends=( ) source=( "${_pkgname}-${pkgver}.tar.gz::https://files.pythonhosted.org/packages/source/${_pkgname::1}/$_pkgname/$_pkgname-$pkgver.tar.gz" - "fix-build.patch" # https://github.com/boschmitt/tweedledum/pull/170.patch + "fix-build.patch::https://github.com/boschmitt/tweedledum/pull/170.patch" + "pybind11.tar.gz::https://github.com/pybind/pybind11/archive/refs/tags/v2.10.4.tar.gz" ) b2sums=( '99da09829a70a316fdc582929bfe8ca5d805f0a7a6f049da3951c57c5e4bec24343a1021020e8d00791683ab5c140647d78ee0dde5dac95370b648e0eee44b04' '24cb2303a6d3fdac0967c6c925bb49113cc317aa03524b024e9131180bdcd7f6dd812d0e2ebfe157f0331e366b0284c972cc1b2b780960af38365c7d70fa078c' + '7b2d86e8262581b2cc6dd720b83336206e242ef8ca99b257b01a11141ed8b127d7f35d7d573bc763dd36f2fe8c8ac91766089deb63a76e9c10029c34eec2d6d3' ) prepare() { cd "${srcdir}/${_pkgname}-${pkgver}" # Fixes https://github.com/boschmitt/tweedledum/issues/169 patch --forward --strip=1 --input="${srcdir}/fix-build.patch" + + # Update external/pybind11 to a recent version + # https://github.com/boschmitt/tweedledum/issues/181 + cd external + rm -r pybind11 + mv "${srcdir}/pybind11-2.10.4" pybind11 + + # Fix for gcc 13 + cd "${srcdir}/${_pkgname}-${pkgver}" + cd include/tweedledum/IR + sed -i '6s/.*/#include /' Cbit.h } build() { diff --git a/python-tweedledum/fix-build.patch b/python-tweedledum/fix-build.patch deleted file mode 100644 index d2d579c..0000000 --- a/python-tweedledum/fix-build.patch +++ /dev/null @@ -1,113324 +0,0 @@ -From e9c1f7cce159c4ab6b0da38a4997b672852ca083 Mon Sep 17 00:00:00 2001 -From: Bruno Schmitt -Date: Sat, 5 Feb 2022 11:09:47 -0300 -Subject: [PATCH 1/2] Update lorina - ---- - external/CMakeLists.txt | 2 +- - external/lorina/lorina/bench.hpp | 68 ++- - external/lorina/lorina/blif.hpp | 35 +- - external/lorina/lorina/bristol.hpp | 5 +- - .../detail/call_in_topological_order.hpp | 408 ++++++++++++++ - external/lorina/lorina/detail/utils.hpp | 114 +--- - external/lorina/lorina/diagnostics.inc | 10 + - external/lorina/lorina/dimacs.hpp | 2 +- - external/lorina/lorina/genlib.hpp | 97 +++- - external/lorina/lorina/super.hpp | 287 ++++++++++ - external/lorina/lorina/verilog.hpp | 522 ++++++++++++------ - .../bool_function_compiler/integer.py | 69 +++ - 12 files changed, 1289 insertions(+), 330 deletions(-) - create mode 100644 external/lorina/lorina/detail/call_in_topological_order.hpp - create mode 100644 external/lorina/lorina/super.hpp - create mode 100644 python/tweedledum/bool_function_compiler/integer.py - -diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 857355f3..b3742f0b 100644 ---- a/external/CMakeLists.txt -+++ b/external/CMakeLists.txt -@@ -45,7 +45,7 @@ if (NOT TARGET rang) - target_include_directories(rang SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rang) - endif() - --# lorina master branch: Feb.2021 -+# lorina master branch: Feb.2022 - if (NOT TARGET lorina) - add_library(lorina INTERFACE) - target_include_directories(lorina SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lorina) -diff --git a/external/lorina/lorina/bench.hpp b/external/lorina/lorina/bench.hpp -index 544c0a2c..7236e7d7 100644 ---- a/external/lorina/lorina/bench.hpp -+++ b/external/lorina/lorina/bench.hpp -@@ -177,23 +177,46 @@ static std::regex gate_asgn( R"((.*)\s+=\s+(.*))" ); - { - return_code result = return_code::success; - -- const auto dispatch_function = [&]( std::vector inputs, std::string output, std::string type ) -- { -- if ( type == "" ) -- { -- reader.on_assign( inputs.front(), output ); -- } -- else if ( type == "DFF" ) -- { -- reader.on_dff( inputs.front(), output ); -- } -- else -- { -- reader.on_gate( inputs, output, type ); -- } -- }; -- -- detail::call_in_topological_order, std::string, std::string> on_action( dispatch_function ); -+ /* Function signature */ -+ using GateFn = detail::Func< -+ std::vector, -+ std::string, -+ std::string -+ >; -+ -+ /* Parameter maps */ -+ using GateParamMap = detail::ParamPackMap< -+ /* Key */ -+ std::string, -+ /* Params */ -+ std::vector, -+ std::string, -+ std::string -+ >; -+ -+ constexpr static const int GATE_FN{0}; -+ -+ using ParamMaps = detail::ParamPackMapN; -+ using PackedFns = detail::FuncPackN; -+ -+ detail::call_in_topological_order -+ on_action( PackedFns( GateFn( [&]( std::vector inputs, -+ std::string output, -+ std::string type ) -+ { -+ if ( type == "" ) -+ { -+ reader.on_assign( inputs.front(), output ); -+ } -+ else if ( type == "DFF" ) -+ { -+ reader.on_dff( inputs.front(), output ); -+ } -+ else -+ { -+ reader.on_gate( inputs, output, type ); -+ } -+ } ) ) ); - on_action.declare_known( "vdd" ); - on_action.declare_known( "gnd" ); - -@@ -226,7 +249,7 @@ static std::regex gate_asgn( R"((.*)\s+=\s+(.*))" ); - const auto type = detail::trim_copy( m[2] ); - const auto args = detail::trim_copy( m[3] ); - const auto inputs = detail::split( args, "," ); -- on_action.call_deferred( inputs, output, inputs, output, type ); -+ on_action.call_deferred( inputs, { output }, std::make_tuple( inputs, output, type ) ); - return true; - } - -@@ -237,7 +260,7 @@ static std::regex gate_asgn( R"((.*)\s+=\s+(.*))" ); - const auto arg = detail::trim_copy( m[2] ); - reader.on_dff_input( output ); - on_action.declare_known( output ); -- on_action.call_deferred( { arg }, output, { arg }, output, "DFF" ); -+ on_action.call_deferred( { arg }, { output }, std::make_tuple( std::vector{ arg }, output, "DFF" ) ); - return true; - } - -@@ -248,7 +271,7 @@ static std::regex gate_asgn( R"((.*)\s+=\s+(.*))" ); - const auto type = detail::trim_copy( m[2] ); - const auto args = detail::trim_copy( m[3] ); - const auto inputs = detail::split( args, "," ); -- on_action.call_deferred( inputs, output, inputs, output, type ); -+ on_action.call_deferred( inputs, { output }, std::make_tuple( inputs, output, type ) ); - return true; - } - -@@ -257,7 +280,7 @@ static std::regex gate_asgn( R"((.*)\s+=\s+(.*))" ); - { - const auto output = detail::trim_copy( m[1] ); - const auto input = detail::trim_copy( m[2] ); -- on_action.call_deferred( { input }, output, { input }, output, "" ); -+ on_action.call_deferred( { input }, { output }, std::make_tuple( std::vector{ input }, output, "" ) ); - return true; - } - -@@ -273,7 +296,10 @@ static std::regex gate_asgn( R"((.*)\s+=\s+(.*))" ); - /* check dangling objects */ - const auto& deps = on_action.unresolved_dependencies(); - if ( deps.size() > 0 ) -+ { - result = return_code::parse_error; -+ } -+ - for ( const auto& r : deps ) - { - if ( diag ) -diff --git a/external/lorina/lorina/blif.hpp b/external/lorina/lorina/blif.hpp -index 628d6637..be496954 100644 ---- a/external/lorina/lorina/blif.hpp -+++ b/external/lorina/lorina/blif.hpp -@@ -278,7 +278,32 @@ static std::regex end( R"(.end)" ); - { - return_code result = return_code::success; - -- const auto dispatch_function = [&]( std::vector inputs, std::string output, std::vector> tt ) -+ /* Function signature */ -+ using GateFn = detail::Func< -+ std::vector, -+ std::string, -+ std::vector> -+ >; -+ -+ /* Parameter maps */ -+ using GateParamMap = detail::ParamPackMap< -+ /* Key */ -+ std::string, -+ /* Params */ -+ std::vector, -+ std::string, -+ std::vector> -+ >; -+ -+ constexpr static const int GATE_FN{0}; -+ -+ using ParamMaps = detail::ParamPackMapN; -+ using PackedFns = detail::FuncPackN; -+ -+ detail::call_in_topological_order -+ on_action( PackedFns( GateFn( [&]( std::vector inputs, -+ std::string output, -+ std::vector> tt ) - { - /* ignore latches */ - if ( output == "" ) -@@ -288,9 +313,7 @@ static std::regex end( R"(.end)" ); - } - - reader.on_gate( inputs, output, tt ); -- }; -- -- detail::call_in_topological_order, std::string, std::vector>> on_action( dispatch_function ); -+ } ) ) ); - - std::smatch m; - detail::foreach_line_in_file_escape( in, [&]( std::string line ) { -@@ -328,7 +351,7 @@ static std::regex end( R"(.end)" ); - return false; - } ); - -- on_action.call_deferred( args, output, args, output, tt ); -+ on_action.call_deferred( args, { output }, std::tuple( args, output, tt ) ); - - if ( in.eof() ) - { -@@ -394,7 +417,7 @@ static std::regex end( R"(.end)" ); - - on_action.declare_known( output ); - reader.on_latch( input, output, type, control, init_value ); -- on_action.compute_dependencies( output ); -+ on_action.compute_dependencies( { output } ); - - return true; - } -diff --git a/external/lorina/lorina/bristol.hpp b/external/lorina/lorina/bristol.hpp -index c35798d4..f53a6b42 100644 ---- a/external/lorina/lorina/bristol.hpp -+++ b/external/lorina/lorina/bristol.hpp -@@ -44,6 +44,7 @@ - #include - #include - #include -+#include - #include - - namespace lorina -@@ -178,7 +179,7 @@ class bristol_parser - * \param diag An optional diagnostic engine with callback methods for parse errors - * \return Success if parsing has been successful, or parse error if parsing has failed - */ --[[nodiscard]] inline return_code read_bristol( std::istream& is, bristol_reader const& reader, diagnostic_engine* diag = nullptr ) -+[[nodiscard]] inline return_code read_bristol( std::istream& is, const bristol_reader& reader, diagnostic_engine* diag = nullptr ) - { - (void)diag; - return bristol_parser( is, reader ).run(); -@@ -194,7 +195,7 @@ class bristol_parser - * \param diag An optional diagnostic engine with callback methods for parse errors - * \return Success if parsing has been successful, or parse error if parsing has failed - */ --[[nodiscard]] inline return_code read_bristol( std::string const& filename, bristol_reader const& reader, diagnostic_engine* diag = nullptr ) -+[[nodiscard]] inline return_code read_bristol( const std::string& filename, const bristol_reader& reader, diagnostic_engine* diag = nullptr ) - { - std::ifstream in( filename, std::ifstream::in ); - if ( !in.is_open() ) -diff --git a/external/lorina/lorina/detail/call_in_topological_order.hpp b/external/lorina/lorina/detail/call_in_topological_order.hpp -new file mode 100644 -index 00000000..cdf7645e ---- /dev/null -+++ b/external/lorina/lorina/detail/call_in_topological_order.hpp -@@ -0,0 +1,408 @@ -+/* lorina: C++ parsing library -+ * Copyright (C) 2018-2021 EPFL -+ * -+ * Permission is hereby granted, free of charge, to any person -+ * obtaining a copy of this software and associated documentation -+ * files (the "Software"), to deal in the Software without -+ * restriction, including without limitation the rights to use, -+ * copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following -+ * conditions: -+ * -+ * The above copyright notice and this permission notice shall be -+ * included in all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ */ -+ -+/*! \cond PRIVATE */ -+ -+#pragma once -+ -+#include -+ -+namespace lorina -+{ -+ -+namespace detail -+{ -+ -+/* std::apply in C++14 taken from https://stackoverflow.com/a/36656413 */ -+template -+auto apply( Function f, Tuple t, std::index_sequence ) -+{ -+ return f( std::get(t)... ); -+} -+ -+template -+auto apply( Function f, Tuple t ) -+{ -+ static constexpr auto size = std::tuple_size::value; -+ return apply( f, t, std::make_index_sequence{} ); -+} -+ -+/* \brief Parameter pack -+ * -+ * A pack of parameters of different types -+ * -+ * Example: -+ * using PackedParams = ParamPack; -+ * -+ * PackedParams params; -+ * params.set( 10, "a", 10.5f ); -+ */ -+template -+class ParamPack -+{ -+public: -+ using Tuple = std::tuple; -+ -+public: -+ explicit ParamPack() {} -+ -+ explicit ParamPack( Args... params ) -+ : params_( std::make_tuple( params... ) ) -+ {} -+ -+ explicit ParamPack( const std::tuple& tup ) -+ : params_( tup ) -+ {} -+ -+ void set( Args... params ) -+ { -+ params_ = std::make_tuple( params... ); -+ } -+ -+ auto get() const -+ { -+ return params_; -+ } -+ -+private: -+ std::tuple params_; -+}; // ParamPack -+ -+/* \brief Multple parameter packs -+ * -+ * A vector of N parameter packs -+ * -+ * Example: -+ * using PackedParamsA = ParamPack; -+ * using PackedParamsB = ParamPack; -+ * -+ * PackedParamsA params_a( 10, "a", 4 ); -+ * PackedParamsB params_b( 2.0, 3 ); -+ * ParamPackN -+ * params( params_a, params_b ); -+ */ -+template -+class ParamPackN -+{ -+public: -+ explicit ParamPackN() -+ {} -+ -+ explicit ParamPackN( ParamPacks... packs ) -+ : packs_( std::make_tuple( packs... ) ) -+ {} -+ -+ template -+ void set( typename std::tuple_element>::type const& pack ) -+ { -+ static_assert( I < sizeof...( ParamPacks ) ); -+ std::get( packs_ ) = pack; -+ } -+ -+ template -+ auto get() const -+ { -+ static_assert( I < sizeof...( ParamPacks ) ); -+ return std::get( packs_ ); -+ } -+ -+private: -+ std::tuple packs_; -+}; // ParamPackN -+ -+/* \brief Parameter pack map -+ * -+ * A map from a KeyType to a parameter pack -+ * -+ * Example: -+ * ParamPackMap param_map; -+ * param_map["a"] = ParamPack( 10, "a", 10 ); -+ * param_map["b"] = ParamPack( 10, "b", 10 ); -+ */ -+template -+class ParamPackMap -+{ -+public: -+ using ValueType = typename detail::ParamPack::Tuple; -+ -+public: -+ explicit ParamPackMap() {} -+ -+ auto& operator[]( const KeyType& key ) -+ { -+ return map_[key]; -+ } -+ -+ const auto& operator[]( const KeyType& key ) const -+ { -+ return map_[key]; -+ } -+ -+ void insert( const KeyType& key, const std::tuple& tup ) -+ { -+ map_[key] = ParamPack( tup ); -+ } -+ -+ void insert( const KeyType& key, Args... args ) -+ { -+ map_[key] = ParamPack( std::make_tuple( args... ) ); -+ } -+ -+private: -+ std::unordered_map> map_; -+}; -+ -+/* \brief Multiple parameter pack maps -+ * -+ * A vector of N parameter pack maps -+ * -+ * Example: -+ * using StringToStringMap = ParamPackMap; -+ * using StringToIntMap = ParamPackMap; -+ * -+ * ParamPackMapN maps; -+ * maps.get<0>()["a"] = ParamPack( std::string{"a"} ); -+ * maps.get<1>()["b"] = ParamPack( 10 ); -+ */ -+template -+class ParamPackMapN -+{ -+public: -+ explicit ParamPackMapN() {} -+ -+ template -+ auto& get() -+ { -+ static_assert( I < sizeof...( ParamMaps ) ); -+ return std::get( maps_ ); -+ } -+ -+ template -+ auto& get() const -+ { -+ static_assert( I < sizeof...( ParamMaps ) ); -+ return std::get( maps_ ); -+ } -+ -+private: -+ std::tuple maps_; -+}; -+ -+/* \brief A callable function -+ * -+ * Example: -+ * Func f; -+ * f( std::tuple( 10, "a" ) ); -+ * f( std::tuple( 12, "b" ) ); -+ */ -+template -+class Func -+{ -+public: -+ using Tuple = std::tuple; -+ -+public: -+ Func( std::function fn ) -+ : fn_( fn ) -+ {} -+ -+ void operator()( Tuple const& tup ) -+ { -+ detail::apply( fn_, tup ); -+ } -+ -+private: -+ std::function fn_; -+}; // FuncPack -+ -+/* \brief Multiple packed functions -+ * -+ * A vector of N packed functions -+ * -+ * Example: -+ * using Fn_A = Func; -+ * using Fn_B = Func; -+ * -+ * FuncPackN functions( -+ * Fn_A( []( int a, std::string b ){ std::cout << a << ' ' << b << std::endl; } ), -+ * Fn_B( []( double a, int b ){ std::cout << a << ' ' << b << std::endl; } ) -+ * ); -+ * -+ * functions.apply<0>( std::tuple( 10, "foo" ) ); -+ * functions.apply<0>( std::tuple( 12, "bar" ) ); -+ * functions.apply<1>( std::tuple( 3.41, 0 ) ); -+ * functions.apply<1>( std::tuple( 2.58, 1 ) ); -+ */ -+template -+class FuncPackN -+{ -+public: -+ explicit FuncPackN( std::tuple fns ) -+ : fns_( fns ) -+ {} -+ -+ explicit FuncPackN( Fns... fns ) -+ : fns_( std::make_tuple( fns... ) ) -+ {} -+ -+ template -+ void apply( typename std::tuple_element>::type::Tuple const& tup ) -+ { -+ std::get( fns_ )( tup ); -+ } -+ -+private: -+ std::tuple fns_; -+}; // FuncPackN -+ -+template -+struct call_in_topological_order; -+ -+template -+struct call_in_topological_order, ParamPackMapN> -+{ -+public: -+ using dependency_type = std::pair; -+ -+public: -+ explicit call_in_topological_order( FuncPackN fns ) -+ : fns_( fns ) -+ {} -+ -+ void declare_known( const std::string& name ) -+ { -+ known_.emplace( name ); -+ } -+ -+ template -+ void call_deferred( const std::vector& inputs, -+ const std::vector& outputs, -+ const typename std::tuple_element>::type::ValueType& tup ) -+ { -+ /* do we have all inputs */ -+ std::unordered_set unknown; -+ for ( const auto& input : inputs ) -+ { -+ if ( known_.find( input ) != std::end( known_ ) ) -+ continue; -+ -+ const auto it = waits_for_.find( input ); -+ if ( it == std::end( waits_for_ ) || !it->second.empty() ) -+ { -+ unknown.insert( input ); -+ } -+ } -+ -+ /* store the parameters */ -+ for ( const auto& output : outputs ) -+ { -+ param_maps_.template get()[output] = ParamPack( tup ); -+ } -+ -+ if ( !unknown.empty() ) -+ { -+ /* defer computation */ -+ for ( const auto& input : unknown ) -+ { -+ for ( const auto& output : outputs ) -+ { -+ triggers_[input].insert( output ); -+ waits_for_[output].insert( input ); -+ } -+ } -+ return; -+ } -+ -+ /* trigger dependency computation */ -+ for ( const auto& output : outputs ) -+ { -+ compute_dependencies( output ); -+ } -+ } -+ -+ template -+ void compute_dependencies( const std::string& output ) -+ { -+ /* init empty, makes sure nothing is waiting for this output */ -+ waits_for_[output]; -+ -+ std::stack computed; -+ computed.push( output ); -+ -+ while ( !computed.empty() ) -+ { -+ auto const next = computed.top(); -+ computed.pop(); -+ -+ // C++17: std::apply( f, _stored_params[next] ); -+ // detail::apply( f_, stored_params_[next] ); -+ -+ fns_.template apply( param_maps_.template get()[next].get() ); -+ -+ /* activate all the triggers */ -+ for ( const auto& other : triggers_[next] ) -+ { -+ waits_for_[other].erase( next ); -+ if ( waits_for_[other].empty() ) -+ { -+ computed.push( other ); -+ } -+ } -+ triggers_[next].clear(); -+ } -+ } -+ -+ std::vector unresolved_dependencies() -+ { -+ std::vector deps; -+ for ( const auto& item : waits_for_ ) -+ { -+ auto const& key = item.first; -+ auto const& wait_list = item.second; -+ -+ if ( wait_list.empty() ) -+ continue; -+ -+ /* collect all keys that are still waiting for an item */ -+ for ( const auto& entry : wait_list ) -+ { -+ deps.emplace_back( key, entry ); -+ } -+ } -+ return deps; -+ } -+ -+private: -+ FuncPackN fns_; -+ ParamPackMapN param_maps_; -+ -+ std::unordered_set known_; -+ std::unordered_map> waits_for_; -+ std::unordered_map> triggers_; -+}; // call_in_topological_order -+ -+} // namespace detail -+ -+} // namespace lorina -diff --git a/external/lorina/lorina/detail/utils.hpp b/external/lorina/lorina/detail/utils.hpp -index 6876bd05..15e9b7ad 100644 ---- a/external/lorina/lorina/detail/utils.hpp -+++ b/external/lorina/lorina/detail/utils.hpp -@@ -50,6 +50,8 @@ - #include - #endif - -+#include "call_in_topological_order.hpp" -+ - namespace lorina - { - -@@ -100,118 +102,6 @@ inline std::istream& getline( std::istream& is, std::string& t ) - } - } - --/* std::apply in C++14 taken from https://stackoverflow.com/a/36656413 */ --template --auto apply(Function f, Tuple t, std::index_sequence) --{ -- return f(std::get(t) ...); --} -- --template --auto apply(Function f, Tuple t) --{ -- static constexpr auto size = std::tuple_size::value; -- return apply(f, t, std::make_index_sequence{}); --} -- --template --class call_in_topological_order --{ --public: -- explicit call_in_topological_order( std::function f ) -- : f( f ) -- { -- } -- -- void declare_known( const std::string& known ) -- { -- _known.emplace( known ); -- } -- -- void call_deferred( const std::vector& inputs, const std::string& output, Args... params ) -- { -- /* do we have all inputs */ -- std::unordered_set unknown; -- for ( const auto& input : inputs ) -- { -- if ( _known.find( input ) != _known.end() ) -- continue; -- -- auto it = _waits_for.find( input ); -- if ( it == _waits_for.end() || !it->second.empty() ) -- { -- unknown.insert( input ); -- } -- } -- -- std::tuple args = std::make_tuple( params... ); -- _stored_params.emplace( output, args ); -- -- if ( !unknown.empty() ) -- { -- /* defer computation */ -- for ( const auto& input : unknown ) -- { -- _triggers[input].insert( output ); -- _waits_for[output].insert( input ); -- } -- return; -- } -- -- /* trigger dependency computation */ -- compute_dependencies( output ); -- } -- -- void compute_dependencies( const std::string& output ) -- { -- /* init empty, makes sure nothing is waiting for this output */ -- _waits_for[output]; -- std::stack computed; -- computed.push( output ); -- while ( !computed.empty() ) -- { -- auto next = computed.top(); -- computed.pop(); -- -- // C++17: std::apply( f, _stored_params[next] ); -- detail::apply( f, _stored_params[next] ); -- -- for ( const auto& other : _triggers[next] ) -- { -- _waits_for[other].erase( next ); -- if ( _waits_for[other].empty() ) -- { -- computed.push( other ); -- } -- } -- _triggers[next].clear(); -- } -- } -- -- std::vector> unresolved_dependencies() -- { -- std::vector> deps; -- for ( const auto& w : _waits_for ) -- { -- if ( !w.second.empty() ) -- { -- for ( const auto& v : w.second ) -- { -- deps.push_back( std::make_pair( w.first, v ) ); -- } -- } -- } -- return deps; -- } -- --private: -- std::unordered_set _known; -- std::unordered_map> _triggers; -- std::unordered_map> _waits_for; -- std::function f; -- std::unordered_map> _stored_params; --}; /* call_in_topological_order */ -- - template - inline std::string join( const T& t, const std::string& sep ) - { -diff --git a/external/lorina/lorina/diagnostics.inc b/external/lorina/lorina/diagnostics.inc -index 9fbd213c..0c96e2d5 100644 ---- a/external/lorina/lorina/diagnostics.inc -+++ b/external/lorina/lorina/diagnostics.inc -@@ -37,6 +37,11 @@ enum class diag_id - ERR_GENLIB_PIN_PHASE, - ERR_GENLIB_FAILED, - -+ /* SUPER */ -+ ERR_SUPER_INFO, -+ ERR_SUPER_UNEXPECTED_STRUCTURE, -+ ERR_SUPER_GATE, -+ - /* sentinel element to mark the end */ - NUM_STATIC_ERROR_IDS, - }; -@@ -78,4 +83,9 @@ static std::pair const diag_info[] = { - { diagnostic_level::fatal, "unexpected `{}` token (expected `PIN`)" }, - { diagnostic_level::fatal, "unknown PIN phase type `{}` (expected `INV`, `NONINV`, or `UNKNOWN`)" }, - { diagnostic_level::fatal, "parsing failed at token `{}`" }, -+ -+ /* SUPER */ -+ { diagnostic_level::fatal, "line `{}` has unexpected structure (expected only one data)" }, -+ { diagnostic_level::fatal, "line `{}` has a wrong structure (too few fields or exceeding the limit)" }, -+ { diagnostic_level::fatal, "gate `{}` has a wrong number of fanin (zero or exceeding the limit)" }, - }; -diff --git a/external/lorina/lorina/dimacs.hpp b/external/lorina/lorina/dimacs.hpp -index dc44b9a1..e8bd60f3 100644 ---- a/external/lorina/lorina/dimacs.hpp -+++ b/external/lorina/lorina/dimacs.hpp -@@ -99,7 +99,7 @@ class dimacs_reader - namespace dimacs_regex - { - static std::regex problem_spec( R"(^p\s+([cd]nf)\s+([0-9]+)\s+([0-9]+)$)" ); --static std::regex clause( R"(((-?[1-9]+)+ +)+0)" ); -+static std::regex clause( R"(((-?[1-9][0-9]*)+ +)+0)" ); - - } // namespace dimacs_regex - -diff --git a/external/lorina/lorina/genlib.hpp b/external/lorina/lorina/genlib.hpp -index 08fa8e91..32583baf 100644 ---- a/external/lorina/lorina/genlib.hpp -+++ b/external/lorina/lorina/genlib.hpp -@@ -29,6 +29,7 @@ - - \author Heinz Riener - \author Shubham Rai -+ \author Alessandro Tempia Calvino - */ - - #pragma once -@@ -72,12 +73,13 @@ struct pin_spec - class genlib_reader - { - public: -- virtual void on_gate( std::string const& name, std::string const& expression, double area, std::vector const& pins ) const -+ virtual void on_gate( std::string const& name, std::string const& expression, double area, std::vector const& pins, std::string const& output_pin ) const - { - (void)name; - (void)expression; - (void)area; - (void)pins; -+ (void)output_pin; - } - }; /* genlib_reader */ - -@@ -97,8 +99,42 @@ class genlib_parser - bool run() - { - std::string line; -- while ( std::getline( in, line ) ) -+ std::string entry; -+ bool new_line; -+ -+ while ( true ) - { -+ new_line = std::getline( in, line ) ? true : false; -+ -+ if ( new_line == false ) -+ { -+ break; -+ } -+ -+ /* remove whitespaces */ -+ detail::trim( line ); -+ -+ /* skip comments and empty lines */ -+ if ( line[0] != '#' && !line.empty() ) -+ { -+ entry = line + " "; -+ break; -+ } -+ } -+ -+ while ( new_line ) -+ { -+ new_line = std::getline( in, line ) ? true : false; -+ -+ if ( new_line == false ) -+ { -+ if ( !parse_gate_definition( entry ) ) -+ { -+ return false; -+ } -+ break; -+ } -+ - /* remove whitespaces */ - detail::trim( line ); - -@@ -108,9 +144,17 @@ class genlib_parser - continue; - } - -- if ( !parse_gate_definition( line ) ) -+ if ( line.find( "GATE" ) != std::string::npos ) - { -- return false; -+ if ( !parse_gate_definition( entry ) ) -+ { -+ return false; -+ } -+ entry = line + " "; -+ } -+ else -+ { -+ entry.append( line + " " ); - } - } - -@@ -124,13 +168,39 @@ class genlib_parser - std::string const deliminators{" \t\r\n"}; - - std::string token; -+ bool in_equation = false; - - std::vector tokens; - while ( std::getline( ss, token ) ) - { -- std::size_t prev = 0, pos; -+ std::size_t prev = 0, pos, eq_pos; - while ( ( pos = line.find_first_of( deliminators, prev ) ) != std::string::npos ) - { -+ /* equation */ -+ if ( tokens.size() == 3 && !in_equation ) -+ { -+ /* equation is not finished */ -+ if ( token.substr( prev, pos - prev ).find_first_of( ";" ) == std::string::npos ) -+ { -+ in_equation = true; -+ eq_pos = prev; -+ prev = pos + 1; -+ continue; -+ } -+ } -+ if ( in_equation ) -+ { -+ if ( token.substr( prev, pos - prev ).find_first_of( ";" ) != std::string::npos ) -+ { -+ in_equation = false; -+ prev = eq_pos; -+ } -+ else -+ { -+ prev = pos + 1; -+ continue; -+ } -+ } - if ( pos > prev ) - { - tokens.emplace_back( token.substr( prev, pos - prev ) ); -@@ -174,10 +244,12 @@ class genlib_parser - - std::string const& name = tokens[1]; - std::string const& expression = tokens[3].substr( beg + 1, end - beg - 1 ); -+ std::string const& output_name = detail::trim_copy( tokens[3].substr( 0, beg ) ); - double const area = std::stod( tokens[2] ); - - std::vector pins; - -+ bool generic_pin{false}; - uint64_t i{4}; - for ( ; i+8 < tokens.size(); i += 9 ) - { -@@ -192,6 +264,10 @@ class genlib_parser - } - - std::string const& name = tokens[i+1]; -+ if ( tokens[i+1] == "*" ) -+ { -+ generic_pin = true; -+ } - phase_type phase{phase_type::UNKNOWN}; - if ( tokens[i+2] == "INV" ) - { -@@ -222,6 +298,15 @@ class genlib_parser - pins.emplace_back( pin_spec{name,phase,input_load,max_load,rise_block_delay,rise_fanout_delay,fall_block_delay,fall_fanout_delay} ); - } - -+ if ( pins.size() > 1 && generic_pin ) -+ { -+ if ( diag ) -+ { -+ diag->report( diag_id::ERR_GENLIB_PIN ).add_argument( line ); -+ } -+ return false; -+ } -+ - if ( i != tokens.size() ) - { - if ( diag ) -@@ -231,7 +316,7 @@ class genlib_parser - return false; - } - -- reader.on_gate( name, expression, area, pins ); -+ reader.on_gate( name, expression, area, pins, output_name ); - return true; - } - -diff --git a/external/lorina/lorina/super.hpp b/external/lorina/lorina/super.hpp -new file mode 100644 -index 00000000..7cd0f44e ---- /dev/null -+++ b/external/lorina/lorina/super.hpp -@@ -0,0 +1,287 @@ -+/* lorina: C++ parsing library -+ * Copyright (C) 2018-2021 EPFL -+ * -+ * Permission is hereby granted, free of charge, to any person -+ * obtaining a copy of this software and associated documentation -+ * files (the "Software"), to deal in the Software without -+ * restriction, including without limitation the rights to use, -+ * copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following -+ * conditions: -+ * -+ * The above copyright notice and this permission notice shall be -+ * included in all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ */ -+ -+/*! -+ \file super.hpp -+ \brief Implements SUPER parser for files generated by ABC -+ -+ \author Alessandro Tempia Calvino -+ \author Shubham Rai -+*/ -+ -+#pragma once -+ -+#include "common.hpp" -+#include "detail/utils.hpp" -+#include "diagnostics.hpp" -+#include -+#include -+#include -+#include -+#include -+ -+namespace lorina -+{ -+ -+/*! \brief A reader visitor for a super format. -+ * -+ * Callbacks for the super format. -+ */ -+class super_reader -+{ -+public: -+ virtual void on_super_info( std::string const& genlib_name, uint32_t max_num_vars, uint32_t max_supergates, uint32_t num_lines ) const -+ { -+ (void) genlib_name; -+ (void) max_num_vars; -+ (void) max_supergates; -+ (void) num_lines; -+ } -+ -+ virtual void on_supergate( std::string const& name, bool const& is_super, std::vector const& fanins_id ) const -+ { -+ (void)name; -+ (void)is_super; -+ (void)fanins_id; -+ } -+}; /* super_reader */ -+ -+/*! \brief Parse for the SUPER format. -+ * -+ */ -+class super_parser -+{ -+public: -+ explicit super_parser( std::istream& in, super_reader const& reader, diagnostic_engine* diag ) -+ : in( in ) -+ , reader( reader ) -+ , diag( diag ) -+ { -+ } -+ -+public: -+ bool run() -+ { -+ std::string line; -+ uint32_t info_lines = 0u; -+ uint32_t max_num_vars = 0u; -+ -+ std::vector info_vec; -+ -+ while ( std::getline( in, line ) ) -+ { -+ /* remove whitespaces */ -+ detail::trim( line ); -+ -+ /* skip comments and empty lines */ -+ if ( line[0] == '#' || line[0] == '\0' || line.empty() ) -+ { -+ continue; -+ } -+ -+ if ( info_lines < 4 ) -+ { -+ if ( !parse_file_info( line, info_vec ) ) -+ { -+ return false; -+ } -+ if ( info_lines == 1 ) -+ { -+ max_num_vars = std::stod( info_vec[1] ); -+ } -+ ++info_lines; -+ } -+ else -+ { -+ if ( !parse_gate_definition( line, max_num_vars ) ) -+ { -+ return false; -+ } -+ } -+ } -+ -+ return true; -+ } -+ -+private: -+ bool parse_file_info( std::string const& line, std::vector& info_vec ) -+ { -+ std::stringstream ss( line ); -+ std::string token; -+ -+ std::vector tokens; -+ -+ while ( std::getline( ss, token ) ) -+ { -+ tokens.emplace_back( token ); -+ info_vec.emplace_back( token ); -+ } -+ -+ if ( line.find_first_of( " " ) != std::string::npos ) -+ { -+ if ( diag ) -+ { -+ diag->report( diag_id::ERR_SUPER_INFO ).add_argument( line ); -+ } -+ return false; -+ } -+ -+ if ( info_vec.size() == 4 ) -+ { -+ reader.on_super_info( info_vec[0], std::stoi( info_vec[1] ), std::stoi( info_vec[2] ), std::stoi( info_vec[3] ) ); -+ } -+ -+ return true; -+ } -+ -+ bool parse_gate_definition( std::string const& line, uint32_t const& max_num_vars ) -+ { -+ std::stringstream ss( line ); -+ std::string const deliminators{ " \t\r\n" }; -+ -+ std::string token; -+ std::vector tokens; -+ -+ std::string name; -+ std::vector fanins_id; -+ -+ while ( std::getline( ss, token ) ) -+ { -+ std::size_t prev = 0, pos; -+ while ( ( pos = line.find_first_of( deliminators, prev ) ) != std::string::npos ) -+ { -+ if ( pos > prev ) -+ { -+ tokens.emplace_back( token.substr( prev, pos - prev ) ); -+ } -+ prev = pos + 1; -+ } -+ -+ if ( prev < line.length() ) -+ { -+ tokens.emplace_back( token.substr( prev, std::string::npos ) ); -+ } -+ } -+ -+ if ( tokens.size() < 2 || tokens.size() > max_num_vars + 2 ) -+ { -+ if ( diag ) -+ { -+ diag->report( diag_id::ERR_SUPER_UNEXPECTED_STRUCTURE ).add_argument( line ); -+ } -+ return false; -+ } -+ -+ bool is_super = false; -+ uint64_t i{2}; -+ if ( tokens[0] == "*" ) -+ { -+ is_super = true; -+ name = tokens[1]; -+ } -+ else -+ { -+ name = tokens[0]; -+ i = 1u; -+ } -+ -+ for ( auto j = i; j < tokens.size(); ++j ) -+ { -+ fanins_id.emplace_back( std::stod( tokens[j] ) ); -+ } -+ -+ if ( fanins_id.size() == 0 ) -+ { -+ if ( diag ) -+ { -+ diag->report( diag_id::ERR_SUPER_GATE ).add_argument( line ); -+ } -+ return false; -+ } -+ -+ reader.on_supergate( name, is_super, fanins_id ); -+ return true; -+ } -+ -+protected: -+ std::istream& in; -+ super_reader const& reader; -+ diagnostic_engine* diag; -+}; /* super_parser */ -+ -+/*! \brief Reader function for the SUPER format. -+ * -+ * Reads SUPER format from a stream and invokes a callback -+ * method for each parsed primitive and each detected parse error. -+ * -+ * \param in Input stream -+ * \param reader SUPER reader with callback methods invoked for parsed primitives -+ * \param diag An optional diagnostic engine with callback methods for parse errors -+ * \return Success if parsing has been successful, or parse error if parsing has failed -+ */ -+[[nodiscard]] inline return_code read_super( std::istream& in, const super_reader& reader, diagnostic_engine* diag = nullptr ) -+{ -+ super_parser parser( in, reader, diag ); -+ auto result = parser.run(); -+ if ( !result ) -+ { -+ return return_code::parse_error; -+ } -+ else -+ { -+ return return_code::success; -+ } -+} -+ -+/*! \brief Reader function for the SUPER format. -+ * -+ * Reads SUPER format from a .super file generated by ABC and invokes a callback -+ * method for each parsed primitive and each detected parse error. -+ * -+ * \param filename Name of the file -+ * \param reader SUPER reader with callback methods invoked for parsed primitives -+ * \param diag An optional diagnostic engine with callback methods for parse errors -+ * \return Success if parsing has been successful, or parse error if parsing has failed -+ */ -+[[nodiscard]] inline return_code read_super( const std::string& filename, const super_reader& reader, diagnostic_engine* diag = nullptr ) -+{ -+ std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); -+ if ( !in.is_open() ) -+ { -+ if ( diag ) -+ { -+ diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); -+ } -+ return return_code::parse_error; -+ } -+ else -+ { -+ auto const ret = read_super( in, reader, diag ); -+ in.close(); -+ return ret; -+ } -+} -+ -+} // namespace lorina -diff --git a/external/lorina/lorina/verilog.hpp b/external/lorina/lorina/verilog.hpp -index 5212a2d4..c9cd92ec 100644 ---- a/external/lorina/lorina/verilog.hpp -+++ b/external/lorina/lorina/verilog.hpp -@@ -35,9 +35,9 @@ - #pragma once - - #include "common.hpp" --#include "diagnostics.hpp" --#include "detail/utils.hpp" - #include "detail/tokenizer.hpp" -+#include "detail/utils.hpp" -+#include "diagnostics.hpp" - #include "verilog_regex.hpp" - #include - #include -@@ -128,7 +128,7 @@ class verilog_reader - * signal in inst_name. - */ - virtual void on_module_instantiation( std::string const& module_name, std::vector const& params, std::string const& inst_name, -- std::vector> const& args ) const -+ std::vector> const& args ) const - { - (void)module_name; - (void)params; -@@ -323,7 +323,8 @@ class verilog_pretty_printer : public verilog_reader - - void on_inputs( const std::vector& inputs, std::string const& size = "" ) const override - { -- if ( inputs.size() == 0 ) return; -+ if ( inputs.size() == 0 ) -+ return; - _os << "input "; - if ( size != "" ) - _os << "[" << size << "] "; -@@ -339,7 +340,8 @@ class verilog_pretty_printer : public verilog_reader - - void on_outputs( const std::vector& outputs, std::string const& size = "" ) const override - { -- if ( outputs.size() == 0 ) return; -+ if ( outputs.size() == 0 ) -+ return; - _os << "output "; - if ( size != "" ) - _os << "[" << size << "] "; -@@ -355,7 +357,8 @@ class verilog_pretty_printer : public verilog_reader - - void on_wires( const std::vector& wires, std::string const& size = "" ) const override - { -- if ( wires.size() == 0 ) return; -+ if ( wires.size() == 0 ) -+ return; - _os << "wire "; - if ( size != "" ) - _os << "[" << size << "] "; -@@ -377,11 +380,11 @@ class verilog_pretty_printer : public verilog_reader - void on_assign( const std::string& lhs, const std::pair& rhs ) const override - { - const std::string param = rhs.second ? fmt::format( "~{}", rhs.first ) : rhs.first; -- _os << fmt::format("assign {} = {} ;\n", lhs, param ); -+ _os << fmt::format( "assign {} = {} ;\n", lhs, param ); - } - - virtual void on_module_instantiation( std::string const& module_name, std::vector const& params, std::string const& inst_name, -- std::vector> const& args ) const override -+ std::vector> const& args ) const override - { - _os << module_name << " "; - if ( params.size() > 0u ) -@@ -412,42 +415,42 @@ class verilog_pretty_printer : public verilog_reader - { - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; -- _os << fmt::format("assign {} = {} & {} ;\n", lhs, p1, p2 ); -+ _os << fmt::format( "assign {} = {} & {} ;\n", lhs, p1, p2 ); - } - - void on_nand( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override - { - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; -- _os << fmt::format("assign {} = ~({} & {}) ;\n", lhs, p1, p2 ); -+ _os << fmt::format( "assign {} = ~({} & {}) ;\n", lhs, p1, p2 ); - } - - void on_or( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override - { - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; -- _os << fmt::format("assign {} = {} | {} ;\n", lhs, p1, p2 ); -+ _os << fmt::format( "assign {} = {} | {} ;\n", lhs, p1, p2 ); - } - - void on_nor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override - { - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; -- _os << fmt::format("assign {} = ~({} | {}) ;\n", lhs, p1, p2 ); -+ _os << fmt::format( "assign {} = ~({} | {}) ;\n", lhs, p1, p2 ); - } - - void on_xor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override - { - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; -- _os << fmt::format("assign {} = {} ^ {} ;\n", lhs, p1, p2 ); -+ _os << fmt::format( "assign {} = {} ^ {} ;\n", lhs, p1, p2 ); - } - - void on_xnor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override - { - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; -- _os << fmt::format("assign {} = ~({} ^ {}) ;\n", lhs, p1, p2 ); -+ _os << fmt::format( "assign {} = ~({} ^ {}) ;\n", lhs, p1, p2 ); - } - - void on_and3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override -@@ -455,7 +458,7 @@ class verilog_pretty_printer : public verilog_reader - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; - const std::string p3 = op3.second ? fmt::format( "~{}", op3.first ) : op3.first; -- _os << fmt::format("assign {} = {} & {} & {} ;\n", lhs, p1, p2, p3 ); -+ _os << fmt::format( "assign {} = {} & {} & {} ;\n", lhs, p1, p2, p3 ); - } - - void on_or3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override -@@ -463,7 +466,7 @@ class verilog_pretty_printer : public verilog_reader - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; - const std::string p3 = op3.second ? fmt::format( "~{}", op3.first ) : op3.first; -- _os << fmt::format("assign {} = {} | {} | {} ;\n", lhs, p1, p2, p3 ); -+ _os << fmt::format( "assign {} = {} | {} | {} ;\n", lhs, p1, p2, p3 ); - } - - void on_xor3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override -@@ -471,7 +474,7 @@ class verilog_pretty_printer : public verilog_reader - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; - const std::string p3 = op3.second ? fmt::format( "~{}", op3.first ) : op3.first; -- _os << fmt::format("assign {} = {} ^ {} ^ {} ;\n", lhs, p1, p2, p3 ); -+ _os << fmt::format( "assign {} = {} ^ {} ^ {} ;\n", lhs, p1, p2, p3 ); - } - - void on_maj3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override -@@ -479,12 +482,13 @@ class verilog_pretty_printer : public verilog_reader - const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; - const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; - const std::string p3 = op3.second ? fmt::format( "~{}", op3.first ) : op3.first; -- _os << fmt::format("assign {0} = ( {1} & {2} ) | ( {1} & {3} ) | ( {2} & {3} ) ;\n", lhs, p1, p2, p3 ); -+ _os << fmt::format( "assign {0} = ( {1} & {2} ) | ( {1} & {3} ) | ( {2} & {3} ) ;\n", lhs, p1, p2, p3 ); - } - - void on_endmodule() const override - { -- _os << "endmodule\n" << std::endl; -+ _os << "endmodule\n" -+ << std::endl; - } - - void on_comment( const std::string& comment ) const override -@@ -493,8 +497,7 @@ class verilog_pretty_printer : public verilog_reader - } - - std::ostream& _os; /*!< Output stream */ --}; /* verilog_pretty_printer */ -- -+}; /* verilog_pretty_printer */ - - /*! \brief A writer for a simplistic VERILOG format. - * -@@ -509,8 +512,9 @@ class verilog_writer - * \param os Output stream - */ - explicit verilog_writer( std::ostream& os ) -- : _os( os ) -- {} -+ : _os( os ) -+ { -+ } - - /*! \brief Callback method for writing begin of a module declaration. - * -@@ -542,7 +546,7 @@ class verilog_writer - */ - virtual void on_input( uint32_t width, std::string const& name ) const - { -- _os << fmt::format( " input [{}:0] {} ;\n", width - 1 , name ); -+ _os << fmt::format( " input [{}:0] {} ;\n", width - 1, name ); - } - - /*! \brief Callback method for writing multiple single 1-bit input. -@@ -580,7 +584,7 @@ class verilog_writer - */ - virtual void on_output( uint32_t width, std::string const& name ) const - { -- _os << fmt::format( " output [{}:0] {} ;\n", width - 1 , name ); -+ _os << fmt::format( " output [{}:0] {} ;\n", width - 1, name ); - } - - /*! \brief Callback method for writing multiple single 1-bit output. -@@ -618,7 +622,7 @@ class verilog_writer - */ - virtual void on_wire( uint32_t width, std::string const& name ) const - { -- _os << fmt::format( " wire [{}:0] {} ;\n", width - 1 , name ); -+ _os << fmt::format( " wire [{}:0] {} ;\n", width - 1, name ); - } - - /*! \brief Callback method for writing multiple single 1-bit wire. -@@ -654,7 +658,7 @@ class verilog_writer - * \param args List of arguments (first: I/O pin name, second: wire name) - */ - virtual void on_module_instantiation( std::string const& module_name, std::vector const& params, std::string const& inst_name, -- std::vector> const& args ) const -+ std::vector> const& args ) const - { - _os << fmt::format( " {} ", module_name ); - if ( params.size() > 0u ) -@@ -666,10 +670,10 @@ class verilog_writer - if ( i + 1 < params.size() ) - _os << ", "; - } -- _os << ")"; -+ _os << ") "; - } - -- _os << fmt::format( " {}( ", inst_name ); -+ _os << fmt::format( "{}( ", inst_name ); - for ( auto i = 0u; i < args.size(); ++i ) - { - _os << fmt::format( ".{} ({})", args.at( i ).first, args.at( i ).second ); -@@ -685,7 +689,7 @@ class verilog_writer - * \param ins List of input signals - * \param op Operator - */ -- virtual void on_assign( std::string const& out, std::vector> const& ins, std::string const& op ) const -+ virtual void on_assign( std::string const& out, std::vector> const& ins, std::string const& op ) const - { - std::string args; - -@@ -705,7 +709,7 @@ class verilog_writer - * \param out Output signal - * \param ins List of three input signals - */ -- virtual void on_assign_maj3( std::string const& out, std::vector> const& ins ) const -+ virtual void on_assign_maj3( std::string const& out, std::vector> const& ins ) const - { - assert( ins.size() == 3u ); - _os << fmt::format( " assign {0} = ( {1}{2} & {3}{4} ) | ( {1}{2} & {5}{6} ) | ( {3}{4} & {5}{6} ) ;\n", -@@ -729,7 +733,7 @@ class verilog_writer - * \param out Output signal - * \param in An input signal - */ -- virtual void on_assign_po( std::string const& out, std::pair const& in ) const -+ virtual void on_assign_po( std::string const& out, std::pair const& in ) const - { - _os << fmt::format( " assign {} = {}{} ;\n", - out, -@@ -738,7 +742,7 @@ class verilog_writer - - protected: - std::ostream& _os; /*!< Output stream */ --}; /* verilog_writer */ -+}; /* verilog_writer */ - - /*! \brief Simple parser for VERILOG format. - * -@@ -761,71 +765,86 @@ class verilog_parser - * \param reader A verilog reader - * \param diag A diagnostic engine - */ -- verilog_parser( std::istream& in, const verilog_reader& reader, diagnostic_engine* diag = nullptr ) -+ verilog_parser( std::istream& in, -+ const verilog_reader& reader, -+ diagnostic_engine* diag = nullptr ) - : tok( in ) - , reader( reader ) - , diag( diag ) -- , on_action([&]( std::vector> inputs, std::string output, std::string type ){ -- if ( type == "assign" ) -- { -- assert( inputs.size() == 1u ); -- reader.on_assign( output, inputs[0] ); -- } -- else if ( type == "and2" ) -- { -- assert( inputs.size() == 2u ); -- reader.on_and( output, inputs[0], inputs[1] ); -- } -- else if ( type == "nand2" ) -- { -- assert( inputs.size() == 2u ); -- reader.on_nand( output, inputs[0], inputs[1] ); -- } -- else if ( type == "or2" ) -- { -- assert( inputs.size() == 2u ); -- reader.on_or( output, inputs[0], inputs[1] ); -- } -- else if ( type == "nor2" ) -- { -- assert( inputs.size() == 2u ); -- reader.on_nor( output, inputs[0], inputs[1] ); -- } -- else if ( type == "xor2" ) -- { -- assert( inputs.size() == 2u ); -- reader.on_xor( output, inputs[0], inputs[1] ); -- } -- else if ( type == "xnor2" ) -- { -- assert( inputs.size() == 2u ); -- reader.on_xnor( output, inputs[0], inputs[1] ); -- } -- else if ( type == "and3" ) -- { -- assert( inputs.size() == 3u ); -- reader.on_and3( output, inputs[0], inputs[1], inputs[2] ); -- } -- else if ( type == "or3" ) -- { -- assert( inputs.size() == 3u ); -- reader.on_or3( output, inputs[0], inputs[1], inputs[2] ); -- } -- else if ( type == "xor3" ) -- { -- assert( inputs.size() == 3u ); -- reader.on_xor3( output, inputs[0], inputs[1], inputs[2] ); -- } -- else if ( type == "maj3" ) -- { -- assert( inputs.size() == 3u ); -- reader.on_maj3( output, inputs[0], inputs[1], inputs[2] ); -- } -- else -- { -- assert( false ); -- } -- }) -+ , on_action( PackedFns( GateFn( [&]( const std::vector>& inputs, -+ const std::string output, -+ const std::string type ) -+ { -+ if ( type == "assign" ) -+ { -+ assert( inputs.size() == 1u ); -+ reader.on_assign( output, inputs[0] ); -+ } -+ else if ( type == "and2" ) -+ { -+ assert( inputs.size() == 2u ); -+ reader.on_and( output, inputs[0], inputs[1] ); -+ } -+ else if ( type == "nand2" ) -+ { -+ assert( inputs.size() == 2u ); -+ reader.on_nand( output, inputs[0], inputs[1] ); -+ } -+ else if ( type == "or2" ) -+ { -+ assert( inputs.size() == 2u ); -+ reader.on_or( output, inputs[0], inputs[1] ); -+ } -+ else if ( type == "nor2" ) -+ { -+ assert( inputs.size() == 2u ); -+ reader.on_nor( output, inputs[0], inputs[1] ); -+ } -+ else if ( type == "xor2" ) -+ { -+ assert( inputs.size() == 2u ); -+ reader.on_xor( output, inputs[0], inputs[1] ); -+ } -+ else if ( type == "xnor2" ) -+ { -+ assert( inputs.size() == 2u ); -+ reader.on_xnor( output, inputs[0], inputs[1] ); -+ } -+ else if ( type == "and3" ) -+ { -+ assert( inputs.size() == 3u ); -+ reader.on_and3( output, inputs[0], inputs[1], inputs[2] ); -+ } -+ else if ( type == "or3" ) -+ { -+ assert( inputs.size() == 3u ); -+ reader.on_or3( output, inputs[0], inputs[1], inputs[2] ); -+ } -+ else if ( type == "xor3" ) -+ { -+ assert( inputs.size() == 3u ); -+ reader.on_xor3( output, inputs[0], inputs[1], inputs[2] ); -+ } -+ else if ( type == "maj3" ) -+ { -+ assert( inputs.size() == 3u ); -+ reader.on_maj3( output, inputs[0], inputs[1], inputs[2] ); -+ } -+ else -+ { -+ assert( false && "unknown gate function" ); -+ std::cerr << "unknown gate function" << std::endl; -+ std::abort(); -+ } -+ } ), -+ ModuleInstFn( [&]( const std::string module_name, -+ const std::vector& params, -+ const std::string instance_name, -+ const std::vector>& pin_to_pin ) -+ { -+ reader.on_module_instantiation( module_name, params, instance_name, pin_to_pin ); -+ } ) -+ ) ) - { - on_action.declare_known( "0" ); - on_action.declare_known( "1" ); -@@ -860,7 +879,7 @@ class verilog_parser - reader.on_comment( token ); - } - /* keep parsing if token is empty or if in the middle or at the end of a comment */ -- } while ( (token == "" && result == detail::tokenizer_return_code::valid) || -+ } while ( ( token == "" && result == detail::tokenizer_return_code::valid ) || - tok.get_comment_mode() || - result == detail::tokenizer_return_code::comment ); - -@@ -875,18 +894,21 @@ class verilog_parser - bool parse_signal_name() - { - valid = get_token( token ); // name -- if ( !valid || token == "[" ) return false; -+ if ( !valid || token == "[" ) -+ return false; - auto const name = token; - - valid = get_token( token ); - if ( token == "[" ) - { - valid = get_token( token ); // size -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - auto const size = token; - - valid = get_token( token ); // size -- if ( !valid && token != "]" ) return false; -+ if ( !valid && token != "]" ) -+ return false; - token = name + "[" + size + "]"; - - return true; -@@ -930,7 +952,8 @@ class verilog_parser - do - { - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - - if ( token == "input" ) - { -@@ -1001,7 +1024,8 @@ class verilog_parser - } - - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - } - else - { -@@ -1016,7 +1040,8 @@ class verilog_parser - } - - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - } - } - -@@ -1031,8 +1056,8 @@ class verilog_parser - if ( diag ) - { - diag->report( diag_id::WRN_UNRESOLVED_DEPENDENCY ) -- .add_argument( r.first ) -- .add_argument( r.second ); -+ .add_argument( r.first ) -+ .add_argument( r.second ); - } - } - -@@ -1054,15 +1079,18 @@ class verilog_parser - - bool parse_module_header() - { -- if ( token != "module" ) return false; -+ if ( token != "module" ) -+ return false; - - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - - module_name = token; - - valid = get_token( token ); -- if ( !valid || token != "(" ) return false; -+ if ( !valid || token != "(" ) -+ return false; - - std::vector inouts; - do -@@ -1072,11 +1100,13 @@ class verilog_parser - inouts.emplace_back( token ); - - valid = get_token( token ); // , or ) -- if ( !valid || (token != "," && token != ")") ) return false; -+ if ( !valid || ( token != "," && token != ")" ) ) -+ return false; - } while ( valid && token != ")" ); - - valid = get_token( token ); -- if ( !valid || token != ";" ) return false; -+ if ( !valid || token != ";" ) -+ return false; - - /* callback */ - reader.on_module_header( module_name, inouts ); -@@ -1087,7 +1117,8 @@ class verilog_parser - bool parse_inputs() - { - std::vector inputs; -- if ( token != "input" ) return false; -+ if ( token != "input" ) -+ return false; - - std::string size = ""; - if ( !parse_signal_name() && token == "[" ) -@@ -1095,7 +1126,8 @@ class verilog_parser - do - { - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - - if ( token != "]" ) - size += token; -@@ -1110,7 +1142,8 @@ class verilog_parser - { - valid = get_token( token ); - -- if ( !valid || (token != "," && token != ";") ) return false; -+ if ( !valid || ( token != "," && token != ";" ) ) -+ return false; - - if ( token == ";" ) - break; -@@ -1126,15 +1159,21 @@ class verilog_parser - modules[module_name].inputs = inputs; - - for ( const auto& i : inputs ) -+ { - on_action.declare_known( i ); -+ } - - if ( std::smatch m; std::regex_match( size, m, verilog_regex::const_size_range ) ) - { - const auto a = std::stoul( m[1].str() ); - const auto b = std::stoul( m[2].str() ); - for ( auto j = std::min( a, b ); j <= std::max( a, b ); ++j ) -+ { - for ( const auto& i : inputs ) -+ { - on_action.declare_known( fmt::format( "{}[{}]", i, j ) ); -+ } -+ } - } - - return true; -@@ -1143,7 +1182,8 @@ class verilog_parser - bool parse_outputs() - { - std::vector outputs; -- if ( token != "output" ) return false; -+ if ( token != "output" ) -+ return false; - - std::string size = ""; - if ( !parse_signal_name() && token == "[" ) -@@ -1151,7 +1191,8 @@ class verilog_parser - do - { - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - - if ( token != "]" ) - size += token; -@@ -1166,7 +1207,8 @@ class verilog_parser - { - valid = get_token( token ); - -- if ( !valid || (token != "," && token != ";") ) return false; -+ if ( !valid || ( token != "," && token != ";" ) ) -+ return false; - - if ( token == ";" ) - break; -@@ -1187,7 +1229,8 @@ class verilog_parser - bool parse_wires() - { - std::vector wires; -- if ( token != "wire" ) return false; -+ if ( token != "wire" ) -+ return false; - - std::string size = ""; - if ( !parse_signal_name() && token == "[" ) -@@ -1195,7 +1238,8 @@ class verilog_parser - do - { - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - - if ( token != "]" ) - size += token; -@@ -1210,7 +1254,8 @@ class verilog_parser - { - valid = get_token( token ); - -- if ( !valid || (token != "," && token != ";") ) return false; -+ if ( !valid || ( token != "," && token != ";" ) ) -+ return false; - - if ( token == ";" ) - break; -@@ -1229,21 +1274,26 @@ class verilog_parser - - bool parse_parameter() - { -- if ( token != "parameter" ) return false; -+ if ( token != "parameter" ) -+ return false; - - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - auto const name = token; - - valid = get_token( token ); -- if ( !valid || (token != "=" ) ) return false; -+ if ( !valid || ( token != "=" ) ) -+ return false; - - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - auto const value = token; - - valid = get_token( token ); -- if ( !valid || (token != ";") ) return false; -+ if ( !valid || ( token != ";" ) ) -+ return false; - - /* callback */ - reader.on_parameter( name, value ); -@@ -1253,14 +1303,16 @@ class verilog_parser - - bool parse_assign() - { -- if ( token != "assign" ) return false; -+ if ( token != "assign" ) -+ return false; - - if ( !parse_signal_name() ) - return false; - - const std::string lhs = token; - valid = get_token( token ); -- if ( !valid || token != "=" ) return false; -+ if ( !valid || token != "=" ) -+ return false; - - /* expression */ - bool success = parse_rhs_expression( lhs ); -@@ -1269,12 +1321,13 @@ class verilog_parser - if ( diag ) - { - diag->report( diag_id::ERR_VERILOG_ASSIGNMENT_RHS ) -- .add_argument( lhs ); -+ .add_argument( lhs ); - } - return false; - } - -- if ( token != ";" ) return false; -+ if ( token != ";" ) -+ return false; - return true; - } - -@@ -1289,7 +1342,7 @@ class verilog_parser - if ( diag ) - { - diag->report( diag_id::ERR_VERILOG_MODULE_INSTANTIATION_UNDECLARED_MODULE ) -- .add_argument( module_name ); -+ .add_argument( module_name ); - } - return false; - } -@@ -1298,41 +1351,49 @@ class verilog_parser - auto const& info = modules[module_name]; - - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - - std::vector params; - if ( token == "#" ) - { -- valid = get_token( token ); // ( -- if ( !valid || token != "(" ) return false; -+ valid = get_token( token ); // ( -+ if ( !valid || token != "(" ) -+ return false; - - do - { - valid = get_token( token ); // param -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - params.emplace_back( token ); - - valid = get_token( token ); // , -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - } while ( valid && token == "," ); - -- if ( !valid || token != ")" ) return false; -+ if ( !valid || token != ")" ) -+ return false; - - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - } - - std::string const inst_name = token; // name of instantiation - - valid = get_token( token ); -- if ( !valid || token != "(" ) return false; -+ if ( !valid || token != "(" ) -+ return false; - -- std::vector> args; -+ std::vector> args; - do - { - valid = get_token( token ); - -- if ( !valid ) return false; // refers to signal -+ if ( !valid ) -+ return false; // refers to signal - std::string const arg0{token}; - - /* check if a signal with this name exists in the module declaration */ -@@ -1342,40 +1403,67 @@ class verilog_parser - if ( diag ) - { - diag->report( diag_id::ERR_VERILOG_MODULE_INSTANTIATION_UNDECLARED_PIN ) -- .add_argument( arg0.substr( 1, arg0.size() ) ) -- .add_argument( module_name ); -+ .add_argument( arg0.substr( 1, arg0.size() ) ) -+ .add_argument( module_name ); - } - - success = false; - } - - valid = get_token( token ); -- if ( !valid || token != "(" ) return false; // ( -+ if ( !valid || token != "(" ) -+ return false; // ( - - valid = get_token( token ); -- if ( !valid ) return false; // signal name -+ if ( !valid ) -+ return false; // signal name - auto const arg1 = token; - - valid = get_token( token ); -- if ( !valid || token != ")" ) return false; // ) -+ if ( !valid || token != ")" ) -+ return false; // ) - - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - - args.emplace_back( std::make_pair( arg0, arg1 ) ); -- if ( std::find( std::begin( info.outputs ), std::end( info.outputs ), arg0 ) == std::end( info.outputs ) ) -- { -- on_action.declare_known( arg1 ); -- } - } while ( token == "," ); - -- if ( !valid || token != ")" ) return false; -+ if ( !valid || token != ")" ) -+ return false; - - valid = get_token( token ); -- if ( !valid || token != ";" ) return false; -+ if ( !valid || token != ";" ) -+ return false; -+ -+ std::vector inputs; -+ for ( const auto& input : modules[module_name].inputs ) -+ { -+ for ( const auto& a : args ) -+ { -+ if ( a.first.substr( 1, a.first.length() - 1 ) == input ) -+ { -+ inputs.emplace_back( a.second ); -+ } -+ } -+ } -+ -+ std::vector outputs; -+ for ( const auto& output : modules[module_name].outputs ) -+ { -+ for ( const auto& a : args ) -+ { -+ if ( a.first.substr( 1, a.first.length() - 1 ) == output ) -+ { -+ outputs.emplace_back( a.second ); -+ } -+ } -+ } - - /* callback */ -- reader.on_module_instantiation( module_name, params, inst_name, args ); -+ on_action.call_deferred( inputs, outputs, -+ std::make_tuple( module_name, params, inst_name, args ) ); - - return success; - } -@@ -1386,9 +1474,11 @@ class verilog_parser - do - { - valid = get_token( token ); -- if ( !valid ) return false; -+ if ( !valid ) -+ return false; - -- if ( token == ";" || token == "assign" || token == "endmodule" ) break; -+ if ( token == ";" || token == "assign" || token == "endmodule" ) -+ break; - s.append( token ); - } while ( token != ";" && token != "assign" && token != "endmodule" ); - -@@ -1396,26 +1486,38 @@ class verilog_parser - if ( std::regex_match( s, sm, verilog_regex::immediate_assign ) ) - { - assert( sm.size() == 3u ); -- on_action.call_deferred( { sm[2] }, lhs, {{sm[2], sm[1] == "~"}}, lhs, "assign" ); -+ std::vector> args{{sm[2], sm[1] == "~"}}; -+ -+ on_action.call_deferred( /* dependencies */ { sm[2] }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "assign" ) -+ ); - } - else if ( std::regex_match( s, sm, verilog_regex::binary_expression ) ) - { - assert( sm.size() == 6u ); -- std::pair arg0 = {sm[2], sm[1] == "~"}; -- std::pair arg1 = {sm[5], sm[4] == "~"}; -+ std::pair arg0 = {sm[2], sm[1] == "~"}; -+ std::pair arg1 = {sm[5], sm[4] == "~"}; -+ std::vector> args{arg0, arg1}; -+ - auto op = sm[3]; - - if ( op == "&" ) - { -- on_action.call_deferred( { arg0.first, arg1.first }, lhs, {arg0, arg1}, lhs, "and2" ); -+ on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "and2" ) -+ ); - } - else if ( op == "|" ) - { -- on_action.call_deferred( { arg0.first, arg1.first }, lhs, {arg0, arg1}, lhs, "or2" ); -+ on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "or2" ) -+ ); - } - else if ( op == "^" ) - { -- on_action.call_deferred( { arg0.first, arg1.first }, lhs, {arg0, arg1}, lhs, "xor2" ); -+ on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "xor2" ) -+ ); - } - else - { -@@ -1425,20 +1527,28 @@ class verilog_parser - else if ( std::regex_match( s, sm, verilog_regex::negated_binary_expression ) ) - { - assert( sm.size() == 6u ); -- std::pair arg0 = {sm[2], sm[1] == "~"}; -- std::pair arg1 = {sm[5], sm[4] == "~"}; -+ std::pair arg0 = {sm[2], sm[1] == "~"}; -+ std::pair arg1 = {sm[5], sm[4] == "~"}; -+ std::vector> args{arg0, arg1}; -+ - auto op = sm[3]; - if ( op == "&" ) - { -- on_action.call_deferred( { arg0.first, arg1.first }, lhs, {arg0, arg1}, lhs, "nand2" ); -+ on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "nand2" ) -+ ); - } - else if ( op == "|" ) - { -- on_action.call_deferred( { arg0.first, arg1.first }, lhs, {arg0, arg1}, lhs, "nor2" ); -+ on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "nor2" ) -+ ); - } - else if ( op == "^" ) - { -- on_action.call_deferred( { arg0.first, arg1.first }, lhs, {arg0, arg1}, lhs, "xnor2" ); -+ on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "xnor2" ) -+ ); - } - else - { -@@ -1448,9 +1558,11 @@ class verilog_parser - else if ( std::regex_match( s, sm, verilog_regex::ternary_expression ) ) - { - assert( sm.size() == 9u ); -- std::pair arg0 = {sm[2], sm[1] == "~"}; -- std::pair arg1 = {sm[5], sm[4] == "~"}; -- std::pair arg2 = {sm[8], sm[7] == "~"}; -+ std::pair arg0 = {sm[2], sm[1] == "~"}; -+ std::pair arg1 = {sm[5], sm[4] == "~"}; -+ std::pair arg2 = {sm[8], sm[7] == "~"}; -+ std::vector> args{arg0, arg1, arg2}; -+ - auto op = sm[3]; - if ( sm[6] != op ) - { -@@ -1459,15 +1571,21 @@ class verilog_parser - - if ( op == "&" ) - { -- on_action.call_deferred( { arg0.first, arg1.first, arg2.first }, lhs, {arg0, arg1, arg2}, lhs, "and3" ); -+ on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first, arg2.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "and3" ) -+ ); - } - else if ( op == "|" ) - { -- on_action.call_deferred( { arg0.first, arg1.first, arg2.first }, lhs, {arg0, arg1, arg2}, lhs, "or3" ); -+ on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first, arg2.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "or3" ) -+ ); - } - else if ( op == "^" ) - { -- on_action.call_deferred( { arg0.first, arg1.first, arg2.first }, lhs, {arg0, arg1, arg2}, lhs, "xor3" ); -+ on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first, arg2.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "xor3" ) -+ ); - } - else - { -@@ -1477,21 +1595,24 @@ class verilog_parser - else if ( std::regex_match( s, sm, verilog_regex::maj3_expression ) ) - { - assert( sm.size() == 13u ); -- std::pair a0 = {sm[2], sm[1] == "~"}; -- std::pair b0 = {sm[4], sm[3] == "~"}; -- std::pair a1 = {sm[6], sm[5] == "~"}; -- std::pair c0 = {sm[8], sm[7] == "~"}; -- std::pair b1 = {sm[10], sm[9] == "~"}; -- std::pair c1 = {sm[12], sm[11] == "~"}; -- -- if ( a0 != a1 || b0 != b1 || c0 != c1 ) return false; -+ std::pair a0 = {sm[2], sm[1] == "~"}; -+ std::pair b0 = {sm[4], sm[3] == "~"}; -+ std::pair a1 = {sm[6], sm[5] == "~"}; -+ std::pair c0 = {sm[8], sm[7] == "~"}; -+ std::pair b1 = {sm[10], sm[9] == "~"}; -+ std::pair c1 = {sm[12], sm[11] == "~"}; -+ -+ if ( a0 != a1 || b0 != b1 || c0 != c1 ) -+ return false; - -- std::vector> args; -+ std::vector> args; - args.push_back( a0 ); - args.push_back( b0 ); - args.push_back( c0 ); - -- on_action.call_deferred( { a0.first, b0.first, c0.first }, lhs, args, lhs, "maj3" ); -+ on_action.call_deferred( /* dependencies */ { a0.first, b0.first, c0.first }, { lhs }, -+ /* gate-function params */ std::make_tuple( args, lhs, "maj3" ) -+ ); - } - else - { -@@ -1501,6 +1622,45 @@ class verilog_parser - return true; - } - -+private: -+ /* Function signatures */ -+ using GateFn = detail::Func< -+ std::vector>, -+ std::string, -+ std::string -+ >; -+ using ModuleInstFn = detail::Func< -+ std::string, -+ std::vector, -+ std::string, -+ std::vector> -+ >; -+ -+ /* Parameter maps */ -+ using GateParamMap = detail::ParamPackMap< -+ /* Key */ -+ std::string, -+ /* Params */ -+ std::vector>, -+ std::string, -+ std::string -+ >; -+ using ModuleInstParamMap = detail::ParamPackMap< -+ /* Key */ -+ std::string, -+ /* Param */ -+ std::string, -+ std::vector, -+ std::string, -+ std::vector> -+ >; -+ -+ constexpr static const int GATE_FN{0}; -+ constexpr static const int MODULE_INST_FN{1}; -+ -+ using ParamMaps = detail::ParamPackMapN; -+ using PackedFns = detail::FuncPackN; -+ - private: - detail::tokenizer tok; - const verilog_reader& reader; -@@ -1512,7 +1672,7 @@ class verilog_parser - - bool valid = false; - -- detail::call_in_topological_order>, std::string, std::string> on_action; -+ detail::call_in_topological_order on_action; - std::unordered_map modules; - }; /* verilog_parser */ - -diff --git a/python/tweedledum/bool_function_compiler/integer.py b/python/tweedledum/bool_function_compiler/integer.py -new file mode 100644 -index 00000000..a4c25d17 ---- /dev/null -+++ b/python/tweedledum/bool_function_compiler/integer.py -@@ -0,0 +1,69 @@ -+#------------------------------------------------------------------------------- -+# Part of Tweedledum Project. This file is distributed under the MIT License. -+# See accompanying file /LICENSE for details. -+#------------------------------------------------------------------------------- -+from .bitvec import BitVec -+ -+class Integer(BitVec): -+ """Class to represent an integer on top of a bit vector -+ """ -+ -+ def __init__(self, length, value = 0): -+ """ -+ Parameters -+ ---------- -+ length : int -+ length of the vector -+ value : int or str (bit string), optional -+ the initial value (default = 0) -+ """ -+ super.__init__(length, value) -+ -+ def __len__(self): -+ return self._length -+ -+ def __bool__(self): -+ if self._length > 1: -+ raise TypeError("[BitVec] A BitVec of length bigger than one " -+ "cannot be converted to a Boolean value") -+ return bool(self._value) -+ -+ def __invert__ (self): -+ return BitVec(self._length, ~self._value & (1 << self._length) - 1) -+ -+ def __and__(self, other): -+ if isinstance(other, BitVec): -+ if (self._length != other._length): -+ raise TypeError("[BitVec] __and__ operation: length mismatch") -+ return BitVec(self._length, self._value & other._value) -+ raise TypeError("[BitVec] __and__ operation: type mismatch") -+ -+ def __or__(self, other): -+ if isinstance(other, BitVec): -+ if (self._length != other._length): -+ raise TypeError("[BitVec] __or__ operation: length mismatch") -+ return BitVec(self._length, self._value | other._value) -+ raise TypeError("[BitVec] __or__ operation: type mismatch") -+ -+ def __xor__(self, other): -+ if isinstance(other, BitVec): -+ if (self._length != other._length): -+ raise TypeError("[BitVec] __xor__ operation: length mismatch") -+ return BitVec(self._length, self._value ^ other._value) -+ raise TypeError("[BitVec] __xor__ operation: type mismatch") -+ -+ def __eq__(self, other): -+ if isinstance(other, BitVec): -+ return BitVec(1, self._length == other._length and self._value == other._value) -+ raise TypeError("__eq__ operation: type mismatch") -+ -+ def __ne__(self, other): -+ if isinstance(other, BitVec): -+ return BitVec(1, self._length != other._length or self._value != other._value) -+ raise TypeError("__ne__ operation: type mismatch") -+ -+ def __repr__(self): -+ return f"BitVec({self._length}, '{str(self)}')" -+ -+ def __str__(self): -+ return "{:0{}b}".format(self._value, self._length) - -From ba449e49249f788f3b2410dea342e84e70d4daf7 Mon Sep 17 00:00:00 2001 -From: Bruno Schmitt -Date: Sun, 6 Feb 2022 20:48:23 -0300 -Subject: [PATCH 2/2] Update fmt - ---- - CMakeLists.txt | 2 +- - external/fmt/CMakeLists.txt | 152 +- - external/fmt/CONTRIBUTING.md | 3 + - external/fmt/ChangeLog.rst | 1516 +- - external/fmt/README.rst | 274 +- - external/fmt/doc/CMakeLists.txt | 8 +- - external/fmt/doc/api.rst | 321 +- - external/fmt/doc/basic-bootstrap/layout.html | 4 +- - external/fmt/doc/build.py | 118 +- - external/fmt/doc/fmt.less | 5 + - external/fmt/doc/index.rst | 72 +- - external/fmt/doc/syntax.rst | 115 +- - external/fmt/doc/usage.rst | 71 +- - external/fmt/include/fmt/args.h | 234 + - external/fmt/include/fmt/chrono.h | 1410 +- - external/fmt/include/fmt/color.h | 210 +- - external/fmt/include/fmt/compile.h | 819 +- - external/fmt/include/fmt/core.h | 2790 ++- - external/fmt/include/fmt/format-inl.h | 2582 +- - external/fmt/include/fmt/format.h | 4493 ++-- - external/fmt/include/fmt/locale.h | 80 +- - external/fmt/include/fmt/os.h | 297 +- - external/fmt/include/fmt/ostream.h | 106 +- - external/fmt/include/fmt/posix.h | 2 - - external/fmt/include/fmt/printf.h | 486 +- - external/fmt/include/fmt/ranges.h | 719 +- - external/fmt/include/fmt/xchar.h | 236 + - external/fmt/src/fmt.cc | 99 + - external/fmt/src/format.cc | 71 +- - external/fmt/src/os.cc | 144 +- - external/fmt/support/README | 2 - - external/fmt/support/appveyor.yml | 10 - - external/fmt/support/bazel/.bazelrc | 1 + - external/fmt/support/bazel/.bazelversion | 1 + - external/fmt/support/bazel/BUILD.bazel | 29 + - external/fmt/support/bazel/README.md | 73 + - external/fmt/support/bazel/WORKSPACE.bazel | 1 + - external/fmt/support/build-docs.py | 58 + - external/fmt/support/build.gradle | 73 +- - external/fmt/support/fmt.pro | 27 - - external/fmt/support/manage.py | 19 +- - external/fmt/support/printable.py | 201 + - external/fmt/support/rst2md.py | 2 +- - external/fmt/support/travis-build.py | 119 - - .../fmt/support/update-coverity-branch.py | 30 - - external/fmt/test/CMakeLists.txt | 198 +- - .../test/add-subdirectory-test/CMakeLists.txt | 16 +- - .../fmt/test/add-subdirectory-test/main.cc | 5 +- - external/fmt/test/args-test.cc | 186 + - external/fmt/test/assert-test.cc | 15 +- - external/fmt/test/chrono-test.cc | 516 +- - external/fmt/test/color-test.cc | 64 +- - .../test/compile-error-test/CMakeLists.txt | 222 +- - external/fmt/test/compile-fp-test.cc | 62 + - external/fmt/test/compile-test.cc | 432 +- - external/fmt/test/core-test.cc | 989 +- - external/fmt/test/custom-formatter-test.cc | 58 - - external/fmt/test/enforce-checks-test.cc | 63 + - .../fmt/test/find-package-test/CMakeLists.txt | 2 +- - external/fmt/test/find-package-test/main.cc | 3 +- - external/fmt/test/format | 855 - - external/fmt/test/format-dyn-args-test.cc | 6 - - external/fmt/test/format-impl-test.cc | 271 +- - external/fmt/test/format-test.cc | 2807 +-- - external/fmt/test/fuzzing/.gitignore | 3 - - external/fmt/test/fuzzing/CMakeLists.txt | 42 +- - external/fmt/test/fuzzing/README.md | 29 +- - external/fmt/test/fuzzing/build.sh | 40 +- - external/fmt/test/fuzzing/chrono-duration.cc | 136 + - external/fmt/test/fuzzing/chrono-timepoint.cc | 32 + - external/fmt/test/fuzzing/chrono_duration.cpp | 152 - - external/fmt/test/fuzzing/float.cc | 39 + - external/fmt/test/fuzzing/fuzzer-common.h | 77 + - external/fmt/test/fuzzing/fuzzer_common.h | 67 - - external/fmt/test/fuzzing/main.cc | 22 + - external/fmt/test/fuzzing/main.cpp | 21 - - external/fmt/test/fuzzing/named-arg.cc | 102 + - external/fmt/test/fuzzing/named_arg.cpp | 128 - - external/fmt/test/fuzzing/one-arg.cc | 92 + - external/fmt/test/fuzzing/one_arg.cpp | 131 - - external/fmt/test/fuzzing/sprintf.cpp | 116 - - external/fmt/test/fuzzing/two-args.cc | 106 + - external/fmt/test/fuzzing/two_args.cpp | 112 - - external/fmt/test/gmock/gmock.h | 14204 ----------- - external/fmt/test/grisu-test.cc | 75 - - external/fmt/test/gtest-extra-test.cc | 124 +- - external/fmt/test/gtest-extra.cc | 45 +- - external/fmt/test/gtest-extra.h | 58 +- - external/fmt/test/gtest/.clang-format | 3 + - external/fmt/test/gtest/CMakeLists.txt | 38 + - .../fmt/test/{ => gtest}/gmock-gtest-all.cc | 9076 ++++--- - external/fmt/test/gtest/gmock/gmock.h | 11645 +++++++++ - external/fmt/test/gtest/gtest.h | 20065 ---------------- - .../fmt/test/gtest/{ => gtest}/gtest-spi.h | 26 +- - external/fmt/test/gtest/gtest/gtest.h | 12398 ++++++++++ - external/fmt/test/header-only-test.cc | 8 + - external/fmt/test/header-only-test2.cc | 3 - - external/fmt/test/locale-test.cc | 109 - - external/fmt/test/mock-allocator.h | 12 +- - external/fmt/test/module-test.cc | 580 + - external/fmt/test/noexception-test.cc | 18 + - external/fmt/test/os-test.cc | 329 +- - external/fmt/test/ostream-test.cc | 240 +- - external/fmt/test/posix-mock-test.cc | 138 +- - external/fmt/test/printf-test.cc | 234 +- - external/fmt/test/ranges-odr-test.cc | 17 + - external/fmt/test/ranges-test.cc | 362 +- - external/fmt/test/scan-test.cc | 38 +- - external/fmt/test/scan.h | 9 +- - .../test/static-export-test/CMakeLists.txt | 30 + - .../fmt/test/static-export-test/library.cc | 5 + - external/fmt/test/static-export-test/main.cc | 6 + - external/fmt/test/std-format-test.cc | 157 - - external/fmt/test/test-assert.h | 13 +- - external/fmt/test/test-main.cc | 3 +- - external/fmt/test/unicode-test.cc | 48 + - external/fmt/test/util.cc | 46 +- - external/fmt/test/util.h | 39 +- - external/fmt/test/xchar-test.cc | 502 + - src/Utils/Visualization/string_utf8.cpp | 2 + - 120 files changed, 48247 insertions(+), 48760 deletions(-) - create mode 100644 external/fmt/include/fmt/args.h - delete mode 100644 external/fmt/include/fmt/posix.h - create mode 100644 external/fmt/include/fmt/xchar.h - create mode 100644 external/fmt/src/fmt.cc - create mode 100644 external/fmt/support/bazel/.bazelrc - create mode 100644 external/fmt/support/bazel/.bazelversion - create mode 100644 external/fmt/support/bazel/BUILD.bazel - create mode 100644 external/fmt/support/bazel/README.md - create mode 100644 external/fmt/support/bazel/WORKSPACE.bazel - create mode 100755 external/fmt/support/build-docs.py - delete mode 100644 external/fmt/support/fmt.pro - create mode 100755 external/fmt/support/printable.py - delete mode 100755 external/fmt/support/travis-build.py - delete mode 100755 external/fmt/support/update-coverity-branch.py - create mode 100644 external/fmt/test/args-test.cc - create mode 100644 external/fmt/test/compile-fp-test.cc - delete mode 100644 external/fmt/test/custom-formatter-test.cc - create mode 100644 external/fmt/test/enforce-checks-test.cc - delete mode 100644 external/fmt/test/format - delete mode 100644 external/fmt/test/format-dyn-args-test.cc - delete mode 100644 external/fmt/test/fuzzing/.gitignore - create mode 100644 external/fmt/test/fuzzing/chrono-duration.cc - create mode 100644 external/fmt/test/fuzzing/chrono-timepoint.cc - delete mode 100644 external/fmt/test/fuzzing/chrono_duration.cpp - create mode 100644 external/fmt/test/fuzzing/float.cc - create mode 100644 external/fmt/test/fuzzing/fuzzer-common.h - delete mode 100644 external/fmt/test/fuzzing/fuzzer_common.h - create mode 100644 external/fmt/test/fuzzing/main.cc - delete mode 100644 external/fmt/test/fuzzing/main.cpp - create mode 100644 external/fmt/test/fuzzing/named-arg.cc - delete mode 100644 external/fmt/test/fuzzing/named_arg.cpp - create mode 100644 external/fmt/test/fuzzing/one-arg.cc - delete mode 100644 external/fmt/test/fuzzing/one_arg.cpp - delete mode 100644 external/fmt/test/fuzzing/sprintf.cpp - create mode 100644 external/fmt/test/fuzzing/two-args.cc - delete mode 100644 external/fmt/test/fuzzing/two_args.cpp - delete mode 100644 external/fmt/test/gmock/gmock.h - delete mode 100644 external/fmt/test/grisu-test.cc - create mode 100644 external/fmt/test/gtest/.clang-format - create mode 100644 external/fmt/test/gtest/CMakeLists.txt - rename external/fmt/test/{ => gtest}/gmock-gtest-all.cc (57%) - create mode 100644 external/fmt/test/gtest/gmock/gmock.h - delete mode 100644 external/fmt/test/gtest/gtest.h - rename external/fmt/test/gtest/{ => gtest}/gtest-spi.h (94%) - create mode 100644 external/fmt/test/gtest/gtest/gtest.h - delete mode 100644 external/fmt/test/header-only-test2.cc - delete mode 100644 external/fmt/test/locale-test.cc - create mode 100644 external/fmt/test/module-test.cc - create mode 100644 external/fmt/test/noexception-test.cc - create mode 100644 external/fmt/test/ranges-odr-test.cc - create mode 100644 external/fmt/test/static-export-test/CMakeLists.txt - create mode 100644 external/fmt/test/static-export-test/library.cc - create mode 100644 external/fmt/test/static-export-test/main.cc - delete mode 100644 external/fmt/test/std-format-test.cc - create mode 100644 external/fmt/test/unicode-test.cc - create mode 100644 external/fmt/test/xchar-test.cc - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index b9b12ea7..d81e7011 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -32,7 +32,7 @@ endif() - # ============================================================================= - list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") - find_package(Eigen3 3.3 REQUIRED) --find_package(fmt 7.0.0 REQUIRED) -+find_package(fmt 8.1.1 REQUIRED) - find_package(nlohmann_json 3.9.0 REQUIRED) - find_package(phmap 1.0.0 REQUIRED) - add_subdirectory(external) -diff --git a/external/fmt/CMakeLists.txt b/external/fmt/CMakeLists.txt -index 977090f7..3bf80f80 100644 ---- a/external/fmt/CMakeLists.txt -+++ b/external/fmt/CMakeLists.txt -@@ -1,29 +1,40 @@ --cmake_minimum_required(VERSION 3.1.0) -+cmake_minimum_required(VERSION 3.1...3.18) - --# Use newer policies if available, up to most recent tested version of CMake. --if(${CMAKE_VERSION} VERSION_LESS 3.11) -+# Fallback for using newer policies on CMake <3.12. -+if(${CMAKE_VERSION} VERSION_LESS 3.12) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) --else() -- cmake_policy(VERSION 3.11) - endif() - - # Determine if fmt is built as a subproject (using add_subdirectory) - # or if it is the master project. --set(MASTER_PROJECT OFF) --if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) -- set(MASTER_PROJECT ON) -- message(STATUS "CMake version: ${CMAKE_VERSION}") -+if (NOT DEFINED FMT_MASTER_PROJECT) -+ set(FMT_MASTER_PROJECT OFF) -+ if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) -+ set(FMT_MASTER_PROJECT ON) -+ message(STATUS "CMake version: ${CMAKE_VERSION}") -+ endif () - endif () - - # Joins arguments and places the results in ${result_var}. - function(join result_var) -- set(result ) -+ set(result "") - foreach (arg ${ARGN}) - set(result "${result}${arg}") - endforeach () - set(${result_var} "${result}" PARENT_SCOPE) - endfunction() - -+function(enable_module target) -+ if (MSVC) -+ set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc) -+ target_compile_options(${target} -+ PRIVATE /interface /ifcOutput ${BMI} -+ INTERFACE /reference fmt=${BMI}) -+ endif () -+ set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI}) -+ set_source_files_properties(${BMI} PROPERTIES GENERATED ON) -+endfunction() -+ - include(CMakeParseArguments) - - # Sets a cache variable with a docstring joined from multiple arguments: -@@ -46,7 +57,7 @@ endfunction() - # Set the default CMAKE_BUILD_TYPE to Release. - # This should be done before the project command since the latter can set - # CMAKE_BUILD_TYPE itself (it does so for nmake). --if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE) -+if (FMT_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE) - set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING - "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or " - "CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") -@@ -55,20 +66,41 @@ endif () - project(FMT CXX) - include(GNUInstallDirs) - set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING -- "Installation directory for include files, a relative path " -- "that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.") -+ "Installation directory for include files, a relative path that " -+ "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.") - - option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF) - option(FMT_WERROR "Halt the compilation with an error on compiler warnings." - OFF) - - # Options that control generation of various targets. --option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT}) --option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT}) --option(FMT_TEST "Generate the test target." ${MASTER_PROJECT}) -+option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT}) -+option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT}) -+option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT}) - option(FMT_FUZZ "Generate the fuzz target." OFF) - option(FMT_CUDA_TEST "Generate the cuda-test target." OFF) - option(FMT_OS "Include core requiring OS (Windows/Posix) " ON) -+option(FMT_MODULE "Build a module instead of a traditional library." OFF) -+option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF) -+ -+set(FMT_CAN_MODULE OFF) -+if (CMAKE_CXX_STANDARD GREATER 17 AND -+ # msvc 16.10-pre4 -+ MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035) -+ set(FMT_CAN_MODULE OFF) -+endif () -+if (NOT FMT_CAN_MODULE) -+ set(FMT_MODULE OFF) -+ message(STATUS "Module support is disabled.") -+endif () -+if (FMT_TEST AND FMT_MODULE) -+ # The tests require {fmt} to be compiled as traditional library -+ message(STATUS "Testing is incompatible with build mode 'module'.") -+endif () -+set(FMT_SYSTEM_HEADERS_ATTRIBUTE "") -+if (FMT_SYSTEM_HEADERS) -+ set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) -+endif () - - # Get version from core.h - file(READ include/fmt/core.h core_h) -@@ -104,24 +136,36 @@ if (${index} GREATER -1) - endif () - message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}") - -+if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) -+ set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING -+ "Preset for the export of private symbols") -+ set_property(CACHE CMAKE_CXX_VISIBILITY_PRESET PROPERTY STRINGS -+ hidden default) -+endif () -+ -+if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN) -+ set_verbose(CMAKE_VISIBILITY_INLINES_HIDDEN ON CACHE BOOL -+ "Whether to add a compile flag to hide symbols of inline functions") -+endif () -+ - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic - -Wold-style-cast -Wundef - -Wredundant-decls -Wwrite-strings -Wpointer-arith - -Wcast-qual -Wformat=2 -Wmissing-include-dirs -- -Wcast-align -Wnon-virtual-dtor -+ -Wcast-align - -Wctor-dtor-privacy -Wdisabled-optimization - -Winvalid-pch -Woverloaded-virtual -- -Wconversion -Wswitch-enum -- -Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow) -+ -Wconversion -Wundef -+ -Wno-ctor-dtor-privacy -Wno-format-nonliteral) - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) -- set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept -+ set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} - -Wno-dangling-else -Wno-unused-local-typedefs) - endif () - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) - set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion - -Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast -- -Wvector-operation-performance -Wsized-deallocation) -+ -Wvector-operation-performance -Wsized-deallocation -Wshadow) - endif () - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) - set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2 -@@ -131,8 +175,9 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") - endif () - - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") -- set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -- -Wno-sign-conversion -Wdeprecated -Wweak-vtables) -+ set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef -+ -Wdeprecated -Wweak-vtables -Wshadow -+ -Wno-gnu-zero-variadic-macro-arguments) - check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING) - if (HAS_NULLPTR_WARNING) - set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -@@ -146,7 +191,7 @@ if (MSVC) - set(WERROR_FLAG /WX) - endif () - --if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio") -+if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio") - # If Microsoft SDK is installed create script run-msbuild.bat that - # calls SetEnv.cmd to set up build environment and runs msbuild. - # It is useful when building Visual Studio projects with the SDK -@@ -185,9 +230,12 @@ function(add_headers VAR) - endfunction() - - # Define the fmt library, its includes and the needed defines. --add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h -- locale.h os.h ostream.h posix.h printf.h ranges.h) --if (FMT_OS) -+add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h -+ format-inl.h locale.h os.h ostream.h printf.h ranges.h -+ xchar.h) -+if (FMT_MODULE) -+ set(FMT_SOURCES src/fmt.cc) -+elseif (FMT_OS) - set(FMT_SOURCES src/format.cc src/os.cc) - else() - set(FMT_SOURCES src/format.cc) -@@ -201,7 +249,10 @@ if (HAVE_STRTOD_L) - endif () - - if (MINGW) -- target_compile_options(fmt PUBLIC "-Wa,-mbig-obj") -+ check_cxx_compiler_flag("-Wa,-mbig-obj" FMT_HAS_MBIG_OBJ) -+ if (${FMT_HAS_MBIG_OBJ}) -+ target_compile_options(fmt PUBLIC "-Wa,-mbig-obj") -+ endif() - endif () - - if (FMT_WERROR) -@@ -210,10 +261,13 @@ endif () - if (FMT_PEDANTIC) - target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) - endif () -+if (FMT_MODULE) -+ enable_module(fmt) -+endif () - - target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES}) - --target_include_directories(fmt PUBLIC -+target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC - $ - $) - -@@ -231,7 +285,8 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug") - endif () - - if (BUILD_SHARED_LIBS) -- if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND NOT EMSCRIPTEN) -+ if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND -+ NOT EMSCRIPTEN) - # Fix rpmlint warning: - # unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6. - target_link_libraries(fmt -Wl,--as-needed) -@@ -248,7 +303,7 @@ add_library(fmt::fmt-header-only ALIAS fmt-header-only) - target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) - target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES}) - --target_include_directories(fmt-header-only INTERFACE -+target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE - $ - $) - -@@ -256,20 +311,22 @@ target_include_directories(fmt-header-only INTERFACE - if (FMT_INSTALL) - include(CMakePackageConfigHelpers) - set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING -- "Installation directory for cmake files, a relative path " -- "that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.") -+ "Installation directory for cmake files, a relative path that " -+ "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute " -+ "path.") - set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) - set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) - set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc) - set(targets_export_name fmt-targets) - - set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING -- "Installation directory for libraries, a relative path " -- "that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.") -+ "Installation directory for libraries, a relative path that " -+ "will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.") - - set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH -- "Installation directory for pkgconfig (.pc) files, a relative path " -- "that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.") -+ "Installation directory for pkgconfig (.pc) files, a relative " -+ "path that will be joined with ${CMAKE_INSTALL_PREFIX} or an " -+ "absolute path.") - - # Generate the version, config and target files into the build directory. - write_basic_package_version_file( -@@ -290,6 +347,13 @@ if (FMT_INSTALL) - INSTALL_DESTINATION ${FMT_CMAKE_DIR}) - - set(INSTALL_TARGETS fmt fmt-header-only) -+ -+ # Install the library and headers. -+ install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} -+ LIBRARY DESTINATION ${FMT_LIB_DIR} -+ ARCHIVE DESTINATION ${FMT_LIB_DIR} -+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -+ - # Use a namespace because CMake provides better diagnostics for namespaced - # imported targets. - export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt:: -@@ -302,12 +366,6 @@ if (FMT_INSTALL) - install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR} - NAMESPACE fmt::) - -- # Install the library and headers. -- install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} -- LIBRARY DESTINATION ${FMT_LIB_DIR} -- ARCHIVE DESTINATION ${FMT_LIB_DIR} -- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -- - install(FILES $ - DESTINATION ${FMT_LIB_DIR} OPTIONAL) - install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt") -@@ -326,11 +384,17 @@ endif () - # Control fuzzing independent of the unit tests. - if (FMT_FUZZ) - add_subdirectory(test/fuzzing) -+ -+ # The FMT_FUZZ macro is used to prevent resource exhaustion in fuzzing -+ # mode and make fuzzing practically possible. It is similar to -+ # FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION but uses a different name to -+ # avoid interfering with fuzzing of projects that use {fmt}. -+ # See also https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode. - target_compile_definitions(fmt PUBLIC FMT_FUZZ) - endif () - - set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore) --if (MASTER_PROJECT AND EXISTS ${gitignore}) -+if (FMT_MASTER_PROJECT AND EXISTS ${gitignore}) - # Get the list of ignored files from .gitignore. - file (STRINGS ${gitignore} lines) - list(REMOVE_ITEM lines /doc/html) -diff --git a/external/fmt/CONTRIBUTING.md b/external/fmt/CONTRIBUTING.md -index 3532bd11..b82f1450 100644 ---- a/external/fmt/CONTRIBUTING.md -+++ b/external/fmt/CONTRIBUTING.md -@@ -14,4 +14,7 @@ exceptions: - * snake_case should be used instead of UpperCamelCase for function and type - names - -+All documentation must adhere to the [Google Developer Documentation Style -+Guide](https://developers.google.com/style). -+ - Thanks for contributing! -diff --git a/external/fmt/ChangeLog.rst b/external/fmt/ChangeLog.rst -index 97a0a7da..18b84c7d 100644 ---- a/external/fmt/ChangeLog.rst -+++ b/external/fmt/ChangeLog.rst -@@ -1,3 +1,1501 @@ -+8.1.1 - 2022-01-06 -+------------------ -+ -+* Restored ABI compatibility with version 8.0.x -+ (`#2695 `_, -+ `#2696 `_). -+ Thanks `@saraedum (Julian Rüth) `_. -+ -+* Fixed chorno formatting on big endian systems -+ (`#2698 `_, -+ `#2699 `_). -+ Thanks `@phprus (Vladislav Shchapov) `_ and -+ `@xvitaly (Vitaly Zaitsev) `_. -+ -+* Fixed a linkage error with mingw -+ (`#2691 `_, -+ `#2692 `_). -+ Thanks `@rbberger (Richard Berger) `_. -+ -+8.1.0 - 2022-01-02 -+------------------ -+ -+* Optimized chrono formatting -+ (`#2500 `_, -+ `#2537 `_, -+ `#2541 `_, -+ `#2544 `_, -+ `#2550 `_, -+ `#2551 `_, -+ `#2576 `_, -+ `#2577 `_, -+ `#2586 `_, -+ `#2591 `_, -+ `#2594 `_, -+ `#2602 `_, -+ `#2617 `_, -+ `#2628 `_, -+ `#2633 `_, -+ `#2670 `_, -+ `#2671 `_). -+ -+ Processing of some specifiers such as ``%z`` and ``%Y`` is now up to 10-20 -+ times faster, for example on GCC 11 with libstdc++:: -+ -+ ---------------------------------------------------------------------------- -+ Benchmark Before After -+ ---------------------------------------------------------------------------- -+ FMTFormatter_z 261 ns 26.3 ns -+ FMTFormatterCompile_z 246 ns 11.6 ns -+ FMTFormatter_Y 263 ns 26.1 ns -+ FMTFormatterCompile_Y 244 ns 10.5 ns -+ ---------------------------------------------------------------------------- -+ -+ Thanks `@phprus (Vladislav Shchapov) `_ and -+ `@toughengineer (Pavel Novikov) `_. -+ -+* Implemented subsecond formatting for chrono durations -+ (`#2623 `_). -+ For example (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("{:%S}", std::chrono::milliseconds(1234)); -+ } -+ -+ prints "01.234". -+ -+ Thanks `@matrackif `_. -+ -+* Fixed handling of precision 0 when formatting chrono durations -+ (`#2587 `_, -+ `#2588 `_). -+ Thanks `@lukester1975 `_. -+ -+* Fixed an overflow on invalid inputs in the ``tm`` formatter -+ (`#2564 `_). -+ Thanks `@phprus (Vladislav Shchapov) `_. -+ -+* Added ``fmt::group_digits`` that formats integers with a non-localized digit -+ separator (comma) for groups of three digits. -+ For example (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("{} dollars", fmt::group_digits(1000000)); -+ } -+ -+ prints "1,000,000 dollars". -+ -+* Added support for faint, conceal, reverse and blink text styles -+ (`#2394 `_): -+ -+ https://user-images.githubusercontent.com/576385/147710227-c68f5317-f8fa-42c3-9123-7c4ba3c398cb.mp4 -+ -+ Thanks `@benit8 (Benoît Lormeau) `_ and -+ `@data-man (Dmitry Atamanov) `_. -+ -+* Added experimental support for compile-time floating point formatting -+ (`#2426 `_, -+ `#2470 `_). -+ It is currently limited to the header-only mode. -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Added UDL-based named argument support to compile-time format string checks -+ (`#2640 `_, -+ `#2649 `_). -+ For example (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ using namespace fmt::literals; -+ fmt::print("{answer:s}", "answer"_a=42); -+ } -+ -+ gives a compile-time error on compilers with C++20 ``consteval`` and non-type -+ template parameter support (gcc 10+) because ``s`` is not a valid format -+ specifier for an integer. -+ -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Implemented escaping of string range elements. -+ For example (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ #include -+ -+ int main() { -+ fmt::print("{}", std::vector{"\naan"}); -+ } -+ -+ is now printed as:: -+ -+ ["\naan"] -+ -+ instead of:: -+ -+ [" -+ aan"] -+ -+* Switched to JSON-like representation of maps and sets for consistency with -+ Python's ``str.format``. -+ For example (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ #include -+ -+ int main() { -+ fmt::print("{}", std::map{{"answer", 42}}); -+ } -+ -+ is now printed as:: -+ -+ {"answer": 42} -+ -+* Extended ``fmt::join`` to support C++20-only ranges -+ (`#2549 `_). -+ Thanks `@BRevzin (Barry Revzin) `_. -+ -+* Optimized handling of non-const-iterable ranges and implemented initial -+ support for non-const-formattable types. -+ -+* Disabled implicit conversions of scoped enums to integers that was -+ accidentally introduced in earlier versions -+ (`#1841 `_). -+ -+* Deprecated implicit conversion of ``[const] signed char*`` and -+ ``[const] unsigned char*`` to C strings. -+ -+* Deprecated ``_format``, a legacy UDL-based format API -+ (`#2646 `_). -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Marked ``format``, ``formatted_size`` and ``to_string`` as ``[[nodiscard]]`` -+ (`#2612 `_). -+ `@0x8000-0000 (Florin Iucha) `_. -+ -+* Added missing diagnostic when trying to format function and member pointers -+ as well as objects convertible to pointers which is explicitly disallowed -+ (`#2598 `_, -+ `#2609 `_, -+ `#2610 `_). -+ Thanks `@AlexGuteniev (Alex Guteniev) `_. -+ -+* Optimized writing to a contiguous buffer with ``format_to_n`` -+ (`#2489 `_). -+ Thanks `@Roman-Koshelev `_. -+ -+* Optimized writing to non-``char`` buffers -+ (`#2477 `_). -+ Thanks `@Roman-Koshelev `_. -+ -+* Decimal point is now localized when using the ``L`` specifier. -+ -+* Improved floating point formatter implementation -+ (`#2498 `_, -+ `#2499 `_). -+ Thanks `@Roman-Koshelev `_. -+ -+* Fixed handling of very large precision in fixed format -+ (`#2616 `_). -+ -+* Made a table of cached powers used in FP formatting static -+ (`#2509 `_). -+ Thanks `@jk-jeon (Junekey Jeon) `_. -+ -+* Resolved a lookup ambiguity with C++20 format-related functions due to ADL -+ (`#2639 `_, -+ `#2641 `_). -+ Thanks `@mkurdej (Marek Kurdej) `_. -+ -+* Removed unnecessary inline namespace qualification -+ (`#2642 `_, -+ `#2643 `_). -+ Thanks `@mkurdej (Marek Kurdej) `_. -+ -+* Implemented argument forwarding in ``format_to_n`` -+ (`#2462 `_, -+ `#2463 `_). -+ Thanks `@owent (WenTao Ou) `_. -+ -+* Fixed handling of implicit conversions in ``fmt::to_string`` and format string -+ compilation (`#2565 `_). -+ -+* Changed the default access mode of files created by ``fmt::output_file`` to -+ ``-rw-r--r--`` for consistency with ``fopen`` -+ (`#2530 `_). -+ -+* Make ``fmt::ostream::flush`` public -+ (`#2435 `_). -+ -+* Improved C++14/17 attribute detection -+ (`#2615 `_). -+ Thanks `@AlexGuteniev (Alex Guteniev) `_. -+ -+* Improved ``consteval`` detection for MSVC -+ (`#2559 `_). -+ Thanks `@DanielaE (Daniela Engert) `_. -+ -+* Improved documentation -+ (`#2406 `_, -+ `#2446 `_, -+ `#2493 `_, -+ `#2513 `_, -+ `#2515 `_, -+ `#2522 `_, -+ `#2562 `_, -+ `#2575 `_, -+ `#2606 `_, -+ `#2620 `_, -+ `#2676 `_). -+ Thanks `@sobolevn (Nikita Sobolev) `_, -+ `@UnePierre (Max FERGER) `_, -+ `@zhsj `_, -+ `@phprus (Vladislav Shchapov) `_, -+ `@ericcurtin (Eric Curtin) `_, -+ `@Lounarok `_. -+ -+* Improved fuzzers and added a fuzzer for chrono timepoint formatting -+ (`#2461 `_, -+ `#2469 `_). -+ `@pauldreik (Paul Dreik) `_, -+ -+* Added the ``FMT_SYSTEM_HEADERS`` CMake option setting which marks {fmt}'s -+ headers as system. It can be used to suppress warnings -+ (`#2644 `_, -+ `#2651 `_). -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Added the Bazel build system support -+ (`#2505 `_, -+ `#2516 `_). -+ Thanks `@Vertexwahn `_. -+ -+* Improved build configuration and tests -+ (`#2437 `_, -+ `#2558 `_, -+ `#2648 `_, -+ `#2650 `_, -+ `#2663 `_, -+ `#2677 `_). -+ Thanks `@DanielaE (Daniela Engert) `_, -+ `@alexezeder (Alexey Ochapov) `_, -+ `@phprus (Vladislav Shchapov) `_. -+ -+* Fixed various warnings and compilation issues -+ (`#2353 `_, -+ `#2356 `_, -+ `#2399 `_, -+ `#2408 `_, -+ `#2414 `_, -+ `#2427 `_, -+ `#2432 `_, -+ `#2442 `_, -+ `#2434 `_, -+ `#2439 `_, -+ `#2447 `_, -+ `#2450 `_, -+ `#2455 `_, -+ `#2465 `_, -+ `#2472 `_, -+ `#2474 `_, -+ `#2476 `_, -+ `#2478 `_, -+ `#2479 `_, -+ `#2481 `_, -+ `#2482 `_, -+ `#2483 `_, -+ `#2490 `_, -+ `#2491 `_, -+ `#2510 `_, -+ `#2518 `_, -+ `#2528 `_, -+ `#2529 `_, -+ `#2539 `_, -+ `#2540 `_, -+ `#2545 `_, -+ `#2555 `_, -+ `#2557 `_, -+ `#2570 `_, -+ `#2573 `_, -+ `#2582 `_, -+ `#2605 `_, -+ `#2611 `_, -+ `#2647 `_, -+ `#2627 `_, -+ `#2630 `_, -+ `#2635 `_, -+ `#2638 `_, -+ `#2653 `_, -+ `#2654 `_, -+ `#2661 `_, -+ `#2664 `_, -+ `#2684 `_). -+ Thanks `@DanielaE (Daniela Engert) `_, -+ `@mwinterb `_, -+ `@cdacamar (Cameron DaCamara) `_, -+ `@TrebledJ (Johnathan) `_, -+ `@bodomartin (brm) `_, -+ `@cquammen (Cory Quammen) `_, -+ `@white238 (Chris White) `_, -+ `@mmarkeloff (Max) `_, -+ `@palacaze (Pierre-Antoine Lacaze) `_, -+ `@jcelerier (Jean-Michaël Celerier) `_, -+ `@mborn-adi (Mathias Born) `_, -+ `@BrukerJWD (Jonathan W) `_, -+ `@spyridon97 (Spiros Tsalikis) `_, -+ `@phprus (Vladislav Shchapov) `_, -+ `@oliverlee (Oliver Lee) `_, -+ `@joshessman-llnl (Josh Essman) `_, -+ `@akohlmey (Axel Kohlmeyer) `_, -+ `@timkalu `_, -+ `@olupton (Olli Lupton) `_, -+ `@Acretock `_, -+ `@alexezeder (Alexey Ochapov) `_, -+ `@andrewcorrigan (Andrew Corrigan) `_, -+ `@lucpelletier `_, -+ `@HazardyKnusperkeks (Björn Schäpers) `_. -+ -+8.0.1 - 2021-07-02 -+------------------ -+ -+* Fixed the version number in the inline namespace -+ (`#2374 `_). -+ -+* Added a missing presentation type check for ``std::string`` -+ (`#2402 `_). -+ -+* Fixed a linkage error when mixing code built with clang and gcc -+ (`#2377 `_). -+ -+* Fixed documentation issues -+ (`#2396 `_, -+ `#2403 `_, -+ `#2406 `_). -+ Thanks `@mkurdej (Marek Kurdej) `_. -+ -+* Removed dead code in FP formatter ( -+ `#2398 `_). -+ Thanks `@javierhonduco (Javier Honduvilla Coto) -+ `_. -+ -+* Fixed various warnings and compilation issues -+ (`#2351 `_, -+ `#2359 `_, -+ `#2365 `_, -+ `#2368 `_, -+ `#2370 `_, -+ `#2376 `_, -+ `#2381 `_, -+ `#2382 `_, -+ `#2386 `_, -+ `#2389 `_, -+ `#2395 `_, -+ `#2397 `_, -+ `#2400 `_, -+ `#2401 `_, -+ `#2407 `_). -+ Thanks `@zx2c4 (Jason A. Donenfeld) `_, -+ `@AidanSun05 (Aidan Sun) `_, -+ `@mattiasljungstrom (Mattias Ljungström) -+ `_, -+ `@joemmett (Jonathan Emmett) `_, -+ `@erengy (Eren Okka) `_, -+ `@patlkli (Patrick Geltinger) `_, -+ `@gsjaardema (Greg Sjaardema) `_, -+ `@phprus (Vladislav Shchapov) `_. -+ -+8.0.0 - 2021-06-21 -+------------------ -+ -+* Enabled compile-time format string checks by default. -+ For example (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("{:d}", "I am not a number"); -+ } -+ -+ gives a compile-time error on compilers with C++20 ``consteval`` support -+ (gcc 10+, clang 11+) because ``d`` is not a valid format specifier for a -+ string. -+ -+ To pass a runtime string wrap it in ``fmt::runtime``: -+ -+ .. code:: c++ -+ -+ fmt::print(fmt::runtime("{:d}"), "I am not a number"); -+ -+* Added compile-time formatting -+ (`#2019 `_, -+ `#2044 `_, -+ `#2056 `_, -+ `#2072 `_, -+ `#2075 `_, -+ `#2078 `_, -+ `#2129 `_, -+ `#2326 `_). -+ For example (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ consteval auto compile_time_itoa(int value) -> std::array { -+ auto result = std::array(); -+ fmt::format_to(result.data(), FMT_COMPILE("{}"), value); -+ return result; -+ } -+ -+ constexpr auto answer = compile_time_itoa(42); -+ -+ Most of the formatting functionality is available at compile time with a -+ notable exception of floating-point numbers and pointers. -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Optimized handling of format specifiers during format string compilation. -+ For example, hexadecimal formatting (``"{:x}"``) is now 3-7x faster than -+ before when using ``format_to`` with format string compilation and a -+ stack-allocated buffer (`#1944 `_). -+ -+ Before (7.1.3):: -+ -+ ---------------------------------------------------------------------------- -+ Benchmark Time CPU Iterations -+ ---------------------------------------------------------------------------- -+ FMTCompileOld/0 15.5 ns 15.5 ns 43302898 -+ FMTCompileOld/42 16.6 ns 16.6 ns 43278267 -+ FMTCompileOld/273123 18.7 ns 18.6 ns 37035861 -+ FMTCompileOld/9223372036854775807 19.4 ns 19.4 ns 35243000 -+ ---------------------------------------------------------------------------- -+ -+ After (8.x):: -+ -+ ---------------------------------------------------------------------------- -+ Benchmark Time CPU Iterations -+ ---------------------------------------------------------------------------- -+ FMTCompileNew/0 1.99 ns 1.99 ns 360523686 -+ FMTCompileNew/42 2.33 ns 2.33 ns 279865664 -+ FMTCompileNew/273123 3.72 ns 3.71 ns 190230315 -+ FMTCompileNew/9223372036854775807 5.28 ns 5.26 ns 130711631 -+ ---------------------------------------------------------------------------- -+ -+ It is even faster than ``std::to_chars`` from libc++ compiled with clang on -+ macOS:: -+ -+ ---------------------------------------------------------------------------- -+ Benchmark Time CPU Iterations -+ ---------------------------------------------------------------------------- -+ ToChars/0 4.42 ns 4.41 ns 160196630 -+ ToChars/42 5.00 ns 4.98 ns 140735201 -+ ToChars/273123 7.26 ns 7.24 ns 95784130 -+ ToChars/9223372036854775807 8.77 ns 8.75 ns 75872534 -+ ---------------------------------------------------------------------------- -+ -+ In other cases, especially involving ``std::string`` construction, the -+ speed up is usually lower because handling format specifiers takes a smaller -+ fraction of the total time. -+ -+* Added the ``_cf`` user-defined literal to represent a compiled format string. -+ It can be used instead of the ``FMT_COMPILE`` macro -+ (`#2043 `_, -+ `#2242 `_): -+ -+ .. code:: c++ -+ -+ #include -+ -+ using namespace fmt::literals; -+ auto s = fmt::format(FMT_COMPILE("{}"), 42); // 🙁 not modern -+ auto s = fmt::format("{}"_cf, 42); // 🙂 modern as hell -+ -+ It requires compiler support for class types in non-type template parameters -+ (a C++20 feature) which is available in GCC 9.3+. -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Format string compilation now requires ``format`` functions of ``formatter`` -+ specializations for user-defined types to be ``const``: -+ -+ .. code:: c++ -+ -+ template <> struct fmt::formatter: formatter { -+ template -+ auto format(my_type obj, FormatContext& ctx) const { // Note const here. -+ // ... -+ } -+ }; -+ -+* Added UDL-based named argument support to format string compilation -+ (`#2243 `_, -+ `#2281 `_). For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ using namespace fmt::literals; -+ auto s = fmt::format(FMT_COMPILE("{answer}"), "answer"_a = 42); -+ -+ Here the argument named "answer" is resolved at compile time with no -+ runtime overhead. -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Added format string compilation support to ``fmt::print`` -+ (`#2280 `_, -+ `#2304 `_). -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Added initial support for compiling {fmt} as a C++20 module -+ (`#2235 `_, -+ `#2240 `_, -+ `#2260 `_, -+ `#2282 `_, -+ `#2283 `_, -+ `#2288 `_, -+ `#2298 `_, -+ `#2306 `_, -+ `#2307 `_, -+ `#2309 `_, -+ `#2318 `_, -+ `#2324 `_, -+ `#2332 `_, -+ `#2340 `_). -+ Thanks `@DanielaE (Daniela Engert) `_. -+ -+* Made symbols private by default reducing shared library size -+ (`#2301 `_). For example there was -+ a ~15% reported reduction on one platform. -+ Thanks `@sergiud (Sergiu Deitsch) `_. -+ -+* Optimized includes making the result of preprocessing ``fmt/format.h`` -+ ~20% smaller with libstdc++/C++20 and slightly improving build times -+ (`#1998 `_). -+ -+* Added support of ranges with non-const ``begin`` / ``end`` -+ (`#1953 `_). -+ Thanks `@kitegi (sarah) `_. -+ -+* Added support of ``std::byte`` and other formattable types to ``fmt::join`` -+ (`#1981 `_, -+ `#2040 `_, -+ `#2050 `_, -+ `#2262 `_). For example: -+ -+ .. code:: c++ -+ -+ #include -+ #include -+ #include -+ -+ int main() { -+ auto bytes = std::vector{std::byte(4), std::byte(2)}; -+ fmt::print("{}", fmt::join(bytes, "")); -+ } -+ -+ prints "42". -+ -+ Thanks `@kamibo (Camille Bordignon) `_. -+ -+* Implemented the default format for ``std::chrono::system_clock`` -+ (`#2319 `_, -+ `#2345 `_). For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("{}", std::chrono::system_clock::now()); -+ } -+ -+ prints "2021-06-18 15:22:00" (the output depends on the current date and -+ time). Thanks `@sunmy2019 `_. -+ -+* Made more chrono specifiers locale independent by default. Use the ``'L'`` -+ specifier to get localized formatting. For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ std::locale::global(std::locale("ru_RU.UTF-8")); -+ auto monday = std::chrono::weekday(1); -+ fmt::print("{}\n", monday); // prints "Mon" -+ fmt::print("{:L}\n", monday); // prints "пн" -+ } -+ -+* Improved locale handling in chrono formatting -+ (`#2337 `_, -+ `#2349 `_, -+ `#2350 `_). -+ Thanks `@phprus (Vladislav Shchapov) `_. -+ -+* Deprecated ``fmt/locale.h`` moving the formatting functions that take a -+ locale to ``fmt/format.h`` (``char``) and ``fmt/xchar`` (other overloads). -+ This doesn't introduce a dependency on ```` so there is virtually no -+ compile time effect. -+ -+* Deprecated an undocumented ``format_to`` overload that takes -+ ``basic_memory_buffer``. -+ -+* Made parameter order in ``vformat_to`` consistent with ``format_to`` -+ (`#2327 `_). -+ -+* Added support for time points with arbitrary durations -+ (`#2208 `_). For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ using tp = std::chrono::time_point< -+ std::chrono::system_clock, std::chrono::seconds>; -+ fmt::print("{:%S}", tp(std::chrono::seconds(42))); -+ } -+ -+ prints "42". -+ -+* Formatting floating-point numbers no longer produces trailing zeros by default -+ for consistency with ``std::format``. For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("{0:.3}", 1.1); -+ } -+ -+ prints "1.1". Use the ``'#'`` specifier to keep trailing zeros. -+ -+* Dropped a limit on the number of elements in a range and replaced ``{}`` with -+ ``[]`` as range delimiters for consistency with Python's ``str.format``. -+ -+* The ``'L'`` specifier for locale-specific numeric formatting can now be -+ combined with presentation specifiers as in ``std::format``. For example: -+ -+ .. code:: c++ -+ -+ #include -+ #include -+ -+ int main() { -+ std::locale::global(std::locale("fr_FR.UTF-8")); -+ fmt::print("{0:.2Lf}", 0.42); -+ } -+ -+ prints "0,42". The deprecated ``'n'`` specifier has been removed. -+ -+* Made the ``0`` specifier ignored for infinity and NaN -+ (`#2305 `_, -+ `#2310 `_). -+ Thanks `@Liedtke (Matthias Liedtke) `_. -+ -+* Made the hexfloat formatting use the right alignment by default -+ (`#2308 `_, -+ `#2317 `_). -+ Thanks `@Liedtke (Matthias Liedtke) `_. -+ -+* Removed the deprecated numeric alignment (``'='``). Use the ``'0'`` specifier -+ instead. -+ -+* Removed the deprecated ``fmt/posix.h`` header that has been replaced with -+ ``fmt/os.h``. -+ -+* Removed the deprecated ``format_to_n_context``, ``format_to_n_args`` and -+ ``make_format_to_n_args``. They have been replaced with ``format_context``, -+ ``format_args` and ``make_format_args`` respectively. -+ -+* Moved ``wchar_t``-specific functions and types to ``fmt/xchar.h``. -+ You can define ``FMT_DEPRECATED_INCLUDE_XCHAR`` to automatically include -+ ``fmt/xchar.h`` from ``fmt/format.h`` but this will be disabled in the next -+ major release. -+ -+* Fixed handling of the ``'+'`` specifier in localized formatting -+ (`#2133 `_). -+ -+* Added support for the ``'s'`` format specifier that gives textual -+ representation of ``bool`` -+ (`#2094 `_, -+ `#2109 `_). For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("{:s}", true); -+ } -+ -+ prints "true". -+ Thanks `@powercoderlol (Ivan Polyakov) `_. -+ -+* Made ``fmt::ptr`` work with function pointers -+ (`#2131 `_). For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("My main: {}\n", fmt::ptr(main)); -+ } -+ -+ Thanks `@mikecrowe (Mike Crowe) `_. -+ -+* The undocumented support for specializing ``formatter`` for pointer types -+ has been removed. -+ -+* Fixed ``fmt::formatted_size`` with format string compilation -+ (`#2141 `_, -+ `#2161 `_). -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Fixed handling of empty format strings during format string compilation -+ (`#2042 `_): -+ -+ .. code:: c++ -+ -+ auto s = fmt::format(FMT_COMPILE("")); -+ -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Fixed handling of enums in ``fmt::to_string`` -+ (`#2036 `_). -+ -+* Improved width computation -+ (`#2033 `_, -+ `#2091 `_). For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("{:-<10}{}\n", "你好", "世界"); -+ fmt::print("{:-<10}{}\n", "hello", "world"); -+ } -+ -+ prints -+ -+ .. image:: https://user-images.githubusercontent.com/576385/ -+ 119840373-cea3ca80-beb9-11eb-91e0-54266c48e181.png -+ -+ on a modern terminal. -+ -+* The experimental fast output stream (``fmt::ostream``) is now truncated by -+ default for consistency with ``fopen`` -+ (`#2018 `_). For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::ostream out1 = fmt::output_file("guide"); -+ out1.print("Zaphod"); -+ out1.close(); -+ fmt::ostream out2 = fmt::output_file("guide"); -+ out2.print("Ford"); -+ } -+ -+ writes "Ford" to the file "guide". To preserve the old file content if any -+ pass ``fmt::file::WRONLY | fmt::file::CREATE`` flags to ``fmt::output_file``. -+ -+* Fixed moving of ``fmt::ostream`` that holds buffered data -+ (`#2197 `_, -+ `#2198 `_). -+ Thanks `@vtta `_. -+ -+* Replaced the ``fmt::system_error`` exception with a function of the same -+ name that constructs ``std::system_error`` -+ (`#2266 `_). -+ -+* Replaced the ``fmt::windows_error`` exception with a function of the same -+ name that constructs ``std::system_error`` with the category returned by -+ ``fmt::system_category()`` -+ (`#2274 `_, -+ `#2275 `_). -+ The latter is similar to ``std::sytem_category`` but correctly handles UTF-8. -+ Thanks `@phprus (Vladislav Shchapov) `_. -+ -+* Replaced ``fmt::error_code`` with ``std::error_code`` and made it formattable -+ (`#2269 `_, -+ `#2270 `_, -+ `#2273 `_). -+ Thanks `@phprus (Vladislav Shchapov) `_. -+ -+* Added speech synthesis support -+ (`#2206 `_). -+ -+* Made ``format_to`` work with a memory buffer that has a custom allocator -+ (`#2300 `_). -+ Thanks `@voxmea `_. -+ -+* Added ``Allocator::max_size`` support to ``basic_memory_buffer``. -+ (`#1960 `_). -+ Thanks `@phprus (Vladislav Shchapov) `_. -+ -+* Added wide string support to ``fmt::join`` -+ (`#2236 `_). -+ Thanks `@crbrz `_. -+ -+* Made iterators passed to ``formatter`` specializations via a format context -+ satisfy C++20 ``std::output_iterator`` requirements -+ (`#2156 `_, -+ `#2158 `_, -+ `#2195 `_, -+ `#2204 `_). -+ Thanks `@randomnetcat (Jason Cobb) `_. -+ -+* Optimized the ``printf`` implementation -+ (`#1982 `_, -+ `#1984 `_, -+ `#2016 `_, -+ `#2164 `_). -+ Thanks `@rimathia `_ and -+ `@moiwi `_. -+ -+* Improved detection of ``constexpr`` ``char_traits`` -+ (`#2246 `_, -+ `#2257 `_). -+ Thanks `@phprus (Vladislav Shchapov) `_. -+ -+* Fixed writing to ``stdout`` when it is redirected to ``NUL`` on Windows -+ (`#2080 `_). -+ -+* Fixed exception propagation from iterators -+ (`#2097 `_). -+ -+* Improved ``strftime`` error handling -+ (`#2238 `_, -+ `#2244 `_). -+ Thanks `@yumeyao `_. -+ -+* Stopped using deprecated GCC UDL template extension. -+ -+* Added ``fmt/args.h`` to the install target -+ (`#2096 `_). -+ -+* Error messages are now passed to assert when exceptions are disabled -+ (`#2145 `_). -+ Thanks `@NobodyXu (Jiahao XU) `_. -+ -+* Added the ``FMT_MASTER_PROJECT`` CMake option to control build and install -+ targets when {fmt} is included via ``add_subdirectory`` -+ (`#2098 `_, -+ `#2100 `_). -+ Thanks `@randomizedthinking `_. -+ -+* Improved build configuration -+ (`#2026 `_, -+ `#2122 `_). -+ Thanks `@luncliff (Park DongHa) `_ and -+ `@ibaned (Dan Ibanez) `_. -+ -+* Fixed various warnings and compilation issues -+ (`#1947 `_, -+ `#1959 `_, -+ `#1963 `_, -+ `#1965 `_, -+ `#1966 `_, -+ `#1974 `_, -+ `#1975 `_, -+ `#1990 `_, -+ `#2000 `_, -+ `#2001 `_, -+ `#2002 `_, -+ `#2004 `_, -+ `#2006 `_, -+ `#2009 `_, -+ `#2010 `_, -+ `#2038 `_, -+ `#2039 `_, -+ `#2047 `_, -+ `#2053 `_, -+ `#2059 `_, -+ `#2065 `_, -+ `#2067 `_, -+ `#2068 `_, -+ `#2073 `_, -+ `#2103 `_, -+ `#2105 `_, -+ `#2106 `_, -+ `#2107 `_, -+ `#2116 `_, -+ `#2117 `_, -+ `#2118 `_, -+ `#2119 `_, -+ `#2127 `_, -+ `#2128 `_, -+ `#2140 `_, -+ `#2142 `_, -+ `#2143 `_, -+ `#2144 `_, -+ `#2147 `_, -+ `#2148 `_, -+ `#2149 `_, -+ `#2152 `_, -+ `#2160 `_, -+ `#2170 `_, -+ `#2175 `_, -+ `#2176 `_, -+ `#2177 `_, -+ `#2178 `_, -+ `#2179 `_, -+ `#2180 `_, -+ `#2181 `_, -+ `#2183 `_, -+ `#2184 `_, -+ `#2185 `_, -+ `#2186 `_, -+ `#2187 `_, -+ `#2190 `_, -+ `#2192 `_, -+ `#2194 `_, -+ `#2205 `_, -+ `#2210 `_, -+ `#2211 `_, -+ `#2215 `_, -+ `#2216 `_, -+ `#2218 `_, -+ `#2220 `_, -+ `#2228 `_, -+ `#2229 `_, -+ `#2230 `_, -+ `#2233 `_, -+ `#2239 `_, -+ `#2248 `_, -+ `#2252 `_, -+ `#2253 `_, -+ `#2255 `_, -+ `#2261 `_, -+ `#2278 `_, -+ `#2284 `_, -+ `#2287 `_, -+ `#2289 `_, -+ `#2290 `_, -+ `#2293 `_, -+ `#2295 `_, -+ `#2296 `_, -+ `#2297 `_, -+ `#2311 `_, -+ `#2313 `_, -+ `#2315 `_, -+ `#2320 `_, -+ `#2321 `_, -+ `#2323 `_, -+ `#2328 `_, -+ `#2329 `_, -+ `#2333 `_, -+ `#2338 `_, -+ `#2341 `_). -+ Thanks `@darklukee `_, -+ `@fagg (Ashton Fagg) `_, -+ `@killerbot242 (Lieven de Cock) `_, -+ `@jgopel (Jonathan Gopel) `_, -+ `@yeswalrus (Walter Gray) `_, -+ `@Finkman `_, -+ `@HazardyKnusperkeks (Björn Schäpers) `_, -+ `@dkavolis (Daumantas Kavolis) `_, -+ `@concatime (Issam Maghni) `_, -+ `@chronoxor (Ivan Shynkarenka) `_, -+ `@summivox (Yin Zhong) `_, -+ `@yNeo `_, -+ `@Apache-HB (Elliot) `_, -+ `@alexezeder (Alexey Ochapov) `_, -+ `@toojays (John Steele Scott) `_, -+ `@Brainy0207 `_, -+ `@vadz (VZ) `_, -+ `@imsherlock (Ryan Sherlock) `_, -+ `@phprus (Vladislav Shchapov) `_, -+ `@white238 (Chris White) `_, -+ `@yafshar (Yaser Afshar) `_, -+ `@BillyDonahue (Billy Donahue) `_, -+ `@jstaahl `_, -+ `@denchat `_, -+ `@DanielaE (Daniela Engert) `_, -+ `@ilyakurdyukov (Ilya Kurdyukov) `_, -+ `@ilmai `_, -+ `@JessyDL (Jessy De Lannoit) `_, -+ `@sergiud (Sergiu Deitsch) `_, -+ `@mwinterb `_, -+ `@sven-herrmann `_, -+ `@jmelas (John Melas) `_, -+ `@twoixter (Jose Miguel Pérez) `_, -+ `@crbrz `_, -+ `@upsj (Tobias Ribizel) `_. -+ -+* Improved documentation -+ (`#1986 `_, -+ `#2051 `_, -+ `#2057 `_, -+ `#2081 `_, -+ `#2084 `_, -+ `#2312 `_). -+ Thanks `@imba-tjd (谭九鼎) `_, -+ `@0x416c69 (AlιAѕѕaѕѕιN) `_, -+ `@mordante `_. -+ -+* Continuous integration and test improvements -+ (`#1969 `_, -+ `#1991 `_, -+ `#2020 `_, -+ `#2110 `_, -+ `#2114 `_, -+ `#2196 `_, -+ `#2217 `_, -+ `#2247 `_, -+ `#2256 `_, -+ `#2336 `_, -+ `#2346 `_). -+ Thanks `@jgopel (Jonathan Gopel) `_, -+ `@alexezeder (Alexey Ochapov) `_ and -+ `@DanielaE (Daniela Engert) `_. -+ -+7.1.3 - 2020-11-24 -+------------------ -+ -+* Fixed handling of buffer boundaries in ``format_to_n`` -+ (`#1996 `_, -+ `#2029 `_). -+ -+* Fixed linkage errors when linking with a shared library -+ (`#2011 `_). -+ -+* Reintroduced ostream support to range formatters -+ (`#2014 `_). -+ -+* Worked around an issue with mixing std versions in gcc -+ (`#2017 `_). -+ -+7.1.2 - 2020-11-04 -+------------------ -+ -+* Fixed floating point formatting with large precision -+ (`#1976 `_). -+ -+7.1.1 - 2020-11-01 -+------------------ -+ -+* Fixed ABI compatibility with 7.0.x -+ (`#1961 `_). -+ -+* Added the ``FMT_ARM_ABI_COMPATIBILITY`` macro to work around ABI -+ incompatibility between GCC and Clang on ARM -+ (`#1919 `_). -+ -+* Worked around a SFINAE bug in GCC 8 -+ (`#1957 `_). -+ -+* Fixed linkage errors when building with GCC's LTO -+ (`#1955 `_). -+ -+* Fixed a compilation error when building without ``__builtin_clz`` or equivalent -+ (`#1968 `_). -+ Thanks `@tohammer (Tobias Hammer) `_. -+ -+* Fixed a sign conversion warning -+ (`#1964 `_). -+ Thanks `@OptoCloud `_. -+ -+7.1.0 - 2020-10-25 -+------------------ -+ -+* Switched from `Grisu3 -+ `_ -+ to `Dragonbox `_ for the default -+ floating-point formatting which gives the shortest decimal representation -+ with round-trip guarantee and correct rounding -+ (`#1882 `_, -+ `#1887 `_, -+ `#1894 `_). This makes {fmt} up to -+ 20-30x faster than common implementations of ``std::ostringstream`` and -+ ``sprintf`` on `dtoa-benchmark `_ -+ and faster than double-conversion and Ryū: -+ -+ .. image:: https://user-images.githubusercontent.com/576385/ -+ 95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png -+ -+ It is possible to get even better performance at the cost of larger binary -+ size by compiling with the ``FMT_USE_FULL_CACHE_DRAGONBOX`` macro set to 1. -+ -+ Thanks `@jk-jeon (Junekey Jeon) `_. -+ -+* Added an experimental unsynchronized file output API which, together with -+ `format string compilation `_, -+ can give `5-9 times speed up compared to fprintf -+ `_ -+ on common platforms (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ auto f = fmt::output_file("guide"); -+ f.print("The answer is {}.", 42); -+ } -+ -+* Added a formatter for ``std::chrono::time_point`` -+ (`#1819 `_, -+ `#1837 `_). For example -+ (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ auto now = std::chrono::system_clock::now(); -+ fmt::print("The time is {:%H:%M:%S}.\n", now); -+ } -+ -+ Thanks `@adamburgess (Adam Burgess) `_. -+ -+* Added support for ranges with non-const ``begin``/``end`` to ``fmt::join`` -+ (`#1784 `_, -+ `#1786 `_). For example -+ (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ #include -+ -+ int main() { -+ using std::literals::string_literals::operator""s; -+ auto strs = std::array{"a"s, "bb"s, "ccc"s}; -+ auto range = strs | ranges::views::filter( -+ [] (const std::string &x) { return x.size() != 2; } -+ ); -+ fmt::print("{}\n", fmt::join(range, "")); -+ } -+ -+ prints "accc". -+ -+ Thanks `@tonyelewis (Tony E Lewis) `_. -+ -+* Added a ``memory_buffer::append`` overload that takes a range -+ (`#1806 `_). -+ Thanks `@BRevzin (Barry Revzin) `_. -+ -+* Improved handling of single code units in ``FMT_COMPILE``. For example: -+ -+ .. code:: c++ -+ -+ #include -+ -+ char* f(char* buf) { -+ return fmt::format_to(buf, FMT_COMPILE("x{}"), 42); -+ } -+ -+ compiles to just (`godbolt `__): -+ -+ .. code:: asm -+ -+ _Z1fPc: -+ movb $120, (%rdi) -+ xorl %edx, %edx -+ cmpl $42, _ZN3fmt2v76detail10basic_dataIvE23zero_or_powers_of_10_32E+8(%rip) -+ movl $3, %eax -+ seta %dl -+ subl %edx, %eax -+ movzwl _ZN3fmt2v76detail10basic_dataIvE6digitsE+84(%rip), %edx -+ cltq -+ addq %rdi, %rax -+ movw %dx, -2(%rax) -+ ret -+ -+ Here a single ``mov`` instruction writes ``'x'`` (``$120``) to the output -+ buffer. -+ -+* Added dynamic width support to format string compilation -+ (`#1809 `_). -+ -+* Improved error reporting for unformattable types: now you'll get the type name -+ directly in the error message instead of the note: -+ -+ .. code:: c++ -+ -+ #include -+ -+ struct how_about_no {}; -+ -+ int main() { -+ fmt::print("{}", how_about_no()); -+ } -+ -+ Error (`godbolt `__): -+ -+ ``fmt/core.h:1438:3: error: static_assert failed due to requirement -+ 'fmt::v7::formattable()' "Cannot format an argument. -+ To make type T formattable provide a formatter specialization: -+ https://fmt.dev/latest/api.html#udt" -+ ...`` -+ -+* Added the `make_args_checked `_ -+ function template that allows you to write formatting functions with -+ compile-time format string checks and avoid binary code bloat -+ (`godbolt `__): -+ -+ .. code:: c++ -+ -+ void vlog(const char* file, int line, fmt::string_view format, -+ fmt::format_args args) { -+ fmt::print("{}: {}: ", file, line); -+ fmt::vprint(format, args); -+ } -+ -+ template -+ void log(const char* file, int line, const S& format, Args&&... args) { -+ vlog(file, line, format, -+ fmt::make_args_checked(format, args...)); -+ } -+ -+ #define MY_LOG(format, ...) \ -+ log(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__) -+ -+ MY_LOG("invalid squishiness: {}", 42); -+ -+* Replaced ``snprintf`` fallback with a faster internal IEEE 754 ``float`` and -+ ``double`` formatter for arbitrary precision. For example -+ (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("{:.500}\n", 4.9406564584124654E-324); -+ } -+ -+ prints -+ -+ ``4.9406564584124654417656879286822137236505980261432476442558568250067550727020875186529983636163599237979656469544571773092665671035593979639877479601078187812630071319031140452784581716784898210368871863605699873072305000638740915356498438731247339727316961514003171538539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748012970999954193198940908041656332452475714786901472678015935523861155013480352649347201937902681071074917033322268447533357208324319360923829e-324``. -+ -+* Made ``format_to_n`` and ``formatted_size`` part of the `core API -+ `__ -+ (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ char buffer[10]; -+ auto result = fmt::format_to_n(buffer, sizeof(buffer), "{}", 42); -+ } -+ -+* Added ``fmt::format_to_n`` overload with format string compilation -+ (`#1764 `_, -+ `#1767 `_, -+ `#1869 `_). For example -+ (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ char buffer[8]; -+ fmt::format_to_n(buffer, sizeof(buffer), FMT_COMPILE("{}"), 42); -+ } -+ -+ Thanks `@Kurkin (Dmitry Kurkin) `_, -+ `@alexezeder (Alexey Ochapov) `_. -+ -+* Added ``fmt::format_to`` overload that take ``text_style`` -+ (`#1593 `_, -+ `#1842 `_, -+ `#1843 `_). For example -+ (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ std::string out; -+ fmt::format_to(std::back_inserter(out), -+ fmt::emphasis::bold | fg(fmt::color::red), -+ "The answer is {}.", 42); -+ } -+ -+ Thanks `@Naios (Denis Blank) `_. -+ -+* Made the ``'#'`` specifier emit trailing zeros in addition to the decimal -+ point (`#1797 `_). For example -+ (`godbolt `__): -+ -+ .. code:: c++ -+ -+ #include -+ -+ int main() { -+ fmt::print("{:#.2g}", 0.5); -+ } -+ -+ prints ``0.50``. -+ -+* Changed the default floating point format to not include ``.0`` for -+ consistency with ``std::format`` and ``std::to_chars`` -+ (`#1893 `_, -+ `#1943 `_). It is possible to get -+ the decimal point and trailing zero with the ``#`` specifier. -+ -+* Fixed an issue with floating-point formatting that could result in addition of -+ a non-significant trailing zero in rare cases e.g. ``1.00e-34`` instead of -+ ``1.0e-34`` (`#1873 `_, -+ `#1917 `_). -+ -+* Made ``fmt::to_string`` fallback on ``ostream`` insertion operator if -+ the ``formatter`` specialization is not provided -+ (`#1815 `_, -+ `#1829 `_). -+ Thanks `@alexezeder (Alexey Ochapov) `_. -+ -+* Added support for the append mode to the experimental file API and -+ improved ``fcntl.h`` detection. -+ (`#1847 `_, -+ `#1848 `_). -+ Thanks `@t-wiser `_. -+ -+* Fixed handling of types that have both an implicit conversion operator and -+ an overloaded ``ostream`` insertion operator -+ (`#1766 `_). -+ -+* Fixed a slicing issue in an internal iterator type -+ (`#1822 `_). -+ Thanks `@BRevzin (Barry Revzin) `_. -+ -+* Fixed an issue in locale-specific integer formatting -+ (`#1927 `_). -+ -+* Fixed handling of exotic code unit types -+ (`#1870 `_, -+ `#1932 `_). -+ -+* Improved ``FMT_ALWAYS_INLINE`` -+ (`#1878 `_). -+ Thanks `@jk-jeon (Junekey Jeon) `_. -+ -+* Removed dependency on ``windows.h`` -+ (`#1900 `_). -+ Thanks `@bernd5 (Bernd Baumanns) `_. -+ -+* Optimized counting of decimal digits on MSVC -+ (`#1890 `_). -+ Thanks `@mwinterb `_. -+ -+* Improved documentation -+ (`#1772 `_, -+ `#1775 `_, -+ `#1792 `_, -+ `#1838 `_, -+ `#1888 `_, -+ `#1918 `_, -+ `#1939 `_). -+ Thanks `@leolchat (Léonard Gérard) `_, -+ `@pepsiman (Malcolm Parsons) `_, -+ `@Klaim (Joël Lamotte) `_, -+ `@ravijanjam (Ravi J) `_, -+ `@francesco-st `_, -+ `@udnaan (Adnan) `_. -+ -+* Added the ``FMT_REDUCE_INT_INSTANTIATIONS`` CMake option that reduces the -+ binary code size at the cost of some integer formatting performance. This can -+ be useful for extremely memory-constrained embedded systems -+ (`#1778 `_, -+ `#1781 `_). -+ Thanks `@kammce (Khalil Estell) `_. -+ -+* Added the ``FMT_USE_INLINE_NAMESPACES`` macro to control usage of inline -+ namespaces (`#1945 `_). -+ Thanks `@darklukee `_. -+ -+* Improved build configuration -+ (`#1760 `_, -+ `#1770 `_, -+ `#1779 `_, -+ `#1783 `_, -+ `#1823 `_). -+ Thanks `@dvetutnev (Dmitriy Vetutnev) `_, -+ `@xvitaly (Vitaly Zaitsev) `_, -+ `@tambry (Raul Tambre) `_, -+ `@medithe `_, -+ `@martinwuehrer (Martin Wührer) `_. -+ -+* Fixed various warnings and compilation issues -+ (`#1790 `_, -+ `#1802 `_, -+ `#1808 `_, -+ `#1810 `_, -+ `#1811 `_, -+ `#1812 `_, -+ `#1814 `_, -+ `#1816 `_, -+ `#1817 `_, -+ `#1818 `_, -+ `#1825 `_, -+ `#1836 `_, -+ `#1855 `_, -+ `#1856 `_, -+ `#1860 `_, -+ `#1877 `_, -+ `#1879 `_, -+ `#1880 `_, -+ `#1896 `_, -+ `#1897 `_, -+ `#1898 `_, -+ `#1904 `_, -+ `#1908 `_, -+ `#1911 `_, -+ `#1912 `_, -+ `#1928 `_, -+ `#1929 `_, -+ `#1935 `_, -+ `#1937 `_, -+ `#1942 `_, -+ `#1949 `_). -+ Thanks `@TheQwertiest `_, -+ `@medithe `_, -+ `@martinwuehrer (Martin Wührer) `_, -+ `@n16h7hunt3r `_, -+ `@Othereum (Seokjin Lee) `_, -+ `@gsjaardema (Greg Sjaardema) `_, -+ `@AlexanderLanin (Alexander Lanin) `_, -+ `@gcerretani (Giovanni Cerretani) `_, -+ `@chronoxor (Ivan Shynkarenka) `_, -+ `@noizefloor (Jan Schwers) `_, -+ `@akohlmey (Axel Kohlmeyer) `_, -+ `@jk-jeon (Junekey Jeon) `_, -+ `@rimathia `_, -+ `@rglarix (Riccardo Ghetta (larix)) `_, -+ `@moiwi `_, -+ `@heckad (Kazantcev Andrey) `_, -+ `@MarcDirven `_. -+ `@BartSiwek (Bart Siwek) `_, -+ `@darklukee `_. -+ - 7.0.3 - 2020-08-06 - ------------------ - -@@ -49,7 +1547,7 @@ - `_. - - * Added a simpler and more efficient `format string compilation API -- `_: -+ `_: - - .. code:: c++ - -@@ -188,7 +1686,7 @@ - - Thanks `@BRevzin (Barry Revzin) `_. - --* Added support for named args, ``clear`` and ``reserve`` to -+* Added support for named arguments, ``clear`` and ``reserve`` to - ``dynamic_format_arg_store`` - (`#1655 `_, - `#1663 `_, -@@ -1705,7 +3203,7 @@ - - * Implemented ``constexpr`` parsing of format strings and `compile-time format - string checks -- `_. For -+ `_. For - example - - .. code:: c++ -@@ -1766,7 +3264,7 @@ - throw format_error("invalid specifier"); - - * Added `iterator support -- `_: -+ `_: - - .. code:: c++ - -@@ -1777,7 +3275,7 @@ - fmt::format_to(std::back_inserter(out), "{}", 42); - - * Added the `format_to_n -- `_ -+ `_ - function that restricts the output to the specified number of characters - (`#298 `_): - -@@ -1788,7 +3286,7 @@ - // out == "1234" (without terminating '\0') - - * Added the `formatted_size -- `_ -+ `_ - function for computing the output size: - - .. code:: c++ -@@ -1798,7 +3296,7 @@ - auto size = fmt::formatted_size("{}", 12345); // size == 5 - - * Improved compile times by reducing dependencies on standard headers and -- providing a lightweight `core API `_: -+ providing a lightweight `core API `_: - - .. code:: c++ - -@@ -1810,7 +3308,7 @@ - `_. - - * Added the `make_format_args -- `_ -+ `_ - function for capturing formatting arguments: - - .. code:: c++ -@@ -1892,7 +3390,7 @@ - fmt::format("{} {two}", 1, fmt::arg("two", 2)); - - * Removed the write API in favor of the `format API -- `_ with compile-time handling of -+ `_ with compile-time handling of - format strings. - - * Disallowed formatting of multibyte strings into a wide character target -diff --git a/external/fmt/README.rst b/external/fmt/README.rst -index 722a65eb..394f28d9 100644 ---- a/external/fmt/README.rst -+++ b/external/fmt/README.rst -@@ -1,54 +1,69 @@ - {fmt} - ===== - --.. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master -- :target: https://travis-ci.org/fmtlib/fmt -+.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg -+ :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux - --.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v -+.. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg -+ :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos -+ -+.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg -+ :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows -+ -+.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v?svg=true - :target: https://ci.appveyor.com/project/vitaut/fmt - --.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg -- :alt: fmt is continuously fuzzed att oss-fuzz -+.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg -+ :alt: fmt is continuously fuzzed at oss-fuzz - :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\ - colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\ -- Summary&q=proj%3Dlibfmt&can=1 -+ Summary&q=proj%3Dfmt&can=1 - - .. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg - :alt: Ask questions at StackOverflow with the tag fmt - :target: https://stackoverflow.com/questions/tagged/fmt - --**{fmt}** is an open-source formatting library for C++. --It can be used as a safe and fast alternative to (s)printf and iostreams. -+**{fmt}** is an open-source formatting library providing a fast and safe -+alternative to C stdio and C++ iostreams. -+ -+If you like this project, please consider donating to the BYSOL -+Foundation that helps victims of political repressions in Belarus: -+https://bysol.org/en/bs/general/. - --`Documentation `__ -+`Documentation `__ - - Q&A: ask questions on `StackOverflow with the tag fmt - `_. - -+Try {fmt} in `Compiler Explorer `_. -+ - Features - -------- - --* Simple `format API `_ with positional arguments -+* Simple `format API `_ with positional arguments - for localization - * Implementation of `C++20 std::format - `__ --* `Format string syntax `_ similar to the one -- of Python's -+* `Format string syntax `_ similar to Python's - `format `_ -+* Fast IEEE 754 floating-point formatter with correct rounding, shortness and -+ round-trip guarantees - * Safe `printf implementation -- `_ including -- the POSIX extension for positional arguments --* Extensibility: support for user-defined types -+ `_ including the POSIX -+ extension for positional arguments -+* Extensibility: `support for user-defined types -+ `_ - * High performance: faster than common standard library implementations of -- `printf `_, -- iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ and -- `Converting a hundred million integers to strings per second -+ ``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ -+ and `Converting a hundred million integers to strings per second - `_ --* Small code size both in terms of source code (the minimum configuration -- consists of just three header files, ``core.h``, ``format.h`` and -- ``format-inl.h``) and compiled code. See `Compile time and code bloat`_ --* Reliability: the library has an extensive set of `unit tests -- `_ and is continuously fuzzed -+* Small code size both in terms of source code with the minimum configuration -+ consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``, -+ and compiled code; see `Compile time and code bloat`_ -+* Reliability: the library has an extensive set of `tests -+ `_ and is `continuously fuzzed -+ `_ - * Safety: the library is fully type safe, errors in format strings can be - reported at compile time, automatic memory management prevents buffer overflow - errors -@@ -57,18 +72,17 @@ Features - `_ - * `Portability `_ with - consistent output across platforms and support for older compilers --* Clean warning-free codebase even on high warning levels -- (``-Wall -Wextra -pedantic``) -+* Clean warning-free codebase even on high warning levels such as -+ ``-Wall -Wextra -pedantic`` - * Locale-independence by default --* Support for wide strings - * Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro - --See the `documentation `_ for more details. -+See the `documentation `_ for more details. - - Examples - -------- - --Print ``Hello, world!`` to ``stdout``: -+**Print to stdout** (`run `_) - - .. code:: c++ - -@@ -78,100 +92,95 @@ Print ``Hello, world!`` to ``stdout``: - fmt::print("Hello, world!\n"); - } - --Format a string: -+**Format a string** (`run `_) - - .. code:: c++ - - std::string s = fmt::format("The answer is {}.", 42); - // s == "The answer is 42." - --Format a string using positional arguments: -+**Format a string using positional arguments** (`run `_) - - .. code:: c++ - - std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); - // s == "I'd rather be happy than right." - --Print a chrono duration: -+**Print chrono durations** (`run `_) - - .. code:: c++ - - #include - - int main() { -- using namespace std::chrono_literals; -- fmt::print("Elapsed time: {}", 42ms); -+ using namespace std::literals::chrono_literals; -+ fmt::print("Default format: {} {}\n", 42s, 100ms); -+ fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); - } - --prints "Elapsed time: 42ms". -+Output:: - --Check a format string at compile time: -+ Default format: 42s 100ms -+ strftime-like format: 03:15:30 - --.. code:: c++ -+**Print a container** (`run `_) - -- // test.cc -- #include -- std::string s = format(FMT_STRING("{:d}"), "hello"); -+.. code:: c++ - --gives a compile-time error because ``d`` is an invalid format specifier for a --string. -+ #include -+ #include - --Use {fmt} as a safe portable replacement for ``itoa`` --(`godbolt `_): -+ int main() { -+ std::vector v = {1, 2, 3}; -+ fmt::print("{}\n", v); -+ } - --.. code:: c++ -+Output:: - -- fmt::memory_buffer buf; -- format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10) -- format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16) -- // access the string with to_string(buf) or buf.data() -+ [1, 2, 3] - --Format objects of user-defined types via a simple `extension API --`_: -+**Check a format string at compile time** - - .. code:: c++ - -- #include -+ std::string s = fmt::format("{:d}", "I am not a number"); - -- struct date { -- int year, month, day; -- }; -+This gives a compile-time error in C++20 because ``d`` is an invalid format -+specifier for a string. -+ -+**Write a file from a single thread** -+ -+.. code:: c++ - -- template <> -- struct fmt::formatter { -- constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } -+ #include - -- template -- auto format(const date& d, FormatContext& ctx) { -- return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day); -- } -- }; -+ int main() { -+ auto out = fmt::output_file("guide.txt"); -+ out.print("Don't {}", "Panic"); -+ } - -- std::string s = fmt::format("The date is {}", date{2012, 12, 9}); -- // s == "The date is 2012-12-9" -+This can be `5 to 9 times faster than fprintf -+`_. - --Create your own functions similar to `format --`_ and --`print `_ --which take arbitrary arguments (`godbolt `_): -+**Print with colors and text styles** - - .. code:: c++ - -- // Prints formatted error message. -- void vreport_error(const char* format, fmt::format_args args) { -- fmt::print("Error: "); -- fmt::vprint(format, args); -- } -- template -- void report_error(const char* format, const Args & ... args) { -- vreport_error(format, fmt::make_format_args(args...)); -+ #include -+ -+ int main() { -+ fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, -+ "Hello, {}!\n", "world"); -+ fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | -+ fmt::emphasis::underline, "Hello, {}!\n", "мир"); -+ fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, -+ "Hello, {}!\n", "世界"); - } - -- report_error("file not found: {}", path); -+Output on a modern terminal: - --Note that ``vreport_error`` is not parameterized on argument types which can --improve compile times and reduce code size compared to a fully parameterized --version. -+.. image:: https://user-images.githubusercontent.com/ -+ 576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png - - Benchmarks - ---------- -@@ -196,14 +205,16 @@ The above results were generated by building ``tinyformat_test.cpp`` on macOS - best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` - or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for - further details refer to the `source --`_. -+`_. - --{fmt} is up to 10x faster than ``std::ostringstream`` and ``sprintf`` on -+{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on - floating-point formatting (`dtoa-benchmark `_) --and faster than `double-conversion `_: -+and faster than `double-conversion `_ and -+`ryu `_: - --.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png -- :target: https://fmt.dev/unknown_mac64_clang10.0.html -+.. image:: https://user-images.githubusercontent.com/576385/ -+ 95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png -+ :target: https://fmt.dev/unknown_mac64_clang12.0.html - - Compile time and code bloat - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -@@ -278,34 +289,46 @@ Then you can run the speed test:: - or the bloat test:: - - $ make bloat-test -+ -+Migrating code -+-------------- -+ -+`clang-tidy-fmt `_ provides clang -+tidy checks for converting occurrences of ``printf`` and ``fprintf`` to -+``fmt::print``. - - Projects using this library - --------------------------- - --* `0 A.D. `_: A free, open-source, cross-platform -+* `0 A.D. `_: a free, open-source, cross-platform - real-time strategy game - -+* `2GIS `_: free business listings with a city map -+ - * `AMPL/MP `_: -- An open-source library for mathematical programming -+ an open-source library for mathematical programming - - * `Aseprite `_: -- Animated sprite editor & pixel art tool -+ animated sprite editor & pixel art tool - --* `AvioBook `_: A comprehensive aircraft -+* `AvioBook `_: a comprehensive aircraft - operations suite - --* `Celestia `_: Real-time 3D visualization of space -+* `Blizzard Battle.net `_: an online gaming platform -+ -+* `Celestia `_: real-time 3D visualization of space - --* `Ceph `_: A scalable distributed storage system -+* `Ceph `_: a scalable distributed storage system - --* `ccache `_: A compiler cache -+* `ccache `_: a compiler cache - --* `ClickHouse `_: analytical database management system -+* `ClickHouse `_: analytical database -+ management system - --* `CUAUV `_: Cornell University's autonomous underwater -+* `CUAUV `_: Cornell University's autonomous underwater - vehicle - --* `Drake `_: A planning, control, and analysis toolbox -+* `Drake `_: a planning, control, and analysis toolbox - for nonlinear dynamical systems (MIT) - - * `Envoy `_: C++ L7 proxy and communication bus -@@ -313,71 +336,82 @@ Projects using this library - - * `FiveM `_: a modification framework for GTA V - -+* `fmtlog `_: a performant fmtlib-style -+ logging library with latency in nanoseconds -+ - * `Folly `_: Facebook open-source library - -+* `Grand Mountain Adventure -+ `_: -+ A beautiful open-world ski & snowboarding game -+ - * `HarpyWar/pvpgn `_: - Player vs Player Gaming Network with tweaks - --* `KBEngine `_: An open-source MMOG server engine -+* `KBEngine `_: an open-source MMOG server -+ engine - --* `Keypirinha `_: A semantic launcher for Windows -+* `Keypirinha `_: a semantic launcher for Windows - --* `Kodi `_ (formerly xbmc): Home theater software -+* `Kodi `_ (formerly xbmc): home theater software - --* `Knuth `_: High-performance Bitcoin full-node -+* `Knuth `_: high-performance Bitcoin full-node - - * `Microsoft Verona `_: -- Research programming language for concurrent ownership -+ research programming language for concurrent ownership - --* `MongoDB `_: Distributed document database -+* `MongoDB `_: distributed document database - --* `MongoDB Smasher `_: A small tool to -+* `MongoDB Smasher `_: a small tool to - generate randomized datasets - --* `OpenSpace `_: An open-source -+* `OpenSpace `_: an open-source - astrovisualization framework - - * `PenUltima Online (POL) `_: -- An MMO server, compatible with most Ultima Online clients -+ an MMO server, compatible with most Ultima Online clients - --* `PyTorch `_: An open-source machine -+* `PyTorch `_: an open-source machine - learning library - --* `quasardb `_: A distributed, high-performance, -+* `quasardb `_: a distributed, high-performance, - associative database -+ -+* `Quill `_: asynchronous low-latency logging library - --* `readpe `_: Read Portable Executable -+* `QKW `_: generalizing aliasing to simplify -+ navigation, and executing complex multi-line terminal command sequences - --* `redis-cerberus `_: A Redis cluster -+* `redis-cerberus `_: a Redis cluster - proxy - --* `redpanda `_: A 10x faster Kafka® replacement -+* `redpanda `_: a 10x faster Kafka® replacement - for mission critical systems written in C++ - --* `rpclib `_: A modern C++ msgpack-RPC server and client -+* `rpclib `_: a modern C++ msgpack-RPC server and client - library - - * `Salesforce Analytics Cloud - `_: -- Business intelligence software -+ business intelligence software - --* `Scylla `_: A Cassandra-compatible NoSQL data store -+* `Scylla `_: a Cassandra-compatible NoSQL data store - that can handle 1 million transactions per second on a single server - --* `Seastar `_: An advanced, open-source C++ -+* `Seastar `_: an advanced, open-source C++ - framework for high-performance server applications on modern hardware - --* `spdlog `_: Super fast C++ logging library -+* `spdlog `_: super fast C++ logging library - --* `Stellar `_: Financial platform -+* `Stellar `_: financial platform - --* `Touch Surgery `_: Surgery simulator -+* `Touch Surgery `_: surgery simulator - --* `TrinityCore `_: Open-source -+* `TrinityCore `_: open-source - MMORPG framework - --* `Windows Terminal `_: The new Windows -- Terminal -+* `Windows Terminal `_: the new Windows -+ terminal - - `More... `_ - -@@ -435,7 +469,7 @@ Boost Format - - This is a very powerful library which supports both ``printf``-like format - strings and positional arguments. Its main drawback is performance. According to --various benchmarks it is much slower than other methods considered here. Boost -+various benchmarks, it is much slower than other methods considered here. Boost - Format also has excessive build times and severe code bloat issues (see - `Benchmarks`_). - -diff --git a/external/fmt/doc/CMakeLists.txt b/external/fmt/doc/CMakeLists.txt -index 108aa71e..06848450 100644 ---- a/external/fmt/doc/CMakeLists.txt -+++ b/external/fmt/doc/CMakeLists.txt -@@ -4,10 +4,14 @@ if (NOT DOXYGEN) - return () - endif () - -+find_package(PythonInterp QUIET REQUIRED) -+ - add_custom_target(doc -- COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION} -+ COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/build.py -+ ${FMT_VERSION} - SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html) - -+include(GNUInstallDirs) - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/ -- DESTINATION share/doc/fmt OPTIONAL -+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt OPTIONAL - PATTERN ".doctrees" EXCLUDE) -diff --git a/external/fmt/doc/api.rst b/external/fmt/doc/api.rst -index ca016895..93fb3faf 100644 ---- a/external/fmt/doc/api.rst -+++ b/external/fmt/doc/api.rst -@@ -6,17 +6,18 @@ API Reference - - The {fmt} library API consists of the following parts: - --* :ref:`fmt/core.h `: the core API providing argument handling -- facilities and a lightweight subset of formatting functions --* :ref:`fmt/format.h `: the full format API providing compile-time -- format string checks, wide string, output iterator and user-defined type -- support --* :ref:`fmt/ranges.h `: additional formatting support for ranges -- and tuples -+* :ref:`fmt/core.h `: the core API providing main formatting functions -+ for ``char``/UTF-8 with compile-time checks and minimal dependencies -+* :ref:`fmt/format.h `: the full format API providing additional -+ formatting functions and locale support -+* :ref:`fmt/ranges.h `: formatting of ranges and tuples - * :ref:`fmt/chrono.h `: date and time formatting - * :ref:`fmt/compile.h `: format string compilation -+* :ref:`fmt/color.h `: terminal color and text style -+* :ref:`fmt/os.h `: system APIs - * :ref:`fmt/ostream.h `: ``std::ostream`` support - * :ref:`fmt/printf.h `: ``printf`` formatting -+* :ref:`fmt/xchar.h `: optional ``wchar_t`` support - - All functions and types provided by the library reside in namespace ``fmt`` and - macros have prefix ``FMT_``. -@@ -26,34 +27,66 @@ macros have prefix ``FMT_``. - Core API - ======== - --``fmt/core.h`` defines the core API which provides argument handling facilities --and a lightweight subset of formatting functions. In the header-only mode --include ``fmt/format.h`` instead of ``fmt/core.h``. -+``fmt/core.h`` defines the core API which provides main formatting functions for -+``char``/UTF-8 with compile-time checks. It has minimal include dependencies for -+better compile times. This header is only beneficial when using {fmt} as a -+library and not in the header-only mode. - - The following functions use :ref:`format string syntax ` - similar to that of Python's `str.format --`_. --They take *format_str* and *args* as arguments. -+`_. -+They take *fmt* and *args* as arguments. - --*format_str* is a format string that contains literal text and replacement --fields surrounded by braces ``{}``. The fields are replaced with formatted --arguments in the resulting string. A function taking *format_str* doesn't --participate in an overload resolution if the latter is not a string. -+*fmt* is a format string that contains literal text and replacement fields -+surrounded by braces ``{}``. The fields are replaced with formatted arguments -+in the resulting string. `~fmt::format_string` is a format string which can be -+implicitly constructed from a string literal or a ``constexpr`` string and is -+checked at compile time in C++20. To pass a runtime format string wrap it in -+`fmt::runtime`. - - *args* is an argument list representing objects to be formatted. - - .. _format: - --.. doxygenfunction:: format(const S&, Args&&...) --.. doxygenfunction:: vformat(const S&, basic_format_args>>) -+.. doxygenfunction:: format(format_string fmt, T&&... args) -> std::string -+.. doxygenfunction:: vformat(string_view fmt, format_args args) -> std::string -+ -+.. doxygenfunction:: format_to(OutputIt out, format_string fmt, T&&... args) -> OutputIt -+.. doxygenfunction:: format_to_n(OutputIt out, size_t n, format_string fmt, T&&... args) -> format_to_n_result -+.. doxygenfunction:: formatted_size(format_string fmt, T&&... args) -> size_t -+ -+.. doxygenstruct:: fmt::format_to_n_result -+ :members: - - .. _print: - --.. doxygenfunction:: print(const S&, Args&&...) --.. doxygenfunction:: vprint(string_view, format_args) -+.. doxygenfunction:: fmt::print(format_string fmt, T&&... args) -+.. doxygenfunction:: fmt::vprint(string_view fmt, format_args args) -+ -+.. doxygenfunction:: print(std::FILE *f, format_string fmt, T&&... args) -+.. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args) -+ -+Compile-time Format String Checks -+--------------------------------- -+ -+Compile-time checks are enabled when using ``FMT_STRING``. They support built-in -+and string types as well as user-defined types with ``constexpr`` ``parse`` -+functions in their ``formatter`` specializations. -+Requires C++14 and is a no-op in C++11. -+ -+.. doxygendefine:: FMT_STRING -+ -+To force the use of compile-time checks, define the preprocessor variable -+``FMT_ENFORCE_COMPILE_STRING``. When set, functions accepting ``FMT_STRING`` -+will fail to compile with regular strings. Runtime-checked -+formatting is still possible using ``fmt::vformat``, ``fmt::vprint``, etc. -+ -+.. doxygenclass:: fmt::basic_format_string -+ :members: -+ -+.. doxygentypedef:: fmt::format_string - --.. doxygenfunction:: print(std::FILE *, const S&, Args&&...) --.. doxygenfunction:: vprint(std::FILE *, string_view, format_args) -+.. doxygenfunction:: fmt::runtime(const S&) - - Named Arguments - --------------- -@@ -65,6 +98,35 @@ Named arguments are not supported in compile-time checks at the moment. - Argument Lists - -------------- - -+You can create your own formatting function with compile-time checks and small -+binary footprint, for example (https://godbolt.org/z/oba4Mc): -+ -+.. code:: c++ -+ -+ #include -+ -+ void vlog(const char* file, int line, fmt::string_view format, -+ fmt::format_args args) { -+ fmt::print("{}: {}: ", file, line); -+ fmt::vprint(format, args); -+ } -+ -+ template -+ void log(const char* file, int line, const S& format, Args&&... args) { -+ vlog(file, line, format, -+ fmt::make_args_checked(format, args...)); -+ } -+ -+ #define MY_LOG(format, ...) \ -+ log(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__) -+ -+ MY_LOG("invalid squishiness: {}", 42); -+ -+Note that ``vlog`` is not parameterized on argument types which improves compile -+times and reduces binary code size compared to a fully parameterized version. -+ -+.. doxygenfunction:: fmt::make_args_checked(const S&, const remove_reference_t&...) -+ - .. doxygenfunction:: fmt::make_format_args(const Args&...) - - .. doxygenclass:: fmt::format_arg_store -@@ -76,11 +138,16 @@ Argument Lists - .. doxygenclass:: fmt::basic_format_args - :members: - --.. doxygenstruct:: fmt::format_args -+.. doxygentypedef:: fmt::format_args - - .. doxygenclass:: fmt::basic_format_arg - :members: - -+.. doxygenclass:: fmt::basic_format_context -+ :members: -+ -+.. doxygentypedef:: fmt::format_context -+ - Compatibility - ------------- - -@@ -88,12 +155,11 @@ Compatibility - :members: - - .. doxygentypedef:: fmt::string_view --.. doxygentypedef:: fmt::wstring_view - - Locale - ------ - --All formatting is locale-independent by default. Use the ``'n'`` format -+All formatting is locale-independent by default. Use the ``'L'`` format - specifier to insert the appropriate number separator characters from the - locale:: - -@@ -108,17 +174,10 @@ locale:: - Format API - ========== - --``fmt/format.h`` defines the full format API providing compile-time format --string checks, wide string, output iterator and user-defined type support. -+``fmt/format.h`` defines the full format API providing additional formatting -+functions and locale support. - --Compile-time Format String Checks ----------------------------------- -- --Compile-time checks are supported for built-in and string types as well as --user-defined types with ``constexpr`` ``parse`` functions in their ``formatter`` --specializations. -- --.. doxygendefine:: FMT_STRING -+.. _udt: - - Formatting User-defined Types - ----------------------------- -@@ -128,16 +187,16 @@ template and implement ``parse`` and ``format`` methods:: - - #include - -- struct point { double x, y; }; -+ struct point { -+ double x, y; -+ }; - -- template <> -- struct fmt::formatter { -+ template <> struct fmt::formatter { - // Presentation format: 'f' - fixed, 'e' - exponential. - char presentation = 'f'; - - // Parses format specifications of the form ['f' | 'e']. -- constexpr auto parse(format_parse_context& ctx) { -- // auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) // c++11 -+ constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { - // [ctx.begin(), ctx.end()) is a character range that contains a part of - // the format string starting from the format specifications to be parsed, - // e.g. in -@@ -154,8 +213,7 @@ template and implement ``parse`` and ``format`` methods:: - if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++; - - // Check if reached the end of the range: -- if (it != end && *it != '}') -- throw format_error("invalid format"); -+ if (it != end && *it != '}') throw format_error("invalid format"); - - // Return an iterator past the end of the parsed range: - return it; -@@ -164,13 +222,11 @@ template and implement ``parse`` and ``format`` methods:: - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. - template -- auto format(const point& p, FormatContext& ctx) { -- // auto format(const point &p, FormatContext &ctx) -> decltype(ctx.out()) // c++11 -+ auto format(const point& p, FormatContext& ctx) -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. -- return format_to( -- ctx.out(), -- presentation == 'f' ? "({:.1f}, {:.1f})" : "({:.1e}, {:.1e})", -- p.x, p.y); -+ return presentation == 'f' -+ ? format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y) -+ : format_to(ctx.out(), "({:.1e}, {:.1e})", p.x, p.y); - } - }; - -@@ -237,15 +293,11 @@ You can also write a formatter for a hierarchy of classes:: - fmt::print("{}", a); // prints "B" - } - --.. doxygenclass:: fmt::basic_format_parse_context -- :members: -+If a type provides both a ``formatter`` specialization and an implicit -+conversion to a formattable type, the specialization takes precedence over the -+conversion. - --Output Iterator Support ------------------------- -- --.. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...) --.. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, const S&, const Args&...) --.. doxygenstruct:: fmt::format_to_n_result -+.. doxygenclass:: fmt::basic_format_parse_context - :members: - - Literal-based API -@@ -253,28 +305,26 @@ Literal-based API - - The following user-defined literals are defined in ``fmt/format.h``. - --.. doxygenfunction:: operator""_format(const char *, size_t) -+.. doxygenfunction:: operator""_format(const char *s, size_t n) -> detail::udl_formatter - --.. doxygenfunction:: operator""_a(const char *, size_t) -+.. doxygenfunction:: operator""_a(const char *s, size_t) -> detail::udl_arg - - Utilities - --------- - --.. doxygenstruct:: fmt::is_char -- --.. doxygentypedef:: fmt::char_t -+.. doxygenfunction:: fmt::ptr(T p) -> const void* -+.. doxygenfunction:: fmt::ptr(const std::unique_ptr &p) -> const void* -+.. doxygenfunction:: fmt::ptr(const std::shared_ptr &p) -> const void* - --.. doxygenfunction:: fmt::formatted_size(string_view, const Args&...) -+.. doxygenfunction:: fmt::to_string(const T &value) -> std::string - --.. doxygenfunction:: fmt::to_string(const T&) -+.. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view - --.. doxygenfunction:: fmt::to_wstring(const T&) -+.. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view, detail::sentinel_t> - --.. doxygenfunction:: fmt::to_string_view(const Char *) -+.. doxygenfunction:: fmt::join(It begin, Sentinel end, string_view sep) -> join_view - --.. doxygenfunction:: fmt::join(const Range&, string_view) -- --.. doxygenfunction:: fmt::join(It, Sentinel, string_view) -+.. doxygenfunction:: fmt::group_digits(T value) -> group_digits_view - - .. doxygenclass:: fmt::detail::buffer - :members: -@@ -286,20 +336,14 @@ Utilities - System Errors - ------------- - --fmt does not use ``errno`` to communicate errors to the user, but it may call --system functions which set ``errno``. Users should not make any assumptions about --the value of ``errno`` being preserved by library functions. -+{fmt} does not use ``errno`` to communicate errors to the user, but it may call -+system functions which set ``errno``. Users should not make any assumptions -+about the value of ``errno`` being preserved by library functions. - --.. doxygenclass:: fmt::system_error -- :members: -+.. doxygenfunction:: fmt::system_error - - .. doxygenfunction:: fmt::format_system_error - --.. doxygenclass:: fmt::windows_error -- :members: -- --.. _formatstrings: -- - Custom Allocators - ----------------- - -@@ -318,8 +362,8 @@ allocator:: - - custom_string vformat(custom_allocator alloc, fmt::string_view format_str, - fmt::format_args args) { -- custom_memory_buffer buf(alloc); -- fmt::vformat_to(buf, format_str, args); -+ auto buf = custom_memory_buffer(alloc); -+ fmt::vformat_to(std::back_inserter(buf), format_str, args); - return custom_string(buf.data(), buf.size(), alloc); - } - -@@ -330,10 +374,10 @@ allocator:: - return vformat(alloc, format_str, fmt::make_format_args(args...)); - } - --The allocator will be used for the output container only. If you are using named --arguments, the container that stores pointers to them will be allocated using --the default allocator. Also floating-point formatting falls back on ``sprintf`` --which may do allocations. -+The allocator will be used for the output container only. Formatting functions -+normally don't do any allocations for built-in and string types except for -+non-default floating-point formatting that occasionally falls back on -+``sprintf``. - - .. _ranges-api: - -@@ -365,41 +409,84 @@ Using ``fmt::join``, you can separate tuple elements with a custom separator:: - Date and Time Formatting - ======================== - --The library supports `strftime --`_-like date and time --formatting:: -+``fmt/chrono.h`` provides formatters for -+ -+* `std::chrono::duration `_ -+* `std::chrono::time_point -+ `_ -+* `std::tm `_ -+ -+The format syntax is described in :ref:`chrono-specs`. -+ -+**Example**:: - - #include - -- std::time_t t = std::time(nullptr); -- // Prints "The date is 2016-04-29." (with the current date) -- fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t)); -+ int main() { -+ std::time_t t = std::time(nullptr); -+ -+ // Prints "The date is 2020-11-07." (with the current date): -+ fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t)); -+ -+ using namespace std::literals::chrono_literals; -+ -+ // Prints "Default format: 42s 100ms": -+ fmt::print("Default format: {} {}\n", 42s, 100ms); - --The format string syntax is described in the documentation of --`strftime `_. -+ // Prints "strftime-like format: 03:15:30": -+ fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); -+ } -+ -+.. doxygenfunction:: localtime(std::time_t time) -+ -+.. doxygenfunction:: gmtime(std::time_t time) - - .. _compile-api: - - Format string compilation - ========================= - --``fmt/compile.h`` provides format string compilation support. Format strings --are parsed at compile time and converted into efficient formatting code. This --supports arguments of built-in and string types as well as user-defined types --with ``constexpr`` ``parse`` functions in their ``formatter`` specializations. --Format string compilation can generate more binary code compared to the default --API and is only recommended in places where formatting is a performance --bottleneck. -+``fmt/compile.h`` provides format string compilation support when using -+``FMT_COMPILE``. Format strings are parsed, checked and converted into efficient -+formatting code at compile-time. This supports arguments of built-in and string -+types as well as user-defined types with ``constexpr`` ``parse`` functions in -+their ``formatter`` specializations. Format string compilation can generate more -+binary code compared to the default API and is only recommended in places where -+formatting is a performance bottleneck. - - .. doxygendefine:: FMT_COMPILE - -+.. _color-api: -+ -+Terminal color and text style -+============================= -+ -+``fmt/color.h`` provides support for terminal color and text style output. -+ -+.. doxygenfunction:: print(const text_style &ts, const S &format_str, const Args&... args) -+ -+.. doxygenfunction:: fg(detail::color_type) -+ -+.. doxygenfunction:: bg(detail::color_type) -+ -+.. _os-api: -+ -+System APIs -+=========== -+ -+.. doxygenclass:: fmt::ostream -+ :members: -+ -+.. doxygenfunction:: fmt::windows_error -+ :members: -+ - .. _ostream-api: - - ``std::ostream`` Support - ======================== - - ``fmt/ostream.h`` provides ``std::ostream`` support including formatting of --user-defined types that have overloaded ``operator<<``:: -+user-defined types that have an overloaded insertion operator (``operator<<``):: - - #include - -@@ -416,7 +503,10 @@ user-defined types that have overloaded ``operator<<``:: - std::string s = fmt::format("The date is {}", date(2012, 12, 9)); - // s == "The date is 2012-12-9" - --.. doxygenfunction:: print(std::basic_ostream&, const S&, Args&&...) -+{fmt} only supports insertion operators that are defined in the same namespaces -+as the types they format and can be found with the argument-dependent lookup. -+ -+.. doxygenfunction:: print(std::basic_ostream &os, const S &format_str, Args&&... args) - - .. _printf-api: - -@@ -425,18 +515,32 @@ user-defined types that have overloaded ``operator<<``:: - - The header ``fmt/printf.h`` provides ``printf``-like formatting functionality. - The following functions use `printf format string syntax --`_ with -+`_ with - the POSIX extension for positional arguments. Unlike their standard - counterparts, the ``fmt`` functions are type-safe and throw an exception if an - argument type doesn't match its format specification. - --.. doxygenfunction:: printf(const S&, const Args&...) -+.. doxygenfunction:: printf(const S &format_str, const T&... args) -+ -+.. doxygenfunction:: fprintf(std::FILE *f, const S &fmt, const T&... args) -> int -+ -+.. doxygenfunction:: sprintf(const S&, const T&...) -+ -+.. _xchar-api: -+ -+``wchar_t`` Support -+=================== - --.. doxygenfunction:: fprintf(std::FILE *, const S&, const Args&...) -+The optional header ``fmt/xchar.h`` provides support for ``wchar_t`` and exotic -+character types. -+ -+.. doxygenstruct:: fmt::is_char -+ -+.. doxygentypedef:: fmt::wstring_view - --.. doxygenfunction:: fprintf(std::basic_ostream&, const S&, const Args&...) -+.. doxygentypedef:: fmt::wformat_context - --.. doxygenfunction:: sprintf(const S&, const Args&...) -+.. doxygenfunction:: fmt::to_wstring(const T &value) - - Compatibility with C++20 ``std::format`` - ======================================== -@@ -447,9 +551,6 @@ differences: - - * Names are defined in the ``fmt`` namespace instead of ``std`` to avoid - collisions with standard library implementations. --* The ``'L'`` format specifier cannot be combined with presentation specifiers -- yet. - * Width calculation doesn't use grapheme clusterization. The latter has been - implemented in a separate branch but hasn't been integrated yet. --* Chrono formatting doesn't support C++20 date types since they are not provided -- by standard library implementations. -+* Most C++20 chrono types are not supported yet. -diff --git a/external/fmt/doc/basic-bootstrap/layout.html b/external/fmt/doc/basic-bootstrap/layout.html -index a257b03d..5519c4b5 100644 ---- a/external/fmt/doc/basic-bootstrap/layout.html -+++ b/external/fmt/doc/basic-bootstrap/layout.html -@@ -90,12 +90,14 @@

{{ _('Navigation') }}

- VERSION: '{{ release|e }}', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}', -+ LINK_SUFFIX: '{{ link_suffix }}', -+ SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}', - HAS_SOURCE: {{ has_source|lower }}, - SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}' - }; - - {%- for scriptfile in script_files %} -- -+ {{ js_tag(scriptfile) }} - {%- endfor %} - {%- endmacro %} - -diff --git a/external/fmt/doc/build.py b/external/fmt/doc/build.py -index 06e105aa..ae1ccfc8 100755 ---- a/external/fmt/doc/build.py -+++ b/external/fmt/doc/build.py -@@ -1,63 +1,35 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - # Build the documentation. - --from __future__ import print_function --import errno, os, shutil, sys, tempfile --from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE --from distutils.version import LooseVersion -+import errno, os, re, sys -+from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT - --versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3'] -+versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1'] - --def pip_install(package, commit=None, **kwargs): -- "Install package using pip." -- min_version = kwargs.get('min_version') -- if min_version: -- from pkg_resources import get_distribution, DistributionNotFound -- try: -- installed_version = get_distribution(os.path.basename(package)).version -- if LooseVersion(installed_version) >= min_version: -- print('{} {} already installed'.format(package, min_version)) -- return -- except DistributionNotFound: -- pass -- if commit: -- package = 'git+https://github.com/{0}.git@{1}'.format(package, commit) -- print('Installing {0}'.format(package)) -- check_call(['pip', 'install', package]) -+class Pip: -+ def __init__(self, venv_dir): -+ self.path = os.path.join(venv_dir, 'bin', 'pip') - --def create_build_env(dirname='virtualenv'): -+ def install(self, package, commit=None): -+ "Install package using pip." -+ if commit: -+ package = 'git+https://github.com/{0}.git@{1}'.format(package, commit) -+ print('Installing {0}'.format(package)) -+ check_call([self.path, 'install', package]) -+ -+def create_build_env(venv_dir='virtualenv'): - # Create virtualenv. -- if not os.path.exists(dirname): -- check_call(['virtualenv', dirname]) -- import sysconfig -- scripts_dir = os.path.basename(sysconfig.get_path('scripts')) -- activate_this_file = os.path.join(dirname, scripts_dir, 'activate_this.py') -- with open(activate_this_file) as f: -- exec(f.read(), dict(__file__=activate_this_file)) -- # Import get_distribution after activating virtualenv to get info about -- # the correct packages. -- from pkg_resources import get_distribution, DistributionNotFound -- # Upgrade pip because installation of sphinx with pip 1.1 available on Travis -- # is broken (see #207) and it doesn't support the show command. -- pip_version = get_distribution('pip').version -- if LooseVersion(pip_version) < LooseVersion('1.5.4'): -- print("Updating pip") -- check_call(['pip', 'install', '--upgrade', 'pip']) -- # Upgrade distribute because installation of sphinx with distribute 0.6.24 -- # available on Travis is broken (see #207). -- try: -- distribute_version = get_distribution('distribute').version -- if LooseVersion(distribute_version) <= LooseVersion('0.6.24'): -- print("Updating distribute") -- check_call(['pip', 'install', '--upgrade', 'distribute']) -- except DistributionNotFound: -- pass -- # Install Sphinx and Breathe. -- pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee', -- min_version='1.4.1.dev20160531') -- pip_install('michaeljones/breathe', -- '129222318f7c8f865d2631e7da7b033567e7f56a', -- min_version='4.2.0') -+ if not os.path.exists(venv_dir): -+ check_call(['python3', '-m', 'venv', venv_dir]) -+ # Install Sphinx and Breathe. Require the exact version of Sphinx which is -+ # compatible with Breathe. -+ pip = Pip(venv_dir) -+ pip.install('wheel') -+ pip.install('six') -+ # See: https://github.com/sphinx-doc/sphinx/issues/9777 -+ pip.install('docutils==0.17.1') -+ pip.install('sphinx-doc/sphinx', 'v3.3.0') -+ pip.install('michaeljones/breathe', 'v4.25.0') - - def build_docs(version='dev', **kwargs): - doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__))) -@@ -66,16 +38,17 @@ def build_docs(version='dev', **kwargs): - 'include_dir', os.path.join(os.path.dirname(doc_dir), 'include', 'fmt')) - # Build docs. - cmd = ['doxygen', '-'] -- p = Popen(cmd, stdin=PIPE) -+ p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT) - doxyxml_dir = os.path.join(work_dir, 'doxyxml') -- p.communicate(input=r''' -+ out, _ = p.communicate(input=r''' - PROJECT_NAME = fmt - GENERATE_LATEX = NO - GENERATE_MAN = NO - GENERATE_RTF = NO - CASE_SENSE_NAMES = NO -- INPUT = {0}/core.h {0}/compile.h {0}/format.h {0}/os.h \ -- {0}/ostream.h {0}/printf.h {0}/time.h -+ INPUT = {0}/chrono.h {0}/color.h {0}/core.h {0}/compile.h \ -+ {0}/format.h {0}/os.h {0}/ostream.h {0}/printf.h \ -+ {0}/xchar.h - QUIET = YES - JAVADOC_AUTOBRIEF = YES - AUTOLINK_SUPPORT = NO -@@ -86,6 +59,8 @@ def build_docs(version='dev', **kwargs): - ALIASES += "endrst=\endverbatim" - MACRO_EXPANSION = YES - PREDEFINED = _WIN32=1 \ -+ __linux__=1 \ -+ FMT_ENABLE_IF(...)= \ - FMT_USE_VARIADIC_TEMPLATES=1 \ - FMT_USE_RVALUE_REFERENCES=1 \ - FMT_USE_USER_DEFINED_LITERALS=1 \ -@@ -94,20 +69,39 @@ def build_docs(version='dev', **kwargs): - "FMT_BEGIN_NAMESPACE=namespace fmt {{" \ - "FMT_END_NAMESPACE=}}" \ - "FMT_STRING_ALIAS=1" \ -- "FMT_ENABLE_IF(B)=" -- EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str -+ "FMT_VARIADIC(...)=" \ -+ "FMT_VARIADIC_W(...)=" \ -+ "FMT_DOC=1" -+ EXCLUDE_SYMBOLS = fmt::formatter fmt::printf_formatter fmt::arg_join \ -+ fmt::basic_format_arg::handle - '''.format(include_dir, doxyxml_dir).encode('UTF-8')) -+ out = out.decode('utf-8') -+ internal_symbols = [ -+ 'fmt::detail::.*', -+ 'basic_data<>', -+ 'fmt::type_identity', -+ 'fmt::dynamic_formatter' -+ ] -+ noisy_warnings = [ -+ 'warning: (Compound|Member .* of class) (' + '|'.join(internal_symbols) + \ -+ ') is not documented.', -+ 'warning: Internal inconsistency: .* does not belong to any container!' -+ ] -+ for w in noisy_warnings: -+ out = re.sub('.*' + w + '\n', '', out) -+ print(out) - if p.returncode != 0: - raise CalledProcessError(p.returncode, cmd) -+ - html_dir = os.path.join(work_dir, 'html') - main_versions = reversed(versions[-3:]) -- check_call(['sphinx-build', -+ check_call([os.path.join(work_dir, 'virtualenv', 'bin', 'sphinx-build'), - '-Dbreathe_projects.format=' + os.path.abspath(doxyxml_dir), - '-Dversion=' + version, '-Drelease=' + version, - '-Aversion=' + version, '-Aversions=' + ','.join(main_versions), - '-b', 'html', doc_dir, html_dir]) - try: -- check_call(['lessc', '--clean-css', -+ check_call(['lessc', '--verbose', '--clean-css', - '--include-path=' + os.path.join(doc_dir, 'bootstrap'), - os.path.join(doc_dir, 'fmt.less'), - os.path.join(html_dir, '_static', 'fmt.css')]) -diff --git a/external/fmt/doc/fmt.less b/external/fmt/doc/fmt.less -index 55c716b8..3a97b9fd 100644 ---- a/external/fmt/doc/fmt.less -+++ b/external/fmt/doc/fmt.less -@@ -56,6 +56,11 @@ div.sphinxsidebar { - padding: 0; - } - -+// Override center alignment in tables. -+td { -+ text-align: left; -+} -+ - p.rubric { - margin-top: 10px; - } -diff --git a/external/fmt/doc/index.rst b/external/fmt/doc/index.rst -index 59dc6652..92221225 100644 ---- a/external/fmt/doc/index.rst -+++ b/external/fmt/doc/index.rst -@@ -23,24 +23,26 @@ Format API - - The format API is similar in spirit to the C ``printf`` family of function but - is safer, simpler and several times `faster --`_ -+`_ - than common standard library implementations. - The `format string syntax `_ is similar to the one used by --`str.format `_ in -+`str.format `_ in - Python: - - .. code:: c++ - -- fmt::format("The answer is {}.", 42); -+ std::string s = fmt::format("The answer is {}.", 42); - - The ``fmt::format`` function returns a string "The answer is 42.". You can use - ``fmt::memory_buffer`` to avoid constructing ``std::string``: - - .. code:: c++ - -- fmt::memory_buffer out; -- format_to(out, "For a moment, {} happened.", "nothing"); -- out.data(); // returns a pointer to the formatted data -+ auto out = fmt::memory_buffer(); -+ format_to(std::back_inserter(out), -+ "For a moment, {} happened.", "nothing"); -+ auto data = out.data(); // pointer to the formatted data -+ auto size = out.size(); // size of the formatted data - - The ``fmt::print`` function performs formatting and writes the result to a stream: - -@@ -48,21 +50,19 @@ The ``fmt::print`` function performs formatting and writes the result to a strea - - fmt::print(stderr, "System error code = {}\n", errno); - --The file argument can be omitted in which case the function prints to --``stdout``: -+If you omit the file argument the function will print to ``stdout``: - - .. code:: c++ - - fmt::print("Don't {}\n", "panic"); - --The Format API also supports positional arguments useful for localization: -+The format API also supports positional arguments useful for localization: - - .. code:: c++ - - fmt::print("I'd rather be {1} than {0}.", "right", "happy"); - --Named arguments can be created with ``fmt::arg``. This makes it easier to track --what goes where when multiple arguments are being formatted: -+You can pass named arguments with ``fmt::arg``: - - .. code:: c++ - -@@ -91,16 +91,17 @@ time. For example, the code - - fmt::format("The answer is {:d}", "forty-two"); - --throws a ``format_error`` exception with description "unknown format code 'd' for --string", because the argument ``"forty-two"`` is a string while the format code --``d`` only applies to integers, while -+throws the ``format_error`` exception because the argument ``"forty-two"`` is a -+string while the format code ``d`` only applies to integers. -+ -+The code - - .. code:: c++ - - format(FMT_STRING("The answer is {:d}"), "forty-two"); - --reports a compile-time error for the same reason on compilers that support --relaxed ``constexpr``. See `here `_ for details. -+reports a compile-time error on compilers that support relaxed ``constexpr``. -+See `here `_ for details. - - The following code - -@@ -109,21 +110,15 @@ The following code - fmt::format("Cyrillic letter {}", L'\x42e'); - - produces a compile-time error because wide character ``L'\x42e'`` cannot be --formatted into a narrow string. You can use a wide format string instead: -- --.. code:: c++ -- -- fmt::format(L"Cyrillic letter {}", L'\x42e'); -- --For comparison, writing a wide character to ``std::ostream`` results in --its numeric value being written to the stream (i.e. 1070 instead of letter 'ю' --which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is --needed. -+formatted into a narrow string. For comparison, writing a wide character to -+``std::ostream`` results in its numeric value being written to the stream -+(i.e. 1070 instead of letter 'ю' which is represented by ``L'\x42e'`` if we -+use Unicode) which is rarely desirable. - - Compact Binary Code - ------------------- - --The library is designed to produce compact per-call compiled code. For example -+The library produces compact per-call compiled code. For example - (`godbolt `_), - - .. code:: c++ -@@ -144,8 +139,8 @@ compiles to just - mov rcx, rsp - mov edi, offset .L.str - mov esi, 17 -- mov edx, 2 -- call fmt::v5::vprint(fmt::v5::basic_string_view, fmt::v5::format_args) -+ mov edx, 1 -+ call fmt::v7::vprint(fmt::v7::basic_string_view, fmt::v7::format_args) - xor eax, eax - add rsp, 24 - ret -@@ -167,20 +162,19 @@ The library is highly portable and relies only on a small set of C++11 features: - * deleted functions - * alias templates - --These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older --compilers use {fmt} `version 4.x --`_ which continues to be --maintained and only requires C++98. -+These are available in GCC 4.8, Clang 3.4, MSVC 19.0 (2015) and more recent -+compiler version. For older compilers use {fmt} `version 4.x -+`_ which is maintained and -+only requires C++98. - --The output of all formatting functions is consistent across platforms. In --particular, formatting a floating-point infinity always gives ``inf`` while the --output of ``printf`` is platform-dependent. For example, -+The output of all formatting functions is consistent across platforms. -+For example, - - .. code:: - - fmt::print("{}", std::numeric_limits::infinity()); - --always prints ``inf``. -+always prints ``inf`` while the output of ``printf`` is platform-dependent. - - .. _ease-of-use: - -@@ -192,11 +186,13 @@ just three header files and no external dependencies. - A permissive MIT `license `_ allows - using the library both in open-source and commercial projects. - -+`Learn more... `_ -+ - .. raw:: html - - GitHub Repository - - -diff --git a/external/fmt/doc/syntax.rst b/external/fmt/doc/syntax.rst -index 8265f702..9d3cb57c 100644 ---- a/external/fmt/doc/syntax.rst -+++ b/external/fmt/doc/syntax.rst -@@ -16,7 +16,7 @@ literal text, it can be escaped by doubling: ``{{`` and ``}}``. - The grammar for a replacement field is as follows: - - .. productionlist:: sf -- replacement_field: "{" [`arg_id`] [":" `format_spec`] "}" -+ replacement_field: "{" [`arg_id`] [":" (`format_spec` | `chrono_format_spec`)] "}" - arg_id: `integer` | `identifier` - integer: `digit`+ - digit: "0"..."9" -@@ -27,8 +27,8 @@ The grammar for a replacement field is as follows: - In less formal terms, the replacement field can start with an *arg_id* - that specifies the argument whose value is to be formatted and inserted into - the output instead of the replacement field. --The *arg_id* is optionally followed by a *format_spec*, which is preceded --by a colon ``':'``. These specify a non-default format for the replacement value. -+The *arg_id* is optionally followed by a *format_spec*, which is preceded by a -+colon ``':'``. These specify a non-default format for the replacement value. - - See also the :ref:`formatspec` section. - -@@ -75,14 +75,14 @@ although some of the formatting options are only supported by the numeric types. - The general form of a *standard format specifier* is: - - .. productionlist:: sf -- format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`] -+ format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`]["L"][`type`] - fill: - align: "<" | ">" | "^" - sign: "+" | "-" | " " - width: `integer` | "{" [`arg_id`] "}" - precision: `integer` | "{" [`arg_id`] "}" -- type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s" -- int_type: "b" | "B" | "d" | "o" | "x" | "X" -+ type: "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | -+ : "o" | "p" | "s" | "x" | "X" - - The *fill* character can be any Unicode code point other than ``'{'`` or - ``'}'``. The presence of a fill character is signaled by the character following -@@ -112,18 +112,18 @@ meaning in this case. - The *sign* option is only valid for number types, and can be one of the - following: - --+---------+----------------------------------------------------------+ --| Option | Meaning | --+=========+==========================================================+ --| ``'+'`` | indicates that a sign should be used for both | --| | positive as well as negative numbers. | --+---------+----------------------------------------------------------+ --| ``'-'`` | indicates that a sign should be used only for negative | --| | numbers (this is the default behavior). | --+---------+----------------------------------------------------------+ --| space | indicates that a leading space should be used on | --| | positive numbers, and a minus sign on negative numbers. | --+---------+----------------------------------------------------------+ -++---------+------------------------------------------------------------+ -+| Option | Meaning | -++=========+============================================================+ -+| ``'+'`` | indicates that a sign should be used for both | -+| | nonnegative as well as negative numbers. | -++---------+------------------------------------------------------------+ -+| ``'-'`` | indicates that a sign should be used only for negative | -+| | numbers (this is the default behavior). | -++---------+------------------------------------------------------------+ -+| space | indicates that a leading space should be used on | -+| | nonnegative numbers, and a minus sign on negative numbers. | -++---------+------------------------------------------------------------+ - - The ``'#'`` option causes the "alternate form" to be used for the - conversion. The alternate form is defined differently for different -@@ -161,7 +161,11 @@ displayed after the decimal point for a floating-point value formatted with - value formatted with ``'g'`` or ``'G'``. For non-number types the field - indicates the maximum field size - in other words, how many characters will be - used from the field content. The *precision* is not allowed for integer, --character, Boolean, and pointer values. -+character, Boolean, and pointer values. Note that a C string must be -+null-terminated even if precision is specified. -+ -+The ``'L'`` option uses the current locale setting to insert the appropriate -+number separator characters. This option is only valid for numeric types. - - Finally, the *type* determines how the data should be presented. - -@@ -200,6 +204,8 @@ The available integer presentation types are: - | | ``'#'`` option with this type adds the prefix ``"0B"`` | - | | to the output value. | - +---------+----------------------------------------------------------+ -+| ``'c'`` | Character format. Outputs the number as a character. | -++---------+----------------------------------------------------------+ - | ``'d'`` | Decimal integer. Outputs the number in base 10. | - +---------+----------------------------------------------------------+ - | ``'o'`` | Octal format. Outputs the number in base 8. | -@@ -214,10 +220,6 @@ The available integer presentation types are: - | | ``'#'`` option with this type adds the prefix ``"0X"`` | - | | to the output value. | - +---------+----------------------------------------------------------+ --| ``'L'`` | Locale-specific format. This is the same as ``'d'``, | --| | except that it uses the current locale setting to insert | --| | the appropriate number separator characters. | --+---------+----------------------------------------------------------+ - | none | The same as ``'d'``. | - +---------+----------------------------------------------------------+ - -@@ -261,14 +263,8 @@ The available presentation types for floating-point values are: - | | ``'E'`` if the number gets too large. The | - | | representations of infinity and NaN are uppercased, too. | - +---------+----------------------------------------------------------+ --| ``'L'`` | Locale-specific format. This is the same as ``'g'``, | --| | except that it uses the current locale setting to insert | --| | the appropriate number separator characters. | --+---------+----------------------------------------------------------+ --| none | Similar to ``'g'``, except that fixed-point notation, | --| | when used, has at least one digit past the decimal | --| | point. The default precision is as high as needed to | --| | represent the particular value. | -+| none | Similar to ``'g'``, except that the default precision is | -+| | as high as needed to represent the particular value. | - +---------+----------------------------------------------------------+ - - .. ifconfig:: False -@@ -303,6 +299,59 @@ The available presentation types for pointers are: - | none | The same as ``'p'``. | - +---------+----------------------------------------------------------+ - -+.. _chrono-specs: -+ -+Chrono Format Specifications -+============================ -+ -+Format specifications for chrono types have the following syntax: -+ -+.. productionlist:: sf -+ chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`] -+ chrono_specs: [`chrono_specs`] `conversion_spec` | `chrono_specs` `literal_char` -+ conversion_spec: "%" [`modifier`] `chrono_type` -+ literal_char: -+ modifier: "E" | "O" -+ chrono_type: "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" | "F" | -+ : "g" | "G" | "h" | "H" | "I" | "j" | "m" | "M" | "n" | "p" | -+ : "q" | "Q" | "r" | "R" | "S" | "t" | "T" | "u" | "U" | "V" | -+ : "w" | "W" | "x" | "X" | "y" | "Y" | "z" | "Z" | "%" -+ -+Literal chars are copied unchanged to the output. Precision is valid only for -+``std::chrono::duration`` types with a floating-point representation type. -+ -+The available presentation types (*chrono_type*) for chrono durations and time -+points are: -+ -++---------+--------------------------------------------------------------------+ -+| Type | Meaning | -++=========+====================================================================+ -+| ``'H'`` | The hour (24-hour clock) as a decimal number. If the result is a | -+| | single digit, it is prefixed with 0. The modified command ``%OH`` | -+| | produces the locale's alternative representation. | -++---------+--------------------------------------------------------------------+ -+| ``'M'`` | The minute as a decimal number. If the result is a single digit, | -+| | it is prefixed with 0. The modified command ``%OM`` produces the | -+| | locale's alternative representation. | -++---------+--------------------------------------------------------------------+ -+| ``'S'`` | Seconds as a decimal number. If the number of seconds is less than | -+| | 10, the result is prefixed with 0. If the precision of the input | -+| | cannot be exactly represented with seconds, then the format is a | -+| | decimal floating-point number with a fixed format and a precision | -+| | matching that of the precision of the input (or to a microseconds | -+| | precision if the conversion to floating-point decimal seconds | -+| | cannot be made within 18 fractional digits). The character for the | -+| | decimal point is localized according to the locale. The modified | -+| | command ``%OS`` produces the locale's alternative representation. | -++---------+--------------------------------------------------------------------+ -+ -+Specifiers that have a calendaric component such as `'d'` (the day of month) -+are valid only for ``std::tm`` and not durations or time points. -+ -+``std::tm`` uses the system's `strftime -+`_ so refer to its -+documentation for details on supported conversion specifiers. -+ - .. _formatexamples: - - Format Examples -@@ -391,7 +440,7 @@ Using type-specific formatting:: - - auto t = tm(); - t.tm_year = 2010 - 1900; -- t.tm_mon = 6; -+ t.tm_mon = 7; - t.tm_mday = 4; - t.tm_hour = 12; - t.tm_min = 15; -@@ -401,7 +450,7 @@ Using type-specific formatting:: - - Using the comma as a thousands separator:: - -- #include -+ #include - - auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890); - // s == "1,234,567,890" -diff --git a/external/fmt/doc/usage.rst b/external/fmt/doc/usage.rst -index d42719e0..f9f81858 100644 ---- a/external/fmt/doc/usage.rst -+++ b/external/fmt/doc/usage.rst -@@ -15,7 +15,7 @@ Building the Library - - The included `CMake build script`__ can be used to build the fmt - library on a wide range of platforms. CMake is freely available for --download from http://www.cmake.org/download/. -+download from https://www.cmake.org/download/. - - __ https://github.com/fmtlib/fmt/blob/master/CMakeLists.txt - -@@ -50,7 +50,15 @@ To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to - - cmake -DBUILD_SHARED_LIBS=TRUE ... - --__ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries -+__ https://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries -+ -+ -+To build a `static library` with position independent code (required if the main -+consumer of the fmt library is a shared library i.e. a Python extension) set the -+``CMAKE_POSITION_INDEPENDENT_CODE`` CMake variable to ``TRUE``:: -+ -+ cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE ... -+ - - Installing the Library - ====================== -@@ -83,6 +91,49 @@ Setting up your target to use a header-only version of ``fmt`` is equally easy:: - - target_link_libraries( PRIVATE fmt::fmt-header-only) - -+Usage with build2 -+================= -+ -+You can use `build2 `_, a dependency manager and a -+build-system combined, to use ``fmt``. -+ -+Currently this package is available in these package repositories: -+ -+- **https://cppget.org/fmt/** for released and published versions. -+- `The git repository with the sources of the build2 package of fmt `_ -+ for unreleased or custom revisions of ``fmt``. -+ -+**Usage:** -+ -+- ``build2`` package name: ``fmt`` -+- Library target name : ``lib{fmt}`` -+ -+For example, to make your ``build2`` project depend on ``fmt``: -+ -+- Add one of the repositories to your configurations, or in your -+ ``repositories.manifest``, if not already there:: -+ -+ : -+ role: prerequisite -+ location: https://pkg.cppget.org/1/stable -+ -+- Add this package as a dependency to your ``./manifest`` file -+ (example for ``v7.0.x``):: -+ -+ depends: fmt ~7.0.0 -+ -+- Import the target and use it as a prerequisite to your own target -+ using `fmt` in the appropriate ``buildfile``:: -+ -+ import fmt = fmt%lib{fmt} -+ lib{mylib} : cxx{**} ... $fmt -+ -+Then build your project as usual with `b` or `bdep update`. -+ -+For ``build2`` newcomers or to get more details and use cases, you can read the -+``build2`` -+`toolchain introduction `_. -+ - Building the Documentation - ========================== - -@@ -130,6 +181,18 @@ The fmt port in vcpkg is kept up to date by Microsoft team members and community - contributors. If the version is out of date, please `create an issue or pull - request `__ on the vcpkg repository. - -+LHelper -+======= -+ -+You can download and install fmt using -+`lhelper `__ dependency manager:: -+ -+ lhelper activate -+ lhelper install fmt -+ -+All the recipes for lhelper are kept in the -+`lhelper's recipe `__ repository. -+ - Android NDK - =========== - -@@ -139,11 +202,11 @@ For an example of using fmt with Android NDK, see the - `android-ndk-example `_ - repository. - --__ https://github.com/fmtlib/fmt/blob/master/Android.mk -+__ https://github.com/fmtlib/fmt/blob/master/support/Android.mk - - Homebrew - ======== - --fmt can be installed on OS X using `Homebrew `_:: -+fmt can be installed on OS X using `Homebrew `_:: - - brew install fmt -diff --git a/external/fmt/include/fmt/args.h b/external/fmt/include/fmt/args.h -new file mode 100644 -index 00000000..9a8e4ed2 ---- /dev/null -+++ b/external/fmt/include/fmt/args.h -@@ -0,0 +1,234 @@ -+// 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: -+ constexpr dynamic_format_arg_store() = default; -+ -+ /** -+ \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/external/fmt/include/fmt/chrono.h b/external/fmt/include/fmt/chrono.h -index e70b8053..682efd8d 100644 ---- a/external/fmt/include/fmt/chrono.h -+++ b/external/fmt/include/fmt/chrono.h -@@ -8,16 +8,32 @@ - #ifndef FMT_CHRONO_H_ - #define FMT_CHRONO_H_ - -+#include - #include - #include -+#include - #include --#include -+#include -+#include - - #include "format.h" --#include "locale.h" - - FMT_BEGIN_NAMESPACE - -+// Enable tzset. -+#ifndef FMT_USE_TZSET -+// UWP doesn't provide _tzset. -+# if FMT_HAS_INCLUDE("winapifamily.h") -+# include -+# endif -+# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ -+ (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) -+# define FMT_USE_TZSET 1 -+# else -+# define FMT_USE_TZSET 0 -+# endif -+#endif -+ - // Enable safe chrono durations, unless explicitly disabled. - #ifndef FMT_SAFE_DURATION_CAST - # define FMT_SAFE_DURATION_CAST 1 -@@ -44,7 +60,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { - static_assert(T::is_integer, "To must be integral"); - - // A and B are both signed, or both unsigned. -- if (F::digits <= T::digits) { -+ if (detail::const_check(F::digits <= T::digits)) { - // From fits in To without any problem. - } else { - // From does not always fit in To, resort to a dynamic check. -@@ -72,43 +88,28 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { - static_assert(F::is_integer, "From must be integral"); - static_assert(T::is_integer, "To must be integral"); - -- if (F::is_signed && !T::is_signed) { -+ if (detail::const_check(F::is_signed && !T::is_signed)) { - // From may be negative, not allowed! - if (fmt::detail::is_negative(from)) { - ec = 1; - return {}; - } -- - // From is positive. Can it always fit in To? -- if (F::digits <= T::digits) { -- // yes, From always fits in To. -- } else { -- // from may not fit in To, we have to do a dynamic check -- if (from > static_cast((T::max)())) { -- ec = 1; -- return {}; -- } -+ if (detail::const_check(F::digits > T::digits) && -+ from > static_cast(detail::max_value())) { -+ ec = 1; -+ return {}; - } - } - -- if (!F::is_signed && T::is_signed) { -- // can from be held in To? -- if (F::digits < T::digits) { -- // yes, From always fits in To. -- } else { -- // from may not fit in To, we have to do a dynamic check -- if (from > static_cast((T::max)())) { -- // outside range. -- ec = 1; -- return {}; -- } -- } -+ if (detail::const_check(!F::is_signed && T::is_signed && -+ F::digits >= T::digits) && -+ from > static_cast(detail::max_value())) { -+ ec = 1; -+ return {}; - } -- -- // reaching here means all is ok for lossless conversion. -- return static_cast(from); -- --} // function -+ return static_cast(from); // Lossless conversion. -+} - - template ::value)> -@@ -190,11 +191,9 @@ To safe_duration_cast(std::chrono::duration from, - // safe conversion to IntermediateRep - IntermediateRep count = - lossless_integral_conversion(from.count(), ec); -- if (ec) { -- return {}; -- } -+ if (ec) return {}; - // multiply with Factor::num without overflow or underflow -- if (Factor::num != 1) { -+ if (detail::const_check(Factor::num != 1)) { - const auto max1 = detail::max_value() / Factor::num; - if (count > max1) { - ec = 1; -@@ -209,17 +208,9 @@ To safe_duration_cast(std::chrono::duration from, - count *= Factor::num; - } - -- // this can't go wrong, right? den>0 is checked earlier. -- if (Factor::den != 1) { -- count /= Factor::den; -- } -- // convert to the to type, safely -- using ToRep = typename To::rep; -- const ToRep tocount = lossless_integral_conversion(count, ec); -- if (ec) { -- return {}; -- } -- return To{tocount}; -+ if (detail::const_check(Factor::den != 1)) count /= Factor::den; -+ auto tocount = lossless_integral_conversion(count, ec); -+ return ec ? To() : To(tocount); - } - - /** -@@ -269,7 +260,7 @@ To safe_duration_cast(std::chrono::duration from, - } - - // multiply with Factor::num without overflow or underflow -- if (Factor::num != 1) { -+ if (detail::const_check(Factor::num != 1)) { - constexpr auto max1 = detail::max_value() / - static_cast(Factor::num); - if (count > max1) { -@@ -286,7 +277,7 @@ To safe_duration_cast(std::chrono::duration from, - } - - // this can't go wrong, right? den>0 is checked earlier. -- if (Factor::den != 1) { -+ if (detail::const_check(Factor::den != 1)) { - using common_t = typename std::common_type::type; - count /= static_cast(Factor::den); - } -@@ -308,13 +299,154 @@ 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 const std::locale& get_classic_locale() { -+ static const auto& locale = std::locale::classic(); -+ return locale; -+} -+ -+template struct codecvt_result { -+ static constexpr const size_t max_size = 32; -+ CodeUnit buf[max_size]; -+ CodeUnit* end; -+}; -+template -+constexpr const size_t codecvt_result::max_size; -+ -+template -+void write_codecvt(codecvt_result& out, string_view in_buf, -+ const std::locale& loc) { -+ using codecvt = std::codecvt; -+#if FMT_CLANG_VERSION -+# pragma clang diagnostic push -+# pragma clang diagnostic ignored "-Wdeprecated" -+ auto& f = std::use_facet(loc); -+# pragma clang diagnostic pop -+#else -+ auto& f = std::use_facet(loc); -+#endif -+ auto mb = std::mbstate_t(); -+ const char* from_next = nullptr; -+ auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, -+ std::begin(out.buf), std::end(out.buf), out.end); -+ if (result != std::codecvt_base::ok) -+ FMT_THROW(format_error("failed to format time")); -+} -+ -+template -+auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) -+ -> OutputIt { -+ if (detail::is_utf8() && loc != get_classic_locale()) { -+ // 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 -+ -+ using unit_t = codecvt_result; -+ unit_t unit; -+ write_codecvt(unit, in, loc); -+ // In UTF-8 is used one to four one-byte code units. -+ auto&& buf = basic_memory_buffer(); -+ for (code_unit* p = unit.buf; p != unit.end; ++p) { -+ uint32_t c = static_cast(*p); -+ if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { -+ // surrogate pair -+ ++p; -+ if (p == unit.end || (c & 0xfc00) != 0xd800 || -+ (*p & 0xfc00) != 0xdc00) { -+ FMT_THROW(format_error("failed to format time")); -+ } -+ c = (c << 10) + static_cast(*p) - 0x35fdc00; -+ } -+ if (c < 0x80) { -+ buf.push_back(static_cast(c)); -+ } else if (c < 0x800) { -+ buf.push_back(static_cast(0xc0 | (c >> 6))); -+ buf.push_back(static_cast(0x80 | (c & 0x3f))); -+ } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { -+ buf.push_back(static_cast(0xe0 | (c >> 12))); -+ buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); -+ buf.push_back(static_cast(0x80 | (c & 0x3f))); -+ } else if (c >= 0x10000 && c <= 0x10ffff) { -+ buf.push_back(static_cast(0xf0 | (c >> 18))); -+ buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); -+ buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); -+ buf.push_back(static_cast(0x80 | (c & 0x3f))); -+ } else { -+ FMT_THROW(format_error("failed to format time")); -+ } -+ } -+ return copy_str(buf.data(), buf.data() + buf.size(), out); -+ } -+ return copy_str(in.data(), in.data() + in.size(), out); -+} -+ -+template ::value)> -+auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) -+ -> OutputIt { -+ codecvt_result unit; -+ write_codecvt(unit, sv, loc); -+ return copy_str(unit.buf, unit.end, out); -+} -+ -+template ::value)> -+auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) -+ -> OutputIt { -+ return write_encoded_tm_str(out, sv, loc); -+} -+ -+template -+inline void do_write(buffer& buf, const std::tm& time, -+ const std::locale& loc, char format, char modifier) { -+ auto&& format_buf = formatbuf>(buf); -+ auto&& os = std::basic_ostream(&format_buf); -+ os.imbue(loc); -+ using iterator = std::ostreambuf_iterator; -+ const auto& facet = std::use_facet>(loc); -+ auto end = facet.put(os, os, Char(' '), &time, format, modifier); -+ if (end.failed()) FMT_THROW(format_error("failed to format time")); -+} -+ -+template ::value)> -+auto write(OutputIt out, const std::tm& time, const std::locale& loc, -+ char format, char modifier = 0) -> OutputIt { -+ auto&& buf = get_buffer(out); -+ do_write(buf, time, loc, format, modifier); -+ return buf.out(); -+} -+ -+template ::value)> -+auto write(OutputIt out, const std::tm& time, const std::locale& loc, -+ char format, char modifier = 0) -> OutputIt { -+ auto&& buf = basic_memory_buffer(); -+ do_write(buf, time, loc, format, modifier); -+ return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); -+} -+ - } // 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_; -@@ -351,7 +483,16 @@ inline std::tm localtime(std::time_t time) { - return lt.tm_; - } - --// Thread-safe replacement for std::gmtime -+inline std::tm localtime( -+ std::chrono::time_point time_point) { -+ return localtime(std::chrono::system_clock::to_time_t(time_point)); -+} -+ -+/** -+ 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_; -@@ -387,85 +528,69 @@ inline std::tm gmtime(std::time_t time) { - return gt.tm_; - } - --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); --} -- --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); -+inline std::tm gmtime( -+ std::chrono::time_point time_point) { -+ return gmtime(std::chrono::system_clock::to_time_t(time_point)); - } --} // namespace detail - --template struct formatter { -- template -- 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'); -- return end; -- } -- -- template -- auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { -- basic_memory_buffer buf; -- size_t start = buf.size(); -- for (;;) { -- size_t size = buf.capacity() - start; -- size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm); -- if (count != 0) { -- 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()); -+FMT_BEGIN_DETAIL_NAMESPACE -+ -+// Writes two-digit numbers a, b and c separated by sep to buf. -+// The method by Pavel Novikov based on -+// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. -+inline void write_digit2_separated(char* buf, unsigned a, unsigned b, -+ unsigned c, char sep) { -+ unsigned long long digits = -+ a | (b << 24) | (static_cast(c) << 48); -+ // Convert each value to BCD. -+ // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b. -+ // The difference is -+ // y - x = a * 6 -+ // a can be found from x: -+ // a = floor(x / 10) -+ // then -+ // y = x + a * 6 = x + floor(x / 10) * 6 -+ // floor(x / 10) is (x * 205) >> 11 (needs 16 bits). -+ digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6; -+ // Put low nibbles to high bytes and high nibbles to low bytes. -+ digits = ((digits & 0x00f00000f00000f0) >> 4) | -+ ((digits & 0x000f00000f00000f) << 8); -+ auto usep = static_cast(sep); -+ // Add ASCII '0' to each digit byte and insert separators. -+ digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); -+ -+ constexpr const size_t len = 8; -+ if (const_check(is_big_endian())) { -+ char tmp[len]; -+ memcpy(tmp, &digits, len); -+ std::reverse_copy(tmp, tmp + len, buf); -+ } else { -+ memcpy(buf, &digits, len); - } -+} - -- basic_memory_buffer tm_format; --}; -- --namespace detail { --template FMT_CONSTEXPR const char* get_units() { -+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, -@@ -504,6 +629,22 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - handler.on_text(tab, tab + 1); - break; - } -+ // Year: -+ case 'Y': -+ handler.on_year(numeric_system::standard); -+ break; -+ case 'y': -+ handler.on_short_year(numeric_system::standard); -+ break; -+ case 'C': -+ handler.on_century(numeric_system::standard); -+ break; -+ case 'G': -+ handler.on_iso_week_based_year(); -+ break; -+ case 'g': -+ handler.on_iso_week_based_short_year(); -+ break; - // Day of the week: - case 'a': - handler.on_abbr_weekday(); -@@ -519,11 +660,34 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - break; - // Month: - case 'b': -+ case 'h': - handler.on_abbr_month(); - break; - case 'B': - handler.on_full_month(); - break; -+ case 'm': -+ handler.on_dec_month(numeric_system::standard); -+ break; -+ // Day of the year/month: -+ case 'U': -+ handler.on_dec0_week_of_year(numeric_system::standard); -+ break; -+ case 'W': -+ handler.on_dec1_week_of_year(numeric_system::standard); -+ break; -+ case 'V': -+ handler.on_iso_week_of_year(numeric_system::standard); -+ break; -+ case 'j': -+ handler.on_day_of_year(); -+ break; -+ case 'd': -+ handler.on_day_of_month(numeric_system::standard); -+ break; -+ case 'e': -+ handler.on_day_of_month_space(numeric_system::standard); -+ break; - // Hour, minute, second: - case 'H': - handler.on_24_hour(numeric_system::standard); -@@ -582,6 +746,15 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { -+ case 'Y': -+ handler.on_year(numeric_system::alternative); -+ break; -+ case 'y': -+ handler.on_offset_year(); -+ break; -+ case 'C': -+ handler.on_century(numeric_system::alternative); -+ break; - case 'c': - handler.on_datetime(numeric_system::alternative); - break; -@@ -600,6 +773,27 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { -+ case 'y': -+ handler.on_short_year(numeric_system::alternative); -+ break; -+ case 'm': -+ handler.on_dec_month(numeric_system::alternative); -+ break; -+ case 'U': -+ handler.on_dec0_week_of_year(numeric_system::alternative); -+ break; -+ case 'W': -+ handler.on_dec1_week_of_year(numeric_system::alternative); -+ break; -+ case 'V': -+ handler.on_iso_week_of_year(numeric_system::alternative); -+ break; -+ case 'd': -+ handler.on_day_of_month(numeric_system::alternative); -+ break; -+ case 'e': -+ handler.on_day_of_month_space(numeric_system::alternative); -+ break; - case 'w': - handler.on_dec0_weekday(numeric_system::alternative); - break; -@@ -631,33 +825,566 @@ 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() {} -+template struct null_chrono_spec_handler { -+ FMT_CONSTEXPR void unsupported() { -+ static_cast(this)->unsupported(); -+ } -+ FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_offset_year() { unsupported(); } -+ FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); } -+ FMT_CONSTEXPR void on_iso_week_based_short_year() { 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_dec_month(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_day_of_year() { unsupported(); } -+ FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } -+ FMT_CONSTEXPR void on_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 tm_format_checker : null_chrono_spec_handler { -+ FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } -+ -+ template -+ FMT_CONSTEXPR void on_text(const Char*, const Char*) {} -+ FMT_CONSTEXPR void on_year(numeric_system) {} -+ FMT_CONSTEXPR void on_short_year(numeric_system) {} -+ FMT_CONSTEXPR void on_offset_year() {} -+ FMT_CONSTEXPR void on_century(numeric_system) {} -+ FMT_CONSTEXPR void on_iso_week_based_year() {} -+ FMT_CONSTEXPR void on_iso_week_based_short_year() {} -+ FMT_CONSTEXPR void on_abbr_weekday() {} -+ FMT_CONSTEXPR void on_full_weekday() {} -+ FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {} -+ FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} -+ FMT_CONSTEXPR void on_abbr_month() {} -+ FMT_CONSTEXPR void on_full_month() {} -+ FMT_CONSTEXPR void on_dec_month(numeric_system) {} -+ FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} -+ FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} -+ FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} -+ FMT_CONSTEXPR void on_day_of_year() {} -+ FMT_CONSTEXPR void on_day_of_month(numeric_system) {} -+ FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} -+ FMT_CONSTEXPR void on_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_datetime(numeric_system) {} -+ FMT_CONSTEXPR void on_loc_date(numeric_system) {} -+ FMT_CONSTEXPR void on_loc_time(numeric_system) {} -+ FMT_CONSTEXPR void on_us_date() {} -+ FMT_CONSTEXPR void on_iso_date() {} -+ 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_utc_offset() {} -+ FMT_CONSTEXPR void on_tz_name() {} -+}; -+ -+inline const char* tm_wday_full_name(int wday) { -+ static constexpr const char* full_name_list[] = { -+ "Sunday", "Monday", "Tuesday", "Wednesday", -+ "Thursday", "Friday", "Saturday"}; -+ return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; -+} -+inline const char* tm_wday_short_name(int wday) { -+ static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", -+ "Thu", "Fri", "Sat"}; -+ return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; -+} -+ -+inline const char* tm_mon_full_name(int mon) { -+ static constexpr const char* full_name_list[] = { -+ "January", "February", "March", "April", "May", "June", -+ "July", "August", "September", "October", "November", "December"}; -+ return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; -+} -+inline const char* tm_mon_short_name(int mon) { -+ static constexpr const char* short_name_list[] = { -+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", -+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", -+ }; -+ return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; -+} -+ -+template -+struct has_member_data_tm_gmtoff : std::false_type {}; -+template -+struct has_member_data_tm_gmtoff> -+ : std::true_type {}; -+ -+template -+struct has_member_data_tm_zone : std::false_type {}; -+template -+struct has_member_data_tm_zone> -+ : std::true_type {}; -+ -+#if FMT_USE_TZSET -+inline void tzset_once() { -+ static bool init = []() -> bool { -+ _tzset(); -+ return true; -+ }(); -+ ignore_unused(init); -+} -+#endif -+ -+template class tm_writer { -+ private: -+ static constexpr int days_per_week = 7; -+ -+ const std::locale& loc_; -+ const bool is_classic_; -+ OutputIt out_; -+ const std::tm& tm_; -+ -+ auto tm_sec() const noexcept -> int { -+ FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, ""); -+ return tm_.tm_sec; -+ } -+ auto tm_min() const noexcept -> int { -+ FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, ""); -+ return tm_.tm_min; -+ } -+ auto tm_hour() const noexcept -> int { -+ FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, ""); -+ return tm_.tm_hour; -+ } -+ auto tm_mday() const noexcept -> int { -+ FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, ""); -+ return tm_.tm_mday; -+ } -+ auto tm_mon() const noexcept -> int { -+ FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, ""); -+ return tm_.tm_mon; -+ } -+ auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; } -+ auto tm_wday() const noexcept -> int { -+ FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, ""); -+ return tm_.tm_wday; -+ } -+ auto tm_yday() const noexcept -> int { -+ FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, ""); -+ return tm_.tm_yday; -+ } -+ -+ auto tm_hour12() const noexcept -> int { -+ const auto h = tm_hour(); -+ const auto z = h < 12 ? h : h - 12; -+ return z == 0 ? 12 : z; -+ } -+ -+ // POSIX and the C Standard are unclear or inconsistent about what %C and %y -+ // do if the year is negative or exceeds 9999. Use the convention that %C -+ // concatenated with %y yields the same output as %Y, and that %Y contains at -+ // least 4 characters, with more only if necessary. -+ auto split_year_lower(long long year) const noexcept -> int { -+ auto l = year % 100; -+ if (l < 0) l = -l; // l in [0, 99] -+ return static_cast(l); -+ } -+ -+ // Algorithm: -+ // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date -+ auto iso_year_weeks(long long curr_year) const noexcept -> int { -+ const auto prev_year = curr_year - 1; -+ const auto curr_p = -+ (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % -+ days_per_week; -+ const auto prev_p = -+ (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % -+ days_per_week; -+ return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); -+ } -+ auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int { -+ return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / -+ days_per_week; -+ } -+ auto tm_iso_week_year() const noexcept -> long long { -+ const auto year = tm_year(); -+ const auto w = iso_week_num(tm_yday(), tm_wday()); -+ if (w < 1) return year - 1; -+ if (w > iso_year_weeks(year)) return year + 1; -+ return year; -+ } -+ auto tm_iso_week_of_year() const noexcept -> int { -+ const auto year = tm_year(); -+ const auto w = iso_week_num(tm_yday(), tm_wday()); -+ if (w < 1) return iso_year_weeks(year - 1); -+ if (w > iso_year_weeks(year)) return 1; -+ return w; -+ } -+ -+ void write1(int value) { -+ *out_++ = static_cast('0' + to_unsigned(value) % 10); -+ } -+ void write2(int value) { -+ const char* d = digits2(to_unsigned(value) % 100); -+ *out_++ = *d++; -+ *out_++ = *d; -+ } -+ -+ void write_year_extended(long long year) { -+ // At least 4 characters. -+ int width = 4; -+ if (year < 0) { -+ *out_++ = '-'; -+ year = 0 - year; -+ --width; -+ } -+ uint32_or_64_or_128_t n = to_unsigned(year); -+ const int num_digits = count_digits(n); -+ if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); -+ out_ = format_decimal(out_, n, num_digits).end; -+ } -+ void write_year(long long year) { -+ if (year >= 0 && year < 10000) { -+ write2(static_cast(year / 100)); -+ write2(static_cast(year % 100)); -+ } else { -+ write_year_extended(year); -+ } -+ } -+ -+ void write_utc_offset(long offset) { -+ if (offset < 0) { -+ *out_++ = '-'; -+ offset = -offset; -+ } else { -+ *out_++ = '+'; -+ } -+ offset /= 60; -+ write2(static_cast(offset / 60)); -+ write2(static_cast(offset % 60)); -+ } -+ template ::value)> -+ void format_utc_offset_impl(const T& tm) { -+ write_utc_offset(tm.tm_gmtoff); -+ } -+ template ::value)> -+ void format_utc_offset_impl(const T& tm) { -+#if defined(_WIN32) && defined(_UCRT) -+# if FMT_USE_TZSET -+ tzset_once(); -+# endif -+ long offset = 0; -+ _get_timezone(&offset); -+ if (tm.tm_isdst) { -+ long dstbias = 0; -+ _get_dstbias(&dstbias); -+ offset += dstbias; -+ } -+ write_utc_offset(-offset); -+#else -+ ignore_unused(tm); -+ format_localized('z'); -+#endif -+ } -+ -+ template ::value)> -+ void format_tz_name_impl(const T& tm) { -+ if (is_classic_) -+ out_ = write_tm_str(out_, tm.tm_zone, loc_); -+ else -+ format_localized('Z'); -+ } -+ template ::value)> -+ void format_tz_name_impl(const T&) { -+ format_localized('Z'); -+ } -+ -+ void format_localized(char format, char modifier = 0) { -+ out_ = write(out_, tm_, loc_, format, modifier); -+ } -+ -+ public: -+ tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm) -+ : loc_(loc), -+ is_classic_(loc_ == get_classic_locale()), -+ out_(out), -+ tm_(tm) {} -+ -+ OutputIt out() const { return out_; } -+ -+ FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { -+ out_ = copy_str(begin, end, out_); -+ } -+ -+ void on_abbr_weekday() { -+ if (is_classic_) -+ out_ = write(out_, tm_wday_short_name(tm_wday())); -+ else -+ format_localized('a'); -+ } -+ void on_full_weekday() { -+ if (is_classic_) -+ out_ = write(out_, tm_wday_full_name(tm_wday())); -+ else -+ format_localized('A'); -+ } -+ void on_dec0_weekday(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); -+ format_localized('w', 'O'); -+ } -+ void on_dec1_weekday(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) { -+ auto wday = tm_wday(); -+ write1(wday == 0 ? days_per_week : wday); -+ } else { -+ format_localized('u', 'O'); -+ } -+ } -+ -+ void on_abbr_month() { -+ if (is_classic_) -+ out_ = write(out_, tm_mon_short_name(tm_mon())); -+ else -+ format_localized('b'); -+ } -+ void on_full_month() { -+ if (is_classic_) -+ out_ = write(out_, tm_mon_full_name(tm_mon())); -+ else -+ format_localized('B'); -+ } -+ -+ void on_datetime(numeric_system ns) { -+ if (is_classic_) { -+ on_abbr_weekday(); -+ *out_++ = ' '; -+ on_abbr_month(); -+ *out_++ = ' '; -+ on_day_of_month_space(numeric_system::standard); -+ *out_++ = ' '; -+ on_iso_time(); -+ *out_++ = ' '; -+ on_year(numeric_system::standard); -+ } else { -+ format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); -+ } -+ } -+ void on_loc_date(numeric_system ns) { -+ if (is_classic_) -+ on_us_date(); -+ else -+ format_localized('x', ns == numeric_system::standard ? '\0' : 'E'); -+ } -+ void on_loc_time(numeric_system ns) { -+ if (is_classic_) -+ on_iso_time(); -+ else -+ format_localized('X', ns == numeric_system::standard ? '\0' : 'E'); -+ } -+ void on_us_date() { -+ char buf[8]; -+ write_digit2_separated(buf, to_unsigned(tm_mon() + 1), -+ to_unsigned(tm_mday()), -+ to_unsigned(split_year_lower(tm_year())), '/'); -+ out_ = copy_str(std::begin(buf), std::end(buf), out_); -+ } -+ void on_iso_date() { -+ auto year = tm_year(); -+ char buf[10]; -+ size_t offset = 0; -+ if (year >= 0 && year < 10000) { -+ copy2(buf, digits2(to_unsigned(year / 100))); -+ } else { -+ offset = 4; -+ write_year_extended(year); -+ year = 0; -+ } -+ write_digit2_separated(buf + 2, static_cast(year % 100), -+ to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), -+ '-'); -+ out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); -+ } -+ -+ void on_utc_offset() { format_utc_offset_impl(tm_); } -+ void on_tz_name() { format_tz_name_impl(tm_); } -+ -+ void on_year(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) -+ return write_year(tm_year()); -+ format_localized('Y', 'E'); -+ } -+ void on_short_year(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) -+ return write2(split_year_lower(tm_year())); -+ format_localized('y', 'O'); -+ } -+ void on_offset_year() { -+ if (is_classic_) return write2(split_year_lower(tm_year())); -+ format_localized('y', 'E'); -+ } -+ -+ void on_century(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) { -+ auto year = tm_year(); -+ auto upper = year / 100; -+ if (year >= -99 && year < 0) { -+ // Zero upper on negative year. -+ *out_++ = '-'; -+ *out_++ = '0'; -+ } else if (upper >= 0 && upper < 100) { -+ write2(static_cast(upper)); -+ } else { -+ out_ = write(out_, upper); -+ } -+ } else { -+ format_localized('C', 'E'); -+ } -+ } -+ -+ void on_dec_month(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) -+ return write2(tm_mon() + 1); -+ format_localized('m', 'O'); -+ } -+ -+ void on_dec0_week_of_year(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) -+ return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); -+ format_localized('U', 'O'); -+ } -+ void on_dec1_week_of_year(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) { -+ auto wday = tm_wday(); -+ write2((tm_yday() + days_per_week - -+ (wday == 0 ? (days_per_week - 1) : (wday - 1))) / -+ days_per_week); -+ } else { -+ format_localized('W', 'O'); -+ } -+ } -+ void on_iso_week_of_year(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) -+ return write2(tm_iso_week_of_year()); -+ format_localized('V', 'O'); -+ } -+ -+ void on_iso_week_based_year() { write_year(tm_iso_week_year()); } -+ void on_iso_week_based_short_year() { -+ write2(split_year_lower(tm_iso_week_year())); -+ } -+ -+ void on_day_of_year() { -+ auto yday = tm_yday() + 1; -+ write1(yday / 100); -+ write2(yday % 100); -+ } -+ void on_day_of_month(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); -+ format_localized('d', 'O'); -+ } -+ void on_day_of_month_space(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) { -+ auto mday = to_unsigned(tm_mday()) % 100; -+ const char* d2 = digits2(mday); -+ *out_++ = mday < 10 ? ' ' : d2[0]; -+ *out_++ = d2[1]; -+ } else { -+ format_localized('e', 'O'); -+ } -+ } -+ -+ void on_24_hour(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour()); -+ format_localized('H', 'O'); -+ } -+ void on_12_hour(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) -+ return write2(tm_hour12()); -+ format_localized('I', 'O'); -+ } -+ void on_minute(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) return write2(tm_min()); -+ format_localized('M', 'O'); -+ } -+ void on_second(numeric_system ns) { -+ if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec()); -+ format_localized('S', 'O'); -+ } -+ -+ void on_12_hour_time() { -+ if (is_classic_) { -+ char buf[8]; -+ write_digit2_separated(buf, to_unsigned(tm_hour12()), -+ to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); -+ out_ = copy_str(std::begin(buf), std::end(buf), out_); -+ *out_++ = ' '; -+ on_am_pm(); -+ } else { -+ format_localized('r'); -+ } -+ } -+ void on_24_hour_time() { -+ write2(tm_hour()); -+ *out_++ = ':'; -+ write2(tm_min()); -+ } -+ void on_iso_time() { -+ char buf[8]; -+ write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()), -+ to_unsigned(tm_sec()), ':'); -+ out_ = copy_str(std::begin(buf), std::end(buf), out_); -+ } -+ -+ void on_am_pm() { -+ if (is_classic_) { -+ *out_++ = tm_hour() < 12 ? 'A' : 'P'; -+ *out_++ = 'M'; -+ } else { -+ format_localized('p'); -+ } -+ } -+ -+ // These apply to chrono durations but not tm. - 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(); } -+}; -+ -+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)> -@@ -673,25 +1400,20 @@ template ::value)> - inline bool isfinite(T) { - return true; - } --template ::value)> --inline bool isfinite(T value) { -- return std::isfinite(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"); -+// 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 && to_unsigned(value) <= to_unsigned(upper), -+ "invalid value"); - (void)upper; -- return static_cast(value); -+ return static_cast(value); - } --template ::value)> --inline int to_nonnegative_int(T value, int upper) { -- FMT_ASSERT( -- std::isnan(value) || (value >= 0 && value <= static_cast(upper)), -- "invalid value"); -- (void)upper; -- return static_cast(value); -+template ::value)> -+inline Int to_nonnegative_int(T value, Int upper) { -+ if (value < 0 || value > static_cast(upper)) -+ FMT_THROW(format_error("invalid value")); -+ return static_cast(value); - } - - template ::value)> -@@ -748,26 +1470,55 @@ inline std::chrono::duration get_milliseconds( - #endif - } - --template ::value)> --inline std::chrono::duration get_milliseconds( -+// Returns the number of fractional digits in the range [0, 18] according to the -+// C++20 spec. If more than 18 fractional digits are required then returns 6 for -+// microseconds precision. -+constexpr int count_fractional_digits(long long num, long long den, int n = 0) { -+ return num % den == 0 -+ ? n -+ : (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1)); -+} -+ -+constexpr long long pow10(std::uint32_t n) { -+ return n == 0 ? 1 : 10 * pow10(n - 1); -+} -+ -+template ::is_signed)> -+constexpr std::chrono::duration abs( -+ std::chrono::duration d) { -+ // We need to compare the duration using the count() method directly -+ // due to a compiler bug in clang-11 regarding the spaceship operator, -+ // when -Wzero-as-null-pointer-constant is enabled. -+ // In clang-12 the bug has been fixed. See -+ // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example: -+ // https://www.godbolt.org/z/Knbb5joYx. -+ return d.count() >= d.zero().count() ? d : -d; -+} -+ -+template ::is_signed)> -+constexpr std::chrono::duration abs( - std::chrono::duration d) { -- using common_type = typename std::common_type::type; -- auto ms = mod(d.count() * static_cast(Period::num) / -- static_cast(Period::den) * 1000, -- 1000); -- return std::chrono::duration(static_cast(ms)); -+ return d; - } - --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 ? presentation_type::fixed_lower -+ : presentation_type::general_lower; -+ return write(out, val, specs); - } -+ - template - OutputIt copy_unit(string_view unit, OutputIt out, Char) { - return std::copy(unit.begin(), unit.end(), out); -@@ -785,18 +1536,44 @@ 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; - } - -+class get_locale { -+ private: -+ union { -+ std::locale locale_; -+ }; -+ bool has_locale_ = false; -+ -+ public: -+ get_locale(bool localized, locale_ref loc) : has_locale_(localized) { -+ if (localized) -+ ::new (&locale_) std::locale(loc.template get()); -+ } -+ ~get_locale() { -+ if (has_locale_) locale_.~locale(); -+ } -+ operator const std::locale&() const { -+ return has_locale_ ? locale_ : get_classic_locale(); -+ } -+}; -+ - template - struct chrono_formatter { - FormatContext& context; - OutputIt out; - int precision; -+ bool localized = false; - // rep is unsigned to avoid overflow. - using rep = - conditional_t::value && sizeof(Rep) < sizeof(int), -@@ -808,9 +1585,10 @@ struct chrono_formatter { - bool negative; - - using char_type = typename FormatContext::char_type; -+ using tm_writer_type = tm_writer; - -- explicit chrono_formatter(FormatContext& ctx, OutputIt o, -- std::chrono::duration d) -+ chrono_formatter(FormatContext& ctx, OutputIt o, -+ std::chrono::duration d) - : context(ctx), - out(o), - val(static_cast(d.count())), -@@ -885,19 +1663,48 @@ struct chrono_formatter { - out = format_decimal(out, n, num_digits).end; - } - -+ template void write_fractional_seconds(Duration d) { -+ constexpr auto num_fractional_digits = -+ count_fractional_digits(Duration::period::num, Duration::period::den); -+ -+ using subsecond_precision = std::chrono::duration< -+ typename std::common_type::type, -+ std::ratio<1, detail::pow10(num_fractional_digits)>>; -+ if (std::ratio_less::value) { -+ *out++ = '.'; -+ // Don't convert long double to integer seconds to avoid overflow. -+ using sec = conditional_t< -+ std::is_same::value, -+ std::chrono::duration, std::chrono::seconds>; -+ auto fractional = detail::abs(d) - std::chrono::duration_cast(d); -+ const auto subseconds = -+ std::chrono::treat_as_floating_point< -+ typename subsecond_precision::rep>::value -+ ? fractional.count() -+ : std::chrono::duration_cast(fractional) -+ .count(); -+ uint32_or_64_or_128_t n = -+ to_unsigned(to_nonnegative_int(subseconds, max_value())); -+ int num_digits = detail::count_digits(n); -+ if (num_fractional_digits > num_digits) -+ out = std::fill_n(out, num_fractional_digits - num_digits, '0'); -+ out = format_decimal(out, n, num_digits).end; -+ } -+ } -+ - void write_nan() { std::copy_n("nan", 3, out); } - void write_pinf() { std::copy_n("inf", 3, out); } - void write_ninf() { std::copy_n("-inf", 4, out); } - -- void format_localized(const tm& time, char format, char modifier = 0) { -+ template -+ void format_tm(const tm& time, Callback cb, Args... args) { - 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); -+ get_locale loc(localized, context.locale()); -+ auto w = tm_writer_type(loc, out, time); -+ (w.*cb)(args...); -+ out = w.out(); - } - - void on_text(const char_type* begin, const char_type* end) { -@@ -918,6 +1725,19 @@ struct chrono_formatter { - void on_iso_date() {} - void on_utc_offset() {} - void on_tz_name() {} -+ void on_year(numeric_system) {} -+ void on_short_year(numeric_system) {} -+ void on_offset_year() {} -+ void on_century(numeric_system) {} -+ void on_iso_week_based_year() {} -+ void on_iso_week_based_short_year() {} -+ void on_dec_month(numeric_system) {} -+ void on_dec0_week_of_year(numeric_system) {} -+ void on_dec1_week_of_year(numeric_system) {} -+ void on_iso_week_of_year(numeric_system) {} -+ void on_day_of_year() {} -+ void on_day_of_month(numeric_system) {} -+ void on_day_of_month_space(numeric_system) {} - - void on_24_hour(numeric_system ns) { - if (handle_nan_inf()) return; -@@ -925,7 +1745,7 @@ struct chrono_formatter { - if (ns == numeric_system::standard) return write(hour(), 2); - auto time = tm(); - time.tm_hour = to_nonnegative_int(hour(), 24); -- format_localized(time, 'H', 'O'); -+ format_tm(time, &tm_writer_type::on_24_hour, ns); - } - - void on_12_hour(numeric_system ns) { -@@ -934,7 +1754,7 @@ struct chrono_formatter { - if (ns == numeric_system::standard) return write(hour12(), 2); - auto time = tm(); - time.tm_hour = to_nonnegative_int(hour12(), 12); -- format_localized(time, 'I', 'O'); -+ format_tm(time, &tm_writer_type::on_12_hour, ns); - } - - void on_minute(numeric_system ns) { -@@ -943,7 +1763,7 @@ struct chrono_formatter { - if (ns == numeric_system::standard) return write(minute(), 2); - auto time = tm(); - time.tm_min = to_nonnegative_int(minute(), 60); -- format_localized(time, 'M', 'O'); -+ format_tm(time, &tm_writer_type::on_minute, ns); - } - - void on_second(numeric_system ns) { -@@ -951,29 +1771,17 @@ struct chrono_formatter { - - if (ns == numeric_system::standard) { - write(second(), 2); --#if FMT_SAFE_DURATION_CAST -- // convert rep->Rep -- using duration_rep = std::chrono::duration; -- using duration_Rep = std::chrono::duration; -- auto tmpval = fmt_safe_duration_cast(duration_rep{val}); --#else -- auto tmpval = std::chrono::duration(val); --#endif -- auto ms = get_milliseconds(tmpval); -- if (ms != std::chrono::milliseconds(0)) { -- *out++ = '.'; -- write(ms.count(), 3); -- } -+ write_fractional_seconds(std::chrono::duration{val}); - return; - } - auto time = tm(); - time.tm_sec = to_nonnegative_int(second(), 60); -- format_localized(time, 'S', 'O'); -+ format_tm(time, &tm_writer_type::on_second, ns); - } - - void on_12_hour_time() { - if (handle_nan_inf()) return; -- format_localized(time(), 'r'); -+ format_tm(time(), &tm_writer_type::on_12_hour_time); - } - - void on_24_hour_time() { -@@ -992,12 +1800,12 @@ struct chrono_formatter { - on_24_hour_time(); - *out++ = ':'; - if (handle_nan_inf()) return; -- write(second(), 2); -+ on_second(numeric_system::standard); - } - - void on_am_pm() { - if (handle_nan_inf()) return; -- format_localized(time(), 'p'); -+ format_tm(time(), &tm_writer_type::on_am_pm); - } - - void on_duration_value() { -@@ -1010,17 +1818,64 @@ 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; } -+}; -+ -+class year_month_day {}; -+#endif -+ -+// A rudimentary weekday formatter. -+template struct formatter { -+ private: -+ bool localized = false; -+ -+ public: -+ FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) -+ -> decltype(ctx.begin()) { -+ auto begin = ctx.begin(), end = ctx.end(); -+ if (begin != end && *begin == 'L') { -+ ++begin; -+ localized = true; -+ } -+ return begin; -+ } -+ -+ template -+ auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { -+ auto time = std::tm(); -+ time.tm_wday = static_cast(wd.c_encoding()); -+ detail::get_locale loc(localized, ctx.locale()); -+ auto w = detail::tm_writer(loc, ctx.out(), time); -+ w.on_abbr_weekday(); -+ return w.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; -+ bool localized = false; -+ basic_string_view format_str; - using duration = std::chrono::duration; - - struct spec_handler { -@@ -1043,17 +1898,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,13 +1937,16 @@ struct formatter, Char> { - else - handler.on_error("precision not allowed for this argument type"); - } -- end = parse_chrono_format(begin, end, detail::chrono_format_checker()); -+ if (begin != end && *begin == 'L') { -+ ++begin; -+ localized = true; -+ } -+ end = detail::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); -@@ -1094,30 +1956,112 @@ 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); -+ } -+}; -+ -+template -+struct formatter, -+ Char> : formatter { -+ FMT_CONSTEXPR formatter() { -+ this->do_parse(default_specs, -+ default_specs + sizeof(default_specs) / sizeof(Char)); -+ } -+ -+ template -+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { -+ return this->do_parse(ctx.begin(), ctx.end(), true); -+ } -+ -+ template -+ auto format(std::chrono::time_point val, -+ FormatContext& ctx) const -> decltype(ctx.out()) { -+ return formatter::format(localtime(val), ctx); -+ } -+ -+ static constexpr const Char default_specs[] = {'%', 'F', ' ', '%', 'T'}; -+}; -+ -+template -+constexpr const Char -+ formatter, -+ Char>::default_specs[]; -+ -+template struct formatter { -+ private: -+ enum class spec { -+ unknown, -+ year_month_day, -+ hh_mm_ss, -+ }; -+ spec spec_ = spec::unknown; -+ basic_string_view specs; -+ -+ protected: -+ template -+ FMT_CONSTEXPR auto do_parse(It begin, It end, bool with_default = false) -+ -> It { -+ if (begin != end && *begin == ':') ++begin; -+ end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); -+ if (!with_default || end != begin) -+ specs = {begin, detail::to_unsigned(end - begin)}; -+ // basic_string_view<>::compare isn't constexpr before C++17. -+ if (specs.size() == 2 && specs[0] == Char('%')) { -+ if (specs[1] == Char('F')) -+ spec_ = spec::year_month_day; -+ else if (specs[1] == Char('T')) -+ spec_ = spec::hh_mm_ss; -+ } -+ return end; -+ } -+ -+ public: -+ template -+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { -+ return this->do_parse(ctx.begin(), ctx.end()); -+ } -+ -+ template -+ auto format(const std::tm& tm, FormatContext& ctx) const -+ -> decltype(ctx.out()) { -+ const auto loc_ref = ctx.locale(); -+ detail::get_locale loc(static_cast(loc_ref), loc_ref); -+ auto w = detail::tm_writer(loc, ctx.out(), tm); -+ if (spec_ == spec::year_month_day) -+ w.on_iso_date(); -+ else if (spec_ == spec::hh_mm_ss) -+ w.on_iso_time(); -+ else -+ detail::parse_chrono_format(specs.begin(), specs.end(), w); -+ return w.out(); - } - }; - -+FMT_MODULE_EXPORT_END - FMT_END_NAMESPACE - - #endif // FMT_CHRONO_H_ -diff --git a/external/fmt/include/fmt/color.h b/external/fmt/include/fmt/color.h -index b65f892a..dfbe4829 100644 ---- a/external/fmt/include/fmt/color.h -+++ b/external/fmt/include/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) -@@ -177,9 +185,13 @@ enum class terminal_color : uint8_t { - - enum class emphasis : uint8_t { - bold = 1, -- italic = 1 << 1, -- underline = 1 << 2, -- strikethrough = 1 << 3 -+ faint = 1 << 1, -+ italic = 1 << 2, -+ underline = 1 << 3, -+ blink = 1 << 4, -+ reverse = 1 << 5, -+ conceal = 1 << 6, -+ strikethrough = 1 << 7, - }; - - // rgb is a struct for red, green and blue colors. -@@ -198,7 +210,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 +233,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 +273,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 +320,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 +358,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 +381,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. -@@ -390,16 +413,18 @@ template struct ansi_color_escape { - buffer[19] = static_cast(0); - } - FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { -- uint8_t em_codes[4] = {}; -- uint8_t em_bits = static_cast(em); -- if (em_bits & static_cast(emphasis::bold)) em_codes[0] = 1; -- if (em_bits & static_cast(emphasis::italic)) em_codes[1] = 3; -- if (em_bits & static_cast(emphasis::underline)) em_codes[2] = 4; -- if (em_bits & static_cast(emphasis::strikethrough)) -- em_codes[3] = 9; -+ uint8_t em_codes[num_emphases] = {}; -+ if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; -+ if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; -+ if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; -+ if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; -+ if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; -+ if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; -+ if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; -+ if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; - - size_t index = 0; -- for (int i = 0; i < 4; ++i) { -+ for (size_t i = 0; i < num_emphases; ++i) { - if (!em_codes[i]) continue; - buffer[index++] = static_cast('\x1b'); - buffer[index++] = static_cast('['); -@@ -411,12 +436,13 @@ 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); - } - - private: -- Char buffer[7u + 3u * 4u + 1u]; -+ static constexpr size_t num_emphases = 8; -+ Char buffer[7u + 3u * num_emphases + 1u]; - - static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, - char delimiter) FMT_NOEXCEPT { -@@ -425,18 +451,22 @@ template struct ansi_color_escape { - out[2] = static_cast('0' + c % 10); - out[3] = static_cast(delimiter); - } -+ static FMT_CONSTEXPR bool has_emphasis(emphasis em, -+ emphasis mask) FMT_NOEXCEPT { -+ return static_cast(em) & static_cast(mask); -+ } - }; - - 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,24 +485,23 @@ 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(basic_memory_buffer& buffer) FMT_NOEXCEPT { -- const char* begin = data::reset_color; -- const char* end = begin + sizeof(data::reset_color) - 1; -- buffer.append(begin, end); -+inline void reset_color(buffer& buffer) FMT_NOEXCEPT { -+ auto reset_color = string_view("\x1b[0m"); -+ buffer.append(reset_color.begin(), reset_color.end()); - } - - template --void vformat_to(basic_memory_buffer& buf, const text_style& ts, -+void vformat_to(buffer& buf, const text_style& ts, - basic_string_view format_str, -- basic_format_args> args) { -+ basic_format_args>> args) { - bool has_style = false; - if (ts.has_emphasis()) { - has_style = true; -@@ -489,14 +518,15 @@ void vformat_to(basic_memory_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, -- basic_format_args> args) { -+ basic_format_args>> args) { - basic_memory_buffer buf; - detail::vformat_to(buf, ts, to_string_view(format), args); - buf.push_back(Char(0)); -@@ -504,28 +534,34 @@ void vprint(std::FILE* f, const text_style& ts, const S& format, - } - - /** -+ \rst - Formats a string and prints it to the specified file stream 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)> - void print(std::FILE* f, const text_style& ts, const S& format_str, - const Args&... args) { -- detail::check_format_string(format_str); -- using context = buffer_context>; -- format_arg_store as{args...}; -- vprint(f, ts, format_str, basic_format_args(as)); -+ vprint(f, ts, format_str, -+ fmt::make_args_checked(format_str, args...)); - } - - /** -+ \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)> -@@ -557,10 +593,46 @@ 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), -- detail::make_args_checked(format_str, args...)); -+ return fmt::vformat(ts, to_string_view(format_str), -+ fmt::make_args_checked(format_str, args...)); -+} -+ -+/** -+ Formats a string with the given text_style and writes the output to ``out``. -+ */ -+template ::value)> -+OutputIt vformat_to( -+ OutputIt out, const text_style& ts, basic_string_view format_str, -+ basic_format_args>> args) { -+ auto&& buf = detail::get_buffer(out); -+ detail::vformat_to(buf, ts, format_str, args); -+ return detail::get_iterator(buf); -+} -+ -+/** -+ \rst -+ Formats arguments with the given text_style, writes the result to the output -+ iterator ``out`` and returns the iterator past the end of the output range. -+ -+ **Example**:: -+ -+ std::vector out; -+ fmt::format_to(std::back_inserter(out), -+ fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); -+ \endrst -+*/ -+template >::value&& -+ detail::is_string::value> -+inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, -+ Args&&... args) -> -+ typename std::enable_if::type { -+ return vformat_to(out, ts, to_string_view(format_str), -+ fmt::make_args_checked(format_str, args...)); - } - -+FMT_MODULE_EXPORT_END - FMT_END_NAMESPACE - - #endif // FMT_COLOR_H_ -diff --git a/external/fmt/include/fmt/compile.h b/external/fmt/include/fmt/compile.h -index d7e6449e..1dba3ddb 100644 ---- a/external/fmt/include/fmt/compile.h -+++ b/external/fmt/include/fmt/compile.h -@@ -8,378 +8,202 @@ - #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 --struct is_compiled_string : std::is_base_of {}; -- --/** -- \rst -- Converts a string literal *s* into a format string that will be parsed at -- compile time and converted into efficient formatting code. Requires C++17 -- ``constexpr if`` compiler support. -- -- **Example**:: -- -- // Converts 42 into std::string using the most efficient method and no -- // runtime format string processing. -- std::string s = fmt::format(FMT_COMPILE("{}"), 42); -- \endrst -- */ --#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) -- --template --const T& first(const T& value, const Tail&...) { -- return value; --} -- --// Part of a compiled format string. It can be either literal text or a --// replacement field. --template struct format_part { -- enum class kind { arg_index, arg_name, text, replacement }; -+// An output iterator that counts the number of objects written to it and -+// discards them. -+class counting_iterator { -+ private: -+ size_t count_; - -- struct replacement { -- arg_ref arg_id; -- dynamic_format_specs specs; -+ public: -+ using iterator_category = std::output_iterator_tag; -+ using difference_type = std::ptrdiff_t; -+ using pointer = void; -+ using reference = void; -+ using _Unchecked_type = counting_iterator; // Mark iterator as checked. -+ -+ struct value_type { -+ template void operator=(const T&) {} - }; - -- kind part_kind; -- union value { -- int arg_index; -- basic_string_view str; -- replacement repl; -+ counting_iterator() : count_(0) {} - -- FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} -- FMT_CONSTEXPR value(basic_string_view s) : str(s) {} -- FMT_CONSTEXPR value(replacement r) : repl(r) {} -- } val; -- // Position past the end of the argument id. -- const Char* arg_id_end = nullptr; -+ size_t count() const { return count_; } - -- FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) -- : part_kind(k), val(v) {} -- -- static FMT_CONSTEXPR format_part make_arg_index(int index) { -- return format_part(kind::arg_index, index); -- } -- static FMT_CONSTEXPR format_part make_arg_name(basic_string_view name) { -- return format_part(kind::arg_name, name); -+ counting_iterator& operator++() { -+ ++count_; -+ return *this; - } -- static FMT_CONSTEXPR format_part make_text(basic_string_view text) { -- return format_part(kind::text, text); -+ counting_iterator operator++(int) { -+ auto it = *this; -+ ++*this; -+ return it; - } -- static FMT_CONSTEXPR format_part make_replacement(replacement repl) { -- return format_part(kind::replacement, repl); -+ -+ friend counting_iterator operator+(counting_iterator it, difference_type n) { -+ it.count_ += static_cast(n); -+ return it; - } -+ -+ value_type operator*() const { return {}; } - }; - --template struct part_counter { -- unsigned num_parts = 0; -+template -+inline counting_iterator copy_str(InputIt begin, InputIt end, -+ counting_iterator it) { -+ return it + (end - begin); -+} - -- FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { -- if (begin != end) ++num_parts; -- } -+template class truncating_iterator_base { -+ protected: -+ OutputIt out_; -+ size_t limit_; -+ size_t count_ = 0; - -- FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; } -- FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; } -- FMT_CONSTEXPR int on_arg_id(basic_string_view) { -- return ++num_parts, 0; -- } -+ truncating_iterator_base() : out_(), limit_(0) {} - -- FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} -- -- FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, -- const Char* end) { -- // Find the matching brace. -- unsigned brace_counter = 0; -- for (; begin != end; ++begin) { -- if (*begin == '{') { -- ++brace_counter; -- } else if (*begin == '}') { -- if (brace_counter == 0u) break; -- --brace_counter; -- } -- } -- return begin; -- } -+ truncating_iterator_base(OutputIt out, size_t limit) -+ : out_(out), limit_(limit) {} - -- FMT_CONSTEXPR void on_error(const char*) {} -+ 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_; } - }; - --// Counts the number of parts in a format string. --template --FMT_CONSTEXPR unsigned count_parts(basic_string_view format_str) { -- part_counter counter; -- parse_format_string(format_str, counter); -- return counter.num_parts; --} -- --template --class format_string_compiler : public error_handler { -- private: -- using part = format_part; -+// An output iterator that truncates the output and counts the number of objects -+// written to it. -+template ::value_type>::type> -+class truncating_iterator; - -- PartHandler handler_; -- part part_; -- basic_string_view format_str_; -- basic_format_parse_context parse_context_; -+template -+class truncating_iterator -+ : public truncating_iterator_base { -+ mutable typename truncating_iterator_base::value_type blackhole_; - - public: -- FMT_CONSTEXPR format_string_compiler(basic_string_view format_str, -- PartHandler handler) -- : handler_(handler), -- format_str_(format_str), -- parse_context_(format_str) {} -- -- FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { -- if (begin != end) -- handler_(part::make_text({begin, to_unsigned(end - begin)})); -- } -- -- FMT_CONSTEXPR int on_arg_id() { -- part_ = part::make_arg_index(parse_context_.next_arg_id()); -- return 0; -- } -+ using value_type = typename truncating_iterator_base::value_type; - -- FMT_CONSTEXPR int on_arg_id(int id) { -- parse_context_.check_arg_id(id); -- part_ = part::make_arg_index(id); -- return 0; -- } -+ truncating_iterator() = default; - -- FMT_CONSTEXPR int on_arg_id(basic_string_view id) { -- part_ = part::make_arg_name(id); -- return 0; -- } -+ truncating_iterator(OutputIt out, size_t limit) -+ : truncating_iterator_base(out, limit) {} - -- FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) { -- part_.arg_id_end = ptr; -- handler_(part_); -+ truncating_iterator& operator++() { -+ if (this->count_++ < this->limit_) ++this->out_; -+ return *this; - } - -- FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, -- const Char* end) { -- auto repl = typename part::replacement(); -- dynamic_specs_handler> handler( -- repl.specs, parse_context_); -- auto it = parse_format_specs(begin, end, handler); -- if (*it != '}') on_error("missing '}' in format string"); -- 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); -+ truncating_iterator operator++(int) { -+ auto it = *this; -+ ++*this; - return it; - } --}; -- --// Compiles a format string and invokes handler(part) for each parsed part. --template --FMT_CONSTEXPR void compile_format_string(basic_string_view format_str, -- PartHandler handler) { -- parse_format_string( -- format_str, -- format_string_compiler(format_str, handler)); --} - --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))); --} -- --// vformat_to is defined in a subnamespace to prevent ADL. --namespace cf { --template --auto vformat_to(OutputIt out, CompiledFormat& cf, -- basic_format_args args) -> typename Context::iterator { -- using char_type = typename Context::char_type; -- basic_format_parse_context parse_ctx( -- to_string_view(cf.format_str_)); -- Context ctx(out, args); -- -- const auto& parts = cf.parts(); -- for (auto part_it = std::begin(parts); part_it != std::end(parts); -- ++part_it) { -- const auto& part = *part_it; -- const auto& value = part.val; -- -- using format_part_t = format_part; -- switch (part.part_kind) { -- case format_part_t::kind::text: { -- const auto text = value.str; -- auto output = ctx.out(); -- auto&& it = reserve(output, text.size()); -- it = std::copy_n(text.begin(), text.size(), it); -- ctx.advance_to(output); -- break; -- } -- -- case format_part_t::kind::arg_index: -- advance_to(parse_ctx, part.arg_id_end); -- detail::format_arg(parse_ctx, ctx, value.arg_index); -- break; -- -- case format_part_t::kind::arg_name: -- advance_to(parse_ctx, part.arg_id_end); -- detail::format_arg(parse_ctx, ctx, value.str); -- break; -- -- case format_part_t::kind::replacement: { -- const auto& arg_id_value = value.repl.arg_id.val; -- const auto arg = value.repl.arg_id.kind == arg_id_kind::index -- ? ctx.arg(arg_id_value.index) -- : ctx.arg(arg_id_value.name); -- -- auto specs = value.repl.specs; -- -- handle_dynamic_spec(specs.width, specs.width_ref, ctx); -- handle_dynamic_spec(specs.precision, -- specs.precision_ref, ctx); -- -- error_handler h; -- numeric_specs_checker checker(h, arg.type()); -- if (specs.align == align::numeric) checker.require_numeric_argument(); -- if (specs.sign != sign::none) checker.check_sign(); -- if (specs.alt) checker.require_numeric_argument(); -- 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)); -- break; -- } -- } -+ value_type& operator*() const { -+ return this->count_ < this->limit_ ? *this->out_ : blackhole_; - } -- return ctx.out(); --} --} // namespace cf -- --struct basic_compiled_format {}; -+}; - --template --struct compiled_format_base : basic_compiled_format { -- using char_type = char_t; -- using parts_container = std::vector>; -+template -+class truncating_iterator -+ : public truncating_iterator_base { -+ public: -+ truncating_iterator() = default; - -- parts_container compiled_parts; -+ truncating_iterator(OutputIt out, size_t limit) -+ : truncating_iterator_base(out, limit) {} - -- explicit compiled_format_base(basic_string_view format_str) { -- compile_format_string(format_str, -- [this](const format_part& part) { -- compiled_parts.push_back(part); -- }); -+ template truncating_iterator& operator=(T val) { -+ if (this->count_++ < this->limit_) *this->out_++ = val; -+ return *this; - } - -- const parts_container& parts() const { return compiled_parts; } --}; -- --template struct format_part_array { -- format_part data[N] = {}; -- FMT_CONSTEXPR format_part_array() = default; -+ truncating_iterator& operator++() { return *this; } -+ truncating_iterator& operator++(int) { return *this; } -+ truncating_iterator& operator*() { return *this; } - }; - --template --FMT_CONSTEXPR format_part_array compile_to_parts( -- basic_string_view format_str) { -- format_part_array parts; -- unsigned counter = 0; -- // This is not a lambda for compatibility with older compilers. -- struct { -- format_part* parts; -- unsigned* counter; -- FMT_CONSTEXPR void operator()(const format_part& part) { -- parts[(*counter)++] = part; -- } -- } collector{parts.data, &counter}; -- compile_format_string(format_str, collector); -- if (counter < N) { -- parts.data[counter] = -- format_part::make_text(basic_string_view()); -- } -- return parts; --} -- --template constexpr const T& constexpr_max(const T& a, const T& b) { -- return (a < b) ? b : a; --} -+// A compile-time string which is compiled into fast formatting code. -+class compiled_string {}; - - template --struct compiled_format_base::value>> -- : basic_compiled_format { -- using char_type = char_t; -+struct is_compiled_string : std::is_base_of {}; -+ -+/** -+ \rst -+ Converts a string literal *s* into a format string that will be parsed at -+ compile time and converted into efficient formatting code. Requires C++17 -+ ``constexpr if`` compiler support. - -- FMT_CONSTEXPR explicit compiled_format_base(basic_string_view) {} -+ **Example**:: - --// Workaround for old compilers. Format string compilation will not be --// performed there anyway. --#if FMT_USE_CONSTEXPR -- static FMT_CONSTEXPR_DECL const unsigned num_format_parts = -- constexpr_max(count_parts(to_string_view(S())), 1u); -+ // Converts 42 into std::string using the most efficient method and no -+ // runtime format string processing. -+ std::string s = fmt::format(FMT_COMPILE("{}"), 42); -+ \endrst -+ */ -+#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -+# define FMT_COMPILE(s) \ -+ FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) - #else -- static const unsigned num_format_parts = 1; -+# define FMT_COMPILE(s) FMT_STRING(s) - #endif - -- using parts_container = format_part[num_format_parts]; -- -- const parts_container& parts() const { -- static FMT_CONSTEXPR_DECL const auto compiled_parts = -- compile_to_parts( -- detail::to_string_view(S())); -- return compiled_parts.data; -+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -+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 --class compiled_format : private compiled_format_base { -- public: -- using typename compiled_format_base::char_type; -- -- private: -- basic_string_view format_str_; -- -- template -- friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf, -- basic_format_args args) -> -- typename Context::iterator; -- -- public: -- compiled_format() = delete; -- explicit constexpr compiled_format(basic_string_view format_str) -- : compiled_format_base(format_str), format_str_(format_str) {} --}; -+template -+const T& first(const T& value, const Tail&...) { -+ return value; -+} - --#ifdef __cpp_if_constexpr -+#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) - template struct type_list {}; - - // Returns a reference to the argument at index N from [first, rest...]. - template --constexpr const auto& get(const T& first, const Args&... rest) { -+constexpr const auto& get([[maybe_unused]] const T& first, -+ [[maybe_unused]] const Args&... rest) { - static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); - if constexpr (N == 0) - return first; - else -- return get(rest...); -+ return detail::get(rest...); -+} -+ -+template -+constexpr int get_arg_index_by_name(basic_string_view name, -+ type_list) { -+ return get_arg_index_by_name(name); - } - - template struct get_type_impl; - - template struct get_type_impl> { -- using type = remove_cvref_t(std::declval()...))>; -+ using type = -+ remove_cvref_t(std::declval()...))>; - }; - - template -@@ -392,7 +216,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); - } - }; -@@ -406,32 +230,87 @@ constexpr text make_text(basic_string_view s, size_t pos, - return {{&s[pos], size}}; - } - -+template struct code_unit { -+ Char value; -+ using char_type = Char; -+ -+ template -+ constexpr OutputIt format(OutputIt out, const Args&...) const { -+ return write(out, value); -+ } -+}; -+ -+// This ensures that the argument type is convertible to `const T&`. -+template -+constexpr const T& get_arg_checked(const Args&... args) { -+ const auto& arg = detail::get(args...); -+ if constexpr (detail::is_named_arg>()) { -+ return arg.value; -+ } else { -+ return arg; -+ } -+} -+ -+template -+struct is_compiled_format> : std::true_type {}; -+ - // A replacement field that refers to argument N. - 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 { -+ return write(out, get_arg_checked(args...)); - } - }; - - 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) { -+ FMT_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 { -- // This ensures that the argument type is convertile to `const T&`. -- const T& arg = get(args...); -- basic_format_context ctx(out, {}); -- return fmt.format(arg, ctx); -+ constexpr FMT_INLINE OutputIt format(OutputIt out, -+ const Args&... args) const { -+ const auto& vargs = -+ fmt::make_format_args>(args...); -+ basic_format_context ctx(out, vargs); -+ return fmt.format(get_arg_checked(args...), ctx); - } - }; - -@@ -444,7 +323,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...); - } -@@ -489,16 +368,82 @@ constexpr auto parse_tail(T head, S format_str) { - template struct parse_specs_result { - formatter fmt; - size_t end; -+ int next_arg_id; - }; - -+constexpr int manual_indexing_id = -1; -+ - template - constexpr parse_specs_result parse_specs(basic_string_view str, -- size_t pos) { -+ size_t pos, int next_arg_id) { - str.remove_prefix(pos); -- auto ctx = basic_format_parse_context(str); -+ 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}; -+ 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 { -+ arg_ref arg_id; -+ -+ constexpr int operator()() { -+ FMT_ASSERT(false, "handler cannot be used with automatic indexing"); -+ return 0; -+ } -+ constexpr int operator()(int id) { -+ arg_id = arg_ref(id); -+ return 0; -+ } -+ constexpr int operator()(basic_string_view id) { -+ arg_id = arg_ref(id); -+ return 0; -+ } -+ -+ constexpr void on_error(const char* message) { -+ FMT_THROW(format_error(message)); -+ } -+}; -+ -+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 arg_id_end = parse_arg_id(begin, end, handler); -+ return parse_arg_id_result{handler.arg_id, arg_id_end}; -+} -+ -+template struct field_type { -+ using type = remove_cvref_t; -+}; -+ -+template -+struct field_type::value>> { -+ using type = remove_cvref_t; -+}; -+ -+template -+constexpr auto parse_replacement_field_then_tail(S format_str) { -+ using char_type = typename S::char_type; -+ constexpr auto str = basic_string_view(format_str); -+ constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); -+ if constexpr (c == '}') { -+ return parse_tail( -+ field::type, ARG_INDEX>(), -+ format_str); -+ } else if constexpr (c == ':') { -+ constexpr auto result = parse_specs::type>( -+ str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); -+ return parse_tail( -+ spec_field::type, ARG_INDEX>{ -+ result.fmt}, -+ format_str); -+ } - } - - // Compiles a non-empty format string and returns the compiled representation -@@ -506,160 +451,192 @@ constexpr parse_specs_result parse_specs(basic_string_view str, - template - constexpr auto compile_format_string(S format_str) { - using char_type = typename S::char_type; -- constexpr basic_string_view str = format_str; -+ constexpr auto str = basic_string_view(format_str); - if constexpr (str[POS] == '{') { -- if (POS + 1 == str.size()) -- throw format_error("unmatched '{' in format string"); -+ if constexpr (POS + 1 == str.size()) -+ FMT_THROW(format_error("unmatched '{' in format string")); - if constexpr (str[POS + 1] == '{') { - return parse_tail(make_text(str, POS, 1), format_str); -- } 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); -- 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"); -+ constexpr auto next_id = -+ ID != manual_indexing_id ? ID + 1 : manual_indexing_id; -+ return parse_replacement_field_then_tail, Args, -+ POS + 1, ID, next_id>( -+ format_str); - } 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; -+ return parse_replacement_field_then_tail, -+ Args, arg_id_end_pos, -+ arg_index, manual_indexing_id>( -+ format_str); -+ } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { -+ constexpr auto arg_index = -+ get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); -+ if constexpr (arg_index != invalid_arg_index) { -+ constexpr auto next_id = -+ ID != manual_indexing_id ? ID + 1 : manual_indexing_id; -+ return parse_replacement_field_then_tail< -+ decltype(get_type::value), Args, arg_id_end_pos, -+ arg_index, next_id>(format_str); -+ } else { -+ 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()) -- throw format_error("unmatched '}' in format string"); -+ if constexpr (POS + 1 == str.size()) -+ FMT_THROW(format_error("unmatched '}' in format string")); - return parse_tail(make_text(str, POS, 1), format_str); - } else { - constexpr auto end = parse_text(str, POS + 1); -- return parse_tail(make_text(str, POS, end - POS), -- format_str); -+ if constexpr (end - POS > 1) { -+ return parse_tail(make_text(str, POS, end - POS), -+ format_str); -+ } else { -+ return parse_tail(code_unit{str[POS]}, -+ format_str); -+ } - } - } - - template ::value || -- detail::is_compiled_string::value)> -+ FMT_ENABLE_IF(detail::is_compiled_string::value)> - constexpr auto compile(S format_str) { -- constexpr basic_string_view str = format_str; -+ constexpr auto str = basic_string_view(format_str); - if constexpr (str.size() == 0) { - return detail::make_text(str, 0, 0); - } else { - 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 --template ::value)> --constexpr auto compile(S format_str) -> detail::compiled_format { -- return detail::compiled_format(to_string_view(format_str)); --} --#endif // __cpp_if_constexpr -- --// Compiles the format string which must be a string literal. --template --auto compile(const Char (&format_str)[N]) -- -> detail::compiled_format { -- return detail::compiled_format( -- basic_string_view(format_str, N - 1)); --} -+#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) - } // namespace detail - --// DEPRECATED! use FMT_COMPILE instead. --template --FMT_DEPRECATED auto compile(const Args&... args) -- -> decltype(detail::compile(args...)) { -- return detail::compile(args...); --} -+FMT_MODULE_EXPORT_BEGIN - --#if FMT_USE_CONSTEXPR --# ifdef __cpp_if_constexpr -+#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) - - template ::value)> - FMT_INLINE std::basic_string format(const CompiledFormat& cf, - const Args&... args) { -- basic_memory_buffer buffer; -- detail::buffer& base = buffer; -- cf.format(std::back_inserter(base), args...); -- return to_string(buffer); -+ auto s = std::basic_string(); -+ cf.format(std::back_inserter(s), args...); -+ return s; - } - - template ::value)> --OutputIt format_to(OutputIt out, const CompiledFormat& cf, -- const Args&... args) { -+constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, -+ const Args&... args) { - return cf.format(out, args...); - } --# endif // __cpp_if_constexpr --#endif // FMT_USE_CONSTEXPR -- --template ::value)> --std::basic_string format(const CompiledFormat& cf, const Args&... args) { -- basic_memory_buffer buffer; -- using context = buffer_context; -- detail::buffer& base = buffer; -- detail::cf::vformat_to(std::back_inserter(base), cf, -- make_format_args(args...)); -- return to_string(buffer); --} - - template ::value)> - FMT_INLINE std::basic_string format(const S&, - Args&&... args) { -- constexpr basic_string_view str = S(); -- if (str.size() == 2 && str[0] == '{' && str[1] == '}') -- return fmt::to_string(detail::first(args...)); -+ if constexpr (std::is_same::value) { -+ constexpr auto str = basic_string_view(S()); -+ 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); -+ } -+ } -+ } - constexpr auto compiled = detail::compile(S()); -- return format(compiled, std::forward(args)...); --} -- --template ::value)> --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, -- make_format_args(args...)); -+ if constexpr (std::is_same, -+ detail::unknown_format>()) { -+ return format(static_cast>(S()), -+ std::forward(args)...); -+ } else { -+ return format(compiled, std::forward(args)...); -+ } - } - - 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...); -+ 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)...); -+ } - } -+#endif - --template < -- typename OutputIt, typename CompiledFormat, typename... Args, -- FMT_ENABLE_IF(detail::is_output_iterator::value&& std::is_base_of< -- detail::basic_compiled_format, CompiledFormat>::value)> -+template ::value)> - format_to_n_result format_to_n(OutputIt out, size_t n, -- const CompiledFormat& cf, -- const Args&... args) { -- auto it = -- format_to(detail::truncating_iterator(out, n), cf, args...); -+ const S& format_str, Args&&... args) { -+ auto it = format_to(detail::truncating_iterator(out, n), format_str, -+ std::forward(args)...); - return {it.base(), it.count()}; - } - --template --size_t formatted_size(const CompiledFormat& cf, const Args&... args) { -- return format_to(detail::counting_iterator(), cf, args...).count(); -+template ::value)> -+size_t formatted_size(const S& format_str, const Args&... args) { -+ return format_to(detail::counting_iterator(), format_str, args...).count(); -+} -+ -+template ::value)> -+void print(std::FILE* f, const S& format_str, const Args&... args) { -+ memory_buffer buffer; -+ format_to(std::back_inserter(buffer), format_str, args...); -+ detail::print(f, {buffer.data(), buffer.size()}); -+} -+ -+template ::value)> -+void print(const S& format_str, const Args&... args) { -+ print(stdout, format_str, args...); -+} -+ -+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -+inline namespace literals { -+template -+constexpr detail::udl_compiled_string< -+ remove_cvref_t, -+ sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> -+operator""_cf() { -+ return {}; - } -+} // namespace literals -+#endif - -+FMT_MODULE_EXPORT_END - FMT_END_NAMESPACE - - #endif // FMT_COMPILE_H_ -diff --git a/external/fmt/include/fmt/core.h b/external/fmt/include/fmt/core.h -index 37799a3a..92a7aa1d 100644 ---- a/external/fmt/include/fmt/core.h -+++ b/external/fmt/include/fmt/core.h -@@ -1,4 +1,4 @@ --// Formatting library for C++ - the core API -+// Formatting library for C++ - the core API for char/UTF-8 - // - // Copyright (c) 2012 - present, Victor Zverovich - // All rights reserved. -@@ -8,40 +8,45 @@ - #ifndef FMT_CORE_H_ - #define FMT_CORE_H_ - --#include // std::FILE -+#include // std::byte -+#include // std::FILE - #include --#include - #include --#include -+#include - #include - #include --#include - - // The fmt library version in the form major * 10000 + minor * 100 + patch. --#define FMT_VERSION 70003 -+#define FMT_VERSION 80101 - --#ifdef __clang__ -+#if defined(__clang__) && !defined(__ibmxl__) - # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) - #else - # define FMT_CLANG_VERSION 0 - #endif - --#if defined(__GNUC__) && !defined(__clang__) -+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ -+ !defined(__NVCOMPILER) - # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) - #else - # define FMT_GCC_VERSION 0 - #endif - --#if defined(__INTEL_COMPILER) --# define FMT_ICC_VERSION __INTEL_COMPILER --#else --# define FMT_ICC_VERSION 0 -+#ifndef FMT_GCC_PRAGMA -+// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. -+# if FMT_GCC_VERSION >= 504 -+# define FMT_GCC_PRAGMA(arg) _Pragma(arg) -+# else -+# define FMT_GCC_PRAGMA(arg) -+# endif - #endif - --#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) --# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION -+#ifdef __ICL -+# define FMT_ICC_VERSION __ICL -+#elif defined(__INTEL_COMPILER) -+# define FMT_ICC_VERSION __INTEL_COMPILER - #else --# define FMT_HAS_GXX_CXX11 0 -+# define FMT_ICC_VERSION 0 - #endif - - #ifdef __NVCC__ -@@ -52,19 +57,21 @@ - - #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 - # define FMT_HAS_FEATURE(x) __has_feature(x) - #else - # define FMT_HAS_FEATURE(x) 0 - #endif - --#if defined(__has_include) && !defined(__INTELLISENSE__) && \ -- !(FMT_ICC_VERSION && FMT_ICC_VERSION < 1600) -+#if defined(__has_include) && \ -+ (!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \ -+ (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) - # define FMT_HAS_INCLUDE(x) __has_include(x) - #else - # define FMT_HAS_INCLUDE(x) 0 -@@ -76,17 +83,23 @@ - # define FMT_HAS_CPP_ATTRIBUTE(x) 0 - #endif - -+#ifdef _MSVC_LANG -+# define FMT_CPLUSPLUS _MSVC_LANG -+#else -+# define FMT_CPLUSPLUS __cplusplus -+#endif -+ - #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ -- (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) -+ (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) - - #define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ -- (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) -+ (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) - - // Check if relaxed C++14 constexpr is supported. - // GCC doesn't allow throw in constexpr until version 6 (bug 67371). - #ifndef FMT_USE_CONSTEXPR - # define FMT_USE_CONSTEXPR \ -- (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ -+ (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \ - (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ - !FMT_NVCC && !FMT_ICC_VERSION - #endif -@@ -94,17 +107,32 @@ - # define FMT_CONSTEXPR constexpr - # define FMT_CONSTEXPR_DECL constexpr - #else --# define FMT_CONSTEXPR inline -+# define FMT_CONSTEXPR - # define FMT_CONSTEXPR_DECL - #endif - --#ifndef FMT_OVERRIDE --# if FMT_HAS_FEATURE(cxx_override) || \ -- (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 --# define FMT_OVERRIDE override --# else --# define FMT_OVERRIDE -+#if ((__cplusplus >= 202002L) && \ -+ (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ -+ (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) -+# define FMT_CONSTEXPR20 constexpr -+#else -+# define FMT_CONSTEXPR20 -+#endif -+ -+// Check if constexpr std::char_traits<>::compare,length is supported. -+#if defined(__GLIBCXX__) -+# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \ -+ _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. -+# define FMT_CONSTEXPR_CHAR_TRAITS constexpr - # endif -+#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \ -+ _LIBCPP_VERSION >= 4000 -+# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -+#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L -+# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -+#endif -+#ifndef FMT_CONSTEXPR_CHAR_TRAITS -+# define FMT_CONSTEXPR_CHAR_TRAITS - #endif - - // Check if exceptions are disabled. -@@ -123,7 +151,7 @@ - #endif - - #if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ -- (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 -+ FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 - # define FMT_DETECTED_NOEXCEPT noexcept - # define FMT_HAS_CXX11_NOEXCEPT 1 - #else -@@ -148,25 +176,39 @@ - # define FMT_NORETURN - #endif - --#ifndef FMT_DEPRECATED --# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 --# define FMT_DEPRECATED [[deprecated]] -+#if __cplusplus == 201103L || __cplusplus == 201402L -+# if defined(__INTEL_COMPILER) || defined(__PGI) -+# define FMT_FALLTHROUGH -+# elif defined(__clang__) -+# define FMT_FALLTHROUGH [[clang::fallthrough]] -+# elif FMT_GCC_VERSION >= 700 && \ -+ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -+# define FMT_FALLTHROUGH [[gnu::fallthrough]] - # else --# if defined(__GNUC__) || defined(__clang__) --# define FMT_DEPRECATED __attribute__((deprecated)) --# elif FMT_MSC_VER --# define FMT_DEPRECATED __declspec(deprecated) --# else --# define FMT_DEPRECATED /* deprecated */ --# endif -+# define FMT_FALLTHROUGH - # endif -+#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) -+# define FMT_FALLTHROUGH [[fallthrough]] -+#else -+# define FMT_FALLTHROUGH - #endif - --// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. --#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC --# define FMT_DEPRECATED_ALIAS --#else --# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED -+#ifndef FMT_NODISCARD -+# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) -+# define FMT_NODISCARD [[nodiscard]] -+# else -+# define FMT_NODISCARD -+# endif -+#endif -+ -+#ifndef FMT_USE_FLOAT -+# define FMT_USE_FLOAT 1 -+#endif -+#ifndef FMT_USE_DOUBLE -+# define FMT_USE_DOUBLE 1 -+#endif -+#ifndef FMT_USE_LONG_DOUBLE -+# define FMT_USE_LONG_DOUBLE 1 - #endif - - #ifndef FMT_INLINE -@@ -177,53 +219,55 @@ - # endif - #endif - --#ifndef FMT_BEGIN_NAMESPACE --# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ -- FMT_MSC_VER >= 1900 --# define FMT_INLINE_NAMESPACE inline namespace --# define FMT_END_NAMESPACE \ -- } \ -- } -+#ifndef FMT_DEPRECATED -+# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 -+# define FMT_DEPRECATED [[deprecated]] - # else --# define FMT_INLINE_NAMESPACE namespace --# define FMT_END_NAMESPACE \ -- } \ -- using namespace v7; \ -- } -+# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -+# define FMT_DEPRECATED __attribute__((deprecated)) -+# elif FMT_MSC_VER -+# define FMT_DEPRECATED __declspec(deprecated) -+# else -+# define FMT_DEPRECATED /* deprecated */ -+# endif - # endif -+#endif -+ -+#ifndef FMT_BEGIN_NAMESPACE - # define FMT_BEGIN_NAMESPACE \ - namespace fmt { \ -- FMT_INLINE_NAMESPACE v7 { -+ inline namespace v8 { -+# define FMT_END_NAMESPACE \ -+ } \ -+ } -+#endif -+ -+#ifndef FMT_MODULE_EXPORT -+# define FMT_MODULE_EXPORT -+# define FMT_MODULE_EXPORT_BEGIN -+# define FMT_MODULE_EXPORT_END -+# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { -+# define FMT_END_DETAIL_NAMESPACE } - #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 --# define FMT_EXPORTED - # elif defined(FMT_SHARED) - # define FMT_API __declspec(dllimport) --# define FMT_EXTERN_TEMPLATE_API FMT_API - # endif - #else - # define FMT_CLASS_API -+# if defined(FMT_EXPORT) || defined(FMT_SHARED) -+# if defined(__GNUC__) || defined(__clang__) -+# define FMT_API __attribute__((visibility("default"))) -+# endif -+# endif - #endif - #ifndef FMT_API - # define FMT_API - #endif --#ifndef FMT_EXTERN_TEMPLATE_API --# define FMT_EXTERN_TEMPLATE_API --#endif --#ifndef FMT_INSTANTIATION_DEF_API --# define FMT_INSTANTIATION_DEF_API FMT_API --#endif -- --#ifndef FMT_HEADER_ONLY --# define FMT_EXTERN extern --#else --# define FMT_EXTERN --#endif - - // libc++ supports string_view in pre-c++17. - #if (FMT_HAS_INCLUDE() && \ -@@ -239,16 +283,43 @@ - #ifndef FMT_UNICODE - # define FMT_UNICODE !FMT_MSC_VER - #endif --#if FMT_UNICODE && FMT_MSC_VER --# pragma execution_character_set("utf-8") -+ -+#ifndef FMT_CONSTEVAL -+# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ -+ __cplusplus > 201703L && !defined(__apple_build_version__)) || \ -+ (defined(__cpp_consteval) && \ -+ (!FMT_MSC_VER || _MSC_FULL_VER >= 193030704)) -+// consteval is broken in MSVC before VS2022 and Apple clang 13. -+# define FMT_CONSTEVAL consteval -+# define FMT_HAS_CONSTEVAL -+# else -+# define FMT_CONSTEVAL -+# endif -+#endif -+ -+#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -+# if defined(__cpp_nontype_template_args) && \ -+ ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \ -+ __cpp_nontype_template_args >= 201911L) -+# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1 -+# else -+# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 -+# endif -+#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 -+FMT_MODULE_EXPORT_BEGIN - - // Implementations of enable_if_t and other metafunctions for older systems. --template -+template - using enable_if_t = typename std::enable_if::type; --template -+template - using conditional_t = typename std::conditional::type; - template using bool_constant = std::integral_constant; - template -@@ -260,18 +331,40 @@ using remove_cvref_t = typename std::remove_cv>::type; - template struct type_identity { using type = T; }; - template using type_identity_t = typename type_identity::type; - --struct monostate {}; -+struct monostate { -+ constexpr monostate() {} -+}; - - // An enable_if helper to be used in template parameters which results in much - // shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed - // to workaround a bug in MSVC 2019 (see #1140 and #1186). --#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 { -+FMT_BEGIN_DETAIL_NAMESPACE - --// A helper function to suppress bogus "conditional expression is constant" --// warnings. --template constexpr T const_check(T value) { return value; } -+// Suppress "unused variable" warnings with the method described in -+// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. -+// (void)var does not work on many Intel compilers. -+template FMT_CONSTEXPR void ignore_unused(const T&...) {} -+ -+constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) -+ FMT_NOEXCEPT -> bool { -+#ifdef __cpp_lib_is_constant_evaluated -+ ignore_unused(default_value); -+ return std::is_constant_evaluated(); -+#else -+ return default_value; -+#endif -+} -+ -+// A function to suppress "conditional expression is constant" warnings. -+template constexpr FMT_INLINE auto const_check(T value) -> T { -+ return value; -+} - - FMT_NORETURN FMT_API void assert_fail(const char* file, int line, - const char* message); -@@ -279,7 +372,8 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, - #ifndef FMT_ASSERT - # ifdef NDEBUG - // FMT_ASSERT is not empty to avoid -Werror=empty-body. --# define FMT_ASSERT(condition, message) ((void)0) -+# define FMT_ASSERT(condition, message) \ -+ ::fmt::detail::ignore_unused((condition), (message)) - # else - # define FMT_ASSERT(condition, message) \ - ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ -@@ -288,6 +382,12 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, - # endif - #endif - -+#ifdef __cpp_lib_byte -+using byte = std::byte; -+#else -+enum class byte : unsigned char {}; -+#endif -+ - #if defined(FMT_USE_STRING_VIEW) - template using std_string_view = std::basic_string_view; - #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) -@@ -299,42 +399,44 @@ template struct std_string_view {}; - - #ifdef FMT_USE_INT128 - // Do nothing. --#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && !(FMT_CLANG_VERSION && FMT_MSC_VER) -+#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ -+ !(FMT_CLANG_VERSION && FMT_MSC_VER) - # define FMT_USE_INT128 1 - using int128_t = __int128_t; - using uint128_t = __uint128_t; -+template inline auto convert_for_visit(T value) -> T { -+ return value; -+} - #else - # define FMT_USE_INT128 0 - #endif - #if !FMT_USE_INT128 --struct int128_t {}; --struct uint128_t {}; -+enum class int128_t {}; -+enum class uint128_t {}; -+// Reduce template instantiations. -+template inline auto convert_for_visit(T) -> monostate { -+ return {}; -+} - #endif - - // Casts a nonnegative integer to unsigned. - template --FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { -+FMT_CONSTEXPR auto to_unsigned(Int value) -> -+ typename std::make_unsigned::type { - FMT_ASSERT(value >= 0, "negative 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 || -- (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5); -+constexpr auto is_utf8() -> bool { -+ // Avoid buggy sign extensions in MSVC's constant evaluation mode. -+ // https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612 -+ using uchar = unsigned char; -+ return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && -+ uchar(micro[1]) == 0xB5); - } -- --#ifdef __cpp_char8_t --using char8_type = char8_t; --#else --enum char8_type : unsigned char {}; --#endif --} // namespace detail -- --#ifdef FMT_USE_INTERNAL --namespace internal = detail; // DEPRECATED --#endif -+FMT_END_DETAIL_NAMESPACE - - /** - An implementation of ``std::basic_string_view`` for pre-C++17. It provides a -@@ -365,11 +467,14 @@ template class basic_string_view { - the size with ``std::char_traits::length``. - \endrst - */ --#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr. -- FMT_CONSTEXPR --#endif -+ FMT_CONSTEXPR_CHAR_TRAITS -+ FMT_INLINE - basic_string_view(const Char* s) -- : data_(s), size_(std::char_traits::length(s)) {} -+ : data_(s), -+ size_(detail::const_check(std::is_same::value && -+ !detail::is_constant_evaluated(true)) -+ ? std::strlen(reinterpret_cast(s)) -+ : std::char_traits::length(s)) {} - - /** Constructs a string reference from a ``std::basic_string`` object. */ - template -@@ -384,23 +489,25 @@ template class basic_string_view { - size_(s.size()) {} - - /** Returns a pointer to the string data. */ -- constexpr const Char* data() const { return data_; } -+ constexpr auto data() const FMT_NOEXCEPT -> const Char* { return data_; } - - /** Returns the string size. */ -- constexpr size_t size() const { return size_; } -+ constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } - -- constexpr iterator begin() const { return data_; } -- constexpr iterator end() const { return data_ + size_; } -+ constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; } -+ constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + size_; } - -- constexpr const Char& operator[](size_t pos) const { return data_[pos]; } -+ constexpr auto operator[](size_t pos) const FMT_NOEXCEPT -> const Char& { -+ return data_[pos]; -+ } - -- FMT_CONSTEXPR void remove_prefix(size_t n) { -+ FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT { - data_ += n; - size_ -= n; - } - - // Lexicographically compare this string reference to other. -- int compare(basic_string_view other) const { -+ FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { - size_t str_size = size_ < other.size_ ? size_ : other.size_; - int result = std::char_traits::compare(data_, other.data_, str_size); - if (result == 0) -@@ -408,72 +515,53 @@ template class basic_string_view { - return result; - } - -- friend bool operator==(basic_string_view lhs, basic_string_view rhs) { -+ FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, -+ basic_string_view rhs) -+ -> bool { - return lhs.compare(rhs) == 0; - } -- friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { -+ friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) != 0; - } -- friend bool operator<(basic_string_view lhs, basic_string_view rhs) { -+ friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) < 0; - } -- friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { -+ friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) <= 0; - } -- friend bool operator>(basic_string_view lhs, basic_string_view rhs) { -+ friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) > 0; - } -- friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { -+ friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) >= 0; - } - }; - - using string_view = basic_string_view; --using wstring_view = basic_string_view; - - /** Specifies if ``T`` is a character type. Can be specialized by users. */ - template struct is_char : std::false_type {}; - template <> struct is_char : std::true_type {}; --template <> struct is_char : std::true_type {}; --template <> struct is_char : std::true_type {}; --template <> struct is_char : std::true_type {}; --template <> struct is_char : std::true_type {}; -- --/** -- \rst -- Returns a string view of `s`. In order to add custom string type support to -- {fmt} provide an overload of `to_string_view` for it in the same namespace as -- the type for the argument-dependent lookup to work. - -- **Example**:: -- -- namespace my_ns { -- inline string_view to_string_view(const my_string& s) { -- return {s.data(), s.length()}; -- } -- } -- std::string message = fmt::format(my_string("The answer is {}"), 42); -- \endrst -- */ -+// Returns a string view of `s`. - template ::value)> --inline basic_string_view to_string_view(const Char* s) { -+FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { - return s; - } -- - template --inline basic_string_view to_string_view( -- const std::basic_string& s) { -+inline auto to_string_view(const std::basic_string& s) -+ -> basic_string_view { - return s; - } -- - template --inline basic_string_view to_string_view(basic_string_view s) { -+constexpr auto to_string_view(basic_string_view s) -+ -> basic_string_view { - return s; - } -- - template >::value)> --inline basic_string_view to_string_view(detail::std_string_view s) { -+inline auto to_string_view(detail::std_string_view s) -+ -> basic_string_view { - return s; - } - -@@ -485,13 +573,15 @@ template - struct is_compile_string : std::is_base_of {}; - - template ::value)> --constexpr basic_string_view to_string_view(const S& s) { -- return s; -+constexpr auto to_string_view(const S& s) -+ -> basic_string_view { -+ return basic_string_view(s); - } - --namespace detail { -+FMT_BEGIN_DETAIL_NAMESPACE -+ - void to_string_view(...); --using fmt::v7::to_string_view; -+using fmt::to_string_view; - - // Specifies whether S is a string type convertible to fmt::basic_string_view. - // It should be a constexpr function but MSVC 2017 fails to compile it in -@@ -506,6 +596,20 @@ template struct char_t_impl::value>> { - using type = typename result::value_type; - }; - -+// Reports a compile-time error if S is not a valid format string. -+template ::value)> -+FMT_INLINE void check_format_string(const S&) { -+#ifdef FMT_ENFORCE_COMPILE_STRING -+ static_assert(is_compile_string::value, -+ "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " -+ "FMT_STRING."); -+#endif -+} -+template ::value)> -+void check_format_string(S); -+ -+FMT_NORETURN FMT_API void throw_format_error(const char* message); -+ - struct error_handler { - constexpr error_handler() = default; - constexpr error_handler(const error_handler&) = default; -@@ -513,7 +617,7 @@ struct error_handler { - // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN FMT_API void on_error(const char* message); - }; --} // namespace detail -+FMT_END_DETAIL_NAMESPACE - - /** String's character type. */ - template using char_t = typename detail::char_t_impl::type; -@@ -522,16 +626,7 @@ template using char_t = typename detail::char_t_impl::type; - \rst - Parsing context consisting of a format string range being parsed and an - argument counter for automatic indexing. -- -- You can use one of the following type aliases for common character types: -- -- +-----------------------+-------------------------------------+ -- | Type | Definition | -- +=======================+=====================================+ -- | format_parse_context | basic_format_parse_context | -- +-----------------------+-------------------------------------+ -- | wformat_parse_context | basic_format_parse_context | -- +-----------------------+-------------------------------------+ -+ You can use the ``format_parse_context`` type alias for ``char`` instead. - \endrst - */ - template -@@ -545,19 +640,24 @@ class basic_format_parse_context : private ErrorHandler { - using iterator = typename basic_string_view::iterator; - - explicit constexpr basic_format_parse_context( -- basic_string_view format_str, ErrorHandler eh = {}) -- : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} -+ basic_string_view format_str, ErrorHandler eh = {}, -+ int next_arg_id = 0) -+ : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {} - - /** - Returns an iterator to the beginning of the format string range being - parsed. - */ -- constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); } -+ constexpr auto begin() const FMT_NOEXCEPT -> iterator { -+ return format_str_.begin(); -+ } - - /** - Returns an iterator past the end of the format string range being parsed. - */ -- constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); } -+ constexpr auto end() const FMT_NOEXCEPT -> iterator { -+ return format_str_.end(); -+ } - - /** Advances the begin iterator to ``it``. */ - FMT_CONSTEXPR void advance_to(iterator it) { -@@ -568,7 +668,7 @@ class basic_format_parse_context : private ErrorHandler { - Reports an error if using the manual argument indexing; otherwise returns - the next argument index and switches to the automatic indexing. - */ -- FMT_CONSTEXPR int next_arg_id() { -+ FMT_CONSTEXPR auto next_arg_id() -> int { - // Don't check if the argument id is valid to avoid overhead and because it - // will be checked during formatting anyway. - if (next_arg_id_ >= 0) return next_arg_id_++; -@@ -593,11 +693,10 @@ class basic_format_parse_context : private ErrorHandler { - ErrorHandler::on_error(message); - } - -- constexpr ErrorHandler error_handler() const { return *this; } -+ constexpr auto error_handler() const -> ErrorHandler { return *this; } - }; - - using format_parse_context = basic_format_parse_context; --using wformat_parse_context = basic_format_parse_context; - - template class basic_format_arg; - template class basic_format_args; -@@ -616,7 +715,59 @@ template - using has_formatter = - std::is_constructible>; - --namespace detail { -+// Checks whether T is a container with contiguous storage. -+template struct is_contiguous : std::false_type {}; -+template -+struct is_contiguous> : std::true_type {}; -+ -+class appender; -+ -+FMT_BEGIN_DETAIL_NAMESPACE -+ -+template -+constexpr auto has_const_formatter_impl(T*) -+ -> decltype(typename Context::template formatter_type().format( -+ std::declval(), std::declval()), -+ true) { -+ return true; -+} -+template -+constexpr auto has_const_formatter_impl(...) -> bool { -+ return false; -+} -+template -+constexpr auto has_const_formatter() -> bool { -+ return has_const_formatter_impl(static_cast(nullptr)); -+} -+ -+// Extracts a reference to the container from back_insert_iterator. -+template -+inline auto get_container(std::back_insert_iterator it) -+ -> Container& { -+ using bi_iterator = std::back_insert_iterator; -+ struct accessor : bi_iterator { -+ accessor(bi_iterator iter) : bi_iterator(iter) {} -+ using bi_iterator::container; -+ }; -+ return *accessor(it).container; -+} -+ -+template -+FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) -+ -> OutputIt { -+ while (begin != end) *out++ = static_cast(*begin++); -+ return out; -+} -+ -+template , U>::value&& is_char::value)> -+FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { -+ if (is_constant_evaluated()) return copy_str(begin, end, out); -+ auto size = to_unsigned(end - begin); -+ memcpy(out, begin, size * sizeof(U)); -+ return out + size; -+} - - /** - \rst -@@ -632,22 +783,25 @@ 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 -- : ptr_(p), -- size_(sz), -- capacity_(cap) {} -+ FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, -+ size_t cap = 0) FMT_NOEXCEPT : ptr_(p), -+ size_(sz), -+ capacity_(cap) {} -+ -+ FMT_CONSTEXPR20 ~buffer() = default; -+ buffer(buffer&&) = default; - - /** Sets the buffer data and capacity. */ -- void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { -+ FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { - ptr_ = buf_data; - capacity_ = buf_capacity; - } - - /** Increases the buffer capacity to hold at least *capacity* elements. */ -- virtual void grow(size_t capacity) = 0; -+ virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; - - public: - using value_type = T; -@@ -655,82 +809,231 @@ template class buffer { - - buffer(const buffer&) = delete; - void operator=(const buffer&) = delete; -- virtual ~buffer() = default; - -- T* begin() FMT_NOEXCEPT { return ptr_; } -- T* end() FMT_NOEXCEPT { return ptr_ + size_; } -+ auto begin() FMT_NOEXCEPT -> T* { return ptr_; } -+ auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; } - -- const T* begin() const FMT_NOEXCEPT { return ptr_; } -- const T* end() const FMT_NOEXCEPT { return ptr_ + size_; } -+ auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; } -+ auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; } - - /** Returns the size of this buffer. */ -- size_t size() const FMT_NOEXCEPT { return size_; } -+ constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } - - /** Returns the capacity of this buffer. */ -- size_t capacity() const FMT_NOEXCEPT { return capacity_; } -+ constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; } - - /** Returns a pointer to the buffer data. */ -- T* data() FMT_NOEXCEPT { return ptr_; } -+ FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; } - - /** Returns a pointer to the buffer data. */ -- const T* data() const FMT_NOEXCEPT { return ptr_; } -- -- /** -- Resizes the buffer. If T is a POD type new elements may not be initialized. -- */ -- void resize(size_t new_size) { -- reserve(new_size); -- size_ = new_size; -- } -+ FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; } - - /** Clears this buffer. */ - void clear() { size_ = 0; } - -- /** Reserves space to store at least *capacity* elements. */ -- void reserve(size_t new_capacity) { -+ // Tries resizing the buffer to contain *count* elements. If T is a POD type -+ // the new elements may not be initialized. -+ FMT_CONSTEXPR20 void try_resize(size_t count) { -+ try_reserve(count); -+ size_ = count <= capacity_ ? count : capacity_; -+ } -+ -+ // Tries increasing the buffer capacity to *new_capacity*. It can increase the -+ // capacity by a smaller amount than requested but guarantees there is space -+ // for at least one additional element either by increasing the capacity or by -+ // flushing the buffer if it is full. -+ FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { - if (new_capacity > capacity_) grow(new_capacity); - } - -- void push_back(const T& value) { -- reserve(size_ + 1); -+ FMT_CONSTEXPR20 void push_back(const T& value) { -+ try_reserve(size_ + 1); - ptr_[size_++] = value; - } - - /** Appends data to the end of the buffer. */ - template void append(const U* begin, const U* end); - -- template T& operator[](I index) { return ptr_[index]; } -- template const T& operator[](I index) const { -+ template FMT_CONSTEXPR auto operator[](I index) -> T& { - return ptr_[index]; - } -+ template -+ FMT_CONSTEXPR auto operator[](I index) const -> const T& { -+ return ptr_[index]; -+ } -+}; -+ -+struct buffer_traits { -+ explicit buffer_traits(size_t) {} -+ auto count() const -> size_t { return 0; } -+ auto limit(size_t size) -> size_t { return size; } -+}; -+ -+class fixed_buffer_traits { -+ private: -+ size_t count_ = 0; -+ size_t limit_; -+ -+ public: -+ explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} -+ auto count() const -> size_t { return count_; } -+ auto limit(size_t size) -> size_t { -+ size_t n = limit_ > count_ ? limit_ - count_ : 0; -+ count_ += size; -+ return size < n ? size : n; -+ } -+}; -+ -+// A buffer that writes to an output iterator when flushed. -+template -+class iterator_buffer final : public Traits, public buffer { -+ private: -+ OutputIt out_; -+ enum { buffer_size = 256 }; -+ T data_[buffer_size]; -+ -+ protected: -+ FMT_CONSTEXPR20 void grow(size_t) override { -+ if (this->size() == buffer_size) flush(); -+ } -+ -+ void flush() { -+ auto size = this->size(); -+ this->clear(); -+ out_ = copy_str(data_, data_ + this->limit(size), out_); -+ } -+ -+ public: -+ explicit iterator_buffer(OutputIt out, size_t n = buffer_size) -+ : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} -+ iterator_buffer(iterator_buffer&& other) -+ : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} -+ ~iterator_buffer() { flush(); } -+ -+ auto out() -> OutputIt { -+ flush(); -+ return out_; -+ } -+ auto count() const -> size_t { return Traits::count() + this->size(); } -+}; -+ -+template -+class iterator_buffer final -+ : public fixed_buffer_traits, -+ public buffer { -+ private: -+ T* out_; -+ enum { buffer_size = 256 }; -+ T data_[buffer_size]; -+ -+ protected: -+ FMT_CONSTEXPR20 void grow(size_t) override { -+ if (this->size() == this->capacity()) flush(); -+ } -+ -+ void flush() { -+ size_t n = this->limit(this->size()); -+ if (this->data() == out_) { -+ out_ += n; -+ this->set(data_, buffer_size); -+ } -+ this->clear(); -+ } -+ -+ public: -+ explicit iterator_buffer(T* out, size_t n = buffer_size) -+ : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} -+ iterator_buffer(iterator_buffer&& other) -+ : fixed_buffer_traits(other), -+ buffer(std::move(other)), -+ out_(other.out_) { -+ if (this->data() != out_) { -+ this->set(data_, buffer_size); -+ this->clear(); -+ } -+ } -+ ~iterator_buffer() { flush(); } -+ -+ auto out() -> T* { -+ flush(); -+ return out_; -+ } -+ auto count() const -> size_t { -+ return fixed_buffer_traits::count() + this->size(); -+ } -+}; -+ -+template class iterator_buffer final : public buffer { -+ protected: -+ FMT_CONSTEXPR20 void grow(size_t) override {} -+ -+ public: -+ explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} -+ -+ auto out() -> T* { return &*this->end(); } - }; - --// A container-backed buffer. -+// A buffer that writes to a container with the contiguous storage. - template --class container_buffer : public buffer { -+class iterator_buffer, -+ enable_if_t::value, -+ typename Container::value_type>> -+ final : public buffer { - private: - Container& container_; - - protected: -- void grow(size_t capacity) FMT_OVERRIDE { -+ FMT_CONSTEXPR20 void grow(size_t capacity) override { - container_.resize(capacity); - this->set(&container_[0], capacity); - } - - public: -- explicit container_buffer(Container& c) -+ explicit iterator_buffer(Container& c) - : buffer(c.size()), container_(c) {} -+ explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) -+ : iterator_buffer(get_container(out)) {} -+ auto out() -> std::back_insert_iterator { -+ return std::back_inserter(container_); -+ } - }; - --// Extracts a reference to the container from back_insert_iterator. --template --inline Container& get_container(std::back_insert_iterator it) { -- using bi_iterator = std::back_insert_iterator; -- struct accessor : bi_iterator { -- accessor(bi_iterator iter) : bi_iterator(iter) {} -- using bi_iterator::container; -- }; -- return *accessor(it).container; -+// A buffer that counts the number of code units written discarding the output. -+template class counting_buffer final : public buffer { -+ private: -+ enum { buffer_size = 256 }; -+ T data_[buffer_size]; -+ size_t count_ = 0; -+ -+ protected: -+ FMT_CONSTEXPR20 void grow(size_t) override { -+ if (this->size() != buffer_size) return; -+ count_ += this->size(); -+ this->clear(); -+ } -+ -+ public: -+ counting_buffer() : buffer(data_, 0, buffer_size) {} -+ -+ auto count() -> size_t { return count_ + this->size(); } -+}; -+ -+template -+using buffer_appender = conditional_t::value, appender, -+ std::back_insert_iterator>>; -+ -+// Maps an output iterator to a buffer. -+template -+auto get_buffer(OutputIt out) -> iterator_buffer { -+ return iterator_buffer(out); -+} -+ -+template -+auto get_iterator(Buffer& buf) -> decltype(buf.out()) { -+ return buf.out(); -+} -+template auto get_iterator(buffer& buf) -> buffer_appender { -+ return buffer_appender(buf); - } - - template -@@ -739,9 +1042,9 @@ struct fallback_formatter { - }; - - // Specifies if T has an enabled fallback_formatter specialization. --template -+template - using has_fallback_formatter = -- std::is_constructible>; -+ std::is_constructible>; - - struct view {}; - -@@ -759,60 +1062,72 @@ template struct named_arg_info { - template - struct arg_data { - // args_[0].named_args points to named_args_ to avoid bloating format_args. -- T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : 1)]; -+ // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. -+ T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)]; - named_arg_info named_args_[NUM_NAMED_ARGS]; - - template - arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} - arg_data(const arg_data& other) = delete; -- const T* args() const { return args_ + 1; } -- named_arg_info* named_args() { return named_args_; } -+ auto args() const -> const T* { return args_ + 1; } -+ auto named_args() -> named_arg_info* { return named_args_; } - }; - - template - struct arg_data { -- T args_[NUM_ARGS != 0 ? NUM_ARGS : 1]; -+ // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. -+ 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 auto args() const -> const T* { return args_; } -+ FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { -+ return nullptr; -+ } - }; - - template - inline void init_named_args(named_arg_info*, int, int) {} - --template -+template struct is_named_arg : std::false_type {}; -+template struct is_statically_named_arg : std::false_type {}; -+ -+template -+struct is_named_arg> : std::true_type {}; -+ -+template ::value)> - void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const T&, const Tail&... args) { - init_named_args(named_args, arg_count + 1, named_arg_count, args...); - } - --template -+template ::value)> - void init_named_args(named_arg_info* named_args, int arg_count, -- int named_arg_count, const named_arg& arg, -- const Tail&... args) { -+ int named_arg_count, const T& arg, const Tail&... args) { - named_args[named_arg_count++] = {arg.name, arg_count}; - init_named_args(named_args, arg_count + 1, named_arg_count, args...); - } - - template --FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {} -- --template struct is_named_arg : std::false_type {}; -- --template --struct is_named_arg> : std::true_type {}; -+FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, -+ const Args&...) {} - --template constexpr size_t count() { return B ? 1 : 0; } --template constexpr size_t count() { -+template constexpr auto count() -> size_t { return B ? 1 : 0; } -+template constexpr auto count() -> size_t { - return (B1 ? 1 : 0) + count(); - } - --template constexpr size_t count_named_args() { -+template constexpr auto count_named_args() -> size_t { - return count::value...>(); - } - -+template -+constexpr auto count_statically_named_args() -> size_t { -+ return count::value...>(); -+} -+ - enum class type { - none_type, - // Integer types should go first, -@@ -868,6 +1183,11 @@ constexpr bool is_arithmetic_type(type t) { - return t > type::none_type && t <= type::last_numeric_type; - } - -+struct unformattable {}; -+struct unformattable_char : unformattable {}; -+struct unformattable_const : unformattable {}; -+struct unformattable_pointer : unformattable {}; -+ - template struct string_value { - const Char* data; - size_t size; -@@ -880,8 +1200,8 @@ template struct named_arg_value { - - template struct custom_value { - using parse_context = typename Context::parse_context_type; -- const void* value; -- void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); -+ void* value; -+ void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); - }; - - // A formatting argument value. -@@ -890,6 +1210,7 @@ template class value { - using char_type = typename Context::char_type; - - union { -+ monostate no_value; - int int_value; - unsigned uint_value; - long long long_long_value; -@@ -907,19 +1228,23 @@ template class value { - named_arg_value named_args; - }; - -- constexpr FMT_INLINE value(int val = 0) : int_value(val) {} -+ constexpr FMT_INLINE value() : no_value() {} -+ constexpr FMT_INLINE value(int val) : 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) {} -+ constexpr FMT_INLINE value(float val) : float_value(val) {} -+ constexpr FMT_INLINE value(double val) : double_value(val) {} - FMT_INLINE value(long double val) : long_double_value(val) {} -- 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(); - } -@@ -927,31 +1252,39 @@ template class value { - FMT_INLINE value(const named_arg_info* args, size_t size) - : named_args{args, size} {} - -- template FMT_INLINE value(const T& val) { -- custom.value = &val; -+ template FMT_CONSTEXPR FMT_INLINE value(T& val) { -+ using value_type = remove_cvref_t; -+ custom.value = const_cast(&val); - // Get the formatter type through the context to allow different contexts - // have different extension points, e.g. `formatter` for `format` and - // `printf_formatter` for `printf`. - custom.format = format_custom_arg< -- T, conditional_t::value, -- typename Context::template formatter_type, -- fallback_formatter>>; -+ value_type, -+ conditional_t::value, -+ typename Context::template formatter_type, -+ fallback_formatter>>; - } -+ value(unformattable); -+ value(unformattable_char); -+ value(unformattable_const); -+ value(unformattable_pointer); - - private: - // Formats an argument of a custom type, such as a user-defined class. - template -- static void format_custom_arg(const void* arg, -+ static void format_custom_arg(void* arg, - typename Context::parse_context_type& parse_ctx, - Context& ctx) { -- Formatter f; -+ auto f = Formatter(); - parse_ctx.advance_to(f.parse(parse_ctx)); -- ctx.advance_to(f.format(*static_cast(arg), ctx)); -+ using qualified_type = -+ conditional_t(), const T, T>; -+ ctx.advance_to(f.format(*static_cast(arg), ctx)); - } - }; - - template --FMT_CONSTEXPR basic_format_arg make_arg(const T& value); -+FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg; - - // To minimize the number of types we need to deal with, long is translated - // either to int or to long long depending on its size. -@@ -960,49 +1293,83 @@ using long_type = conditional_t; - using ulong_type = conditional_t; - - // Maps formatting arguments to core types. -+// arg_mapper reports errors by returning unformattable instead of using -+// static_assert because it's used in the is_formattable trait. - template struct arg_mapper { - using char_type = typename Context::char_type; - -- FMT_CONSTEXPR 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; } -- -- template ::value)> -- FMT_CONSTEXPR char_type map(T val) { -- static_assert( -- std::is_same::value || std::is_same::value, -- "mixing character types is disallowed"); -+ FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { - return val; - } -+ FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { -+ return val; -+ } -+ FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { -+ return val; -+ } -+ FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) -+ -> unsigned long long { -+ return val; -+ } -+ FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { 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 const char_type* map(char_type* val) { return val; } -- FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; } -- template ::value)> -- FMT_CONSTEXPR basic_string_view map(const T& val) { -- static_assert(std::is_same>::value, -- "mixing character types is disallowed"); -- return to_string_view(val); -+ template ::value || -+ std::is_same::value)> -+ FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { -+ return val; -+ } -+ template ::value || -+#ifdef __cpp_char8_t -+ std::is_same::value || -+#endif -+ std::is_same::value || -+ std::is_same::value) && -+ !std::is_same::value, -+ int> = 0> -+ FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { -+ return {}; -+ } -+ -+ FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { -+ return val; -+ } -+ -+ FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { -+ return val; -+ } -+ FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { -+ return val; -+ } -+ template ::value && !std::is_pointer::value && -+ std::is_same>::value)> -+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -+ -> basic_string_view { -+ return to_string_view(val); -+ } -+ template ::value && !std::is_pointer::value && -+ !std::is_same>::value)> -+ FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { -+ return {}; - } - template , T>::value && - !is_string::value && !has_formatter::value && -- !has_fallback_formatter::value)> -- FMT_CONSTEXPR basic_string_view map(const T& val) { -+ !has_fallback_formatter::value)> -+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -+ -> basic_string_view { - return basic_string_view(val); - } - template < -@@ -1011,71 +1378,113 @@ template struct arg_mapper { - std::is_constructible, T>::value && - !std::is_constructible, T>::value && - !is_string::value && !has_formatter::value && -- !has_fallback_formatter::value)> -- FMT_CONSTEXPR basic_string_view map(const T& val) { -+ !has_fallback_formatter::value)> -+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -+ -> basic_string_view { - return std_string_view(val); - } -- FMT_CONSTEXPR const char* map(const signed char* val) { -- static_assert(std::is_same::value, "invalid string type"); -- return reinterpret_cast(val); -+ -+ using cstring_result = conditional_t::value, -+ const char*, unformattable_pointer>; -+ -+ FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) -+ -> cstring_result { -+ return map(reinterpret_cast(val)); - } -- FMT_CONSTEXPR const char* map(const unsigned char* val) { -- static_assert(std::is_same::value, "invalid string type"); -- return reinterpret_cast(val); -+ FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) -+ -> cstring_result { -+ return map(reinterpret_cast(val)); - } -- FMT_CONSTEXPR const char* map(signed char* val) { -- const auto* const_val = val; -- return map(const_val); -+ FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) -+ -> cstring_result { -+ return map(reinterpret_cast(val)); - } -- FMT_CONSTEXPR const char* map(unsigned char* val) { -- const auto* const_val = val; -- return map(const_val); -+ FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) -+ -> cstring_result { -+ return map(reinterpret_cast(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*) { -- // 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 -- // by iostreams. -- static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); -- return 0; -+ FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } -+ FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { -+ return val; -+ } -+ FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { -+ return val; -+ } -+ -+ // We use SFINAE instead of a const T* parameter to avoid conflicting with -+ // the C array overload. -+ template < -+ typename T, -+ FMT_ENABLE_IF( -+ std::is_member_pointer::value || -+ std::is_function::type>::value || -+ (std::is_convertible::value && -+ !std::is_convertible::value))> -+ FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { -+ return {}; -+ } -+ -+ template ::value)> -+ FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { -+ return values; - } - - template ::value && -- !has_formatter::value && -- !has_fallback_formatter::value)> -- FMT_CONSTEXPR auto map(const T& val) -+ FMT_ENABLE_IF( -+ std::is_enum::value&& std::is_convertible::value && -+ !has_formatter::value && -+ !has_fallback_formatter::value)> -+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> decltype(std::declval().map( - static_cast::type>(val))) { - return map(static_cast::type>(val)); - } -- template ::value && !is_char::value && -- (has_formatter::value || -- has_fallback_formatter::value))> -- FMT_CONSTEXPR const T& map(const T& val) { -+ -+ FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned { -+ return map(static_cast(val)); -+ } -+ -+ template > -+ struct formattable -+ : bool_constant() || -+ !std::is_const>::value || -+ has_fallback_formatter::value> {}; -+ -+#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910 -+ // Workaround a bug in MSVC. -+ template FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { -+ return val; -+ } -+#else -+ template ::value)> -+ FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { - return val; - } -+ template ::value)> -+ FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const { -+ return {}; -+ } -+#endif - -- template -- FMT_CONSTEXPR auto map(const named_arg& val) -- -> decltype(std::declval().map(val.value)) { -- return map(val.value); -+ template , -+ FMT_ENABLE_IF(!is_string::value && !is_char::value && -+ !std::is_array::value && -+ (has_formatter::value || -+ has_fallback_formatter::value))> -+ FMT_CONSTEXPR FMT_INLINE auto map(T&& val) -+ -> decltype(this->do_map(std::forward(val))) { -+ return do_map(std::forward(val)); - } - -- int map(...) { -- constexpr bool formattable = sizeof(Context) == 0; -- static_assert( -- formattable, -- "Cannot format argument. To make type T formattable provide a " -- "formatter specialization: " -- "https://fmt.dev/latest/api.html#formatting-user-defined-types"); -- return 0; -+ template ::value)> -+ FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) -+ -> decltype(std::declval().map(named_arg.value)) { -+ return map(named_arg.value); - } -+ -+ auto map(...) -> unformattable { return {}; } - }; - - // A type constant after applying arg_mapper. -@@ -1089,7 +1498,28 @@ enum { packed_arg_bits = 4 }; - enum { max_packed_args = 62 / packed_arg_bits }; - enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; - enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; --} // namespace detail -+ -+FMT_END_DETAIL_NAMESPACE -+ -+// An output iterator that appends to a buffer. -+// It is used to reduce symbol sizes for the common case. -+class appender : public std::back_insert_iterator> { -+ using base = std::back_insert_iterator>; -+ -+ template -+ friend auto get_buffer(appender out) -> detail::buffer& { -+ return detail::get_container(out); -+ } -+ -+ public: -+ using std::back_insert_iterator>::back_insert_iterator; -+ appender(base it) FMT_NOEXCEPT : base(it) {} -+ using _Unchecked_type = appender; // Mark iterator as checked. -+ -+ auto operator++() FMT_NOEXCEPT -> appender& { return *this; } -+ -+ auto operator++(int) FMT_NOEXCEPT -> appender { return *this; } -+}; - - // A formatting argument. It is a trivially copyable/constructible type to - // allow storage in basic_memory_buffer. -@@ -1099,8 +1529,8 @@ template class basic_format_arg { - detail::type type_; - - template -- friend FMT_CONSTEXPR basic_format_arg detail::make_arg( -- const T& value); -+ friend FMT_CONSTEXPR auto detail::make_arg(const T& value) -+ -> basic_format_arg; - - template - friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, -@@ -1138,10 +1568,12 @@ template class basic_format_arg { - return type_ != detail::type::none_type; - } - -- detail::type type() const { return type_; } -+ auto type() const -> detail::type { return type_; } - -- bool is_integral() const { return detail::is_integral_type(type_); } -- bool is_arithmetic() const { return detail::is_arithmetic_type(type_); } -+ auto is_integral() const -> bool { return detail::is_integral_type(type_); } -+ auto is_arithmetic() const -> bool { -+ return detail::is_arithmetic_type(type_); -+ } - }; - - /** -@@ -1152,9 +1584,8 @@ template class basic_format_arg { - \endrst - */ - template --FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( -+FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( - Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { -- using char_type = typename Context::char_type; - switch (arg.type_) { - case detail::type::none_type: - break; -@@ -1166,16 +1597,10 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( - return vis(arg.value_.long_long_value); - case detail::type::ulong_long_type: - return vis(arg.value_.ulong_long_value); --#if FMT_USE_INT128 -- case detail::type::int128_type: -- return vis(arg.value_.int128_value); -- case detail::type::uint128_type: -- return vis(arg.value_.uint128_value); --#else - case detail::type::int128_type: -+ return vis(detail::convert_for_visit(arg.value_.int128_value)); - case detail::type::uint128_type: -- break; --#endif -+ return vis(detail::convert_for_visit(arg.value_.uint128_value)); - case detail::type::bool_type: - return vis(arg.value_.bool_value); - case detail::type::char_type: -@@ -1189,8 +1614,8 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( - case detail::type::cstring_type: - return vis(arg.value_.string.data); - case detail::type::string_type: -- return vis(basic_string_view(arg.value_.string.data, -- arg.value_.string.size)); -+ using sv = basic_string_view; -+ return vis(sv(arg.value_.string.data, arg.value_.string.size)); - case detail::type::pointer_type: - return vis(arg.value_.pointer); - case detail::type::custom_type: -@@ -1199,14 +1624,32 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( - return vis(monostate()); - } - --// Checks whether T is a container with contiguous storage. --template struct is_contiguous : std::false_type {}; --template --struct is_contiguous> : std::true_type {}; --template --struct is_contiguous> : std::true_type {}; -+FMT_BEGIN_DETAIL_NAMESPACE - --namespace detail { -+template -+auto copy_str(InputIt begin, InputIt end, appender out) -> appender { -+ get_container(out).append(begin, end); -+ return out; -+} -+ -+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 -+// A workaround for gcc 4.8 to make void_t work in a SFINAE context. -+template struct void_t_impl { using type = void; }; -+template -+using void_t = typename detail::void_t_impl::type; -+#else -+template using void_t = void; -+#endif -+ -+template -+struct is_output_iterator : std::false_type {}; -+ -+template -+struct is_output_iterator< -+ It, T, -+ void_t::iterator_category, -+ decltype(*std::declval() = std::declval())>> -+ : std::true_type {}; - - template - struct is_back_insert_iterator : std::false_type {}; -@@ -1219,6 +1662,8 @@ struct is_contiguous_back_insert_iterator : std::false_type {}; - template - struct is_contiguous_back_insert_iterator> - : is_contiguous {}; -+template <> -+struct is_contiguous_back_insert_iterator : std::true_type {}; - - // A type-erased reference to an std::locale to avoid heavy include. - class locale_ref { -@@ -1226,24 +1671,26 @@ 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; } - -- template Locale get() const; -+ template auto get() const -> Locale; - }; - --template constexpr unsigned long long encode_types() { return 0; } -+template constexpr auto encode_types() -> unsigned long long { -+ return 0; -+} - - template --constexpr unsigned long long encode_types() { -+constexpr auto encode_types() -> unsigned long long { - return static_cast(mapped_type_constant::value) | - (encode_types() << packed_arg_bits); - } - - template --FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { -+FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { - basic_format_arg arg; - arg.type_ = mapped_type_constant::value; - arg.value_ = arg_mapper().map(value); -@@ -1255,57 +1702,41 @@ FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { - // another (not recommended). - template --inline value make_arg(const T& val) { -- return arg_mapper().map(val); -+FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { -+ const auto& arg = arg_mapper().map(std::forward(val)); -+ -+ constexpr bool formattable_char = -+ !std::is_same::value; -+ static_assert(formattable_char, "Mixing character types is disallowed."); -+ -+ constexpr bool formattable_const = -+ !std::is_same::value; -+ static_assert(formattable_const, "Cannot format a const argument."); -+ -+ // 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 -+ // by iostreams. -+ constexpr bool formattable_pointer = -+ !std::is_same::value; -+ static_assert(formattable_pointer, -+ "Formatting of non-void pointers is disallowed."); -+ -+ constexpr bool formattable = -+ !std::is_same::value; -+ static_assert( -+ formattable, -+ "Cannot format an argument. To make type T formattable provide a " -+ "formatter specialization: https://fmt.dev/latest/api.html#udt"); -+ return {arg}; - } - - template --inline basic_format_arg make_arg(const T& value) { -+inline auto make_arg(const T& value) -> basic_format_arg { - 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 -+FMT_END_DETAIL_NAMESPACE - - // Formatting context. - template class basic_format_context { -@@ -1324,45 +1755,58 @@ template class basic_format_context { - using parse_context_type = basic_format_parse_context; - template using formatter_type = formatter; - -+ basic_format_context(basic_format_context&&) = default; - basic_format_context(const basic_format_context&) = delete; - void operator=(const basic_format_context&) = delete; - /** - Constructs a ``basic_format_context`` object. References to the arguments are - stored in the object so make sure they have appropriate lifetimes. - */ -- 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); } -- int arg_id(basic_string_view name) { return args_.get_id(name); } -- const basic_format_args& args() const { return args_; } -+ constexpr auto arg(int id) const -> format_arg { return args_.get(id); } -+ FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { -+ return args_.get(name); -+ } -+ FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { -+ return args_.get_id(name); -+ } -+ auto args() const -> const basic_format_args& { -+ return args_; -+ } - -- detail::error_handler error_handler() { return {}; } -+ FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } - void on_error(const char* message) { error_handler().on_error(message); } - - // Returns an iterator to the beginning of the output range. -- iterator out() { return out_; } -+ FMT_CONSTEXPR auto out() -> iterator { return out_; } - - // Advances the begin iterator to ``it``. - void advance_to(iterator it) { - if (!detail::is_back_insert_iterator()) out_ = it; - } - -- detail::locale_ref locale() { return loc_; } -+ FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } - }; - - template - using buffer_context = -- basic_format_context>, Char>; -+ basic_format_context, Char>; - using format_context = buffer_context; --using wformat_context = buffer_context; - --// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164. -+// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. - #define FMT_BUFFER_CONTEXT(Char) \ -- basic_format_context>, Char> -+ basic_format_context, Char> -+ -+template -+using is_formattable = bool_constant< -+ !std::is_base_of>().map( -+ std::declval()))>::value && -+ !detail::has_fallback_formatter::value>; - - /** - \rst -@@ -1400,36 +1844,39 @@ class format_arg_store - : 0); - - public: -- format_arg_store(const Args&... args) -+ template -+ FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args) - : - #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - basic_format_args(*this), - #endif - data_{detail::make_arg< - is_packed, Context, -- detail::mapped_type_constant::value>(args)...} { -+ detail::mapped_type_constant, Context>::value>( -+ std::forward(args))...} { - detail::init_named_args(data_.named_args(), 0, 0, args...); - } - }; - - /** - \rst -- Constructs an `~fmt::format_arg_store` object that contains references to -+ Constructs a `~fmt::format_arg_store` object that contains references to - arguments and can be implicitly converted to `~fmt::format_args`. `Context` - can be omitted in which case it defaults to `~fmt::context`. - See `~fmt::arg` for lifetime considerations. - \endrst - */ - template --inline format_arg_store make_format_args( -- const Args&... args) { -- return {args...}; -+constexpr auto make_format_args(Args&&... args) -+ -> format_arg_store...> { -+ return {std::forward(args)...}; - } - - /** - \rst -- Returns a named argument to be used in a formatting function. It should only -- be used in a call to a formatting function. -+ 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**:: - -@@ -1437,184 +1884,11 @@ inline format_arg_store make_format_args( - \endrst - */ - template --inline detail::named_arg arg(const Char* name, const T& arg) { -+inline auto arg(const Char* name, const T& arg) -> detail::named_arg { - static_assert(!detail::is_named_arg(), "nested named arguments"); - 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 -@@ -1646,25 +1920,27 @@ template class basic_format_args { - const format_arg* args_; - }; - -- bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; } -- bool has_named_args() const { -+ constexpr auto is_packed() const -> bool { -+ return (desc_ & detail::is_unpacked_bit) == 0; -+ } -+ auto has_named_args() const -> bool { - return (desc_ & detail::has_named_args_bit) != 0; - } - -- detail::type type(int index) const { -+ FMT_CONSTEXPR auto type(int index) const -> detail::type { - int shift = index * detail::packed_arg_bits; - unsigned int mask = (1 << detail::packed_arg_bits) - 1; - return static_cast((desc_ >> shift) & mask); - } - -- 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 -@@ -1672,8 +1948,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 -@@ -1681,7 +1959,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()) {} - - /** -@@ -1689,12 +1968,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 auto get(int id) const -> format_arg { - format_arg arg; - if (!is_packed()) { - if (id < max_size()) arg = args_[id]; -@@ -1707,12 +1986,14 @@ template class basic_format_args { - return arg; - } - -- template format_arg get(basic_string_view name) const { -+ template -+ auto get(basic_string_view name) const -> format_arg { - int id = get_id(name); - return id >= 0 ? get(id) : format_arg(); - } - -- template int get_id(basic_string_view name) const { -+ template -+ auto get_id(basic_string_view name) const -> int { - if (!has_named_args()) return -1; - const auto& named_args = - (is_packed() ? values_[-1] : args_[-1].value_).named_args; -@@ -1722,161 +2003,1234 @@ template class basic_format_args { - return -1; - } - -- int max_size() const { -+ auto max_size() const -> int { - unsigned long long max_packed = detail::max_packed_args; - return static_cast(is_packed() ? max_packed - : desc_ & ~detail::is_unpacked_bit); - } - }; - --/** An alias to ``basic_format_args``. */ --// It is a separate type rather than an alias to make symbols readable. --struct format_args : basic_format_args { -- template -- FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {} -+/** An alias to ``basic_format_args``. */ -+// A separate type would result in shorter symbols but break ABI compatibility -+// between clang and gcc on ARM (#1919). -+using format_args = basic_format_args; -+ -+// We cannot use enum classes as bit fields because of a gcc bug -+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. -+namespace align { -+enum type { none, left, right, center, numeric }; -+} -+using align_t = align::type; -+namespace sign { -+enum type { none, minus, plus, space }; -+} -+using sign_t = sign::type; -+ -+FMT_BEGIN_DETAIL_NAMESPACE -+ -+// Workaround an array initialization issue in gcc 4.8. -+template struct fill_t { -+ private: -+ enum { max_size = 4 }; -+ Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; -+ unsigned char size_ = 1; -+ -+ public: -+ FMT_CONSTEXPR void operator=(basic_string_view s) { -+ auto size = s.size(); -+ if (size > max_size) return throw_format_error("invalid fill"); -+ for (size_t i = 0; i < size; ++i) data_[i] = s[i]; -+ size_ = static_cast(size); -+ } -+ -+ constexpr auto size() const -> size_t { return size_; } -+ constexpr auto data() const -> const Char* { return data_; } -+ -+ FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; } -+ FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& { -+ return data_[index]; -+ } - }; --struct wformat_args : basic_format_args { -- using basic_format_args::basic_format_args; -+FMT_END_DETAIL_NAMESPACE -+ -+enum class presentation_type : unsigned char { -+ none, -+ // Integer types should go first, -+ dec, // 'd' -+ oct, // 'o' -+ hex_lower, // 'x' -+ hex_upper, // 'X' -+ bin_lower, // 'b' -+ bin_upper, // 'B' -+ hexfloat_lower, // 'a' -+ hexfloat_upper, // 'A' -+ exp_lower, // 'e' -+ exp_upper, // 'E' -+ fixed_lower, // 'f' -+ fixed_upper, // 'F' -+ general_lower, // 'g' -+ general_upper, // 'G' -+ chr, // 'c' -+ string, // 's' -+ pointer // 'p' - }; - --namespace detail { -+// Format specifiers for built-in and string types. -+template struct basic_format_specs { -+ int width; -+ int precision; -+ presentation_type type; -+ align_t align : 4; -+ sign_t sign : 3; -+ bool alt : 1; // Alternate form ('#'). -+ bool localized : 1; -+ detail::fill_t fill; -+ -+ constexpr basic_format_specs() -+ : width(0), -+ precision(-1), -+ type(presentation_type::none), -+ align(align::none), -+ sign(sign::none), -+ alt(false), -+ localized(false) {} -+}; - --// Reports a compile-time error if S is not a valid format string. --template ::value)> --FMT_INLINE void check_format_string(const S&) { --#ifdef FMT_ENFORCE_COMPILE_STRING -- static_assert(is_compile_string::value, -- "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " -- "FMT_STRING."); --#endif -+using format_specs = basic_format_specs; -+ -+FMT_BEGIN_DETAIL_NAMESPACE -+ -+enum class arg_id_kind { none, index, name }; -+ -+// An argument reference. -+template struct arg_ref { -+ FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} -+ -+ FMT_CONSTEXPR explicit arg_ref(int index) -+ : kind(arg_id_kind::index), val(index) {} -+ FMT_CONSTEXPR explicit arg_ref(basic_string_view name) -+ : kind(arg_id_kind::name), val(name) {} -+ -+ FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& { -+ kind = arg_id_kind::index; -+ val.index = idx; -+ return *this; -+ } -+ -+ arg_id_kind kind; -+ union value { -+ FMT_CONSTEXPR value(int id = 0) : index{id} {} -+ FMT_CONSTEXPR value(basic_string_view n) : name(n) {} -+ -+ int index; -+ basic_string_view name; -+ } val; -+}; -+ -+// Format specifiers with width and precision resolved at formatting rather -+// than parsing time to allow re-using the same parsed specifiers with -+// different sets of arguments (precompilation of format strings). -+template -+struct dynamic_format_specs : basic_format_specs { -+ arg_ref width_ref; -+ arg_ref precision_ref; -+}; -+ -+struct auto_id {}; -+ -+// A format specifier handler that sets fields in basic_format_specs. -+template class specs_setter { -+ protected: -+ basic_format_specs& specs_; -+ -+ public: -+ explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) -+ : specs_(specs) {} -+ -+ FMT_CONSTEXPR specs_setter(const specs_setter& other) -+ : specs_(other.specs_) {} -+ -+ FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } -+ FMT_CONSTEXPR void on_fill(basic_string_view fill) { -+ specs_.fill = fill; -+ } -+ FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; } -+ FMT_CONSTEXPR void on_hash() { specs_.alt = true; } -+ FMT_CONSTEXPR void on_localized() { specs_.localized = true; } -+ -+ FMT_CONSTEXPR void on_zero() { -+ if (specs_.align == align::none) specs_.align = align::numeric; -+ specs_.fill[0] = Char('0'); -+ } -+ -+ FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } -+ FMT_CONSTEXPR void on_precision(int precision) { -+ specs_.precision = precision; -+ } -+ FMT_CONSTEXPR void end_precision() {} -+ -+ FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; } -+}; -+ -+// Format spec handler that saves references to arguments representing dynamic -+// width and precision to be resolved at formatting time. -+template -+class dynamic_specs_handler -+ : public specs_setter { -+ public: -+ using char_type = typename ParseContext::char_type; -+ -+ FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs& specs, -+ ParseContext& ctx) -+ : specs_setter(specs), specs_(specs), context_(ctx) {} -+ -+ FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other) -+ : specs_setter(other), -+ specs_(other.specs_), -+ context_(other.context_) {} -+ -+ template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { -+ specs_.width_ref = make_arg_ref(arg_id); -+ } -+ -+ template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { -+ specs_.precision_ref = make_arg_ref(arg_id); -+ } -+ -+ FMT_CONSTEXPR void on_error(const char* message) { -+ context_.on_error(message); -+ } -+ -+ private: -+ dynamic_format_specs& specs_; -+ ParseContext& context_; -+ -+ using arg_ref_type = arg_ref; -+ -+ FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type { -+ context_.check_arg_id(arg_id); -+ return arg_ref_type(arg_id); -+ } -+ -+ FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type { -+ return arg_ref_type(context_.next_arg_id()); -+ } -+ -+ FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) -+ -> arg_ref_type { -+ context_.check_arg_id(arg_id); -+ basic_string_view format_str( -+ context_.begin(), to_unsigned(context_.end() - context_.begin())); -+ return arg_ref_type(arg_id); -+ } -+}; -+ -+template constexpr bool is_ascii_letter(Char c) { -+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); -+} -+ -+// Converts a character to ASCII. Returns a number > 127 on conversion failure. -+template ::value)> -+constexpr auto to_ascii(Char value) -> Char { -+ return value; -+} -+template ::value)> -+constexpr auto to_ascii(Char value) -> -+ typename std::underlying_type::type { -+ return value; - } --template ::value)> --void check_format_string(S); - --template > --inline format_arg_store, remove_reference_t...> --make_args_checked(const S& format_str, -- const remove_reference_t&... args) { -- static_assert(count<(std::is_base_of>::value && -- std::is_reference::value)...>() == 0, -- "passing views as lvalues is disallowed"); -- check_format_string(format_str); -- return {args...}; -+template -+FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { -+ if (const_check(sizeof(Char) != 1)) return 1; -+ auto 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"; -+ 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 ::value)> --std::basic_string vformat( -- basic_string_view format_str, -- basic_format_args>> args); -+// Return the result via the out param to workaround gcc bug 77539. -+template -+FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { -+ for (out = first; out != last; ++out) { -+ if (*out == value) return true; -+ } -+ return false; -+} - --FMT_API std::string vformat(string_view format_str, format_args args); -+template <> -+inline auto find(const char* first, const char* last, char value, -+ const char*& out) -> bool { -+ out = static_cast( -+ std::memchr(first, value, to_unsigned(last - first))); -+ return out != nullptr; -+} - -+// Parses the range [begin, end) as an unsigned integer. This function assumes -+// that the range is non-empty and the first character is a digit. - template --typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to( -- buffer& buf, basic_string_view format_str, -- basic_format_args)> args); -+FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, -+ int error_value) noexcept -> int { -+ FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); -+ unsigned value = 0, prev = 0; -+ auto p = begin; -+ do { -+ prev = value; -+ value = value * 10 + unsigned(*p - '0'); -+ ++p; -+ } while (p != end && '0' <= *p && *p <= '9'); -+ auto num_digits = p - begin; -+ begin = p; -+ if (num_digits <= std::numeric_limits::digits10) -+ return static_cast(value); -+ // Check for overflow. -+ const unsigned max = to_unsigned((std::numeric_limits::max)()); -+ return num_digits == std::numeric_limits::digits10 + 1 && -+ prev * 10ull + unsigned(p[-1] - '0') <= max -+ ? static_cast(value) -+ : error_value; -+} -+ -+// Parses fill and alignment. -+template -+FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, -+ Handler&& handler) -> const Char* { -+ FMT_ASSERT(begin != end, ""); -+ auto align = align::none; -+ auto p = begin + code_point_length(begin); -+ if (p >= end) p = begin; -+ for (;;) { -+ switch (to_ascii(*p)) { -+ case '<': -+ align = align::left; -+ break; -+ case '>': -+ align = align::right; -+ break; -+ case '^': -+ align = align::center; -+ break; -+ default: -+ break; -+ } -+ if (align != align::none) { -+ if (p != begin) { -+ auto c = *begin; -+ if (c == '{') -+ return handler.on_error("invalid fill character '{'"), begin; -+ handler.on_fill(basic_string_view(begin, to_unsigned(p - begin))); -+ begin = p + 1; -+ } else -+ ++begin; -+ handler.on_align(align); -+ break; -+ } else if (p == begin) { -+ break; -+ } -+ p = begin; -+ } -+ return begin; -+} -+ -+template FMT_CONSTEXPR bool is_name_start(Char c) { -+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; -+} -+ -+template -+FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, -+ IDHandler&& handler) -> const Char* { -+ FMT_ASSERT(begin != end, ""); -+ Char c = *begin; -+ if (c >= '0' && c <= '9') { -+ int index = 0; -+ if (c != '0') -+ index = -+ parse_nonnegative_int(begin, end, (std::numeric_limits::max)()); -+ else -+ ++begin; -+ if (begin == end || (*begin != '}' && *begin != ':')) -+ handler.on_error("invalid format string"); -+ else -+ handler(index); -+ return begin; -+ } -+ if (!is_name_start(c)) { -+ handler.on_error("invalid format string"); -+ return begin; -+ } -+ auto it = begin; -+ do { -+ ++it; -+ } while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9'))); -+ handler(basic_string_view(begin, to_unsigned(it - begin))); -+ return it; -+} -+ -+template -+FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, -+ IDHandler&& handler) -> const Char* { -+ Char c = *begin; -+ if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); -+ handler(); -+ return begin; -+} -+ -+template -+FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, -+ Handler&& handler) -> const Char* { -+ using detail::auto_id; -+ struct width_adapter { -+ Handler& handler; -+ -+ FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); } -+ FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); } -+ FMT_CONSTEXPR void operator()(basic_string_view id) { -+ handler.on_dynamic_width(id); -+ } -+ FMT_CONSTEXPR void on_error(const char* message) { -+ if (message) handler.on_error(message); -+ } -+ }; -+ -+ FMT_ASSERT(begin != end, ""); -+ if ('0' <= *begin && *begin <= '9') { -+ int width = parse_nonnegative_int(begin, end, -1); -+ if (width != -1) -+ handler.on_width(width); -+ else -+ handler.on_error("number is too big"); -+ } else if (*begin == '{') { -+ ++begin; -+ if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler}); -+ if (begin == end || *begin != '}') -+ return handler.on_error("invalid format string"), begin; -+ ++begin; -+ } -+ return begin; -+} -+ -+template -+FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, -+ Handler&& handler) -> const Char* { -+ using detail::auto_id; -+ struct precision_adapter { -+ Handler& handler; -+ -+ FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); } -+ FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); } -+ FMT_CONSTEXPR void operator()(basic_string_view id) { -+ handler.on_dynamic_precision(id); -+ } -+ FMT_CONSTEXPR void on_error(const char* message) { -+ if (message) handler.on_error(message); -+ } -+ }; -+ -+ ++begin; -+ auto c = begin != end ? *begin : Char(); -+ if ('0' <= c && c <= '9') { -+ auto precision = parse_nonnegative_int(begin, end, -1); -+ if (precision != -1) -+ handler.on_precision(precision); -+ else -+ handler.on_error("number is too big"); -+ } else if (c == '{') { -+ ++begin; -+ if (begin != end) -+ begin = parse_arg_id(begin, end, precision_adapter{handler}); -+ if (begin == end || *begin++ != '}') -+ return handler.on_error("invalid format string"), begin; -+ } else { -+ return handler.on_error("missing precision specifier"), begin; -+ } -+ handler.end_precision(); -+ return begin; -+} -+ -+template -+FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type { -+ switch (to_ascii(type)) { -+ case 'd': -+ return presentation_type::dec; -+ case 'o': -+ return presentation_type::oct; -+ case 'x': -+ return presentation_type::hex_lower; -+ case 'X': -+ return presentation_type::hex_upper; -+ case 'b': -+ return presentation_type::bin_lower; -+ case 'B': -+ return presentation_type::bin_upper; -+ case 'a': -+ return presentation_type::hexfloat_lower; -+ case 'A': -+ return presentation_type::hexfloat_upper; -+ case 'e': -+ return presentation_type::exp_lower; -+ case 'E': -+ return presentation_type::exp_upper; -+ case 'f': -+ return presentation_type::fixed_lower; -+ case 'F': -+ return presentation_type::fixed_upper; -+ case 'g': -+ return presentation_type::general_lower; -+ case 'G': -+ return presentation_type::general_upper; -+ case 'c': -+ return presentation_type::chr; -+ case 's': -+ return presentation_type::string; -+ case 'p': -+ return presentation_type::pointer; -+ default: -+ return presentation_type::none; -+ } -+} -+ -+// Parses standard format specifiers and sends notifications about parsed -+// components to handler. -+template -+FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, -+ const Char* end, -+ SpecHandler&& handler) -+ -> const Char* { -+ if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) && -+ *begin != 'L') { -+ presentation_type type = parse_presentation_type(*begin++); -+ if (type == presentation_type::none) -+ handler.on_error("invalid type specifier"); -+ handler.on_type(type); -+ return begin; -+ } -+ -+ if (begin == end) return begin; -+ -+ begin = parse_align(begin, end, handler); -+ if (begin == end) return begin; -+ -+ // Parse sign. -+ switch (to_ascii(*begin)) { -+ case '+': -+ handler.on_sign(sign::plus); -+ ++begin; -+ break; -+ case '-': -+ handler.on_sign(sign::minus); -+ ++begin; -+ break; -+ case ' ': -+ handler.on_sign(sign::space); -+ ++begin; -+ break; -+ default: -+ break; -+ } -+ if (begin == end) return begin; -+ -+ if (*begin == '#') { -+ handler.on_hash(); -+ if (++begin == end) return begin; -+ } -+ -+ // Parse zero flag. -+ if (*begin == '0') { -+ handler.on_zero(); -+ if (++begin == end) return begin; -+ } -+ -+ begin = parse_width(begin, end, handler); -+ if (begin == end) return begin; -+ -+ // Parse precision. -+ if (*begin == '.') { -+ begin = parse_precision(begin, end, handler); -+ if (begin == end) return begin; -+ } -+ -+ if (*begin == 'L') { -+ handler.on_localized(); -+ ++begin; -+ } -+ -+ // Parse type. -+ if (begin != end && *begin != '}') { -+ presentation_type type = parse_presentation_type(*begin++); -+ if (type == presentation_type::none) -+ handler.on_error("invalid type specifier"); -+ handler.on_type(type); -+ } -+ return begin; -+} -+ -+template -+FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, -+ Handler&& handler) -> const Char* { -+ struct id_adapter { -+ Handler& handler; -+ int arg_id; -+ -+ FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); } -+ FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); } -+ FMT_CONSTEXPR void operator()(basic_string_view id) { -+ arg_id = handler.on_arg_id(id); -+ } -+ FMT_CONSTEXPR void on_error(const char* message) { -+ if (message) handler.on_error(message); -+ } -+ }; -+ -+ ++begin; -+ if (begin == end) return handler.on_error("invalid format string"), end; -+ if (*begin == '}') { -+ handler.on_replacement_field(handler.on_arg_id(), begin); -+ } else if (*begin == '{') { -+ handler.on_text(begin, begin + 1); -+ } else { -+ auto adapter = id_adapter{handler, 0}; -+ begin = parse_arg_id(begin, end, adapter); -+ Char c = begin != end ? *begin : Char(); -+ if (c == '}') { -+ handler.on_replacement_field(adapter.arg_id, begin); -+ } else if (c == ':') { -+ begin = handler.on_format_specs(adapter.arg_id, begin + 1, end); -+ if (begin == end || *begin != '}') -+ return handler.on_error("unknown format specifier"), end; -+ } else { -+ return handler.on_error("missing '}' in format string"), end; -+ } -+ } -+ return begin + 1; -+} -+ -+template -+FMT_CONSTEXPR FMT_INLINE void parse_format_string( -+ basic_string_view format_str, Handler&& handler) { -+ // Workaround a name-lookup bug in MSVC's modules implementation. -+ using detail::find; -+ -+ auto begin = format_str.data(); -+ auto end = begin + format_str.size(); -+ if (end - begin < 32) { -+ // Use a simple loop instead of memchr for small strings. -+ const Char* p = begin; -+ while (p != end) { -+ auto c = *p++; -+ if (c == '{') { -+ handler.on_text(begin, p - 1); -+ begin = p = parse_replacement_field(p - 1, end, handler); -+ } else if (c == '}') { -+ if (p == end || *p != '}') -+ return handler.on_error("unmatched '}' in format string"); -+ handler.on_text(begin, p); -+ begin = ++p; -+ } -+ } -+ handler.on_text(begin, end); -+ return; -+ } -+ struct writer { -+ FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) { -+ if (pbegin == pend) return; -+ for (;;) { -+ const Char* p = nullptr; -+ if (!find(pbegin, pend, Char('}'), p)) -+ return handler_.on_text(pbegin, pend); -+ ++p; -+ if (p == pend || *p != '}') -+ return handler_.on_error("unmatched '}' in format string"); -+ handler_.on_text(pbegin, p); -+ pbegin = p + 1; -+ } -+ } -+ Handler& handler_; -+ } write{handler}; -+ while (begin != end) { -+ // Doing two passes with memchr (one for '{' and another for '}') is up to -+ // 2.5x faster than the naive one-pass implementation on big format strings. -+ const Char* p = begin; -+ if (*begin != '{' && !find(begin + 1, end, Char('{'), p)) -+ return write(begin, end); -+ write(begin, p); -+ begin = parse_replacement_field(p, end, handler); -+ } -+} -+ -+template -+FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) -+ -> decltype(ctx.begin()) { -+ using char_type = typename ParseContext::char_type; -+ using context = buffer_context; -+ using mapped_type = conditional_t< -+ mapped_type_constant::value != type::custom_type, -+ decltype(arg_mapper().map(std::declval())), T>; -+ auto f = conditional_t::value, -+ formatter, -+ fallback_formatter>(); -+ return f.parse(ctx); -+} -+ -+// A parse context with extra argument id checks. It is only used at compile -+// time because adding checks at runtime would introduce substantial overhead -+// and would be redundant since argument ids are checked when arguments are -+// retrieved anyway. -+template -+class compile_parse_context -+ : public basic_format_parse_context { -+ private: -+ int num_args_; -+ using base = basic_format_parse_context; -+ -+ public: -+ explicit FMT_CONSTEXPR compile_parse_context( -+ basic_string_view format_str, -+ int num_args = (std::numeric_limits::max)(), ErrorHandler eh = {}) -+ : base(format_str, eh), num_args_(num_args) {} -+ -+ FMT_CONSTEXPR auto next_arg_id() -> int { -+ int id = base::next_arg_id(); -+ if (id >= num_args_) this->on_error("argument not found"); -+ return id; -+ } -+ -+ FMT_CONSTEXPR void check_arg_id(int id) { -+ base::check_arg_id(id); -+ if (id >= num_args_) this->on_error("argument not found"); -+ } -+ using base::check_arg_id; -+}; -+ -+template -+FMT_CONSTEXPR void check_int_type_spec(presentation_type type, -+ ErrorHandler&& eh) { -+ if (type > presentation_type::bin_upper && type != presentation_type::chr) -+ eh.on_error("invalid type specifier"); -+} -+ -+// Checks char specs and returns true if the type spec is char (and not int). -+template -+FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs, -+ ErrorHandler&& eh = {}) -> bool { -+ if (specs.type != presentation_type::none && -+ specs.type != presentation_type::chr) { -+ check_int_type_spec(specs.type, eh); -+ return false; -+ } -+ if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) -+ eh.on_error("invalid format specifier for char"); -+ return true; -+} -+ -+// A floating-point presentation format. -+enum class float_format : unsigned char { -+ general, // General: exponent notation or fixed point based on magnitude. -+ exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. -+ fixed, // Fixed point with the default precision of 6, e.g. 0.0012. -+ hex -+}; -+ -+struct float_specs { -+ int precision; -+ float_format format : 8; -+ sign_t sign : 8; -+ bool upper : 1; -+ bool locale : 1; -+ bool binary32 : 1; -+ bool fallback : 1; -+ bool showpoint : 1; -+}; -+ -+template -+FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, -+ ErrorHandler&& eh = {}) -+ -> float_specs { -+ auto result = float_specs(); -+ result.showpoint = specs.alt; -+ result.locale = specs.localized; -+ switch (specs.type) { -+ case presentation_type::none: -+ result.format = float_format::general; -+ break; -+ case presentation_type::general_upper: -+ result.upper = true; -+ FMT_FALLTHROUGH; -+ case presentation_type::general_lower: -+ result.format = float_format::general; -+ break; -+ case presentation_type::exp_upper: -+ result.upper = true; -+ FMT_FALLTHROUGH; -+ case presentation_type::exp_lower: -+ result.format = float_format::exp; -+ result.showpoint |= specs.precision != 0; -+ break; -+ case presentation_type::fixed_upper: -+ result.upper = true; -+ FMT_FALLTHROUGH; -+ case presentation_type::fixed_lower: -+ result.format = float_format::fixed; -+ result.showpoint |= specs.precision != 0; -+ break; -+ case presentation_type::hexfloat_upper: -+ result.upper = true; -+ FMT_FALLTHROUGH; -+ case presentation_type::hexfloat_lower: -+ result.format = float_format::hex; -+ break; -+ default: -+ eh.on_error("invalid type specifier"); -+ break; -+ } -+ return result; -+} -+ -+template -+FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, -+ ErrorHandler&& eh = {}) -> bool { -+ if (type == presentation_type::none || type == presentation_type::string) -+ return true; -+ if (type != presentation_type::pointer) eh.on_error("invalid type specifier"); -+ return false; -+} -+ -+template -+FMT_CONSTEXPR void check_string_type_spec(presentation_type type, -+ ErrorHandler&& eh = {}) { -+ if (type != presentation_type::none && type != presentation_type::string) -+ eh.on_error("invalid type specifier"); -+} -+ -+template -+FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type, -+ ErrorHandler&& eh) { -+ if (type != presentation_type::none && type != presentation_type::pointer) -+ eh.on_error("invalid type specifier"); -+} - --template ::value)> --inline void vprint_mojibake(std::FILE*, basic_string_view, const Args&) {} -+// A parse_format_specs handler that checks if specifiers are consistent with -+// the argument type. -+template class specs_checker : public Handler { -+ private: -+ detail::type arg_type_; -+ -+ FMT_CONSTEXPR void require_numeric_argument() { -+ if (!is_arithmetic_type(arg_type_)) -+ this->on_error("format specifier requires numeric argument"); -+ } -+ -+ public: -+ FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) -+ : Handler(handler), arg_type_(arg_type) {} -+ -+ FMT_CONSTEXPR void on_align(align_t align) { -+ if (align == align::numeric) require_numeric_argument(); -+ Handler::on_align(align); -+ } -+ -+ FMT_CONSTEXPR void on_sign(sign_t s) { -+ require_numeric_argument(); -+ if (is_integral_type(arg_type_) && arg_type_ != type::int_type && -+ arg_type_ != type::long_long_type && arg_type_ != type::char_type) { -+ this->on_error("format specifier requires signed argument"); -+ } -+ Handler::on_sign(s); -+ } -+ -+ FMT_CONSTEXPR void on_hash() { -+ require_numeric_argument(); -+ Handler::on_hash(); -+ } -+ -+ FMT_CONSTEXPR void on_localized() { -+ require_numeric_argument(); -+ Handler::on_localized(); -+ } -+ -+ FMT_CONSTEXPR void on_zero() { -+ require_numeric_argument(); -+ Handler::on_zero(); -+ } -+ -+ FMT_CONSTEXPR void end_precision() { -+ if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) -+ this->on_error("precision not allowed for this argument type"); -+ } -+}; -+ -+constexpr int invalid_arg_index = -1; -+ -+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -+template -+constexpr auto get_arg_index_by_name(basic_string_view name) -> int { -+ if constexpr (detail::is_statically_named_arg()) { -+ if (name == T::name) return N; -+ } -+ if constexpr (sizeof...(Args) > 0) -+ return get_arg_index_by_name(name); -+ (void)name; // Workaround an MSVC bug about "unused" parameter. -+ return invalid_arg_index; -+} -+#endif -+ -+template -+FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -+ if constexpr (sizeof...(Args) > 0) -+ return get_arg_index_by_name<0, Args...>(name); -+#endif -+ (void)name; -+ return invalid_arg_index; -+} -+ -+template -+class format_string_checker { -+ private: -+ using parse_context_type = compile_parse_context; -+ enum { num_args = sizeof...(Args) }; -+ -+ // Format specifier parsing function. -+ using parse_func = const Char* (*)(parse_context_type&); -+ -+ parse_context_type context_; -+ parse_func parse_funcs_[num_args > 0 ? num_args : 1]; -+ -+ public: -+ explicit FMT_CONSTEXPR format_string_checker( -+ basic_string_view format_str, ErrorHandler eh) -+ : context_(format_str, num_args, eh), -+ parse_funcs_{&parse_format_specs...} {} -+ -+ FMT_CONSTEXPR void on_text(const Char*, const Char*) {} -+ -+ FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } -+ FMT_CONSTEXPR auto on_arg_id(int id) -> int { -+ return context_.check_arg_id(id), id; -+ } -+ FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -+ auto index = get_arg_index_by_name(id); -+ if (index == invalid_arg_index) on_error("named argument is not found"); -+ return context_.check_arg_id(index), index; -+#else -+ (void)id; -+ on_error("compile-time checks for named arguments require C++20 support"); -+ return 0; -+#endif -+ } -+ -+ FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} -+ -+ FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) -+ -> const Char* { -+ context_.advance_to(context_.begin() + (begin - &*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) { -+ context_.on_error(message); -+ } -+}; -+ -+template ::value), int>> -+void check_format_string(S format_str) { -+ FMT_CONSTEXPR auto s = to_string_view(format_str); -+ using checker = format_string_checker...>; -+ FMT_CONSTEXPR bool invalid_format = -+ (parse_format_string(s, checker(s, {})), true); -+ ignore_unused(invalid_format); -+} -+ -+template -+void vformat_to( -+ buffer& buf, basic_string_view fmt, -+ basic_format_args)> args, -+ locale_ref loc = {}); - - FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); - #ifndef _WIN32 - inline void vprint_mojibake(std::FILE*, string_view, format_args) {} - #endif --} // namespace detail -+FMT_END_DETAIL_NAMESPACE - --/** Formats a string and writes the output to ``out``. */ --// GCC 8 and earlier cannot handle std::back_insert_iterator with --// vformat_to(...) overload, so SFINAE on iterator type instead. --template < -- typename OutputIt, typename S, typename Char = char_t, -- FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator::value)> --OutputIt vformat_to( -- OutputIt out, const S& format_str, -- basic_format_args>> args) { -- auto& c = detail::get_container(out); -- detail::container_buffer> buf(c); -- detail::vformat_to(buf, to_string_view(format_str), args); -- return out; --} -+// A formatter specialization for the core types corresponding to detail::type -+// constants. -+template -+struct formatter::value != -+ detail::type::custom_type>> { -+ private: -+ detail::dynamic_format_specs specs_; - --template ::value&& detail::is_string::value)> --inline std::back_insert_iterator format_to( -- std::back_insert_iterator out, const S& format_str, -- Args&&... args) { -- return vformat_to(out, to_string_view(format_str), -- detail::make_args_checked(format_str, args...)); -+ public: -+ // Parses format specifiers stopping either at the end of the range or at the -+ // terminating '}'. -+ template -+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { -+ auto begin = ctx.begin(), end = ctx.end(); -+ if (begin == end) return begin; -+ using handler_type = detail::dynamic_specs_handler; -+ auto type = detail::type_constant::value; -+ auto checker = -+ detail::specs_checker(handler_type(specs_, ctx), type); -+ auto it = detail::parse_format_specs(begin, end, checker); -+ auto eh = ctx.error_handler(); -+ switch (type) { -+ case detail::type::none_type: -+ FMT_ASSERT(false, "invalid argument type"); -+ break; -+ case detail::type::bool_type: -+ if (specs_.type == presentation_type::none || -+ specs_.type == presentation_type::string) { -+ break; -+ } -+ FMT_FALLTHROUGH; -+ case detail::type::int_type: -+ case detail::type::uint_type: -+ case detail::type::long_long_type: -+ case detail::type::ulong_long_type: -+ case detail::type::int128_type: -+ case detail::type::uint128_type: -+ detail::check_int_type_spec(specs_.type, eh); -+ break; -+ case detail::type::char_type: -+ detail::check_char_specs(specs_, eh); -+ break; -+ case detail::type::float_type: -+ if (detail::const_check(FMT_USE_FLOAT)) -+ detail::parse_float_type_spec(specs_, eh); -+ else -+ FMT_ASSERT(false, "float support disabled"); -+ break; -+ case detail::type::double_type: -+ if (detail::const_check(FMT_USE_DOUBLE)) -+ detail::parse_float_type_spec(specs_, eh); -+ else -+ FMT_ASSERT(false, "double support disabled"); -+ break; -+ case detail::type::long_double_type: -+ if (detail::const_check(FMT_USE_LONG_DOUBLE)) -+ detail::parse_float_type_spec(specs_, eh); -+ else -+ FMT_ASSERT(false, "long double support disabled"); -+ break; -+ case detail::type::cstring_type: -+ detail::check_cstring_type_spec(specs_.type, eh); -+ break; -+ case detail::type::string_type: -+ detail::check_string_type_spec(specs_.type, eh); -+ break; -+ case detail::type::pointer_type: -+ detail::check_pointer_type_spec(specs_.type, eh); -+ break; -+ case detail::type::custom_type: -+ // Custom format specifiers are checked in parse functions of -+ // formatter specializations. -+ break; -+ } -+ return it; -+ } -+ -+ template -+ FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const -+ -> decltype(ctx.out()); -+}; -+ -+template struct basic_runtime { basic_string_view str; }; -+ -+/** A compile-time format string. */ -+template class basic_format_string { -+ private: -+ basic_string_view str_; -+ -+ public: -+ template >::value)> -+ FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { -+ static_assert( -+ detail::count< -+ (std::is_base_of>::value && -+ std::is_reference::value)...>() == 0, -+ "passing views as lvalues is disallowed"); -+#ifdef FMT_HAS_CONSTEVAL -+ if constexpr (detail::count_named_args() == -+ detail::count_statically_named_args()) { -+ using checker = detail::format_string_checker...>; -+ detail::parse_format_string(str_, checker(s, {})); -+ } -+#else -+ detail::check_format_string(s); -+#endif -+ } -+ basic_format_string(basic_runtime r) : str_(r.str) {} -+ -+ FMT_INLINE operator basic_string_view() const { return str_; } -+}; -+ -+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -+// Workaround broken conversion on older gcc. -+template using format_string = string_view; -+template auto runtime(const S& s) -> basic_string_view> { -+ return s; - } -+#else -+template -+using format_string = basic_format_string...>; -+/** -+ \rst -+ Creates a runtime format string. -+ -+ **Example**:: - --template > --FMT_INLINE std::basic_string vformat( -- const S& format_str, -- basic_format_args>> args) { -- return detail::vformat(to_string_view(format_str), args); -+ // Check format string at runtime instead of compile-time. -+ fmt::print(fmt::runtime("{:d}"), "I am not a number"); -+ \endrst -+ */ -+template auto runtime(const S& s) -> basic_runtime> { -+ return {{s}}; - } -+#endif -+ -+FMT_API auto vformat(string_view fmt, format_args args) -> std::string; - - /** - \rst -- Formats arguments and returns the result as a string. -+ Formats ``args`` according to specifications in ``fmt`` and returns the result -+ as a string. - - **Example**:: - - #include -- std::string message = fmt::format("The answer is {}", 42); -+ std::string message = fmt::format("The answer is {}.", 42); - \endrst - */ --// Pass char_t as a default template parameter instead of using --// std::basic_string> to reduce the symbol size. --template > --FMT_INLINE std::basic_string format(const S& format_str, Args&&... args) { -- const auto& vargs = detail::make_args_checked(format_str, args...); -- return detail::vformat(to_string_view(format_str), vargs); -+template -+FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) -+ -> std::string { -+ return vformat(fmt, fmt::make_format_args(args...)); - } - --FMT_API void vprint(string_view, format_args); --FMT_API void vprint(std::FILE*, string_view, format_args); -+/** Formats a string and writes the output to ``out``. */ -+template ::value)> -+auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { -+ using detail::get_buffer; -+ auto&& buf = get_buffer(out); -+ detail::vformat_to(buf, fmt, args, {}); -+ return detail::get_iterator(buf); -+} -+ -+/** -+ \rst -+ Formats ``args`` according to specifications in ``fmt``, writes the result to -+ the output iterator ``out`` and returns the iterator past the end of the output -+ range. `format_to` does not append a terminating null character. -+ -+ **Example**:: -+ -+ auto out = std::vector(); -+ fmt::format_to(std::back_inserter(out), "{}", 42); -+ \endrst -+ */ -+template ::value)> -+FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) -+ -> OutputIt { -+ return vformat_to(out, fmt, fmt::make_format_args(args...)); -+} -+ -+template struct format_to_n_result { -+ /** Iterator past the end of the output range. */ -+ OutputIt out; -+ /** Total (not truncated) output size. */ -+ size_t size; -+}; -+ -+template ::value)> -+auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) -+ -> format_to_n_result { -+ using traits = detail::fixed_buffer_traits; -+ auto buf = detail::iterator_buffer(out, n); -+ detail::vformat_to(buf, fmt, args, {}); -+ return {buf.out(), buf.count()}; -+} - - /** - \rst -- Formats ``args`` according to specifications in ``format_str`` and writes the -- output to the file ``f``. Strings are assumed to be Unicode-encoded unless the -- ``FMT_UNICODE`` macro is set to 0. -+ Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` -+ characters of the result to the output iterator ``out`` and returns the total -+ (not truncated) output size and the iterator past the end of the output range. -+ `format_to_n` does not append a terminating null character. -+ \endrst -+ */ -+template ::value)> -+FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, -+ T&&... args) -> format_to_n_result { -+ return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); -+} -+ -+/** Returns the number of chars in the output of ``format(fmt, args...)``. */ -+template -+FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, -+ T&&... args) -> size_t { -+ auto buf = detail::counting_buffer<>(); -+ detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {}); -+ return buf.count(); -+} -+ -+FMT_API void vprint(string_view fmt, format_args args); -+FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); -+ -+/** -+ \rst -+ Formats ``args`` according to specifications in ``fmt`` and writes the output -+ to ``stdout``. - - **Example**:: - -- fmt::print(stderr, "Don't {}!", "panic"); -+ fmt::print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst - */ --template > --inline void print(std::FILE* f, const S& format_str, Args&&... args) { -- const auto& vargs = detail::make_args_checked(format_str, args...); -- return detail::is_unicode() -- ? vprint(f, to_string_view(format_str), vargs) -- : detail::vprint_mojibake(f, to_string_view(format_str), vargs); -+template -+FMT_INLINE void print(format_string fmt, T&&... args) { -+ const auto& vargs = fmt::make_format_args(args...); -+ return detail::is_utf8() ? vprint(fmt, vargs) -+ : detail::vprint_mojibake(stdout, fmt, vargs); - } - - /** - \rst -- Formats ``args`` according to specifications in ``format_str`` and writes -- the output to ``stdout``. Strings are assumed to be Unicode-encoded unless -- the ``FMT_UNICODE`` macro is set to 0. -+ Formats ``args`` according to specifications in ``fmt`` and writes the -+ output to the file ``f``. - - **Example**:: - -- fmt::print("Elapsed time: {0:.2f} seconds", 1.23); -+ fmt::print(stderr, "Don't {}!", "panic"); - \endrst - */ --template > --inline void print(const S& format_str, Args&&... args) { -- const auto& vargs = detail::make_args_checked(format_str, args...); -- return detail::is_unicode() -- ? vprint(to_string_view(format_str), vargs) -- : detail::vprint_mojibake(stdout, to_string_view(format_str), -- vargs); -+template -+FMT_INLINE void print(std::FILE* f, format_string fmt, T&&... args) { -+ const auto& vargs = fmt::make_format_args(args...); -+ return detail::is_utf8() ? vprint(f, fmt, vargs) -+ : detail::vprint_mojibake(f, fmt, vargs); - } -+ -+FMT_MODULE_EXPORT_END -+FMT_GCC_PRAGMA("GCC pop_options") - FMT_END_NAMESPACE - -+#ifdef FMT_HEADER_ONLY -+# include "format.h" -+#endif - #endif // FMT_CORE_H_ -diff --git a/external/fmt/include/fmt/format-inl.h b/external/fmt/include/fmt/format-inl.h -index d8c9c8a5..2c51c50a 100644 ---- a/external/fmt/include/fmt/format-inl.h -+++ b/external/fmt/include/fmt/format-inl.h -@@ -8,42 +8,25 @@ - #ifndef FMT_FORMAT_INL_H_ - #define FMT_FORMAT_INL_H_ - --#include -+#include - #include -+#include // errno - #include - #include - #include --#include // for std::memmove -+#include // std::memmove - #include - #include - --#include "format.h" --#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) -+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR - # include - #endif - - #ifdef _WIN32 --# if !defined(NOMINMAX) && !defined(WIN32_LEAN_AND_MEAN) --# define NOMINMAX --# define WIN32_LEAN_AND_MEAN --# include --# undef WIN32_LEAN_AND_MEAN --# undef NOMINMAX --# else --# include --# endif --# include -+# include // _isatty - #endif - --#ifdef _MSC_VER --# pragma warning(push) --# pragma warning(disable : 4702) // unreachable code --#endif -- --// Dummy implementations of strerror_r and strerror_s called if corresponding --// system functions are not available. --inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; } --inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; } -+#include "format.h" - - FMT_BEGIN_NAMESPACE - namespace detail { -@@ -57,6 +40,10 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) { - std::terminate(); - } - -+FMT_FUNC void throw_format_error(const char* message) { -+ FMT_THROW(format_error(message)); -+} -+ - #ifndef _MSC_VER - # define FMT_SNPRINTF snprintf - #else // _MSC_VER -@@ -70,82 +57,12 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { - # define FMT_SNPRINTF fmt_snprintf - #endif // _MSC_VER - --// A portable thread-safe version of strerror. --// Sets buffer to point to a string describing the error code. --// This can be either a pointer to a string stored in buffer, --// or a pointer to some static immutable string. --// Returns one of the following values: --// 0 - success --// ERANGE - buffer is not large enough to store the error message --// other - failure --// Buffer should be at least of size 1. --FMT_FUNC int safe_strerror(int error_code, char*& buffer, -- size_t buffer_size) FMT_NOEXCEPT { -- FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer"); -- -- class dispatcher { -- private: -- int error_code_; -- char*& buffer_; -- size_t buffer_size_; -- -- // A noop assignment operator to avoid bogus warnings. -- void operator=(const dispatcher&) {} -- -- // Handle the result of XSI-compliant version of strerror_r. -- int handle(int result) { -- // glibc versions before 2.13 return result in errno. -- return result == -1 ? errno : result; -- } -- -- // Handle the result of GNU-specific version of strerror_r. -- FMT_MAYBE_UNUSED -- int handle(char* message) { -- // If the buffer is full then the message is probably truncated. -- if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) -- return ERANGE; -- buffer_ = message; -- return 0; -- } -- -- // Handle the case when strerror_r is not available. -- FMT_MAYBE_UNUSED -- int handle(detail::null<>) { -- return fallback(strerror_s(buffer_, buffer_size_, error_code_)); -- } -- -- // Fallback to strerror_s when strerror_r is not available. -- FMT_MAYBE_UNUSED -- int fallback(int result) { -- // If the buffer is full then the message is probably truncated. -- return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE -- : result; -- } -- --#if !FMT_MSC_VER -- // Fallback to strerror if strerror_r and strerror_s are not available. -- int fallback(detail::null<>) { -- errno = 0; -- buffer_ = strerror(error_code_); -- return errno; -- } --#endif -- -- public: -- dispatcher(int err_code, char*& buf, size_t buf_size) -- : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} -- -- int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); } -- }; -- return dispatcher(error_code, buffer, buffer_size).run(); --} -- - FMT_FUNC void format_error_code(detail::buffer& out, int error_code, - string_view message) FMT_NOEXCEPT { - // Report error code making sure that the output fits into - // inline_buffer_size to avoid dynamic memory allocation and potential - // bad_alloc. -- out.resize(0); -+ out.try_resize(0); - static const char SEP[] = ": "; - static const char ERROR_STR[] = "error "; - // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. -@@ -156,33 +73,30 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, - ++error_code_size; - } - error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); -- auto it = std::back_inserter(out); -+ 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, -- string_view message) FMT_NOEXCEPT { -+ const char* message) FMT_NOEXCEPT { - memory_buffer full_message; - func(full_message, error_code, message); - // Don't use fwrite_fully because the latter may throw. -- (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr); -- std::fputc('\n', stderr); -+ if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) -+ std::fputc('\n', stderr); - } - - // A wrapper around fwrite that throws on error. --FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count, -- FILE* stream) { -+inline void fwrite_fully(const void* ptr, size_t size, size_t count, -+ FILE* stream) { - 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, ""); -@@ -193,41 +107,36 @@ template Locale locale_ref::get() const { - return locale_ ? *static_cast(locale_) : std::locale(); - } - --template FMT_FUNC std::string grouping_impl(locale_ref loc) { -- return std::use_facet>(loc.get()).grouping(); --} --template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { -- return std::use_facet>(loc.get()) -- .thousands_sep(); -+template -+FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { -+ auto& facet = std::use_facet>(loc.get()); -+ auto grouping = facet.grouping(); -+ auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); -+ return {std::move(grouping), thousands_sep}; - } - template FMT_FUNC Char decimal_point_impl(locale_ref loc) { - return std::use_facet>(loc.get()) - .decimal_point(); - } --} // namespace detail - #else - template --FMT_FUNC std::string detail::grouping_impl(locale_ref) { -- return "\03"; --} --template FMT_FUNC Char detail::thousands_sep_impl(locale_ref) { -- return FMT_STATIC_THOUSANDS_SEPARATOR; -+FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { -+ return {"\03", 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 - -+#if !FMT_MSC_VER - FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; --FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default; -+#endif - --FMT_FUNC void system_error::init(int err_code, string_view format_str, -- format_args args) { -- error_code_ = err_code; -- memory_buffer buffer; -- format_system_error(buffer, err_code, vformat(format_str, args)); -- std::runtime_error& base = *this; -- base = std::runtime_error(to_string(buffer)); -+FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, -+ format_args args) { -+ auto ec = std::error_code(error_code, std::generic_category()); -+ return std::system_error(ec, vformat(format_str, args)); - } - - namespace detail { -@@ -240,224 +149,153 @@ template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { - return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; - } - --template --const typename basic_data::digit_pair basic_data::digits[] = { -- {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, -- {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, -- {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, -- {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'}, -- {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, -- {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, -- {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, -- {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, -- {'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'}, -- {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'}, -- {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, -- {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, -- {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, -- {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, -- {'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'}, -- {'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'}, -- {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'}, -- {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, -- {'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, \ -- (factor)*1000000000 -- --template --const uint64_t basic_data::powers_of_10_64[] = { -- 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), -- 10000000000000000000ULL}; -- --template --const uint32_t basic_data::zero_or_powers_of_10_32[] = {0, -- FMT_POWERS_OF_10(1)}; -+// log10(2) = 0x0.4d104d427de7fbcc... -+static constexpr uint64_t log10_2_significand = 0x4d104d427de7fbcc; -+ -+template struct basic_impl_data { -+ // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. -+ // These are generated by support/compute-powers.py. -+ static constexpr uint64_t pow10_significands[87] = { -+ 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, -+ 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, -+ 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, -+ 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, -+ 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, -+ 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, -+ 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, -+ 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, -+ 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, -+ 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, -+ 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, -+ 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, -+ 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, -+ 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, -+ 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, -+ 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, -+ 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, -+ 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, -+ 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, -+ 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, -+ 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, -+ 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, -+ 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, -+ 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, -+ 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, -+ 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, -+ 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, -+ 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, -+ 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, -+ }; - --template --const uint64_t basic_data::zero_or_powers_of_10_64[] = { -- 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), -- 10000000000000000000ULL}; -+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -+# pragma GCC diagnostic push -+# pragma GCC diagnostic ignored "-Wnarrowing" -+#endif -+ // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding -+ // to significands above. -+ static constexpr int16_t pow10_exponents[87] = { -+ -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -+ -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, -+ -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, -+ -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, -+ -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, -+ 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, -+ 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, -+ 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; -+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -+# pragma GCC diagnostic pop -+#endif - --// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. --// These are generated by support/compute-powers.py. --template --const uint64_t basic_data::pow10_significands[] = { -- 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, -- 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, -- 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, -- 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, -- 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, -- 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, -- 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, -- 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, -- 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, -- 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, -- 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, -- 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, -- 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, -- 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, -- 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, -- 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, -- 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, -- 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, -- 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, -- 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, -- 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, -- 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, -- 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, -- 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, -- 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, -- 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, -- 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, -- 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, -- 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, -+ static constexpr uint64_t power_of_10_64[20] = { -+ 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), -+ 10000000000000000000ULL}; - }; - --// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding --// to significands above. --template --const int16_t basic_data::pow10_exponents[] = { -- -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -- -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, -- -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, -- -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, -- -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, -- 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, -- 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, -- 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; -+// This is a struct rather than an alias to avoid shadowing warnings in gcc. -+struct impl_data : basic_impl_data<> {}; - -+#if __cplusplus < 201703L - template --const char basic_data::foreground_color[] = "\x1b[38;2;"; --template --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, '-', '+', ' '}; --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 uint64_t basic_impl_data::pow10_significands[]; -+template constexpr int16_t basic_impl_data::pow10_exponents[]; -+template constexpr uint64_t basic_impl_data::power_of_10_64[]; -+#endif - - template struct bits { - static FMT_CONSTEXPR_DECL const int value = - static_cast(sizeof(T) * std::numeric_limits::digits); - }; - --class fp; --template fp normalize(fp value); -- --// Lower (upper) boundary is a value half way between a floating-point value --// and its predecessor (successor). Boundaries have the same exponent as the --// value so only significands are stored. --struct boundaries { -- uint64_t lower; -- uint64_t upper; --}; -- --// A handmade floating-point number f * pow(2, e). --class fp { -- private: -- using significand_type = uint64_t; -+// Returns the number of significand bits in Float excluding the implicit bit. -+template constexpr int num_significand_bits() { -+ // Subtract 1 to account for an implicit most significant bit in the -+ // normalized form. -+ return std::numeric_limits::digits - 1; -+} - -- public: -- significand_type f; -+// A floating-point number f * pow(2, e). -+struct fp { -+ uint64_t f; - int e; - -- // All sizes are in bits. -- // Subtract 1 to account for an implicit most significant bit in the -- // normalized form. -- static FMT_CONSTEXPR_DECL const int double_significand_size = -- std::numeric_limits::digits - 1; -- static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = -- 1ULL << double_significand_size; -- static FMT_CONSTEXPR_DECL const int significand_size = -- bits::value; -+ static constexpr const int num_significand_bits = bits::value; -+ -+ constexpr fp() : f(0), e(0) {} -+ constexpr fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} - -- fp() : f(0), e(0) {} -- fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} -+ // Constructs fp from an IEEE754 floating-point number. It is a template to -+ // prevent compile errors on systems where n is not IEEE754. -+ template explicit FMT_CONSTEXPR fp(Float n) { assign(n); } - -- // Constructs fp from an IEEE754 double. It is a template to prevent compile -- // errors on platforms where double is not IEEE754. -- template explicit fp(Double d) { assign(d); } -+ template -+ using is_supported = bool_constant; - - // Assigns d to this and return true iff predecessor is closer than successor. -- template -- bool assign(Double d) { -- // Assume double is in the format [sign][exponent][significand]. -- using limits = std::numeric_limits; -- const int exponent_size = -- bits::value - double_significand_size - 1; // -1 for sign -+ template ::value)> -+ FMT_CONSTEXPR bool assign(Float n) { -+ // Assume float is in the format [sign][exponent][significand]. -+ const int num_float_significand_bits = -+ detail::num_significand_bits(); -+ const uint64_t implicit_bit = 1ULL << num_float_significand_bits; - const uint64_t significand_mask = implicit_bit - 1; -- const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; -- const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; -- auto u = bit_cast(d); -+ constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); -+ auto u = bit_cast>(n); - f = u & significand_mask; -+ const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; - int biased_e = -- static_cast((u & exponent_mask) >> double_significand_size); -- // Predecessor is closer if d is a normalized power of 2 (f == 0) other than -- // the smallest normalized number (biased_e > 1). -+ static_cast((u & exponent_mask) >> num_float_significand_bits); -+ // The predecessor is closer if n is a normalized power of 2 (f == 0) other -+ // than the smallest normalized number (biased_e > 1). - bool is_predecessor_closer = f == 0 && biased_e > 1; - if (biased_e != 0) - f += implicit_bit; - else - biased_e = 1; // Subnormals use biased exponent 1 (min exponent). -- e = biased_e - exponent_bias - double_significand_size; -+ const int exponent_bias = std::numeric_limits::max_exponent - 1; -+ e = biased_e - exponent_bias - num_float_significand_bits; - return is_predecessor_closer; - } - -- template -- bool assign(Double) { -- *this = fp(); -+ template ::value)> -+ bool assign(Float) { -+ FMT_ASSERT(false, ""); - return false; - } -- -- // Assigns d to this together with computing lower and upper boundaries, -- // where a boundary is a value half way between the number and its predecessor -- // (lower) or successor (upper). The upper boundary is normalized and lower -- // has the same exponent but may be not normalized. -- template boundaries assign_with_boundaries(Double d) { -- bool is_lower_closer = assign(d); -- fp lower = -- is_lower_closer ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1); -- // 1 in normalize accounts for the exponent shift above. -- fp upper = normalize<1>(fp((f << 1) + 1, e - 1)); -- lower.f <<= lower.e - upper.e; -- return boundaries{lower.f, upper.f}; -- } -- -- template boundaries assign_float_with_boundaries(Double d) { -- assign(d); -- constexpr int min_normal_e = std::numeric_limits::min_exponent - -- std::numeric_limits::digits; -- significand_type half_ulp = 1 << (std::numeric_limits::digits - -- std::numeric_limits::digits - 1); -- if (min_normal_e > e) half_ulp <<= min_normal_e - e; -- fp upper = normalize<0>(fp(f + half_ulp, e)); -- fp lower = fp( -- f - (half_ulp >> ((f == implicit_bit && e > min_normal_e) ? 1 : 0)), e); -- lower.f <<= lower.e - upper.e; -- return boundaries{lower.f, upper.f}; -- } - }; - - // Normalizes the value converted from double and multiplied by (1 << SHIFT). --template fp normalize(fp value) { -+template FMT_CONSTEXPR fp normalize(fp value) { - // Handle subnormals. -- const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; -+ const uint64_t implicit_bit = 1ULL << num_significand_bits(); -+ const auto shifted_implicit_bit = implicit_bit << SHIFT; - while ((value.f & shifted_implicit_bit) == 0) { - value.f <<= 1; - --value.e; - } - // Subtract 1 to account for hidden bit. - const auto offset = -- fp::significand_size - fp::double_significand_size - SHIFT - 1; -+ fp::num_significand_bits - num_significand_bits() - SHIFT - 1; - value.f <<= offset; - value.e -= offset; - return value; -@@ -466,7 +304,7 @@ template fp normalize(fp value) { - inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } - - // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. --inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { -+FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { - #if FMT_USE_INT128 - auto product = static_cast<__uint128_t>(lhs) * rhs; - auto f = static_cast(product >> 64); -@@ -483,16 +321,20 @@ inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { - #endif - } - --inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; } -+FMT_CONSTEXPR inline fp operator*(fp x, fp y) { -+ return {multiply(x.f, y.f), x.e + y.e + 64}; -+} - - // Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its - // (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. --inline fp get_cached_power(int min_exponent, int& pow10_exponent) { -- const int64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10)) -+FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, -+ int& pow10_exponent) { -+ const int shift = 32; -+ const auto significand = static_cast(log10_2_significand); - int index = static_cast( -- ((min_exponent + fp::significand_size - 1) * one_over_log2_10 + -- ((int64_t(1) << 32) - 1)) // ceil -- >> 32 // arithmetic shift -+ ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + -+ ((int64_t(1) << shift) - 1)) // ceil -+ >> 32 // arithmetic shift - ); - // Decimal exponent of the first (smallest) cached power of 10. - const int first_dec_exp = -348; -@@ -500,7 +342,8 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) { - const int dec_exp_step = 8; - index = (index - first_dec_exp - 1) / dec_exp_step + 1; - pow10_exponent = first_dec_exp + index * dec_exp_step; -- return {data::pow10_significands[index], data::pow10_exponents[index]}; -+ return {impl_data::pow10_significands[index], -+ impl_data::pow10_exponents[index]}; - } - - // A simple accumulator to hold the sums of terms in bigint::square if uint128_t -@@ -509,15 +352,17 @@ struct accumulator { - uint64_t lower; - uint64_t upper; - -- accumulator() : lower(0), upper(0) {} -- explicit operator uint32_t() const { return static_cast(lower); } -+ constexpr accumulator() : lower(0), upper(0) {} -+ constexpr explicit operator uint32_t() const { -+ return static_cast(lower); -+ } - -- void operator+=(uint64_t n) { -+ FMT_CONSTEXPR void operator+=(uint64_t n) { - lower += n; - if (lower < n) ++upper; - } -- void operator>>=(int shift) { -- assert(shift == 32); -+ FMT_CONSTEXPR void operator>>=(int shift) { -+ FMT_ASSERT(shift == 32, ""); - (void)shift; - lower = (upper << 32) | (lower >> 32); - upper >>= 32; -@@ -534,39 +379,42 @@ class bigint { - basic_memory_buffer bigits_; - int exp_; - -- bigit operator[](int index) const { return bigits_[to_unsigned(index)]; } -- bigit& operator[](int index) { return bigits_[to_unsigned(index)]; } -+ FMT_CONSTEXPR20 bigit operator[](int index) const { -+ return bigits_[to_unsigned(index)]; -+ } -+ FMT_CONSTEXPR20 bigit& operator[](int index) { -+ return bigits_[to_unsigned(index)]; -+ } - - static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; - - friend struct formatter; - -- void subtract_bigits(int index, bigit other, bigit& borrow) { -+ FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast((*this)[index]) - other - borrow; - (*this)[index] = static_cast(result); - borrow = static_cast(result >> (bigit_bits * 2 - 1)); - } - -- void remove_leading_zeros() { -+ FMT_CONSTEXPR20 void remove_leading_zeros() { - int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; - bigits_.resize(to_unsigned(num_bigits + 1)); - } - - // Computes *this -= other assuming aligned bigints and *this >= other. -- void subtract_aligned(const bigint& other) { -+ FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { - FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); - FMT_ASSERT(compare(*this, other) >= 0, ""); - bigit borrow = 0; - int i = other.exp_ - exp_; -- for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) { -+ for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) - subtract_bigits(i, other.bigits_[j], borrow); -- } - while (borrow > 0) subtract_bigits(i, 0, borrow); - remove_leading_zeros(); - } - -- void multiply(uint32_t value) { -+ FMT_CONSTEXPR20 void multiply(uint32_t value) { - const double_bigit wide_value = value; - bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { -@@ -577,7 +425,7 @@ class bigint { - if (carry != 0) bigits_.push_back(carry); - } - -- void multiply(uint64_t value) { -+ FMT_CONSTEXPR20 void multiply(uint64_t value) { - const bigit mask = ~bigit(0); - const double_bigit lower = value & mask; - const double_bigit upper = value >> bigit_bits; -@@ -595,14 +443,16 @@ class bigint { - } - - public: -- bigint() : exp_(0) {} -+ FMT_CONSTEXPR20 bigint() : exp_(0) {} - explicit bigint(uint64_t n) { assign(n); } -- ~bigint() { assert(bigits_.capacity() <= bigits_capacity); } -+ FMT_CONSTEXPR20 ~bigint() { -+ FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); -+ } - - bigint(const bigint&) = delete; - void operator=(const bigint&) = delete; - -- void assign(const bigint& other) { -+ FMT_CONSTEXPR20 void assign(const bigint& other) { - auto size = other.bigits_.size(); - bigits_.resize(size); - auto data = other.bigits_.data(); -@@ -610,7 +460,7 @@ class bigint { - exp_ = other.exp_; - } - -- void assign(uint64_t n) { -+ FMT_CONSTEXPR20 void assign(uint64_t n) { - size_t num_bigits = 0; - do { - bigits_[num_bigits++] = n & ~bigit(0); -@@ -620,10 +470,12 @@ class bigint { - exp_ = 0; - } - -- int num_bigits() const { return static_cast(bigits_.size()) + exp_; } -+ FMT_CONSTEXPR20 int num_bigits() const { -+ return static_cast(bigits_.size()) + exp_; -+ } - -- FMT_NOINLINE bigint& operator<<=(int shift) { -- assert(shift >= 0); -+ FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { -+ FMT_ASSERT(shift >= 0, ""); - exp_ += shift / bigit_bits; - shift %= bigit_bits; - if (shift == 0) return *this; -@@ -637,13 +489,13 @@ class bigint { - return *this; - } - -- template bigint& operator*=(Int value) { -+ template FMT_CONSTEXPR20 bigint& operator*=(Int value) { - FMT_ASSERT(value > 0, ""); - multiply(uint32_or_64_or_128_t(value)); - return *this; - } - -- friend int compare(const bigint& lhs, const bigint& rhs) { -+ friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { - int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); - if (num_lhs_bigits != num_rhs_bigits) - return num_lhs_bigits > num_rhs_bigits ? 1 : -1; -@@ -660,8 +512,8 @@ class bigint { - } - - // Returns compare(lhs1 + lhs2, rhs). -- friend int add_compare(const bigint& lhs1, const bigint& lhs2, -- const bigint& rhs) { -+ friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, -+ const bigint& rhs) { - int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); - int num_rhs_bigits = rhs.num_bigits(); - if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; -@@ -684,8 +536,8 @@ class bigint { - } - - // Assigns pow(10, exp) to this bigint. -- void assign_pow10(int exp) { -- assert(exp >= 0); -+ FMT_CONSTEXPR20 void assign_pow10(int exp) { -+ FMT_ASSERT(exp >= 0, ""); - if (exp == 0) return assign(1); - // Find the top bit. - int bitmask = 1; -@@ -703,10 +555,10 @@ class bigint { - *this <<= exp; // Multiply by pow(2, exp) by shifting. - } - -- void square() { -- basic_memory_buffer n(std::move(bigits_)); -+ FMT_CONSTEXPR20 void square() { - int num_bigits = static_cast(bigits_.size()); - int num_result_bigits = 2 * num_bigits; -+ basic_memory_buffer n(std::move(bigits_)); - bigits_.resize(to_unsigned(num_result_bigits)); - using accumulator_t = conditional_t; - auto sum = accumulator_t(); -@@ -728,27 +580,30 @@ class bigint { - (*this)[bigit_index] = static_cast(sum); - sum >>= bits::value; - } -- --num_result_bigits; - remove_leading_zeros(); - exp_ *= 2; - } - -+ // If this bigint has a bigger exponent than other, adds trailing zero to make -+ // exponents equal. This simplifies some operations such as subtraction. -+ FMT_CONSTEXPR20 void align(const bigint& other) { -+ int exp_difference = exp_ - other.exp_; -+ if (exp_difference <= 0) return; -+ int num_bigits = static_cast(bigits_.size()); -+ bigits_.resize(to_unsigned(num_bigits + exp_difference)); -+ for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) -+ bigits_[j] = bigits_[i]; -+ std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); -+ exp_ -= exp_difference; -+ } -+ - // Divides this bignum by divisor, assigning the remainder to this and - // returning the quotient. -- int divmod_assign(const bigint& divisor) { -+ FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { - FMT_ASSERT(this != &divisor, ""); - if (compare(*this, divisor) < 0) return 0; -- int num_bigits = static_cast(bigits_.size()); - FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); -- int exp_difference = exp_ - divisor.exp_; -- if (exp_difference > 0) { -- // Align bigints by adding trailing zeros to simplify subtraction. -- bigits_.resize(to_unsigned(num_bigits + exp_difference)); -- for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) -- bigits_[j] = bigits_[i]; -- std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); -- exp_ -= exp_difference; -- } -+ align(divisor); - int quotient = 0; - do { - subtract_aligned(divisor); -@@ -764,8 +619,9 @@ enum class round_direction { unknown, up, down }; - // some number v and the error, returns whether v should be rounded up, down, or - // whether the rounding direction can't be determined due to error. - // error should be less than divisor / 2. --inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, -- uint64_t error) { -+FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, -+ uint64_t remainder, -+ uint64_t error) { - FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. - FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. - FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. -@@ -788,26 +644,52 @@ enum result { - }; - } - --// A version of count_digits optimized for grisu_gen_digits. --inline int grisu_count_digits(uint32_t n) { -- if (n < 10) return 1; -- if (n < 100) return 2; -- if (n < 1000) return 3; -- if (n < 10000) return 4; -- if (n < 100000) return 5; -- if (n < 1000000) return 6; -- if (n < 10000000) return 7; -- if (n < 100000000) return 8; -- if (n < 1000000000) return 9; -- return 10; --} -+struct gen_digits_handler { -+ char* buf; -+ int size; -+ int precision; -+ int exp10; -+ bool fixed; -+ -+ FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, -+ uint64_t remainder, uint64_t error, -+ bool integral) { -+ FMT_ASSERT(remainder < divisor, ""); -+ buf[size++] = digit; -+ if (!integral && error >= remainder) return digits::error; -+ if (size < precision) return digits::more; -+ if (!integral) { -+ // Check if error * 2 < divisor with overflow prevention. -+ // The check is not needed for the integral part because error = 1 -+ // and divisor > (1 << 32) there. -+ if (error >= divisor || error >= divisor - error) return digits::error; -+ } else { -+ FMT_ASSERT(error == 1 && divisor > 2, ""); -+ } -+ auto dir = get_round_direction(divisor, remainder, error); -+ if (dir != round_direction::up) -+ return dir == round_direction::down ? digits::done : digits::error; -+ ++buf[size - 1]; -+ for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { -+ buf[i] = '0'; -+ ++buf[i - 1]; -+ } -+ if (buf[0] > '9') { -+ buf[0] = '1'; -+ if (fixed) -+ buf[size++] = '0'; -+ else -+ ++exp10; -+ } -+ return digits::done; -+ } -+}; - - // Generates output using the Grisu digit-gen algorithm. - // error: the size of the region (lower, upper) outside of which numbers - // definitely do not round to value (Delta in Grisu3). --template --FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, -- int& exp, Handler& handler) { -+FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits( -+ fp value, uint64_t error, int& exp, gen_digits_handler& handler) { - const fp one(1ULL << -value.e, value.e); - // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be - // zero because it contains a product of two 64-bit numbers with MSB set (due -@@ -817,11 +699,29 @@ FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, - FMT_ASSERT(integral == value.f >> -one.e, ""); - // The fractional part of scaled value (p2 in Grisu) c = value % one. - uint64_t fractional = value.f & (one.f - 1); -- exp = grisu_count_digits(integral); // kappa in Grisu. -- // Divide by 10 to prevent overflow. -- auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e, -- value.f / 10, error * 10, exp); -- if (result != digits::more) return result; -+ exp = count_digits(integral); // kappa in Grisu. -+ // Non-fixed formats require at least one digit and no precision adjustment. -+ if (handler.fixed) { -+ // Adjust fixed precision by exponent because it is relative to decimal -+ // point. -+ int precision_offset = exp + handler.exp10; -+ if (precision_offset > 0 && -+ handler.precision > max_value() - precision_offset) { -+ FMT_THROW(format_error("number is too big")); -+ } -+ handler.precision += precision_offset; -+ // Check if precision is satisfied just by leading zeros, e.g. -+ // format("{:.2f}", 0.001) gives "0.00" without generating any digits. -+ if (handler.precision <= 0) { -+ if (handler.precision < 0) return digits::done; -+ // Divide by 10 to prevent overflow. -+ uint64_t divisor = impl_data::power_of_10_64[exp - 1] << -one.e; -+ auto dir = get_round_direction(divisor, value.f / 10, error * 10); -+ if (dir == round_direction::unknown) return digits::error; -+ handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; -+ return digits::done; -+ } -+ } - // Generate digits for the integral part. This can produce up to 10 digits. - do { - uint32_t digit = 0; -@@ -867,139 +767,1474 @@ FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, - FMT_ASSERT(false, "invalid number of digits"); - } - --exp; -- uint64_t remainder = -- (static_cast(integral) << -one.e) + fractional; -- result = handler.on_digit(static_cast('0' + digit), -- data::powers_of_10_64[exp] << -one.e, remainder, -- error, exp, true); -+ auto remainder = (static_cast(integral) << -one.e) + fractional; -+ auto result = handler.on_digit(static_cast('0' + digit), -+ impl_data::power_of_10_64[exp] << -one.e, -+ remainder, error, true); - if (result != digits::more) return result; - } while (exp > 0); - // Generate digits for the fractional part. - for (;;) { - fractional *= 10; - error *= 10; -- char digit = -- static_cast('0' + static_cast(fractional >> -one.e)); -+ char digit = static_cast('0' + (fractional >> -one.e)); - fractional &= one.f - 1; - --exp; -- result = handler.on_digit(digit, one.f, fractional, error, exp, false); -+ auto result = handler.on_digit(digit, one.f, fractional, error, false); - if (result != digits::more) return result; - } - } - --// The fixed precision digit handler. --struct fixed_handler { -- char* buf; -- int size; -- int precision; -- int exp10; -- bool fixed; -+// A 128-bit integer type used internally, -+struct uint128_wrapper { -+ uint128_wrapper() = default; - -- digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error, -- int& exp) { -- // Non-fixed formats require at least one digit and no precision adjustment. -- if (!fixed) return digits::more; -- // Adjust fixed precision by exponent because it is relative to decimal -- // point. -- precision += exp + exp10; -- // Check if precision is satisfied just by leading zeros, e.g. -- // format("{:.2f}", 0.001) gives "0.00" without generating any digits. -- if (precision > 0) return digits::more; -- if (precision < 0) return digits::done; -- auto dir = get_round_direction(divisor, remainder, error); -- if (dir == round_direction::unknown) return digits::error; -- buf[size++] = dir == round_direction::up ? '1' : '0'; -- return digits::done; -+#if FMT_USE_INT128 -+ uint128_t internal_; -+ -+ constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT -+ : internal_{static_cast(low) | -+ (static_cast(high) << 64)} {} -+ -+ constexpr uint128_wrapper(uint128_t u) : internal_{u} {} -+ -+ constexpr uint64_t high() const FMT_NOEXCEPT { -+ return uint64_t(internal_ >> 64); - } -+ constexpr uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); } - -- digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, -- uint64_t error, int, bool integral) { -- FMT_ASSERT(remainder < divisor, ""); -- buf[size++] = digit; -- if (size < precision) return digits::more; -- if (!integral) { -- // Check if error * 2 < divisor with overflow prevention. -- // The check is not needed for the integral part because error = 1 -- // and divisor > (1 << 32) there. -- if (error >= divisor || error >= divisor - error) return digits::error; -- } else { -- FMT_ASSERT(error == 1 && divisor > 2, ""); -- } -- auto dir = get_round_direction(divisor, remainder, error); -- if (dir != round_direction::up) -- return dir == round_direction::down ? digits::done : digits::error; -- ++buf[size - 1]; -- for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { -- buf[i] = '0'; -- ++buf[i - 1]; -- } -- if (buf[0] > '9') { -- buf[0] = '1'; -- buf[size++] = '0'; -- } -- return digits::done; -+ uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { -+ internal_ += n; -+ return *this; -+ } -+#else -+ uint64_t high_; -+ uint64_t low_; -+ -+ constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT -+ : high_{high}, -+ low_{low} {} -+ -+ constexpr uint64_t high() const FMT_NOEXCEPT { return high_; } -+ constexpr uint64_t low() const FMT_NOEXCEPT { return low_; } -+ -+ uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { -+# if defined(_MSC_VER) && defined(_M_X64) -+ unsigned char carry = _addcarry_u64(0, low_, n, &low_); -+ _addcarry_u64(carry, high_, 0, &high_); -+ return *this; -+# else -+ uint64_t sum = low_ + n; -+ high_ += (sum < low_ ? 1 : 0); -+ low_ = sum; -+ return *this; -+# endif - } -+#endif - }; - --// The shortest representation digit handler. --struct grisu_shortest_handler { -- char* buf; -- int size; -- // Distance between scaled value and upper bound (wp_W in Grisu3). -- uint64_t diff; -+// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. -+namespace dragonbox { -+// Computes 128-bit result of multiplication of two 64-bit unsigned integers. -+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) -+ uint128_wrapper result; -+ result.low_ = _umul128(x, y, &result.high_); -+ return result; -+#else -+ const uint64_t mask = (uint64_t(1) << 32) - uint64_t(1); -+ -+ uint64_t a = x >> 32; -+ uint64_t b = x & mask; -+ uint64_t c = y >> 32; -+ uint64_t d = y & mask; -+ -+ uint64_t ac = a * c; -+ uint64_t bc = b * c; -+ uint64_t ad = a * d; -+ uint64_t bd = b * d; - -- digits::result on_start(uint64_t, uint64_t, uint64_t, int&) { -- return digits::more; -+ uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); -+ -+ return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), -+ (intermediate << 32) + (bd & mask)}; -+#endif -+} -+ -+// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. -+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); -+#elif defined(_MSC_VER) && defined(_M_X64) -+ return __umulh(x, y); -+#else -+ return umul128(x, y).high(); -+#endif -+} -+ -+// Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a -+// 128-bit unsigned integer. -+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(); -+} -+ -+// Computes upper 32 bits of multiplication of a 32-bit unsigned integer and a -+// 64-bit unsigned integer. -+inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT { -+ return static_cast(umul128_upper64(x, y)); -+} -+ -+// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a -+// 128-bit unsigned integer. -+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; -+} -+ -+// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a -+// 64-bit unsigned integer. -+inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT { -+ return x * y; -+} -+ -+// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from -+// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4. -+inline int floor_log10_pow2(int e) FMT_NOEXCEPT { -+ FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); -+ const int shift = 22; -+ return (e * static_cast(log10_2_significand >> (64 - shift))) >> shift; -+} -+ -+// Various fast log computations. -+inline int floor_log2_pow10(int e) FMT_NOEXCEPT { -+ FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); -+ const uint64_t log2_10_integer_part = 3; -+ const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9; -+ const int shift_amount = 19; -+ return (e * static_cast( -+ (log2_10_integer_part << shift_amount) | -+ (log2_10_fractional_digits >> (64 - shift_amount)))) >> -+ shift_amount; -+} -+inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT { -+ FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); -+ const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375; -+ const int shift_amount = 22; -+ return (e * static_cast(log10_2_significand >> (64 - shift_amount)) - -+ static_cast(log10_4_over_3_fractional_digits >> -+ (64 - shift_amount))) >> -+ shift_amount; -+} -+ -+// Returns true iff x is divisible by pow(2, exp). -+inline bool divisible_by_power_of_2(uint32_t x, int exp) FMT_NOEXCEPT { -+ FMT_ASSERT(exp >= 1, ""); -+ FMT_ASSERT(x != 0, ""); -+#ifdef FMT_BUILTIN_CTZ -+ return FMT_BUILTIN_CTZ(x) >= exp; -+#else -+ return exp < num_bits() && x == ((x >> exp) << exp); -+#endif -+} -+inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT { -+ FMT_ASSERT(exp >= 1, ""); -+ FMT_ASSERT(x != 0, ""); -+#ifdef FMT_BUILTIN_CTZLL -+ return FMT_BUILTIN_CTZLL(x) >= exp; -+#else -+ return exp < num_bits() && x == ((x >> exp) << exp); -+#endif -+} -+ -+// Table entry type for divisibility test. -+template struct divtest_table_entry { -+ T mod_inv; -+ T max_quotient; -+}; -+ -+// Returns true iff x is divisible by pow(5, exp). -+inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT { -+ FMT_ASSERT(exp <= 10, "too large exponent"); -+ static constexpr const divtest_table_entry divtest_table[] = { -+ {0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333}, -+ {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba}, -+ {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5}, -+ {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf}, -+ {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897}, -+ {0x3ed61f49, 0x000001b7}}; -+ return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; -+} -+inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT { -+ FMT_ASSERT(exp <= 23, "too large exponent"); -+ static constexpr const divtest_table_entry divtest_table[] = { -+ {0x0000000000000001, 0xffffffffffffffff}, -+ {0xcccccccccccccccd, 0x3333333333333333}, -+ {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70}, -+ {0x1cac083126e978d5, 0x020c49ba5e353f7c}, -+ {0xd288ce703afb7e91, 0x0068db8bac710cb2}, -+ {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0}, -+ {0x790fb65668c26139, 0x000431bde82d7b63}, -+ {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a}, -+ {0xc767074b22e90e21, 0x00002af31dc46118}, -+ {0x8e47ce423a2e9c6d, 0x0000089705f4136b}, -+ {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b}, -+ {0x0fee64690c913975, 0x00000057f5ff85e5}, -+ {0x3662e0e1cf503eb1, 0x000000119799812d}, -+ {0xa47a2cf9f6433fbd, 0x0000000384b84d09}, -+ {0x54186f653140a659, 0x00000000b424dc35}, -+ {0x7738164770402145, 0x0000000024075f3d}, -+ {0xe4a4d1417cd9a041, 0x000000000734aca5}, -+ {0xc75429d9e5c5200d, 0x000000000170ef54}, -+ {0xc1773b91fac10669, 0x000000000049c977}, -+ {0x26b172506559ce15, 0x00000000000ec1e4}, -+ {0xd489e3a9addec2d1, 0x000000000002f394}, -+ {0x90e860bb892c8d5d, 0x000000000000971d}, -+ {0x502e79bf1b6f4f79, 0x0000000000001e39}, -+ {0xdcd618596be30fe5, 0x000000000000060b}}; -+ return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; -+} -+ -+// Replaces n by floor(n / pow(5, N)) returning true if and only if n is -+// divisible by pow(5, N). -+// Precondition: n <= 2 * pow(5, N + 1). -+template -+bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT { -+ static constexpr struct { -+ uint32_t magic_number; -+ int bits_for_comparison; -+ uint32_t threshold; -+ int shift_amount; -+ } infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}}; -+ constexpr auto info = infos[N - 1]; -+ n *= info.magic_number; -+ const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1; -+ bool result = (n & comparison_mask) <= info.threshold; -+ n >>= info.shift_amount; -+ return result; -+} -+ -+// Computes floor(n / pow(10, N)) for small n and N. -+// Precondition: n <= pow(10, N + 1). -+template uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT { -+ static constexpr struct { -+ uint32_t magic_number; -+ int shift_amount; -+ uint32_t divisor_times_10; -+ } infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}}; -+ constexpr auto info = infos[N - 1]; -+ FMT_ASSERT(n <= info.divisor_times_10, "n is too large"); -+ return n * info.magic_number >> info.shift_amount; -+} -+ -+// Computes floor(n / 10^(kappa + 1)) (float) -+inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) FMT_NOEXCEPT { -+ return n / float_info::big_divisor; -+} -+// Computes floor(n / 10^(kappa + 1)) (double) -+inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT { -+ return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9; -+} -+ -+// Various subroutines using pow10 cache -+template struct cache_accessor; -+ -+template <> struct cache_accessor { -+ using carrier_uint = float_info::carrier_uint; -+ using cache_entry_type = uint64_t; -+ -+ static uint64_t get_cached_power(int k) FMT_NOEXCEPT { -+ FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, -+ "k is out of range"); -+ static constexpr const uint64_t pow10_significands[] = { -+ 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, -+ 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, -+ 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, -+ 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, -+ 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, -+ 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, -+ 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, -+ 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, -+ 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, -+ 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, -+ 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, -+ 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, -+ 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, -+ 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, -+ 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, -+ 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, -+ 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, -+ 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, -+ 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, -+ 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, -+ 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296, -+ 0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6, -+ 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, -+ 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, -+ 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719, -+ 0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e}; -+ return pow10_significands[k - float_info::min_k]; - } - -- // Decrement the generated number approaching value from above. -- void round(uint64_t d, uint64_t divisor, uint64_t& remainder, -- uint64_t error) { -- while ( -- remainder < d && error - remainder >= divisor && -- (remainder + divisor < d || d - remainder >= remainder + divisor - d)) { -- --buf[size - 1]; -- remainder += divisor; -- } -+ static carrier_uint compute_mul(carrier_uint u, -+ const cache_entry_type& cache) FMT_NOEXCEPT { -+ return umul96_upper32(u, cache); - } - -- // Implements Grisu's round_weed. -- digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, -- uint64_t error, int exp, bool integral) { -- buf[size++] = digit; -- if (remainder >= error) return digits::more; -- uint64_t unit = integral ? 1 : data::powers_of_10_64[-exp]; -- uint64_t up = (diff - 1) * unit; // wp_Wup -- round(up, divisor, remainder, error); -- uint64_t down = (diff + 1) * unit; // wp_Wdown -- if (remainder < down && error - remainder >= divisor && -- (remainder + divisor < down || -- down - remainder > remainder + divisor - down)) { -- return digits::error; -- } -- return 2 * unit <= remainder && remainder <= error - 4 * unit -- ? digits::done -- : digits::error; -+ static uint32_t compute_delta(const cache_entry_type& cache, -+ int beta_minus_1) FMT_NOEXCEPT { -+ return static_cast(cache >> (64 - 1 - beta_minus_1)); -+ } -+ -+ static bool compute_mul_parity(carrier_uint two_f, -+ const cache_entry_type& cache, -+ int beta_minus_1) FMT_NOEXCEPT { -+ FMT_ASSERT(beta_minus_1 >= 1, ""); -+ FMT_ASSERT(beta_minus_1 < 64, ""); -+ -+ return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; -+ } -+ -+ static carrier_uint compute_left_endpoint_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { -+ return static_cast( -+ (cache - (cache >> (float_info::significand_bits + 2))) >> -+ (64 - float_info::significand_bits - 1 - beta_minus_1)); -+ } -+ -+ static carrier_uint compute_right_endpoint_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { -+ return static_cast( -+ (cache + (cache >> (float_info::significand_bits + 1))) >> -+ (64 - float_info::significand_bits - 1 - beta_minus_1)); -+ } -+ -+ static carrier_uint compute_round_up_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { -+ return (static_cast( -+ cache >> -+ (64 - float_info::significand_bits - 2 - beta_minus_1)) + -+ 1) / -+ 2; - } - }; - --// Formats value using a variation of the Fixed-Precision Positive --// Floating-Point Printout ((FPP)^2) algorithm by Steele & White: --// https://fmt.dev/p372-steele.pdf. --template --void fallback_format(Double d, buffer& buf, int& exp10) { -+template <> struct cache_accessor { -+ using carrier_uint = float_info::carrier_uint; -+ using cache_entry_type = uint128_wrapper; -+ -+ static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT { -+ FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, -+ "k is out of range"); -+ -+ static constexpr const uint128_wrapper pow10_significands[] = { -+#if FMT_USE_FULL_CACHE_DRAGONBOX -+ {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, -+ {0x9faacf3df73609b1, 0x77b191618c54e9ad}, -+ {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, -+ {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, -+ {0x9becce62836ac577, 0x4ee367f9430aec33}, -+ {0xc2e801fb244576d5, 0x229c41f793cda740}, -+ {0xf3a20279ed56d48a, 0x6b43527578c11110}, -+ {0x9845418c345644d6, 0x830a13896b78aaaa}, -+ {0xbe5691ef416bd60c, 0x23cc986bc656d554}, -+ {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, -+ {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, -+ {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, -+ {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, -+ {0x91376c36d99995be, 0x23100809b9c21fa2}, -+ {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, -+ {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, -+ {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, -+ {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, -+ {0xdd95317f31c7fa1d, 0x40405643d711d584}, -+ {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, -+ {0xad1c8eab5ee43b66, 0xda3243650005eed0}, -+ {0xd863b256369d4a40, 0x90bed43e40076a83}, -+ {0x873e4f75e2224e68, 0x5a7744a6e804a292}, -+ {0xa90de3535aaae202, 0x711515d0a205cb37}, -+ {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, -+ {0x8412d9991ed58091, 0xe858790afe9486c3}, -+ {0xa5178fff668ae0b6, 0x626e974dbe39a873}, -+ {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, -+ {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, -+ {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, -+ {0xc987434744ac874e, 0xa327ffb266b56221}, -+ {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, -+ {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, -+ {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, -+ {0xf6019da07f549b2b, 0x7e2a53a146606a49}, -+ {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, -+ {0xc0314325637a1939, 0xfa911155fefb5309}, -+ {0xf03d93eebc589f88, 0x793555ab7eba27cb}, -+ {0x96267c7535b763b5, 0x4bc1558b2f3458df}, -+ {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, -+ {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, -+ {0x92a1958a7675175f, 0x0bfacd89ec191eca}, -+ {0xb749faed14125d36, 0xcef980ec671f667c}, -+ {0xe51c79a85916f484, 0x82b7e12780e7401b}, -+ {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, -+ {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, -+ {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, -+ {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, -+ {0xaecc49914078536d, 0x58fae9f773886e19}, -+ {0xda7f5bf590966848, 0xaf39a475506a899f}, -+ {0x888f99797a5e012d, 0x6d8406c952429604}, -+ {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, -+ {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, -+ {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, -+ {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, -+ {0xd0601d8efc57b08b, 0xf13b94daf124da27}, -+ {0x823c12795db6ce57, 0x76c53d08d6b70859}, -+ {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, -+ {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, -+ {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, -+ {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, -+ {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, -+ {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, -+ {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, -+ {0xc21094364dfb5636, 0x985915fc12f542e5}, -+ {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, -+ {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, -+ {0xbd8430bd08277231, 0x50c6ff782a838354}, -+ {0xece53cec4a314ebd, 0xa4f8bf5635246429}, -+ {0x940f4613ae5ed136, 0x871b7795e136be9a}, -+ {0xb913179899f68584, 0x28e2557b59846e40}, -+ {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, -+ {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, -+ {0xb4bca50b065abe63, 0x0fed077a756b53aa}, -+ {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, -+ {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, -+ {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, -+ {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, -+ {0x89e42caaf9491b60, 0xf41686c49db57245}, -+ {0xac5d37d5b79b6239, 0x311c2875c522ced6}, -+ {0xd77485cb25823ac7, 0x7d633293366b828c}, -+ {0x86a8d39ef77164bc, 0xae5dff9c02033198}, -+ {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, -+ {0xd267caa862a12d66, 0xd072df63c324fd7c}, -+ {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, -+ {0xa46116538d0deb78, 0x52d9be85f074e609}, -+ {0xcd795be870516656, 0x67902e276c921f8c}, -+ {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, -+ {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, -+ {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, -+ {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, -+ {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, -+ {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, -+ {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, -+ {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, -+ {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, -+ {0xef340a98172aace4, 0x86fb897116c87c35}, -+ {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, -+ {0xbae0a846d2195712, 0x8974836059cca10a}, -+ {0xe998d258869facd7, 0x2bd1a438703fc94c}, -+ {0x91ff83775423cc06, 0x7b6306a34627ddd0}, -+ {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, -+ {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, -+ {0x8e938662882af53e, 0x547eb47b7282ee9d}, -+ {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, -+ {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, -+ {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, -+ {0xae0b158b4738705e, 0x9624ab50b148d446}, -+ {0xd98ddaee19068c76, 0x3badd624dd9b0958}, -+ {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, -+ {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, -+ {0xd47487cc8470652b, 0x7647c32000696720}, -+ {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, -+ {0xa5fb0a17c777cf09, 0xf468107100525891}, -+ {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, -+ {0x81ac1fe293d599bf, 0xc6f14cd848405531}, -+ {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, -+ {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, -+ {0xfd442e4688bd304a, 0x908f4a166d1da664}, -+ {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, -+ {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, -+ {0xf7549530e188c128, 0xd12bee59e68ef47d}, -+ {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, -+ {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, -+ {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, -+ {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, -+ {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, -+ {0xebdf661791d60f56, 0x111b495b3464ad22}, -+ {0x936b9fcebb25c995, 0xcab10dd900beec35}, -+ {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, -+ {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, -+ {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, -+ {0xb3f4e093db73a093, 0x59ed216765690f57}, -+ {0xe0f218b8d25088b8, 0x306869c13ec3532d}, -+ {0x8c974f7383725573, 0x1e414218c73a13fc}, -+ {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, -+ {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, -+ {0x894bc396ce5da772, 0x6b8bba8c328eb784}, -+ {0xab9eb47c81f5114f, 0x066ea92f3f326565}, -+ {0xd686619ba27255a2, 0xc80a537b0efefebe}, -+ {0x8613fd0145877585, 0xbd06742ce95f5f37}, -+ {0xa798fc4196e952e7, 0x2c48113823b73705}, -+ {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, -+ {0x82ef85133de648c4, 0x9a984d73dbe722fc}, -+ {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, -+ {0xcc963fee10b7d1b3, 0x318df905079926a9}, -+ {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, -+ {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, -+ {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, -+ {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, -+ {0x9c1661a651213e2d, 0x06bea10ca65c084f}, -+ {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, -+ {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, -+ {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, -+ {0xbe89523386091465, 0xf6bbb397f1135824}, -+ {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, -+ {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, -+ {0xba121a4650e4ddeb, 0x92f34d62616ce414}, -+ {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, -+ {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, -+ {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, -+ {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, -+ {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, -+ {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, -+ {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, -+ {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, -+ {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, -+ {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, -+ {0x87625f056c7c4a8b, 0x11471cd764ad4973}, -+ {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, -+ {0xd389b47879823479, 0x4aff1d108d4ec2c4}, -+ {0x843610cb4bf160cb, 0xcedf722a585139bb}, -+ {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, -+ {0xce947a3da6a9273e, 0x733d226229feea33}, -+ {0x811ccc668829b887, 0x0806357d5a3f5260}, -+ {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, -+ {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, -+ {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, -+ {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, -+ {0xc5029163f384a931, 0x0a9e795e65d4df12}, -+ {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, -+ {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, -+ {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, -+ {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, -+ {0x964e858c91ba2655, 0x3a6a07f8d510f870}, -+ {0xbbe226efb628afea, 0x890489f70a55368c}, -+ {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, -+ {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, -+ {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, -+ {0xe55990879ddcaabd, 0xcc420a6a101d0516}, -+ {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, -+ {0xb32df8e9f3546564, 0x47939822dc96abfa}, -+ {0xdff9772470297ebd, 0x59787e2b93bc56f8}, -+ {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, -+ {0xaefae51477a06b03, 0xede622920b6b23f2}, -+ {0xdab99e59958885c4, 0xe95fab368e45ecee}, -+ {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, -+ {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, -+ {0xd59944a37c0752a2, 0x4be76d3346f04960}, -+ {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, -+ {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, -+ {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, -+ {0x825ecc24c873782f, 0x8ed400668c0c28c9}, -+ {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, -+ {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, -+ {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, -+ {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, -+ {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, -+ {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, -+ {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, -+ {0xc24452da229b021b, 0xfbe85badce996169}, -+ {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, -+ {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, -+ {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, -+ {0xed246723473e3813, 0x290123e9aab23b69}, -+ {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, -+ {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, -+ {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, -+ {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, -+ {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, -+ {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, -+ {0x8d590723948a535f, 0x579c487e5a38ad0f}, -+ {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, -+ {0xdcdb1b2798182244, 0xf8e431456cf88e66}, -+ {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, -+ {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, -+ {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, -+ {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, -+ {0xa87fea27a539e9a5, 0x3f2398d747b36225}, -+ {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, -+ {0x83a3eeeef9153e89, 0x1953cf68300424ad}, -+ {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, -+ {0xcdb02555653131b6, 0x3792f412cb06794e}, -+ {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, -+ {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, -+ {0xc8de047564d20a8b, 0xf245825a5a445276}, -+ {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, -+ {0x9ced737bb6c4183d, 0x55464dd69685606c}, -+ {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, -+ {0xf53304714d9265df, 0xd53dd99f4b3066a9}, -+ {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, -+ {0xbf8fdb78849a5f96, 0xde98520472bdd034}, -+ {0xef73d256a5c0f77c, 0x963e66858f6d4441}, -+ {0x95a8637627989aad, 0xdde7001379a44aa9}, -+ {0xbb127c53b17ec159, 0x5560c018580d5d53}, -+ {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, -+ {0x9226712162ab070d, 0xcab3961304ca70e9}, -+ {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, -+ {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, -+ {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, -+ {0xb267ed1940f1c61c, 0x55f038b237591ed4}, -+ {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, -+ {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, -+ {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, -+ {0xd9c7dced53c72255, 0x96e7bd358c904a22}, -+ {0x881cea14545c7575, 0x7e50d64177da2e55}, -+ {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, -+ {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, -+ {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, -+ {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, -+ {0xcfb11ead453994ba, 0x67de18eda5814af3}, -+ {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, -+ {0xa2425ff75e14fc31, 0xa1258379a94d028e}, -+ {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, -+ {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, -+ {0x9e74d1b791e07e48, 0x775ea264cf55347e}, -+ {0xc612062576589dda, 0x95364afe032a819e}, -+ {0xf79687aed3eec551, 0x3a83ddbd83f52205}, -+ {0x9abe14cd44753b52, 0xc4926a9672793543}, -+ {0xc16d9a0095928a27, 0x75b7053c0f178294}, -+ {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, -+ {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, -+ {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, -+ {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, -+ {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, -+ {0xb877aa3236a4b449, 0x09befeb9fad487c3}, -+ {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, -+ {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, -+ {0xb424dc35095cd80f, 0x538484c19ef38c95}, -+ {0xe12e13424bb40e13, 0x2865a5f206b06fba}, -+ {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, -+ {0xafebff0bcb24aafe, 0xf78f69a51539d749}, -+ {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, -+ {0x89705f4136b4a597, 0x31680a88f8953031}, -+ {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, -+ {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, -+ {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, -+ {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, -+ {0xd1b71758e219652b, 0xd3c36113404ea4a9}, -+ {0x83126e978d4fdf3b, 0x645a1cac083126ea}, -+ {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, -+ {0xcccccccccccccccc, 0xcccccccccccccccd}, -+ {0x8000000000000000, 0x0000000000000000}, -+ {0xa000000000000000, 0x0000000000000000}, -+ {0xc800000000000000, 0x0000000000000000}, -+ {0xfa00000000000000, 0x0000000000000000}, -+ {0x9c40000000000000, 0x0000000000000000}, -+ {0xc350000000000000, 0x0000000000000000}, -+ {0xf424000000000000, 0x0000000000000000}, -+ {0x9896800000000000, 0x0000000000000000}, -+ {0xbebc200000000000, 0x0000000000000000}, -+ {0xee6b280000000000, 0x0000000000000000}, -+ {0x9502f90000000000, 0x0000000000000000}, -+ {0xba43b74000000000, 0x0000000000000000}, -+ {0xe8d4a51000000000, 0x0000000000000000}, -+ {0x9184e72a00000000, 0x0000000000000000}, -+ {0xb5e620f480000000, 0x0000000000000000}, -+ {0xe35fa931a0000000, 0x0000000000000000}, -+ {0x8e1bc9bf04000000, 0x0000000000000000}, -+ {0xb1a2bc2ec5000000, 0x0000000000000000}, -+ {0xde0b6b3a76400000, 0x0000000000000000}, -+ {0x8ac7230489e80000, 0x0000000000000000}, -+ {0xad78ebc5ac620000, 0x0000000000000000}, -+ {0xd8d726b7177a8000, 0x0000000000000000}, -+ {0x878678326eac9000, 0x0000000000000000}, -+ {0xa968163f0a57b400, 0x0000000000000000}, -+ {0xd3c21bcecceda100, 0x0000000000000000}, -+ {0x84595161401484a0, 0x0000000000000000}, -+ {0xa56fa5b99019a5c8, 0x0000000000000000}, -+ {0xcecb8f27f4200f3a, 0x0000000000000000}, -+ {0x813f3978f8940984, 0x4000000000000000}, -+ {0xa18f07d736b90be5, 0x5000000000000000}, -+ {0xc9f2c9cd04674ede, 0xa400000000000000}, -+ {0xfc6f7c4045812296, 0x4d00000000000000}, -+ {0x9dc5ada82b70b59d, 0xf020000000000000}, -+ {0xc5371912364ce305, 0x6c28000000000000}, -+ {0xf684df56c3e01bc6, 0xc732000000000000}, -+ {0x9a130b963a6c115c, 0x3c7f400000000000}, -+ {0xc097ce7bc90715b3, 0x4b9f100000000000}, -+ {0xf0bdc21abb48db20, 0x1e86d40000000000}, -+ {0x96769950b50d88f4, 0x1314448000000000}, -+ {0xbc143fa4e250eb31, 0x17d955a000000000}, -+ {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, -+ {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, -+ {0xb7abc627050305ad, 0xf14a3d9e40000000}, -+ {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, -+ {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, -+ {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, -+ {0xe0352f62a19e306e, 0xd50b2037ad200000}, -+ {0x8c213d9da502de45, 0x4526f422cc340000}, -+ {0xaf298d050e4395d6, 0x9670b12b7f410000}, -+ {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, -+ {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, -+ {0xab0e93b6efee0053, 0x8eea0d047a457a00}, -+ {0xd5d238a4abe98068, 0x72a4904598d6d880}, -+ {0x85a36366eb71f041, 0x47a6da2b7f864750}, -+ {0xa70c3c40a64e6c51, 0x999090b65f67d924}, -+ {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, -+ {0x82818f1281ed449f, 0xbff8f10e7a8921a4}, -+ {0xa321f2d7226895c7, 0xaff72d52192b6a0d}, -+ {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490}, -+ {0xfee50b7025c36a08, 0x02f236d04753d5b4}, -+ {0x9f4f2726179a2245, 0x01d762422c946590}, -+ {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5}, -+ {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2}, -+ {0x9b934c3b330c8577, 0x63cc55f49f88eb2f}, -+ {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb}, -+ {0xf316271c7fc3908a, 0x8bef464e3945ef7a}, -+ {0x97edd871cfda3a56, 0x97758bf0e3cbb5ac}, -+ {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317}, -+ {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd}, -+ {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a}, -+ {0xb975d6b6ee39e436, 0xb3e2fd538e122b44}, -+ {0xe7d34c64a9c85d44, 0x60dbbca87196b616}, -+ {0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd}, -+ {0xb51d13aea4a488dd, 0x6babab6398bdbe41}, -+ {0xe264589a4dcdab14, 0xc696963c7eed2dd1}, -+ {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2}, -+ {0xb0de65388cc8ada8, 0x3b25a55f43294bcb}, -+ {0xdd15fe86affad912, 0x49ef0eb713f39ebe}, -+ {0x8a2dbf142dfcc7ab, 0x6e3569326c784337}, -+ {0xacb92ed9397bf996, 0x49c2c37f07965404}, -+ {0xd7e77a8f87daf7fb, 0xdc33745ec97be906}, -+ {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3}, -+ {0xa8acd7c0222311bc, 0xc40832ea0d68ce0c}, -+ {0xd2d80db02aabd62b, 0xf50a3fa490c30190}, -+ {0x83c7088e1aab65db, 0x792667c6da79e0fa}, -+ {0xa4b8cab1a1563f52, 0x577001b891185938}, -+ {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, -+ {0x80b05e5ac60b6178, 0x544f8158315b05b4}, -+ {0xa0dc75f1778e39d6, 0x696361ae3db1c721}, -+ {0xc913936dd571c84c, 0x03bc3a19cd1e38e9}, -+ {0xfb5878494ace3a5f, 0x04ab48a04065c723}, -+ {0x9d174b2dcec0e47b, 0x62eb0d64283f9c76}, -+ {0xc45d1df942711d9a, 0x3ba5d0bd324f8394}, -+ {0xf5746577930d6500, 0xca8f44ec7ee36479}, -+ {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb}, -+ {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e}, -+ {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e}, -+ {0x95d04aee3b80ece5, 0xbba1f1d158724a12}, -+ {0xbb445da9ca61281f, 0x2a8a6e45ae8edc97}, -+ {0xea1575143cf97226, 0xf52d09d71a3293bd}, -+ {0x924d692ca61be758, 0x593c2626705f9c56}, -+ {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c}, -+ {0xe498f455c38b997a, 0x0b6dfb9c0f956447}, -+ {0x8edf98b59a373fec, 0x4724bd4189bd5eac}, -+ {0xb2977ee300c50fe7, 0x58edec91ec2cb657}, -+ {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed}, -+ {0x8b865b215899f46c, 0xbd79e0d20082ee74}, -+ {0xae67f1e9aec07187, 0xecd8590680a3aa11}, -+ {0xda01ee641a708de9, 0xe80e6f4820cc9495}, -+ {0x884134fe908658b2, 0x3109058d147fdcdd}, -+ {0xaa51823e34a7eede, 0xbd4b46f0599fd415}, -+ {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a}, -+ {0x850fadc09923329e, 0x03e2cf6bc604ddb0}, -+ {0xa6539930bf6bff45, 0x84db8346b786151c}, -+ {0xcfe87f7cef46ff16, 0xe612641865679a63}, -+ {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e}, -+ {0xa26da3999aef7749, 0xe3be5e330f38f09d}, -+ {0xcb090c8001ab551c, 0x5cadf5bfd3072cc5}, -+ {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6}, -+ {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa}, -+ {0xc646d63501a1511d, 0xb281e1fd541501b8}, -+ {0xf7d88bc24209a565, 0x1f225a7ca91a4226}, -+ {0x9ae757596946075f, 0x3375788de9b06958}, -+ {0xc1a12d2fc3978937, 0x0052d6b1641c83ae}, -+ {0xf209787bb47d6b84, 0xc0678c5dbd23a49a}, -+ {0x9745eb4d50ce6332, 0xf840b7ba963646e0}, -+ {0xbd176620a501fbff, 0xb650e5a93bc3d898}, -+ {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe}, -+ {0x93ba47c980e98cdf, 0xc66f336c36b10137}, -+ {0xb8a8d9bbe123f017, 0xb80b0047445d4184}, -+ {0xe6d3102ad96cec1d, 0xa60dc059157491e5}, -+ {0x9043ea1ac7e41392, 0x87c89837ad68db2f}, -+ {0xb454e4a179dd1877, 0x29babe4598c311fb}, -+ {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a}, -+ {0x8ce2529e2734bb1d, 0x1899e4a65f58660c}, -+ {0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f}, -+ {0xdc21a1171d42645d, 0x76707543f4fa1f73}, -+ {0x899504ae72497eba, 0x6a06494a791c53a8}, -+ {0xabfa45da0edbde69, 0x0487db9d17636892}, -+ {0xd6f8d7509292d603, 0x45a9d2845d3c42b6}, -+ {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, -+ {0xa7f26836f282b732, 0x8e6cac7768d7141e}, -+ {0xd1ef0244af2364ff, 0x3207d795430cd926}, -+ {0x8335616aed761f1f, 0x7f44e6bd49e807b8}, -+ {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6}, -+ {0xcd036837130890a1, 0x36dba887c37a8c0f}, -+ {0x802221226be55a64, 0xc2494954da2c9789}, -+ {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c}, -+ {0xc83553c5c8965d3d, 0x6f92829494e5acc7}, -+ {0xfa42a8b73abbf48c, 0xcb772339ba1f17f9}, -+ {0x9c69a97284b578d7, 0xff2a760414536efb}, -+ {0xc38413cf25e2d70d, 0xfef5138519684aba}, -+ {0xf46518c2ef5b8cd1, 0x7eb258665fc25d69}, -+ {0x98bf2f79d5993802, 0xef2f773ffbd97a61}, -+ {0xbeeefb584aff8603, 0xaafb550ffacfd8fa}, -+ {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38}, -+ {0x952ab45cfa97a0b2, 0xdd945a747bf26183}, -+ {0xba756174393d88df, 0x94f971119aeef9e4}, -+ {0xe912b9d1478ceb17, 0x7a37cd5601aab85d}, -+ {0x91abb422ccb812ee, 0xac62e055c10ab33a}, -+ {0xb616a12b7fe617aa, 0x577b986b314d6009}, -+ {0xe39c49765fdf9d94, 0xed5a7e85fda0b80b}, -+ {0x8e41ade9fbebc27d, 0x14588f13be847307}, -+ {0xb1d219647ae6b31c, 0x596eb2d8ae258fc8}, -+ {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb}, -+ {0x8aec23d680043bee, 0x25de7bb9480d5854}, -+ {0xada72ccc20054ae9, 0xaf561aa79a10ae6a}, -+ {0xd910f7ff28069da4, 0x1b2ba1518094da04}, -+ {0x87aa9aff79042286, 0x90fb44d2f05d0842}, -+ {0xa99541bf57452b28, 0x353a1607ac744a53}, -+ {0xd3fa922f2d1675f2, 0x42889b8997915ce8}, -+ {0x847c9b5d7c2e09b7, 0x69956135febada11}, -+ {0xa59bc234db398c25, 0x43fab9837e699095}, -+ {0xcf02b2c21207ef2e, 0x94f967e45e03f4bb}, -+ {0x8161afb94b44f57d, 0x1d1be0eebac278f5}, -+ {0xa1ba1ba79e1632dc, 0x6462d92a69731732}, -+ {0xca28a291859bbf93, 0x7d7b8f7503cfdcfe}, -+ {0xfcb2cb35e702af78, 0x5cda735244c3d43e}, -+ {0x9defbf01b061adab, 0x3a0888136afa64a7}, -+ {0xc56baec21c7a1916, 0x088aaa1845b8fdd0}, -+ {0xf6c69a72a3989f5b, 0x8aad549e57273d45}, -+ {0x9a3c2087a63f6399, 0x36ac54e2f678864b}, -+ {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd}, -+ {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5}, -+ {0x969eb7c47859e743, 0x9f644ae5a4b1b325}, -+ {0xbc4665b596706114, 0x873d5d9f0dde1fee}, -+ {0xeb57ff22fc0c7959, 0xa90cb506d155a7ea}, -+ {0x9316ff75dd87cbd8, 0x09a7f12442d588f2}, -+ {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f}, -+ {0xe5d3ef282a242e81, 0x8f1668c8a86da5fa}, -+ {0x8fa475791a569d10, 0xf96e017d694487bc}, -+ {0xb38d92d760ec4455, 0x37c981dcc395a9ac}, -+ {0xe070f78d3927556a, 0x85bbe253f47b1417}, -+ {0x8c469ab843b89562, 0x93956d7478ccec8e}, -+ {0xaf58416654a6babb, 0x387ac8d1970027b2}, -+ {0xdb2e51bfe9d0696a, 0x06997b05fcc0319e}, -+ {0x88fcf317f22241e2, 0x441fece3bdf81f03}, -+ {0xab3c2fddeeaad25a, 0xd527e81cad7626c3}, -+ {0xd60b3bd56a5586f1, 0x8a71e223d8d3b074}, -+ {0x85c7056562757456, 0xf6872d5667844e49}, -+ {0xa738c6bebb12d16c, 0xb428f8ac016561db}, -+ {0xd106f86e69d785c7, 0xe13336d701beba52}, -+ {0x82a45b450226b39c, 0xecc0024661173473}, -+ {0xa34d721642b06084, 0x27f002d7f95d0190}, -+ {0xcc20ce9bd35c78a5, 0x31ec038df7b441f4}, -+ {0xff290242c83396ce, 0x7e67047175a15271}, -+ {0x9f79a169bd203e41, 0x0f0062c6e984d386}, -+ {0xc75809c42c684dd1, 0x52c07b78a3e60868}, -+ {0xf92e0c3537826145, 0xa7709a56ccdf8a82}, -+ {0x9bbcc7a142b17ccb, 0x88a66076400bb691}, -+ {0xc2abf989935ddbfe, 0x6acff893d00ea435}, -+ {0xf356f7ebf83552fe, 0x0583f6b8c4124d43}, -+ {0x98165af37b2153de, 0xc3727a337a8b704a}, -+ {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c}, -+ {0xeda2ee1c7064130c, 0x1162def06f79df73}, -+ {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8}, -+ {0xb9a74a0637ce2ee1, 0x6d953e2bd7173692}, -+ {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437}, -+ {0x910ab1d4db9914a0, 0x1d9c9892400a22a2}, -+ {0xb54d5e4a127f59c8, 0x2503beb6d00cab4b}, -+ {0xe2a0b5dc971f303a, 0x2e44ae64840fd61d}, -+ {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, -+ {0xb10d8e1456105dad, 0x7425a83e872c5f47}, -+ {0xdd50f1996b947518, 0xd12f124e28f77719}, -+ {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f}, -+ {0xace73cbfdc0bfb7b, 0x636cc64d1001550b}, -+ {0xd8210befd30efa5a, 0x3c47f7e05401aa4e}, -+ {0x8714a775e3e95c78, 0x65acfaec34810a71}, -+ {0xa8d9d1535ce3b396, 0x7f1839a741a14d0d}, -+ {0xd31045a8341ca07c, 0x1ede48111209a050}, -+ {0x83ea2b892091e44d, 0x934aed0aab460432}, -+ {0xa4e4b66b68b65d60, 0xf81da84d5617853f}, -+ {0xce1de40642e3f4b9, 0x36251260ab9d668e}, -+ {0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019}, -+ {0xa1075a24e4421730, 0xb24cf65b8612f81f}, -+ {0xc94930ae1d529cfc, 0xdee033f26797b627}, -+ {0xfb9b7cd9a4a7443c, 0x169840ef017da3b1}, -+ {0x9d412e0806e88aa5, 0x8e1f289560ee864e}, -+ {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2}, -+ {0xf5b5d7ec8acb58a2, 0xae10af696774b1db}, -+ {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29}, -+ {0xbff610b0cc6edd3f, 0x17fd090a58d32af3}, -+ {0xeff394dcff8a948e, 0xddfc4b4cef07f5b0}, -+ {0x95f83d0a1fb69cd9, 0x4abdaf101564f98e}, -+ {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1}, -+ {0xea53df5fd18d5513, 0x84c86189216dc5ed}, -+ {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4}, -+ {0xb7118682dbb66a77, 0x3fbc8c33221dc2a1}, -+ {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, -+ {0x8f05b1163ba6832d, 0x29cb4d87f2a7400e}, -+ {0xb2c71d5bca9023f8, 0x743e20e9ef511012}, -+ {0xdf78e4b2bd342cf6, 0x914da9246b255416}, -+ {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e}, -+ {0xae9672aba3d0c320, 0xa184ac2473b529b1}, -+ {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e}, -+ {0x8865899617fb1871, 0x7e2fa67c7a658892}, -+ {0xaa7eebfb9df9de8d, 0xddbb901b98feeab7}, -+ {0xd51ea6fa85785631, 0x552a74227f3ea565}, -+ {0x8533285c936b35de, 0xd53a88958f87275f}, -+ {0xa67ff273b8460356, 0x8a892abaf368f137}, -+ {0xd01fef10a657842c, 0x2d2b7569b0432d85}, -+ {0x8213f56a67f6b29b, 0x9c3b29620e29fc73}, -+ {0xa298f2c501f45f42, 0x8349f3ba91b47b8f}, -+ {0xcb3f2f7642717713, 0x241c70a936219a73}, -+ {0xfe0efb53d30dd4d7, 0xed238cd383aa0110}, -+ {0x9ec95d1463e8a506, 0xf4363804324a40aa}, -+ {0xc67bb4597ce2ce48, 0xb143c6053edcd0d5}, -+ {0xf81aa16fdc1b81da, 0xdd94b7868e94050a}, -+ {0x9b10a4e5e9913128, 0xca7cf2b4191c8326}, -+ {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0}, -+ {0xf24a01a73cf2dccf, 0xbc633b39673c8cec}, -+ {0x976e41088617ca01, 0xd5be0503e085d813}, -+ {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18}, -+ {0xec9c459d51852ba2, 0xddf8e7d60ed1219e}, -+ {0x93e1ab8252f33b45, 0xcabb90e5c942b503}, -+ {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, -+ {0xe7109bfba19c0c9d, 0x0cc512670a783ad4}, -+ {0x906a617d450187e2, 0x27fb2b80668b24c5}, -+ {0xb484f9dc9641e9da, 0xb1f9f660802dedf6}, -+ {0xe1a63853bbd26451, 0x5e7873f8a0396973}, -+ {0x8d07e33455637eb2, 0xdb0b487b6423e1e8}, -+ {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62}, -+ {0xdc5c5301c56b75f7, 0x7641a140cc7810fb}, -+ {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d}, -+ {0xac2820d9623bf429, 0x546345fa9fbdcd44}, -+ {0xd732290fbacaf133, 0xa97c177947ad4095}, -+ {0x867f59a9d4bed6c0, 0x49ed8eabcccc485d}, -+ {0xa81f301449ee8c70, 0x5c68f256bfff5a74}, -+ {0xd226fc195c6a2f8c, 0x73832eec6fff3111}, -+ {0x83585d8fd9c25db7, 0xc831fd53c5ff7eab}, -+ {0xa42e74f3d032f525, 0xba3e7ca8b77f5e55}, -+ {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb}, -+ {0x80444b5e7aa7cf85, 0x7980d163cf5b81b3}, -+ {0xa0555e361951c366, 0xd7e105bcc332621f}, -+ {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7}, -+ {0xfa856334878fc150, 0xb14f98f6f0feb951}, -+ {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3}, -+ {0xc3b8358109e84f07, 0x0a862f80ec4700c8}, -+ {0xf4a642e14c6262c8, 0xcd27bb612758c0fa}, -+ {0x98e7e9cccfbd7dbd, 0x8038d51cb897789c}, -+ {0xbf21e44003acdd2c, 0xe0470a63e6bd56c3}, -+ {0xeeea5d5004981478, 0x1858ccfce06cac74}, -+ {0x95527a5202df0ccb, 0x0f37801e0c43ebc8}, -+ {0xbaa718e68396cffd, 0xd30560258f54e6ba}, -+ {0xe950df20247c83fd, 0x47c6b82ef32a2069}, -+ {0x91d28b7416cdd27e, 0x4cdc331d57fa5441}, -+ {0xb6472e511c81471d, 0xe0133fe4adf8e952}, -+ {0xe3d8f9e563a198e5, 0x58180fddd97723a6}, -+ {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648}, -+ {0xb201833b35d63f73, 0x2cd2cc6551e513da}, -+ {0xde81e40a034bcf4f, 0xf8077f7ea65e58d1}, -+ {0x8b112e86420f6191, 0xfb04afaf27faf782}, -+ {0xadd57a27d29339f6, 0x79c5db9af1f9b563}, -+ {0xd94ad8b1c7380874, 0x18375281ae7822bc}, -+ {0x87cec76f1c830548, 0x8f2293910d0b15b5}, -+ {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22}, -+ {0xd433179d9c8cb841, 0x5fa60692a46151eb}, -+ {0x849feec281d7f328, 0xdbc7c41ba6bcd333}, -+ {0xa5c7ea73224deff3, 0x12b9b522906c0800}, -+ {0xcf39e50feae16bef, 0xd768226b34870a00}, -+ {0x81842f29f2cce375, 0xe6a1158300d46640}, -+ {0xa1e53af46f801c53, 0x60495ae3c1097fd0}, -+ {0xca5e89b18b602368, 0x385bb19cb14bdfc4}, -+ {0xfcf62c1dee382c42, 0x46729e03dd9ed7b5}, -+ {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1}, -+ {0xc5a05277621be293, 0xc7098b7305241885}, -+ { 0xf70867153aa2db38, -+ 0xb8cbee4fc66d1ea7 } -+#else -+ {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, -+ {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, -+ {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, -+ {0x86a8d39ef77164bc, 0xae5dff9c02033198}, -+ {0xd98ddaee19068c76, 0x3badd624dd9b0958}, -+ {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, -+ {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, -+ {0xe55990879ddcaabd, 0xcc420a6a101d0516}, -+ {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, -+ {0x95a8637627989aad, 0xdde7001379a44aa9}, -+ {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, -+ {0xc350000000000000, 0x0000000000000000}, -+ {0x9dc5ada82b70b59d, 0xf020000000000000}, -+ {0xfee50b7025c36a08, 0x02f236d04753d5b4}, -+ {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, -+ {0xa6539930bf6bff45, 0x84db8346b786151c}, -+ {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, -+ {0xd910f7ff28069da4, 0x1b2ba1518094da04}, -+ {0xaf58416654a6babb, 0x387ac8d1970027b2}, -+ {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, -+ {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, -+ {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, -+ { 0x95527a5202df0ccb, -+ 0x0f37801e0c43ebc8 } -+#endif -+ }; -+ -+#if FMT_USE_FULL_CACHE_DRAGONBOX -+ return pow10_significands[k - float_info::min_k]; -+#else -+ static constexpr const uint64_t powers_of_5_64[] = { -+ 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, -+ 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, -+ 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, -+ 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, -+ 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, -+ 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, -+ 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, -+ 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, -+ 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; -+ -+ static constexpr const uint32_t pow10_recovery_errors[] = { -+ 0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001, -+ 0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555, -+ 0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015, -+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110, -+ 0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454, -+ 0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014, -+ 0x69514555, 0x05151109, 0x00155555}; -+ -+ static const int compression_ratio = 27; -+ -+ // Compute base index. -+ int cache_index = (k - float_info::min_k) / compression_ratio; -+ int kb = cache_index * compression_ratio + float_info::min_k; -+ int offset = k - kb; -+ -+ // Get base cache. -+ uint128_wrapper base_cache = pow10_significands[cache_index]; -+ if (offset == 0) return base_cache; -+ -+ // Compute the required amount of bit-shift. -+ int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset; -+ FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected"); -+ -+ // Try to recover the real cache. -+ uint64_t pow5 = powers_of_5_64[offset]; -+ uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); -+ uint128_wrapper middle_low = -+ umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); -+ -+ recovered_cache += middle_low.high(); -+ -+ uint64_t high_to_middle = recovered_cache.high() << (64 - alpha); -+ uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); -+ -+ recovered_cache = -+ uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, -+ ((middle_low.low() >> alpha) | middle_to_low)}; -+ -+ if (kb < 0) recovered_cache += 1; -+ -+ // Get error. -+ int error_idx = (k - float_info::min_k) / 16; -+ uint32_t error = (pow10_recovery_errors[error_idx] >> -+ ((k - float_info::min_k) % 16) * 2) & -+ 0x3; -+ -+ // Add the error back. -+ FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), ""); -+ return {recovered_cache.high(), recovered_cache.low() + error}; -+#endif -+ } -+ -+ static carrier_uint compute_mul(carrier_uint u, -+ const cache_entry_type& cache) FMT_NOEXCEPT { -+ return umul192_upper64(u, cache); -+ } -+ -+ static uint32_t compute_delta(cache_entry_type const& cache, -+ int beta_minus_1) FMT_NOEXCEPT { -+ return static_cast(cache.high() >> (64 - 1 - beta_minus_1)); -+ } -+ -+ static bool compute_mul_parity(carrier_uint two_f, -+ const cache_entry_type& cache, -+ int beta_minus_1) FMT_NOEXCEPT { -+ FMT_ASSERT(beta_minus_1 >= 1, ""); -+ FMT_ASSERT(beta_minus_1 < 64, ""); -+ -+ return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; -+ } -+ -+ static carrier_uint compute_left_endpoint_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { -+ return (cache.high() - -+ (cache.high() >> (float_info::significand_bits + 2))) >> -+ (64 - float_info::significand_bits - 1 - beta_minus_1); -+ } -+ -+ static carrier_uint compute_right_endpoint_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { -+ return (cache.high() + -+ (cache.high() >> (float_info::significand_bits + 1))) >> -+ (64 - float_info::significand_bits - 1 - beta_minus_1); -+ } -+ -+ static carrier_uint compute_round_up_for_shorter_interval_case( -+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { -+ return ((cache.high() >> -+ (64 - float_info::significand_bits - 2 - beta_minus_1)) + -+ 1) / -+ 2; -+ } -+}; -+ -+// Various integer checks -+template -+bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT { -+ return exponent >= -+ float_info< -+ T>::case_shorter_interval_left_endpoint_lower_threshold && -+ exponent <= -+ float_info::case_shorter_interval_left_endpoint_upper_threshold; -+} -+template -+bool is_endpoint_integer(typename float_info::carrier_uint two_f, -+ int exponent, int minus_k) FMT_NOEXCEPT { -+ if (exponent < float_info::case_fc_pm_half_lower_threshold) return false; -+ // For k >= 0. -+ if (exponent <= float_info::case_fc_pm_half_upper_threshold) return true; -+ // For k < 0. -+ if (exponent > float_info::divisibility_check_by_5_threshold) return false; -+ return divisible_by_power_of_5(two_f, minus_k); -+} -+ -+template -+bool is_center_integer(typename float_info::carrier_uint two_f, int exponent, -+ int minus_k) FMT_NOEXCEPT { -+ // Exponent for 5 is negative. -+ if (exponent > float_info::divisibility_check_by_5_threshold) return false; -+ if (exponent > float_info::case_fc_upper_threshold) -+ return divisible_by_power_of_5(two_f, minus_k); -+ // Both exponents are nonnegative. -+ if (exponent >= float_info::case_fc_lower_threshold) return true; -+ // Exponent for 2 is negative. -+ return divisible_by_power_of_2(two_f, minus_k - exponent + 1); -+} -+ -+// Remove trailing zeros from n and return the number of zeros removed (float) -+FMT_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { -+#ifdef FMT_BUILTIN_CTZ -+ int t = FMT_BUILTIN_CTZ(n); -+#else -+ int t = ctz(n); -+#endif -+ if (t > float_info::max_trailing_zeros) -+ t = float_info::max_trailing_zeros; -+ -+ const uint32_t mod_inv1 = 0xcccccccd; -+ const uint32_t max_quotient1 = 0x33333333; -+ const uint32_t mod_inv2 = 0xc28f5c29; -+ const uint32_t max_quotient2 = 0x0a3d70a3; -+ -+ int s = 0; -+ for (; s < t - 1; s += 2) { -+ if (n * mod_inv2 > max_quotient2) break; -+ n *= mod_inv2; -+ } -+ if (s < t && n * mod_inv1 <= max_quotient1) { -+ n *= mod_inv1; -+ ++s; -+ } -+ n >>= s; -+ return s; -+} -+ -+// Removes trailing zeros and returns the number of zeros removed (double) -+FMT_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { -+#ifdef FMT_BUILTIN_CTZLL -+ int t = FMT_BUILTIN_CTZLL(n); -+#else -+ int t = ctzll(n); -+#endif -+ if (t > float_info::max_trailing_zeros) -+ t = float_info::max_trailing_zeros; -+ // Divide by 10^8 and reduce to 32-bits -+ // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17, -+ // both of the quotient and the r should fit in 32-bits -+ -+ const uint32_t mod_inv1 = 0xcccccccd; -+ const uint32_t max_quotient1 = 0x33333333; -+ const uint64_t mod_inv8 = 0xc767074b22e90e21; -+ const uint64_t max_quotient8 = 0x00002af31dc46118; -+ -+ // If the number is divisible by 1'0000'0000, work with the quotient -+ if (t >= 8) { -+ auto quotient_candidate = n * mod_inv8; -+ -+ if (quotient_candidate <= max_quotient8) { -+ auto quotient = static_cast(quotient_candidate >> 8); -+ -+ int s = 8; -+ for (; s < t; ++s) { -+ if (quotient * mod_inv1 > max_quotient1) break; -+ quotient *= mod_inv1; -+ } -+ quotient >>= (s - 8); -+ n = quotient; -+ return s; -+ } -+ } -+ -+ // Otherwise, work with the remainder -+ auto quotient = static_cast(n / 100000000); -+ auto remainder = static_cast(n - 100000000 * quotient); -+ -+ if (t == 0 || remainder * mod_inv1 > max_quotient1) { -+ return 0; -+ } -+ remainder *= mod_inv1; -+ -+ if (t == 1 || remainder * mod_inv1 > max_quotient1) { -+ n = (remainder >> 1) + quotient * 10000000ull; -+ return 1; -+ } -+ remainder *= mod_inv1; -+ -+ if (t == 2 || remainder * mod_inv1 > max_quotient1) { -+ n = (remainder >> 2) + quotient * 1000000ull; -+ return 2; -+ } -+ remainder *= mod_inv1; -+ -+ if (t == 3 || remainder * mod_inv1 > max_quotient1) { -+ n = (remainder >> 3) + quotient * 100000ull; -+ return 3; -+ } -+ remainder *= mod_inv1; -+ -+ if (t == 4 || remainder * mod_inv1 > max_quotient1) { -+ n = (remainder >> 4) + quotient * 10000ull; -+ return 4; -+ } -+ remainder *= mod_inv1; -+ -+ if (t == 5 || remainder * mod_inv1 > max_quotient1) { -+ n = (remainder >> 5) + quotient * 1000ull; -+ return 5; -+ } -+ remainder *= mod_inv1; -+ -+ if (t == 6 || remainder * mod_inv1 > max_quotient1) { -+ n = (remainder >> 6) + quotient * 100ull; -+ return 6; -+ } -+ remainder *= mod_inv1; -+ -+ n = (remainder >> 7) + quotient * 10ull; -+ return 7; -+} -+ -+// The main algorithm for shorter interval case -+template -+FMT_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); -+ const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); -+ -+ // Compute xi and zi -+ using cache_entry_type = typename cache_accessor::cache_entry_type; -+ const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); -+ -+ auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( -+ cache, beta_minus_1); -+ auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( -+ cache, beta_minus_1); -+ -+ // If the left endpoint is not an integer, increase it -+ if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; -+ -+ // Try bigger divisor -+ ret_value.significand = zi / 10; -+ -+ // If succeed, remove trailing zeros if necessary and return -+ if (ret_value.significand * 10 >= xi) { -+ ret_value.exponent = minus_k + 1; -+ ret_value.exponent += remove_trailing_zeros(ret_value.significand); -+ return ret_value; -+ } -+ -+ // Otherwise, compute the round-up of y -+ ret_value.significand = -+ cache_accessor::compute_round_up_for_shorter_interval_case( -+ cache, beta_minus_1); -+ ret_value.exponent = minus_k; -+ -+ // When tie occurs, choose one of them according to the rule -+ if (exponent >= float_info::shorter_interval_tie_lower_threshold && -+ exponent <= float_info::shorter_interval_tie_upper_threshold) { -+ ret_value.significand = ret_value.significand % 2 == 0 -+ ? ret_value.significand -+ : ret_value.significand - 1; -+ } else if (ret_value.significand < xi) { -+ ++ret_value.significand; -+ } -+ return ret_value; -+} -+ -+template decimal_fp to_decimal(T x) FMT_NOEXCEPT { -+ // Step 1: integer promotion & Schubfach multiplier calculation. -+ -+ using carrier_uint = typename float_info::carrier_uint; -+ using cache_entry_type = typename cache_accessor::cache_entry_type; -+ auto br = bit_cast(x); -+ -+ // Extract significand bits and exponent bits. -+ const carrier_uint significand_mask = -+ (static_cast(1) << float_info::significand_bits) - 1; -+ carrier_uint significand = (br & significand_mask); -+ int exponent = static_cast((br & exponent_mask()) >> -+ float_info::significand_bits); -+ -+ if (exponent != 0) { // Check if normal. -+ exponent += float_info::exponent_bias - float_info::significand_bits; -+ -+ // Shorter interval case; proceed like Schubfach. -+ if (significand == 0) return shorter_interval_case(exponent); -+ -+ significand |= -+ (static_cast(1) << float_info::significand_bits); -+ } else { -+ // Subnormal case; the interval is always regular. -+ if (significand == 0) return {0, 0}; -+ exponent = float_info::min_exponent - float_info::significand_bits; -+ } -+ -+ const bool include_left_endpoint = (significand % 2 == 0); -+ const bool include_right_endpoint = include_left_endpoint; -+ -+ // Compute k and beta. -+ const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; -+ const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); -+ const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); -+ -+ // Compute zi and deltai -+ // 10^kappa <= deltai < 10^(kappa + 1) -+ const uint32_t deltai = cache_accessor::compute_delta(cache, beta_minus_1); -+ const carrier_uint two_fc = significand << 1; -+ const carrier_uint two_fr = two_fc | 1; -+ const carrier_uint zi = -+ cache_accessor::compute_mul(two_fr << beta_minus_1, cache); -+ -+ // Step 2: Try larger divisor; remove trailing zeros if necessary -+ -+ // Using an upper bound on zi, we might be able to optimize the division -+ // better than the compiler; we are computing zi / big_divisor here -+ decimal_fp ret_value; -+ ret_value.significand = divide_by_10_to_kappa_plus_1(zi); -+ uint32_t r = static_cast(zi - float_info::big_divisor * -+ ret_value.significand); -+ -+ if (r > deltai) { -+ goto small_divisor_case_label; -+ } else if (r < deltai) { -+ // Exclude the right endpoint if necessary -+ if (r == 0 && !include_right_endpoint && -+ is_endpoint_integer(two_fr, exponent, minus_k)) { -+ --ret_value.significand; -+ r = float_info::big_divisor; -+ goto small_divisor_case_label; -+ } -+ } else { -+ // r == deltai; compare fractional parts -+ // Check conditions in the order different from the paper -+ // to take advantage of short-circuiting -+ const carrier_uint two_fl = two_fc - 1; -+ if ((!include_left_endpoint || -+ !is_endpoint_integer(two_fl, exponent, minus_k)) && -+ !cache_accessor::compute_mul_parity(two_fl, cache, beta_minus_1)) { -+ goto small_divisor_case_label; -+ } -+ } -+ ret_value.exponent = minus_k + float_info::kappa + 1; -+ -+ // We may need to remove trailing zeros -+ ret_value.exponent += remove_trailing_zeros(ret_value.significand); -+ return ret_value; -+ -+ // Step 3: Find the significand with the smaller divisor -+ -+small_divisor_case_label: -+ ret_value.significand *= 10; -+ ret_value.exponent = minus_k + float_info::kappa; -+ -+ const uint32_t mask = (1u << float_info::kappa) - 1; -+ auto dist = r - (deltai / 2) + (float_info::small_divisor / 2); -+ -+ // Is dist divisible by 2^kappa? -+ if ((dist & mask) == 0) { -+ const bool approx_y_parity = -+ ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; -+ dist >>= float_info::kappa; -+ -+ // Is dist divisible by 5^kappa? -+ if (check_divisibility_and_divide_by_pow5::kappa>(dist)) { -+ ret_value.significand += dist; -+ -+ // Check z^(f) >= epsilon^(f) -+ // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, -+ // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) -+ // Since there are only 2 possibilities, we only need to care about the -+ // parity. Also, zi and r should have the same parity since the divisor -+ // is an even number -+ if (cache_accessor::compute_mul_parity(two_fc, cache, beta_minus_1) != -+ approx_y_parity) { -+ --ret_value.significand; -+ } else { -+ // If z^(f) >= epsilon^(f), we might have a tie -+ // when z^(f) == epsilon^(f), or equivalently, when y is an integer -+ if (is_center_integer(two_fc, exponent, minus_k)) { -+ ret_value.significand = ret_value.significand % 2 == 0 -+ ? ret_value.significand -+ : ret_value.significand - 1; -+ } -+ } -+ } -+ // Is dist not divisible by 5^kappa? -+ else { -+ ret_value.significand += dist; -+ } -+ } -+ // Is dist not divisible by 2^kappa? -+ else { -+ // Since we know dist is small, we might be able to optimize the division -+ // better than the compiler; we are computing dist / small_divisor here -+ ret_value.significand += -+ small_division_by_pow10::kappa>(dist); -+ } -+ return ret_value; -+} -+} // namespace dragonbox -+ -+// Formats a floating-point number using a variation of the Fixed-Precision -+// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: -+// https://fmt.dev/papers/p372-steele.pdf. -+FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer, -+ int num_digits, buffer& buf, -+ int& exp10) { - bigint numerator; // 2 * R in (FPP)^2. - bigint denominator; // 2 * S in (FPP)^2. - // lower and upper are differences between value and corresponding boundaries. - bigint lower; // (M^- in (FPP)^2). - bigint upper_store; // upper's value if different from lower. - bigint* upper = nullptr; // (M^+ in (FPP)^2). -- fp value; - // Shift numerator and denominator by an extra bit or two (if lower boundary - // is closer) to make lower and upper integers. This eliminates multiplication - // by 2 during later computations. -- // TODO: handle float -- int shift = value.assign(d) ? 2 : 1; -+ int shift = is_predecessor_closer ? 2 : 1; - uint64_t significand = value.f << shift; - if (value.e >= 0) { - numerator.assign(significand); -@@ -1012,7 +2247,7 @@ void fallback_format(Double d, buffer& buf, int& exp10) { - upper = &upper_store; - } - denominator.assign_pow10(exp10); -- denominator <<= 1; -+ denominator <<= shift; - } else if (exp10 < 0) { - numerator.assign_pow10(-exp10); - lower.assign(numerator); -@@ -1034,42 +2269,79 @@ void fallback_format(Double d, buffer& buf, int& exp10) { - upper = &upper_store; - } - } -- if (!upper) upper = &lower; - // Invariant: value == (numerator / denominator) * pow(10, exp10). -- bool even = (value.f & 1) == 0; -- int num_digits = 0; -- char* data = buf.data(); -- for (;;) { -- int digit = numerator.divmod_assign(denominator); -- bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. -- // numerator + upper >[=] pow10: -- bool high = add_compare(numerator, *upper, denominator) + even > 0; -- data[num_digits++] = static_cast('0' + digit); -- if (low || high) { -- if (!low) { -- ++data[num_digits - 1]; -- } else if (high) { -- int result = add_compare(numerator, numerator, denominator); -- // Round half to even. -- if (result > 0 || (result == 0 && (digit % 2) != 0)) -+ if (num_digits < 0) { -+ // Generate the shortest representation. -+ if (!upper) upper = &lower; -+ bool even = (value.f & 1) == 0; -+ num_digits = 0; -+ char* data = buf.data(); -+ for (;;) { -+ int digit = numerator.divmod_assign(denominator); -+ bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. -+ // numerator + upper >[=] pow10: -+ bool high = add_compare(numerator, *upper, denominator) + even > 0; -+ data[num_digits++] = static_cast('0' + digit); -+ if (low || high) { -+ if (!low) { - ++data[num_digits - 1]; -+ } else if (high) { -+ int result = add_compare(numerator, numerator, denominator); -+ // Round half to even. -+ if (result > 0 || (result == 0 && (digit % 2) != 0)) -+ ++data[num_digits - 1]; -+ } -+ buf.try_resize(to_unsigned(num_digits)); -+ exp10 -= num_digits - 1; -+ return; - } -- buf.resize(to_unsigned(num_digits)); -- exp10 -= num_digits - 1; -- return; -+ numerator *= 10; -+ lower *= 10; -+ if (upper != &lower) *upper *= 10; - } -+ } -+ // Generate the given number of digits. -+ exp10 -= num_digits - 1; -+ if (num_digits == 0) { -+ denominator *= 10; -+ auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; -+ buf.push_back(digit); -+ return; -+ } -+ buf.try_resize(to_unsigned(num_digits)); -+ for (int i = 0; i < num_digits - 1; ++i) { -+ int digit = numerator.divmod_assign(denominator); -+ buf[i] = static_cast('0' + digit); - numerator *= 10; -- lower *= 10; -- if (upper != &lower) *upper *= 10; - } -+ int digit = numerator.divmod_assign(denominator); -+ auto result = add_compare(numerator, numerator, denominator); -+ if (result > 0 || (result == 0 && (digit % 2) != 0)) { -+ if (digit == 9) { -+ const auto overflow = '0' + 10; -+ buf[num_digits - 1] = overflow; -+ // Propagate the carry. -+ for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { -+ buf[i] = '0'; -+ ++buf[i - 1]; -+ } -+ if (buf[0] == overflow) { -+ buf[0] = '1'; -+ ++exp10; -+ } -+ return; -+ } -+ ++digit; -+ } -+ buf[num_digits - 1] = static_cast('0' + digit); - } - --// Formats value using the Grisu algorithm --// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf) --// if T is a IEEE754 binary32 or binary64 and snprintf otherwise. --template --int format_float(T value, int precision, float_specs specs, buffer& buf) { -- static_assert(!std::is_same::value, ""); -+template -+FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, -+ float_specs specs, -+ buffer& buf) { -+ // float is passed as double to reduce the number of instantiations. -+ static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); - - const bool fixed = specs.format == float_format::fixed; -@@ -1078,65 +2350,67 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { - buf.push_back('0'); - return 0; - } -- buf.resize(to_unsigned(precision)); -- std::uninitialized_fill_n(buf.data(), precision, '0'); -+ buf.try_resize(to_unsigned(precision)); -+ fill_n(buf.data(), precision, '0'); - return -precision; - } - -- if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf); -+ if (specs.fallback) return snprintf_float(value, precision, specs, buf); - -- int exp = 0; -- const int min_exp = -60; // alpha in Grisu. -- int cached_exp10 = 0; // K in Grisu. -- if (precision < 0) { -- fp fp_value; -- auto boundaries = specs.binary32 -- ? fp_value.assign_float_with_boundaries(value) -- : fp_value.assign_with_boundaries(value); -- fp_value = normalize(fp_value); -- // Find a cached power of 10 such that multiplying value by it will bring -- // the exponent in the range [min_exp, -32]. -- const fp cached_pow = get_cached_power( -- min_exp - (fp_value.e + fp::significand_size), cached_exp10); -- // Multiply value and boundaries by the cached power of 10. -- fp_value = fp_value * cached_pow; -- boundaries.lower = multiply(boundaries.lower, cached_pow.f); -- boundaries.upper = multiply(boundaries.upper, cached_pow.f); -- assert(min_exp <= fp_value.e && fp_value.e <= -32); -- --boundaries.lower; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}. -- ++boundaries.upper; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}. -- // Numbers outside of (lower, upper) definitely do not round to value. -- grisu_shortest_handler handler{buf.data(), 0, -- boundaries.upper - fp_value.f}; -- auto result = -- grisu_gen_digits(fp(boundaries.upper, fp_value.e), -- boundaries.upper - boundaries.lower, exp, handler); -- if (result == digits::error) { -- exp += handler.size - cached_exp10 - 1; -- fallback_format(value, buf, exp); -- return exp; -+ if (!is_constant_evaluated() && precision < 0) { -+ // Use Dragonbox for the shortest format. -+ if (specs.binary32) { -+ auto dec = dragonbox::to_decimal(static_cast(value)); -+ write(buffer_appender(buf), dec.significand); -+ return dec.exponent; - } -- buf.resize(to_unsigned(handler.size)); -- } else { -- if (precision > 17) return snprintf_float(value, precision, specs, buf); -+ auto dec = dragonbox::to_decimal(static_cast(value)); -+ write(buffer_appender(buf), dec.significand); -+ return dec.exponent; -+ } -+ -+ int exp = 0; -+ bool use_dragon = true; -+ if (is_fast_float()) { -+ // Use Grisu + Dragon4 for the given precision: -+ // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. -+ const int min_exp = -60; // alpha in Grisu. -+ int cached_exp10 = 0; // K in Grisu. - fp normalized = normalize(fp(value)); - const auto cached_pow = get_cached_power( -- min_exp - (normalized.e + fp::significand_size), cached_exp10); -+ min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); - normalized = normalized * cached_pow; -- fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; -- if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) -- return snprintf_float(value, precision, specs, buf); -- int num_digits = handler.size; -- if (!fixed) { -- // Remove trailing zeros. -- while (num_digits > 0 && buf[num_digits - 1] == '0') { -- --num_digits; -- ++exp; -- } -+ gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; -+ if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && -+ !is_constant_evaluated()) { -+ exp += handler.exp10; -+ buf.try_resize(to_unsigned(handler.size)); -+ use_dragon = false; -+ } else { -+ exp += handler.size - cached_exp10 - 1; -+ precision = handler.precision; -+ } -+ } -+ if (use_dragon) { -+ auto f = fp(); -+ bool is_predecessor_closer = -+ specs.binary32 ? f.assign(static_cast(value)) : f.assign(value); -+ // Limit precision to the maximum possible number of significant digits in -+ // an IEEE754 double because we don't need to generate zeros. -+ const int max_double_digits = 767; -+ if (precision > max_double_digits) precision = max_double_digits; -+ format_dragon(f, is_predecessor_closer, precision, buf, exp); -+ } -+ if (!fixed && !specs.showpoint) { -+ // Remove trailing zeros. -+ auto num_digits = buf.size(); -+ while (num_digits > 0 && buf[num_digits - 1] == '0') { -+ --num_digits; -+ ++exp; - } -- buf.resize(to_unsigned(num_digits)); -+ buf.try_resize(num_digits); - } -- return exp - cached_exp10; -+ return exp; - } - - template -@@ -1185,19 +2459,20 @@ int snprintf_float(T value, int precision, float_specs specs, - ? snprintf_ptr(begin, capacity, format, precision, value) - : snprintf_ptr(begin, capacity, format, value); - if (result < 0) { -- buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially. -+ // The buffer will grow exponentially. -+ buf.try_reserve(buf.capacity() + 1); - continue; - } - auto size = to_unsigned(result); - // Size equal to capacity means that the last character was truncated. - if (size >= capacity) { -- buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'. -+ buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. - continue; - } - auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; - if (specs.format == float_format::fixed) { - if (precision == 0) { -- buf.resize(size); -+ buf.try_resize(size); - return 0; - } - // Find and remove the decimal point. -@@ -1207,11 +2482,11 @@ int snprintf_float(T value, int precision, float_specs specs, - } while (is_digit(*p)); - int fraction_size = static_cast(end - p - 1); - std::memmove(p, p + 1, to_unsigned(fraction_size)); -- buf.resize(size - 1); -+ buf.try_resize(size - 1); - return -fraction_size; - } - if (specs.format == float_format::hex) { -- buf.resize(size + offset); -+ buf.try_resize(size + offset); - return 0; - } - // Find and parse the exponent. -@@ -1220,11 +2495,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; -@@ -1237,69 +2512,15 @@ int snprintf_float(T value, int precision, float_specs specs, - fraction_size = static_cast(fraction_end - begin - 1); - std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); - } -- buf.resize(to_unsigned(fraction_size) + offset + 1); -+ buf.try_resize(to_unsigned(fraction_size) + offset + 1); - return exp - fraction_size; - } - } -- --// 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. -- */ --FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) { -- static const 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}; -- 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}; -- -- auto s = reinterpret_cast(buf); -- int len = lengths[s[0] >> 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. -- const char* next = buf + len + !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. -- *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; --} - } // 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(); - } - -@@ -1310,24 +2531,22 @@ 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); -- if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); -+ for_each_codepoint(s, [this](uint32_t cp, string_view) { -+ if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8")); - if (cp <= 0xFFFF) { - buffer_.push_back(static_cast(cp)); - } else { -@@ -1335,98 +2554,73 @@ 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); -- } -+ return true; -+ }); - buffer_.push_back(0); - } - - FMT_FUNC void format_system_error(detail::buffer& out, int error_code, -- string_view message) FMT_NOEXCEPT { -+ const char* message) FMT_NOEXCEPT { - FMT_TRY { -- memory_buffer buf; -- buf.resize(inline_buffer_size); -- for (;;) { -- char* system_message = &buf[0]; -- int result = -- detail::safe_strerror(error_code, system_message, buf.size()); -- if (result == 0) { -- format_to(std::back_inserter(out), "{}: {}", message, system_message); -- return; -- } -- if (result != ERANGE) -- break; // Can't get error message, report error code instead. -- buf.resize(buf.size() * 2); -- } -+ auto ec = std::error_code(error_code, std::generic_category()); -+ write(std::back_inserter(out), std::system_error(ec, message).what()); -+ return; - } - FMT_CATCH(...) {} - format_error_code(out, error_code, message); - } - --FMT_FUNC void detail::error_handler::on_error(const char* message) { -- FMT_THROW(format_error(message)); --} -- - FMT_FUNC void report_system_error(int error_code, -- fmt::string_view message) FMT_NOEXCEPT { -+ const char* message) FMT_NOEXCEPT { - report_error(format_system_error, error_code, message); - } - --struct stringifier { -- template FMT_INLINE std::string operator()(T value) const { -- return to_string(value); -- } -- std::string operator()(basic_format_arg::handle h) const { -- memory_buffer buf; -- detail::buffer& base = buf; -- format_parse_context parse_ctx({}); -- format_context format_ctx(std::back_inserter(base), {}, {}); -- h.format(parse_ctx, format_ctx); -- return to_string(buf); -- } --}; -+// DEPRECATED! -+// This function is defined here and not inline for ABI compatiblity. -+FMT_FUNC void detail::error_handler::on_error(const char* message) { -+ throw_format_error(message); -+} - --FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) { -- if (format_str.size() == 2 && equal2(format_str.data(), "{}")) { -- auto arg = args.get(0); -- if (!arg) error_handler().on_error("argument not found"); -- return visit_format_arg(stringifier(), arg); -- } -- memory_buffer buffer; -- detail::vformat_to(buffer, format_str, args); -+FMT_FUNC std::string vformat(string_view fmt, format_args args) { -+ // Don't optimize the "{}" case to keep the binary size small and because it -+ // can be better optimized in fmt::format anyway. -+ auto buffer = memory_buffer(); -+ detail::vformat_to(buffer, fmt, args); - return to_string(buffer); - } - --FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { -- memory_buffer buffer; -- detail::vformat_to(buffer, format_str, -- basic_format_args>(args)); -+#ifdef _WIN32 -+namespace detail { -+using dword = conditional_t; -+extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // -+ void*, const void*, dword, dword*, void*); -+} // namespace detail -+#endif -+ -+namespace detail { -+FMT_FUNC void print(std::FILE* f, string_view text) { - #ifdef _WIN32 - auto fd = _fileno(f); - if (_isatty(fd)) { -- detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); -- auto written = DWORD(); -- if (!WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), -- u16.c_str(), static_cast(u16.size()), &written, -- nullptr)) { -- FMT_THROW(format_error("failed to write to console")); -+ detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); -+ auto written = detail::dword(); -+ 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); -+ detail::fwrite_fully(text.data(), 1, text.size(), f); -+} -+} // namespace detail -+ -+FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { -+ memory_buffer buffer; -+ detail::vformat_to(buffer, format_str, args); -+ detail::print(f, {buffer.data(), buffer.size()}); - } - - #ifdef _WIN32 -@@ -1446,8 +2640,4 @@ FMT_FUNC void vprint(string_view format_str, format_args args) { - - FMT_END_NAMESPACE - --#ifdef _MSC_VER --# pragma warning(pop) --#endif -- - #endif // FMT_FORMAT_INL_H_ -diff --git a/external/fmt/include/fmt/format.h b/external/fmt/include/fmt/format.h -index 17509b7b..ee69651c 100644 ---- a/external/fmt/include/fmt/format.h -+++ b/external/fmt/include/fmt/format.h -@@ -33,22 +33,24 @@ - #ifndef FMT_FORMAT_H_ - #define FMT_FORMAT_H_ - --#include --#include --#include --#include --#include --#include --#include -+#include // std::signbit -+#include // uint32_t -+#include // std::numeric_limits -+#include // std::uninitialized_copy -+#include // std::runtime_error -+#include // std::system_error -+#include // std::swap -+ -+#ifdef __cpp_lib_bit_cast -+# include // std::bitcast -+#endif - - #include "core.h" - --#ifdef __INTEL_COMPILER --# define FMT_ICC_VERSION __INTEL_COMPILER --#elif defined(__ICL) --# define FMT_ICC_VERSION __ICL -+#if FMT_GCC_VERSION -+# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) - #else --# define FMT_ICC_VERSION 0 -+# define FMT_GCC_VISIBILITY_HIDDEN - #endif - - #ifdef __NVCC__ -@@ -69,28 +71,10 @@ - # define FMT_NOINLINE - #endif - --#if __cplusplus == 201103L || __cplusplus == 201402L --# if defined(__clang__) --# define FMT_FALLTHROUGH [[clang::fallthrough]] --# elif FMT_GCC_VERSION >= 700 && !defined(__PGI) && \ -- (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) --# define FMT_FALLTHROUGH [[gnu::fallthrough]] --# else --# define FMT_FALLTHROUGH --# endif --#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ -- (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) --# define FMT_FALLTHROUGH [[fallthrough]] -+#if FMT_MSC_VER -+# define FMT_MSC_DEFAULT = default - #else --# define FMT_FALLTHROUGH --#endif -- --#ifndef FMT_MAYBE_UNUSED --# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) --# define FMT_MAYBE_UNUSED [[maybe_unused]] --# else --# define FMT_MAYBE_UNUSED --# endif -+# define FMT_MSC_DEFAULT - #endif - - #ifndef FMT_THROW -@@ -111,10 +95,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 -@@ -127,6 +110,21 @@ FMT_END_NAMESPACE - # define FMT_CATCH(x) if (false) - #endif - -+#ifndef FMT_MAYBE_UNUSED -+# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) -+# define FMT_MAYBE_UNUSED [[maybe_unused]] -+# else -+# define FMT_MAYBE_UNUSED -+# endif -+#endif -+ -+// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. -+#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC -+# define FMT_DEPRECATED_ALIAS -+#else -+# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED -+#endif -+ - #ifndef FMT_USE_USER_DEFINED_LITERALS - // EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. - # if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ -@@ -138,118 +136,178 @@ FMT_END_NAMESPACE - # endif - #endif - --#ifndef FMT_USE_UDL_TEMPLATE --// EDG frontend based compilers (icc, nvcc, 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) --# define FMT_USE_UDL_TEMPLATE 1 --# else --# define FMT_USE_UDL_TEMPLATE 0 --# endif --#endif -- --#ifndef FMT_USE_FLOAT --# define FMT_USE_FLOAT 1 -+// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of -+// 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 - --#ifndef FMT_USE_DOUBLE --# define FMT_USE_DOUBLE 1 -+// __builtin_clz is broken in clang with Microsoft CodeGen: -+// https://github.com/fmtlib/fmt/issues/519. -+#if !FMT_MSC_VER -+# if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION -+# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -+# endif -+# if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION -+# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -+# endif - #endif - --#ifndef FMT_USE_LONG_DOUBLE --# define FMT_USE_LONG_DOUBLE 1 -+// __builtin_ctz is broken in Intel Compiler Classic on Windows: -+// https://github.com/fmtlib/fmt/issues/2510. -+#ifndef __ICL -+# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION -+# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) -+# endif -+# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || FMT_ICC_VERSION -+# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) -+# endif - #endif - --// __builtin_clz is broken in clang with Microsoft CodeGen: --// https://github.com/fmtlib/fmt/issues/519 --#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER --# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) --#endif --#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER --# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -+#if FMT_MSC_VER -+# include // _BitScanReverse[64], _BitScanForward[64], _umul128 - #endif - - // 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(_MANAGED) --# include // _BitScanReverse, _BitScanReverse64 -- -+#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 intrinsic(_BitScanForward) - # pragma intrinsic(_BitScanReverse) -+# if defined(_WIN64) -+# pragma intrinsic(_BitScanForward64) -+# pragma intrinsic(_BitScanReverse64) -+# endif - # endif --inline uint32_t clz(uint32_t x) { -+ -+inline auto clz(uint32_t x) -> int { - unsigned long r = 0; - _BitScanReverse(&r, x); -- - FMT_ASSERT(x != 0, ""); - // Static analysis complains about using uninitialized data - // "r", but the only way that can happen is if "x" is 0, - // which the callers guarantee to not happen. -- FMT_SUPPRESS_MSC_WARNING(6102) -- return 31 - r; -+ FMT_MSC_WARNING(suppress : 6102) -+ return 31 ^ static_cast(r); - } - # define FMT_BUILTIN_CLZ(n) detail::clz(n) - --# if defined(_WIN64) && !defined(__clang__) --# pragma intrinsic(_BitScanReverse64) --# endif -- --inline uint32_t clzll(uint64_t x) { -+inline auto clzll(uint64_t x) -> int { - unsigned long r = 0; - # ifdef _WIN64 - _BitScanReverse64(&r, x); - # else - // Scan the high 32 bits. -- if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 - (r + 32); -- -+ if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 ^ (r + 32); - // Scan the low 32 bits. - _BitScanReverse(&r, static_cast(x)); - # endif -- - FMT_ASSERT(x != 0, ""); -- // Static analysis complains about using uninitialized data -- // "r", but the only way that can happen is if "x" is 0, -- // which the callers guarantee to not happen. -- FMT_SUPPRESS_MSC_WARNING(6102) -- return 63 - r; -+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. -+ return 63 ^ static_cast(r); - } - # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) -+ -+inline auto ctz(uint32_t x) -> int { -+ unsigned long r = 0; -+ _BitScanForward(&r, x); -+ FMT_ASSERT(x != 0, ""); -+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. -+ return static_cast(r); -+} -+# define FMT_BUILTIN_CTZ(n) detail::ctz(n) -+ -+inline auto ctzll(uint64_t x) -> int { -+ unsigned long r = 0; -+ FMT_ASSERT(x != 0, ""); -+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. -+# ifdef _WIN64 -+ _BitScanForward64(&r, x); -+# else -+ // Scan the low 32 bits. -+ if (_BitScanForward(&r, static_cast(x))) return static_cast(r); -+ // Scan the high 32 bits. -+ _BitScanForward(&r, static_cast(x >> 32)); -+ r += 32; -+# endif -+ return static_cast(r); -+} -+# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) - } // namespace detail - FMT_END_NAMESPACE - #endif - --// Enable the deprecated numeric alignment. --#ifndef FMT_DEPRECATED_NUMERIC_ALIGN --# define FMT_DEPRECATED_NUMERIC_ALIGN 0 -+#ifdef FMT_HEADER_ONLY -+# define FMT_HEADER_ONLY_CONSTEXPR20 FMT_CONSTEXPR20 -+#else -+# define FMT_HEADER_ONLY_CONSTEXPR20 - #endif - - FMT_BEGIN_NAMESPACE - namespace detail { - --// 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); --template --inline Dest bit_cast(const Source& source) { -- static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); -- Dest dest; -- std::memcpy(&dest, &source, sizeof(dest)); -- return dest; -+template class formatbuf : public Streambuf { -+ private: -+ using char_type = typename Streambuf::char_type; -+ using streamsize = decltype(std::declval().sputn(nullptr, 0)); -+ using int_type = typename Streambuf::int_type; -+ using traits_type = typename Streambuf::traits_type; -+ -+ buffer& buffer_; -+ -+ public: -+ explicit formatbuf(buffer& buf) : buffer_(buf) {} -+ -+ protected: -+ // The put area is always empty. This makes the implementation simpler and has -+ // the advantage that the streambuf and the buffer are always in sync and -+ // sputc never writes into uninitialized memory. A disadvantage is that each -+ // call to sputc always results in a (virtual) call to overflow. There is no -+ // disadvantage here for sputn since this always results in a call to xsputn. -+ -+ auto overflow(int_type ch) -> int_type override { -+ if (!traits_type::eq_int_type(ch, traits_type::eof())) -+ buffer_.push_back(static_cast(ch)); -+ return ch; -+ } -+ -+ auto xsputn(const char_type* s, streamsize count) -> streamsize override { -+ buffer_.append(s, s + count); -+ return count; -+ } -+}; -+ -+// Implementation of std::bit_cast for pre-C++20. -+template -+FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { -+ static_assert(sizeof(To) == sizeof(From), "size mismatch"); -+#ifdef __cpp_lib_bit_cast -+ if (is_constant_evaluated()) return std::bit_cast(from); -+#endif -+ auto to = To(); -+ std::memcpy(&to, &from, sizeof(to)); -+ return to; - } - --inline bool is_big_endian() { -- const auto u = 1u; -+inline auto is_big_endian() -> bool { -+#ifdef _WIN32 -+ return false; -+#elif defined(__BIG_ENDIAN__) -+ return true; -+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) -+ return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; -+#else - struct bytes { -- char data[sizeof(u)]; -+ char data[sizeof(int)]; - }; -- return bit_cast(u).data[0] == 0; -+ return bit_cast(1).data[0] == 0; -+#endif - } - - // A fallback implementation of uintptr_t for systems that lack it. -@@ -259,7 +317,7 @@ struct fallback_uintptr { - fallback_uintptr() = default; - explicit fallback_uintptr(const void* p) { - *this = bit_cast(p); -- if (is_big_endian()) { -+ if (const_check(is_big_endian())) { - for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) - std::swap(value[i], value[j]); - } -@@ -267,26 +325,28 @@ struct fallback_uintptr { - }; - #ifdef UINTPTR_MAX - using uintptr_t = ::uintptr_t; --inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } -+inline auto to_uintptr(const void* p) -> uintptr_t { -+ return bit_cast(p); -+} - #else - using uintptr_t = fallback_uintptr; --inline fallback_uintptr to_uintptr(const void* p) { -+inline auto to_uintptr(const void* p) -> fallback_uintptr { - return fallback_uintptr(p); - } - #endif - - // Returns the largest possible value for type T. Same as - // std::numeric_limits::max() but shorter and not affected by the max macro. --template constexpr T max_value() { -+template constexpr auto max_value() -> T { - return (std::numeric_limits::max)(); - } --template constexpr int num_bits() { -+template constexpr auto num_bits() -> int { - return std::numeric_limits::digits; - } - // std::numeric_limits::digits may return 0 for 128-bit ints. --template <> constexpr int num_bits() { return 128; } --template <> constexpr int num_bits() { return 128; } --template <> constexpr int num_bits() { -+template <> constexpr auto num_bits() -> int { return 128; } -+template <> constexpr auto num_bits() -> int { return 128; } -+template <> constexpr auto num_bits() -> int { - return static_cast(sizeof(void*) * - std::numeric_limits::digits); - } -@@ -298,283 +358,295 @@ FMT_INLINE void assume(bool condition) { - #endif - } - --// 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; -- - // An approximation of iterator_t for pre-C++20 systems. - template - using iterator_t = decltype(std::begin(std::declval())); - template using sentinel_t = decltype(std::end(std::declval())); - --// Detect the iterator category of *any* given type in a SFINAE-friendly way. --// Unfortunately, older implementations of std::iterator_traits are not safe --// for use in a SFINAE-context. --template --struct iterator_category : std::false_type {}; -- --template struct iterator_category { -- using type = std::random_access_iterator_tag; --}; -- --template --struct iterator_category> { -- using type = typename It::iterator_category; --}; -- --// Detect if *any* given type models the OutputIterator concept. --template class is_output_iterator { -- // Check for mutability because all iterator categories derived from -- // std::input_iterator_tag *may* also meet the requirements of an -- // OutputIterator, thereby falling into the category of 'mutable iterators' -- // [iterator.requirements.general] clause 4. The compiler reveals this -- // property only at the point of *actually dereferencing* the iterator! -- template -- static decltype(*(std::declval())) test(std::input_iterator_tag); -- template static char& test(std::output_iterator_tag); -- template static const char& test(...); -- -- using type = decltype(test(typename iterator_category::type{})); -- -- public: -- enum { value = !std::is_const>::value }; --}; -- - // A workaround for std::string not having mutable data() until C++17. --template inline Char* get_data(std::basic_string& s) { -+template -+inline auto get_data(std::basic_string& s) -> Char* { - return &s[0]; - } - template --inline typename Container::value_type* get_data(Container& c) { -+inline auto get_data(Container& c) -> typename Container::value_type* { - return c.data(); - } - - #if defined(_SECURE_SCL) && _SECURE_SCL - // Make a checked iterator to avoid MSVC warnings. - template using checked_ptr = stdext::checked_array_iterator; --template checked_ptr make_checked(T* p, size_t size) { -+template -+constexpr auto make_checked(T* p, size_t size) -> checked_ptr { - return {p, size}; - } - #else - template using checked_ptr = T*; --template inline T* make_checked(T* p, size_t) { return p; } -+template constexpr auto make_checked(T* p, size_t) -> T* { -+ return p; -+} - #endif - -+// Attempts to reserve space for n extra characters in the output range. -+// Returns a pointer to the reserved range or a reference to it. - template ::value)> --#if FMT_CLANG_VERSION -+#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION - __attribute__((no_sanitize("undefined"))) - #endif --inline checked_ptr --reserve(std::back_insert_iterator it, size_t n) { -+inline auto -+reserve(std::back_insert_iterator it, size_t n) -+ -> checked_ptr { - Container& c = get_container(it); - size_t size = c.size(); - c.resize(size + n); - return make_checked(get_data(c) + size, n); - } - --template inline Iterator& reserve(Iterator& it, size_t) { -- return it; --} -- --template ::value)> --inline std::back_insert_iterator base_iterator( -- std::back_insert_iterator& it, -- checked_ptr) { -+template -+inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { -+ buffer& buf = get_container(it); -+ buf.try_reserve(buf.size() + n); - return it; - } - - template --inline Iterator base_iterator(Iterator, Iterator it) { -+constexpr auto reserve(Iterator& it, size_t) -> Iterator& { - return it; - } - --// An output iterator that counts the number of objects written to it and --// discards them. --class counting_iterator { -- private: -- size_t count_; -- -- public: -- using iterator_category = std::output_iterator_tag; -- using difference_type = std::ptrdiff_t; -- using pointer = void; -- using reference = void; -- using _Unchecked_type = counting_iterator; // Mark iterator as checked. -- -- struct value_type { -- template void operator=(const T&) {} -- }; -- -- counting_iterator() : count_(0) {} -- -- size_t count() const { return count_; } -- -- counting_iterator& operator++() { -- ++count_; -- return *this; -- } -- -- counting_iterator operator++(int) { -- auto it = *this; -- ++*this; -- return it; -- } -- -- 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; -+using reserve_iterator = -+ remove_reference_t(), 0))>; - -- truncating_iterator(OutputIt out, size_t limit) -- : truncating_iterator_base(out, limit) {} -+template -+constexpr auto to_pointer(OutputIt, size_t) -> T* { -+ return nullptr; -+} -+template auto to_pointer(buffer_appender it, size_t n) -> T* { -+ buffer& buf = get_container(it); -+ auto size = buf.size(); -+ if (buf.capacity() < size + n) return nullptr; -+ buf.try_resize(size + n); -+ return buf.data() + size; -+} - -- truncating_iterator& operator++() { -- if (this->count_++ < this->limit_) ++this->out_; -- return *this; -- } -+template ::value)> -+inline auto base_iterator(std::back_insert_iterator& it, -+ checked_ptr) -+ -> std::back_insert_iterator { -+ return it; -+} - -- truncating_iterator operator++(int) { -- auto it = *this; -- ++*this; -- return it; -- } -+template -+constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { -+ return it; -+} - -- value_type& operator*() const { -- return this->count_ < this->limit_ ? *this->out_ : blackhole_; -+// is spectacularly slow to compile in C++20 so use a simple fill_n -+// instead (#1998). -+template -+FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) -+ -> OutputIt { -+ for (Size i = 0; i < count; ++i) *out++ = value; -+ return out; -+} -+template -+FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { -+ if (is_constant_evaluated()) { -+ return fill_n(out, count, value); - } --}; -+ std::memset(out, value, to_unsigned(count)); -+ return out + count; -+} - --template --class truncating_iterator -- : public truncating_iterator_base { -- public: -- truncating_iterator(OutputIt out, size_t limit) -- : truncating_iterator_base(out, limit) {} -+#ifdef __cpp_char8_t -+using char8_type = char8_t; -+#else -+enum char8_type : unsigned char {}; -+#endif - -- template truncating_iterator& operator=(T val) { -- if (this->count_++ < this->limit_) *this->out_++ = val; -- return *this; -+template -+FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, -+ OutputIt out) -> OutputIt { -+ return copy_str(begin, end, out); -+} -+ -+// 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 auto utf8_decode(const char* s, uint32_t* c, int* e) -+ -> const char* { -+ constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; -+ constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; -+ constexpr const int shiftc[] = {0, 18, 12, 6, 0}; -+ constexpr const int shifte[] = {0, 6, 4, 2, 0}; -+ -+ 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; -+} -+ -+constexpr uint32_t invalid_code_point = ~uint32_t(); -+ -+// Invokes f(cp, sv) for every code point cp in s with sv being the string view -+// corresponding to the code point. cp is invalid_code_point on error. -+template -+FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { -+ auto decode = [f](const char* buf_ptr, const char* ptr) { -+ auto cp = uint32_t(); -+ auto error = 0; -+ auto end = utf8_decode(buf_ptr, &cp, &error); -+ bool result = f(error ? invalid_code_point : cp, -+ string_view(ptr, to_unsigned(end - buf_ptr))); -+ return result ? end : nullptr; -+ }; -+ auto p = s.data(); -+ const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. -+ if (s.size() >= block_size) { -+ for (auto end = p + s.size() - block_size + 1; p < end;) { -+ p = decode(p, p); -+ if (!p) return; -+ } - } -- -- truncating_iterator& operator++() { return *this; } -- truncating_iterator& operator++(int) { return *this; } -- truncating_iterator& operator*() { return *this; } --}; -+ if (auto num_chars_left = s.data() + s.size() - p) { -+ char buf[2 * block_size - 1] = {}; -+ copy_str(p, p + num_chars_left, buf); -+ const char* buf_ptr = buf; -+ do { -+ auto end = decode(buf_ptr, p); -+ if (!end) return; -+ p += end - buf_ptr; -+ buf_ptr = end; -+ } while (buf_ptr - buf < num_chars_left); -+ } -+} - - template --inline size_t count_code_points(basic_string_view s) { -+inline auto compute_width(basic_string_view s) -> size_t { - 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 auto operator()(uint32_t cp, string_view) const -> bool { -+ *count += detail::to_unsigned( -+ 1 + -+ (cp >= 0x1100 && -+ (cp <= 0x115f || // Hangul Jamo init. consonants -+ cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET -+ cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET -+ // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE: -+ (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)))); -+ return true; -+ } -+ }; -+ 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 auto compute_width(basic_string_view s) -> size_t { -+ return compute_width(basic_string_view( - reinterpret_cast(s.data()), s.size())); - } - - template --inline size_t code_point_index(basic_string_view s, size_t n) { -+inline auto code_point_index(basic_string_view s, size_t n) -> size_t { - size_t size = s.size(); - return n < size ? n : size; - } - - // Calculates the index of the nth code point in a UTF-8 string. --inline size_t code_point_index(basic_string_view s, size_t n) { -+inline auto code_point_index(basic_string_view s, size_t n) -+ -> size_t { - 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 ::value> -+struct is_fast_float : bool_constant::is_iec559 && -+ sizeof(T) <= sizeof(double)> {}; -+template struct is_fast_float : std::false_type {}; - --#ifndef FMT_USE_GRISU --# define FMT_USE_GRISU 1 -+#ifndef FMT_USE_FULL_CACHE_DRAGONBOX -+# define FMT_USE_FULL_CACHE_DRAGONBOX 0 - #endif - --template constexpr bool use_grisu() { -- return FMT_USE_GRISU && std::numeric_limits::is_iec559 && -- sizeof(T) <= sizeof(double); --} -- - template - template - void buffer::append(const U* begin, const U* end) { -- size_t new_size = size_ + to_unsigned(end - begin); -- reserve(new_size); -- std::uninitialized_copy(begin, end, -- make_checked(ptr_ + size_, capacity_ - size_)); -- size_ = new_size; -+ while (begin != end) { -+ auto count = to_unsigned(end - begin); -+ try_reserve(size_ + count); -+ auto free_cap = capacity_ - size_; -+ if (free_cap < count) count = free_cap; -+ std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); -+ size_ += count; -+ begin += count; -+ } - } -+ -+template -+struct is_locale : std::false_type {}; -+template -+struct is_locale> : std::true_type {}; - } // namespace detail - -+FMT_MODULE_EXPORT_BEGIN -+ - // The number of characters to store in the basic_memory_buffer object itself - // to avoid dynamic memory allocation. - enum { inline_buffer_size = 500 }; -@@ -584,20 +656,12 @@ enum { inline_buffer_size = 500 }; - A dynamically growing memory buffer for trivially copyable/constructible types - with the first ``SIZE`` elements stored in the object itself. - -- You can use one of the following type aliases for common character types: -- -- +----------------+------------------------------+ -- | Type | Definition | -- +================+==============================+ -- | memory_buffer | basic_memory_buffer | -- +----------------+------------------------------+ -- | wmemory_buffer | basic_memory_buffer | -- +----------------+------------------------------+ -+ You can use the ``memory_buffer`` type alias for ``char`` instead. - - **Example**:: - -- fmt::memory_buffer out; -- format_to(out, "The answer is {}.", 42); -+ auto out = fmt::memory_buffer(); -+ format_to(std::back_inserter(out), "The answer is {}.", 42); - - This will append the following output to the ``out`` object: - -@@ -610,7 +674,7 @@ enum { inline_buffer_size = 500 }; - */ - template > --class basic_memory_buffer : public detail::buffer { -+class basic_memory_buffer final : public detail::buffer { - private: - T store_[SIZE]; - -@@ -618,34 +682,43 @@ class basic_memory_buffer : public detail::buffer { - Allocator alloc_; - - // Deallocate memory allocated by the buffer. -- void deallocate() { -+ FMT_CONSTEXPR20 void deallocate() { - T* data = this->data(); - if (data != store_) alloc_.deallocate(data, this->capacity()); - } - - protected: -- void grow(size_t size) FMT_OVERRIDE; -+ FMT_CONSTEXPR20 void grow(size_t size) override; - - public: - using value_type = T; - using const_reference = const T&; - -- explicit basic_memory_buffer(const Allocator& alloc = Allocator()) -+ FMT_CONSTEXPR20 explicit basic_memory_buffer( -+ const Allocator& alloc = Allocator()) - : alloc_(alloc) { - this->set(store_, SIZE); -+ if (detail::is_constant_evaluated()) { -+ detail::fill_n(store_, SIZE, T{}); -+ } - } -- ~basic_memory_buffer() FMT_OVERRIDE { deallocate(); } -+ FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } - - private: - // Move data from other to this buffer. -- void move(basic_memory_buffer& other) { -+ FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { - alloc_ = std::move(other.alloc_); - T* data = other.data(); - size_t size = other.size(), capacity = other.capacity(); - if (data == other.store_) { - this->set(store_, capacity); -- std::uninitialized_copy(other.store_, other.store_ + size, -- detail::make_checked(store_, capacity)); -+ if (detail::is_constant_evaluated()) { -+ detail::copy_str(other.store_, other.store_ + size, -+ detail::make_checked(store_, capacity)); -+ } else { -+ std::uninitialized_copy(other.store_, other.store_ + size, -+ detail::make_checked(store_, capacity)); -+ } - } else { - this->set(data, capacity); - // Set pointer to the inline array so that delete is not called -@@ -662,14 +735,18 @@ class basic_memory_buffer : public detail::buffer { - of the other object to it. - \endrst - */ -- basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); } -+ FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) -+ FMT_NOEXCEPT { -+ move(other); -+ } - - /** - \rst - Moves the content of the other ``basic_memory_buffer`` object to this one. - \endrst - */ -- basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT { -+ auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT -+ -> basic_memory_buffer& { - FMT_ASSERT(this != &other, ""); - deallocate(); - move(other); -@@ -677,17 +754,38 @@ class basic_memory_buffer : public detail::buffer { - } - - // Returns a copy of the allocator associated with this buffer. -- Allocator get_allocator() const { return alloc_; } -+ auto get_allocator() const -> Allocator { return alloc_; } -+ -+ /** -+ Resizes the buffer to contain *count* elements. If T is a POD type new -+ elements may not be initialized. -+ */ -+ FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } -+ -+ /** Increases the buffer capacity to *new_capacity*. */ -+ void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } -+ -+ // Directly append data into the buffer -+ using detail::buffer::append; -+ template -+ void append(const ContiguousRange& range) { -+ append(range.data(), range.data() + range.size()); -+ } - }; - - template --void basic_memory_buffer::grow(size_t size) { -+FMT_CONSTEXPR20 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); -@@ -702,12 +800,15 @@ void basic_memory_buffer::grow(size_t size) { - } - - using memory_buffer = basic_memory_buffer; --using wmemory_buffer = basic_memory_buffer; - - template - struct is_contiguous> : std::true_type { - }; - -+namespace detail { -+FMT_API void print(std::FILE*, string_view); -+} -+ - /** A formatting error such as invalid format string. */ - FMT_CLASS_API - class FMT_API format_error : public std::runtime_error { -@@ -719,10 +820,62 @@ class FMT_API format_error : public std::runtime_error { - format_error& operator=(const format_error&) = default; - format_error(format_error&&) = default; - format_error& operator=(format_error&&) = default; -- ~format_error() FMT_NOEXCEPT FMT_OVERRIDE; -+ ~format_error() FMT_NOEXCEPT override FMT_MSC_DEFAULT; - }; - --namespace detail { -+/** -+ \rst -+ Constructs a `~fmt::format_arg_store` object that contains references -+ to arguments and can be implicitly converted to `~fmt::format_args`. -+ If ``fmt`` is a compile-time string then `make_args_checked` checks -+ its validity at compile time. -+ \endrst -+ */ -+template > -+FMT_INLINE auto make_args_checked(const S& fmt, -+ const remove_reference_t&... args) -+ -> format_arg_store, remove_reference_t...> { -+ static_assert( -+ detail::count<( -+ std::is_base_of>::value && -+ std::is_reference::value)...>() == 0, -+ "passing views as lvalues is disallowed"); -+ detail::check_format_string(fmt); -+ return {args...}; -+} -+ -+// compile-time support -+namespace detail_exported { -+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -+template struct fixed_string { -+ constexpr fixed_string(const Char (&str)[N]) { -+ detail::copy_str(static_cast(str), -+ str + N, data); -+ } -+ Char data[N]{}; -+}; -+#endif -+ -+// Converts a compile-time string to basic_string_view. -+template -+constexpr auto compile_string_to_view(const Char (&s)[N]) -+ -> basic_string_view { -+ // Remove trailing NUL character if needed. Won't be present if this is used -+ // with a raw character array (i.e. not defined as a string). -+ return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; -+} -+template -+constexpr auto compile_string_to_view(detail::std_string_view s) -+ -> basic_string_view { -+ return {s.data(), s.size()}; -+} -+} // namespace detail_exported -+ -+FMT_BEGIN_DETAIL_NAMESPACE -+ -+template struct is_integral : std::is_integral {}; -+template <> struct is_integral : std::true_type {}; -+template <> struct is_integral : std::true_type {}; - - template - using is_signed = -@@ -732,67 +885,55 @@ using is_signed = - // Returns true if value is negative, false otherwise. - // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. - template ::value)> --FMT_CONSTEXPR bool is_negative(T value) { -+FMT_CONSTEXPR auto is_negative(T value) -> bool { - return value < 0; - } - template ::value)> --FMT_CONSTEXPR bool is_negative(T) { -+FMT_CONSTEXPR auto is_negative(T) -> bool { - return false; - } - - template ::value)> --FMT_CONSTEXPR bool is_supported_floating_point(T) { -+FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t { - return (std::is_same::value && FMT_USE_FLOAT) || - (std::is_same::value && FMT_USE_DOUBLE) || - (std::is_same::value && FMT_USE_LONG_DOUBLE); - } - - // Smallest of uint32_t, uint64_t, uint128_t that is large enough to --// represent all values of T. -+// represent all values of an integral type T. - template - using uint32_or_64_or_128_t = -- conditional_t() <= 32, uint32_t, -+ conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, -+ uint32_t, - conditional_t() <= 64, uint64_t, uint128_t>>; -- --// Static data is placed in this class template for the header-only config. --template struct FMT_EXTERN_TEMPLATE_API basic_data { -- static const uint64_t powers_of_10_64[]; -- static const uint32_t zero_or_powers_of_10_32[]; -- static const uint64_t zero_or_powers_of_10_64[]; -- static const uint64_t pow10_significands[]; -- static const int16_t pow10_exponents[]; -- // 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 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]; --}; -- --#ifndef FMT_EXPORTED --FMT_EXTERN template struct basic_data; -+template -+using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; -+ -+#define FMT_POWERS_OF_10(factor) \ -+ factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ -+ (factor)*1000000, (factor)*10000000, (factor)*100000000, \ -+ (factor)*1000000000 -+ -+// Converts value in the range [0, 100) to a string. -+constexpr const char* digits2(size_t value) { -+ // GCC generates slightly better code when value is pointer-size. -+ return &"0001020304050607080910111213141516171819" -+ "2021222324252627282930313233343536373839" -+ "4041424344454647484950515253545556575859" -+ "6061626364656667686970717273747576777879" -+ "8081828384858687888990919293949596979899"[value * 2]; -+} -+ -+// Sign is a template parameter to workaround a bug in gcc 4.8. -+template constexpr Char sign(Sign s) { -+#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 -+ static_assert(std::is_same::value, ""); - #endif -- --// 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) { -- // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 -- // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. -- int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; -- return t - (n < data::zero_or_powers_of_10_64[t]) + 1; -+ return static_cast("\0-+ "[s]); - } --#else --// Fallback version of count_digits used when __builtin_clz is not available. --inline int count_digits(uint64_t n) { -+ -+template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { - int count = 1; - for (;;) { - // Integer division is slow so do it for a group of four digits instead -@@ -806,94 +947,152 @@ inline int count_digits(uint64_t n) { - count += 4; - } - } -+#if FMT_USE_INT128 -+FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int { -+ return count_digits_fallback(n); -+} - #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; -- } -+#ifdef FMT_BUILTIN_CLZLL -+// It is a separate function rather than a part of count_digits to workaround -+// the lack of static constexpr in constexpr functions. -+inline auto do_count_digits(uint64_t n) -> int { -+ // This has comparable performance to the version by Kendall Willets -+ // (https://github.com/fmtlib/format-benchmark/blob/master/digits10) -+ // but uses smaller tables. -+ // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). -+ static constexpr uint8_t bsr2log10[] = { -+ 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, -+ 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, -+ 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, -+ 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; -+ auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; -+ static constexpr const uint64_t zero_or_powers_of_10[] = { -+ 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), -+ 10000000000000000000ULL}; -+ return t - (n < zero_or_powers_of_10[t]); - } - #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 auto count_digits(uint64_t n) -> int { -+#ifdef FMT_BUILTIN_CLZLL -+ if (!is_constant_evaluated()) { -+ return do_count_digits(n); -+ } -+#endif -+ return count_digits_fallback(n); -+} -+ - // Counts the number of digits in n. BITS = log2(radix). --template inline int count_digits(UInt n) { -- int num_digits = 0; -- do { -- ++num_digits; -- } while ((n >>= BITS) != 0); -- return num_digits; -+template -+FMT_CONSTEXPR auto count_digits(UInt n) -> int { -+#ifdef FMT_BUILTIN_CLZ -+ if (num_bits() == 32) -+ return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; -+#endif -+ // Lambda avoids unreachable code warnings from NVHPC. -+ return [](UInt m) { -+ int num_digits = 0; -+ do { -+ ++num_digits; -+ } while ((m >>= BITS) != 0); -+ return num_digits; -+ }(n); - } - --template <> int count_digits<4>(detail::fallback_uintptr n); -+template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; - --#if FMT_GCC_VERSION || FMT_CLANG_VERSION --# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) --#else --# define FMT_ALWAYS_INLINE -+#ifdef FMT_BUILTIN_CLZ -+// It is a separate function rather than a part of count_digits to workaround -+// the lack of static constexpr in constexpr functions. -+FMT_INLINE auto do_count_digits(uint32_t n) -> int { -+// An optimization by Kendall Willets from https://bit.ly/3uOIQrB. -+// This increments the upper 32 bits (log10(T) - 1) when >= T is added. -+# define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T) -+ static constexpr uint64_t table[] = { -+ FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 -+ FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 -+ FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512 -+ FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096 -+ FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k -+ FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k -+ FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k -+ FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M -+ FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M -+ FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M -+ FMT_INC(1000000000), FMT_INC(1000000000) // 4B -+ }; -+ auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31]; -+ return static_cast((n + inc) >> 32); -+} - #endif - --#ifdef FMT_BUILTIN_CLZ - // Optional version of count_digits for better performance on 32-bit platforms. --inline int count_digits(uint32_t n) { -- int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; -- return t - (n < data::zero_or_powers_of_10_32[t]) + 1; --} -+FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { -+#ifdef FMT_BUILTIN_CLZ -+ if (!is_constant_evaluated()) { -+ return do_count_digits(n); -+ } - #endif -+ return count_digits_fallback(n); -+} - --template constexpr int digits10() FMT_NOEXCEPT { -+template constexpr auto digits10() FMT_NOEXCEPT -> int { - return std::numeric_limits::digits10; - } --template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } --template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } -- --template FMT_API std::string grouping_impl(locale_ref loc); --template inline std::string grouping(locale_ref loc) { -- return grouping_impl(loc); -+template <> constexpr auto digits10() FMT_NOEXCEPT -> int { -+ return 38; - } --template <> inline std::string grouping(locale_ref loc) { -- return grouping_impl(loc); -+template <> constexpr auto digits10() FMT_NOEXCEPT -> int { -+ return 38; - } - --template FMT_API Char thousands_sep_impl(locale_ref loc); --template inline Char thousands_sep(locale_ref loc) { -- return Char(thousands_sep_impl(loc)); -+template struct thousands_sep_result { -+ std::string grouping; -+ Char thousands_sep; -+}; -+ -+template -+FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; -+template -+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { -+ auto result = thousands_sep_impl(loc); -+ return {result.grouping, Char(result.thousands_sep)}; - } --template <> inline wchar_t thousands_sep(locale_ref loc) { -+template <> -+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { - return thousands_sep_impl(loc); - } - --template FMT_API Char decimal_point_impl(locale_ref loc); --template inline Char decimal_point(locale_ref loc) { -+template -+FMT_API auto decimal_point_impl(locale_ref loc) -> Char; -+template inline auto decimal_point(locale_ref loc) -> Char { - return Char(decimal_point_impl(loc)); - } --template <> inline wchar_t decimal_point(locale_ref loc) { -+template <> inline auto decimal_point(locale_ref loc) -> wchar_t { - return decimal_point_impl(loc); - } - - // Compares two characters for equality. --template bool equal2(const Char* lhs, const char* rhs) { -- return lhs[0] == rhs[0] && lhs[1] == rhs[1]; -+template auto equal2(const Char* lhs, const char* rhs) -> bool { -+ return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); - } --inline bool equal2(const char* lhs, const char* rhs) { -+inline auto equal2(const char* lhs, const char* rhs) -> bool { - return memcmp(lhs, rhs, 2) == 0; - } - - // Copies two characters from src to dst. --template void copy2(Char* dst, const char* src) { -+template -+FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { -+ if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { -+ memcpy(dst, src, 2); -+ return; -+ } - *dst++ = static_cast(*src++); - *dst = static_cast(*src); - } --inline void copy2(char* dst, const char* src) { memcpy(dst, src, 2); } - - template struct format_decimal_result { - Iterator begin; -@@ -904,8 +1103,8 @@ 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 auto format_decimal(Char* out, UInt value, int size) -+ -> format_decimal_result { - FMT_ASSERT(size >= count_digits(value), "invalid digit count"); - out += size; - Char* end = out; -@@ -914,7 +1113,7 @@ inline format_decimal_result format_decimal(Char* out, UInt value, - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - out -= 2; -- copy2(out, data::digits[value % 100]); -+ copy2(out, digits2(static_cast(value % 100))); - value /= 100; - } - if (value < 10) { -@@ -922,28 +1121,27 @@ inline format_decimal_result format_decimal(Char* out, UInt value, - return {out, end}; - } - out -= 2; -- copy2(out, data::digits[value]); -+ copy2(out, digits2(static_cast(value))); - return {out, end}; - } - - template >::value)> --inline format_decimal_result format_decimal(Iterator out, UInt value, -- int num_digits) { -- // Buffer should be large enough to hold all digits (<= digits10 + 1). -- enum { max_size = digits10() + 1 }; -- Char buffer[2 * max_size]; -- auto end = format_decimal(buffer, value, num_digits).end; -- return {out, detail::copy_str(buffer, end, out)}; -+inline auto format_decimal(Iterator out, UInt value, int size) -+ -> format_decimal_result { -+ // Buffer is large enough to hold all digits (digits10 + 1). -+ Char buffer[digits10() + 1]; -+ auto end = format_decimal(buffer, value, size).end; -+ return {out, detail::copy_str_noinline(buffer, end, out)}; - } - - template --inline Char* format_uint(Char* buffer, UInt value, int num_digits, -- bool upper = false) { -+FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, -+ bool upper = false) -> Char* { - buffer += num_digits; - Char* end = buffer; - do { -- const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; -+ const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = (value & ((1 << BASE_BITS) - 1)); - *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) - : digits[digit]); -@@ -952,8 +1150,8 @@ inline Char* format_uint(Char* buffer, UInt value, int num_digits, - } - - template --Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, -- bool = false) { -+auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, -+ bool = false) -> Char* { - auto char_digits = std::numeric_limits::digits / 4; - int start = (num_digits + char_digits - 1) / char_digits - 1; - if (int start_digits = num_digits % char_digits) { -@@ -966,7 +1164,7 @@ Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, - auto p = buffer; - for (int i = 0; i < char_digits; ++i) { - unsigned digit = (value & ((1 << BASE_BITS) - 1)); -- *--p = static_cast(data::hex_digits[digit]); -+ *--p = static_cast("0123456789abcdef"[digit]); - value >>= BASE_BITS; - } - } -@@ -974,120 +1172,109 @@ Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, - } - - template --inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { -+inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) -+ -> It { -+ if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { -+ format_uint(ptr, value, num_digits, upper); -+ return out; -+ } - // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). - char buffer[num_bits() / BASE_BITS + 1]; - format_uint(buffer, value, num_digits, upper); -- return detail::copy_str(buffer, buffer + num_digits, out); -+ return detail::copy_str_noinline(buffer, buffer + num_digits, out); - } - - // A converter from UTF-8 to UTF-16. - class utf8_to_utf16 { - private: -- wmemory_buffer buffer_; -+ basic_memory_buffer buffer_; - - public: - FMT_API explicit utf8_to_utf16(string_view s); -- operator wstring_view() const { return {&buffer_[0], size()}; } -- size_t size() const { return buffer_.size() - 1; } -- const wchar_t* c_str() const { return &buffer_[0]; } -- std::wstring str() const { return {&buffer_[0], size()}; } -+ operator basic_string_view() const { return {&buffer_[0], size()}; } -+ auto size() const -> size_t { return buffer_.size() - 1; } -+ auto c_str() const -> const wchar_t* { return &buffer_[0]; } -+ auto str() const -> std::wstring { return {&buffer_[0], size()}; } - }; - --template struct null {}; -+namespace dragonbox { -+ -+// Type-specific information that Dragonbox uses. -+template struct float_info; -+ -+template <> struct float_info { -+ using carrier_uint = uint32_t; -+ static const int significand_bits = 23; -+ static const int exponent_bits = 8; -+ static const int min_exponent = -126; -+ static const int max_exponent = 127; -+ static const int exponent_bias = -127; -+ static const int decimal_digits = 9; -+ static const int kappa = 1; -+ static const int big_divisor = 100; -+ static const int small_divisor = 10; -+ static const int min_k = -31; -+ static const int max_k = 46; -+ static const int cache_bits = 64; -+ static const int divisibility_check_by_5_threshold = 39; -+ static const int case_fc_pm_half_lower_threshold = -1; -+ static const int case_fc_pm_half_upper_threshold = 6; -+ static const int case_fc_lower_threshold = -2; -+ static const int case_fc_upper_threshold = 6; -+ static const int case_shorter_interval_left_endpoint_lower_threshold = 2; -+ static const int case_shorter_interval_left_endpoint_upper_threshold = 3; -+ static const int shorter_interval_tie_lower_threshold = -35; -+ static const int shorter_interval_tie_upper_threshold = -35; -+ static const int max_trailing_zeros = 7; -+}; - --// Workaround an array initialization issue in gcc 4.8. --template struct fill_t { -- private: -- enum { max_size = 4 }; -- Char data_[max_size]; -- unsigned char size_; -- -- public: -- FMT_CONSTEXPR void operator=(basic_string_view s) { -- auto size = s.size(); -- if (size > max_size) { -- FMT_THROW(format_error("invalid fill")); -- return; -- } -- for (size_t i = 0; i < size; ++i) data_[i] = s[i]; -- size_ = static_cast(size); -- } -- -- size_t size() const { return size_; } -- 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 { -- return data_[index]; -- } -- -- static FMT_CONSTEXPR fill_t make() { -- auto fill = fill_t(); -- fill[0] = Char(' '); -- fill.size_ = 1; -- return fill; -- } -+template <> struct float_info { -+ using carrier_uint = uint64_t; -+ static const int significand_bits = 52; -+ static const int exponent_bits = 11; -+ static const int min_exponent = -1022; -+ static const int max_exponent = 1023; -+ static const int exponent_bias = -1023; -+ static const int decimal_digits = 17; -+ static const int kappa = 2; -+ static const int big_divisor = 1000; -+ static const int small_divisor = 100; -+ static const int min_k = -292; -+ static const int max_k = 326; -+ static const int cache_bits = 128; -+ static const int divisibility_check_by_5_threshold = 86; -+ static const int case_fc_pm_half_lower_threshold = -2; -+ static const int case_fc_pm_half_upper_threshold = 9; -+ static const int case_fc_lower_threshold = -4; -+ static const int case_fc_upper_threshold = 9; -+ static const int case_shorter_interval_left_endpoint_lower_threshold = 2; -+ static const int case_shorter_interval_left_endpoint_upper_threshold = 3; -+ static const int shorter_interval_tie_lower_threshold = -77; -+ static const int shorter_interval_tie_upper_threshold = -77; -+ static const int max_trailing_zeros = 16; - }; --} // namespace detail - --// We cannot use enum classes as bit fields because of a gcc bug --// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. --namespace align { --enum type { none, left, right, center, numeric }; --} --using align_t = align::type; -- --namespace sign { --enum type { none, minus, plus, space }; --} --using sign_t = sign::type; -- --// Format specifiers for built-in and string types. --template struct basic_format_specs { -- int width; -- int precision; -- char type; -- align_t align : 4; -- sign_t sign : 3; -- bool alt : 1; // Alternate form ('#'). -- detail::fill_t fill; -- -- constexpr basic_format_specs() -- : width(0), -- precision(-1), -- type(0), -- align(align::none), -- sign(sign::none), -- alt(false), -- fill(detail::fill_t::make()) {} -+template struct decimal_fp { -+ using significand_type = typename float_info::carrier_uint; -+ significand_type significand; -+ int exponent; - }; - --using format_specs = basic_format_specs; -- --namespace detail { -- --// A floating-point presentation format. --enum class float_format : unsigned char { -- general, // General: exponent notation or fixed point based on magnitude. -- exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. -- fixed, // Fixed point with the default precision of 6, e.g. 0.0012. -- hex --}; -+template -+FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp; -+} // namespace dragonbox - --struct float_specs { -- int precision; -- float_format format : 8; -- sign_t sign : 8; -- bool upper : 1; -- bool locale : 1; -- bool binary32 : 1; -- bool use_grisu : 1; -- bool showpoint : 1; --}; -+template -+constexpr auto exponent_mask() -> -+ typename dragonbox::float_info::carrier_uint { -+ using uint = typename dragonbox::float_info::carrier_uint; -+ return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) -+ << dragonbox::float_info::significand_bits; -+} - - // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. --template It write_exponent(int exp, It it) { -+template -+FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { - FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); - if (exp < 0) { - *it++ = static_cast('-'); -@@ -1096,292 +1283,42 @@ template It write_exponent(int exp, It it) { - *it++ = static_cast('+'); - } - if (exp >= 100) { -- const char* top = data::digits[exp / 100]; -+ const char* top = digits2(to_unsigned(exp / 100)); - if (exp >= 1000) *it++ = static_cast(top[0]); - *it++ = static_cast(top[1]); - exp %= 100; - } -- const char* d = data::digits[exp]; -+ const char* d = digits2(to_unsigned(exp)); - *it++ = static_cast(d[0]); - *it++ = static_cast(d[1]); - return it; - } - --template class float_writer { -- private: -- // The number is given as v = digits_ * pow(10, exp_). -- const char* digits_; -- int num_digits_; -- int exp_; -- size_t size_; -- float_specs specs_; -- Char decimal_point_; -- -- template It prettify(It it) const { -- // pow(10, full_exp - 1) <= v <= pow(10, full_exp). -- int full_exp = num_digits_ + exp_; -- if (specs_.format == float_format::exp) { -- // Insert a decimal point after the first digit and add an exponent. -- *it++ = static_cast(*digits_); -- int num_zeros = specs_.precision - num_digits_; -- if (num_digits_ > 1 || specs_.showpoint) *it++ = decimal_point_; -- it = copy_str(digits_ + 1, digits_ + num_digits_, it); -- if (num_zeros > 0 && specs_.showpoint) -- it = std::fill_n(it, num_zeros, static_cast('0')); -- *it++ = static_cast(specs_.upper ? 'E' : 'e'); -- return write_exponent(full_exp - 1, it); -- } -- if (num_digits_ <= full_exp) { -- // 1234e7 -> 12340000000[.0+] -- it = copy_str(digits_, digits_ + num_digits_, it); -- it = std::fill_n(it, full_exp - num_digits_, static_cast('0')); -- if (specs_.showpoint || specs_.precision < 0) { -- *it++ = decimal_point_; -- int num_zeros = specs_.precision - full_exp; -- if (num_zeros <= 0) { -- if (specs_.format != float_format::fixed) -- *it++ = static_cast('0'); -- return it; -- } --#ifdef FMT_FUZZ -- if (num_zeros > 5000) -- throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); --#endif -- it = std::fill_n(it, num_zeros, static_cast('0')); -- } -- } else if (full_exp > 0) { -- // 1234e-2 -> 12.34[0+] -- it = copy_str(digits_, digits_ + full_exp, it); -- if (!specs_.showpoint) { -- // Remove trailing zeros. -- int num_digits = num_digits_; -- while (num_digits > full_exp && digits_[num_digits - 1] == '0') -- --num_digits; -- if (num_digits != full_exp) *it++ = decimal_point_; -- return copy_str(digits_ + full_exp, digits_ + num_digits, it); -- } -- *it++ = decimal_point_; -- it = copy_str(digits_ + full_exp, digits_ + num_digits_, it); -- if (specs_.precision > num_digits_) { -- // Add trailing zeros. -- int num_zeros = specs_.precision - num_digits_; -- it = std::fill_n(it, num_zeros, static_cast('0')); -- } -- } else { -- // 1234e-6 -> 0.001234 -- *it++ = static_cast('0'); -- int num_zeros = -full_exp; -- int num_digits = num_digits_; -- if (num_digits == 0 && specs_.precision >= 0 && -- specs_.precision < num_zeros) { -- num_zeros = specs_.precision; -- } -- // Remove trailing zeros. -- if (!specs_.showpoint) -- while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits; -- if (num_zeros != 0 || num_digits != 0 || specs_.showpoint) { -- *it++ = decimal_point_; -- it = std::fill_n(it, num_zeros, static_cast('0')); -- it = copy_str(digits_, digits_ + num_digits, it); -- } -- } -- return it; -- } -- -- public: -- float_writer(const char* digits, int num_digits, int exp, float_specs specs, -- Char decimal_point) -- : digits_(digits), -- num_digits_(num_digits), -- exp_(exp), -- specs_(specs), -- decimal_point_(decimal_point) { -- int full_exp = num_digits + exp - 1; -- int precision = specs.precision > 0 ? specs.precision : 16; -- if (specs_.format == float_format::general && -- !(full_exp >= -4 && full_exp < precision)) { -- specs_.format = float_format::exp; -- } -- size_ = prettify(counting_iterator()).count(); -- size_ += specs.sign ? 1 : 0; -- } -- -- size_t size() const { return size_; } -- -- template It operator()(It it) const { -- if (specs_.sign) *it++ = static_cast(data::signs[specs_.sign]); -- return prettify(it); -- } --}; -- - template --int format_float(T value, int precision, float_specs specs, buffer& buf); -+FMT_HEADER_ONLY_CONSTEXPR20 auto format_float(T value, int precision, -+ float_specs specs, -+ buffer& buf) -> int; - - // Formats a floating-point number with snprintf. - template --int snprintf_float(T value, int precision, float_specs specs, -- buffer& buf); -- --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; -- switch (specs.type) { -- case 0: -- result.format = float_format::general; -- result.showpoint |= specs.precision > 0; -- break; -- case 'G': -- result.upper = true; -- FMT_FALLTHROUGH; -- case 'g': -- result.format = float_format::general; -- break; -- case 'E': -- result.upper = true; -- FMT_FALLTHROUGH; -- case 'e': -- result.format = float_format::exp; -- result.showpoint |= specs.precision != 0; -- break; -- case 'F': -- result.upper = true; -- FMT_FALLTHROUGH; -- case 'f': -- result.format = float_format::fixed; -- result.showpoint |= specs.precision != 0; -- break; -- case 'A': -- result.upper = true; -- FMT_FALLTHROUGH; -- case 'a': -- result.format = float_format::hex; -- break; --#ifdef FMT_DEPRECATED_N_SPECIFIER -- case 'n': --#endif -- case 'L': -- result.locale = true; -- break; -- default: -- eh.on_error("invalid type specifier"); -- break; -- } -- return result; --} -- --template --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) -- handler.on_error("invalid format specifier for char"); -- handler.on_char(); --} -- --template --FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) { -- if (spec == 0 || spec == 's') -- handler.on_string(); -- else if (spec == 'p') -- handler.on_pointer(); -- else -- handler.on_error("invalid type specifier"); --} -+auto snprintf_float(T value, int precision, float_specs specs, -+ buffer& buf) -> int; - --template --FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) { -- if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); -+template constexpr auto promote_float(T value) -> T { -+ return value; - } -- --template --FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { -- if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); -+constexpr auto promote_float(float value) -> double { -+ return static_cast(value); - } - --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: -- char type_; -- -- public: -- 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_char() {} --}; -- --template --class cstring_type_checker : public ErrorHandler { -- public: -- FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) -- : ErrorHandler(eh) {} -- -- FMT_CONSTEXPR void on_string() {} -- FMT_CONSTEXPR void on_pointer() {} --}; -- - template --FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { -+FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, -+ const fill_t& fill) -> OutputIt { - auto fill_size = fill.size(); -- if (fill_size == 1) return 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; - } - -@@ -1390,39 +1327,73 @@ 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, const F& f) { -+FMT_CONSTEXPR auto write_padded(OutputIt out, -+ const basic_format_specs& specs, -+ size_t size, size_t width, F&& f) -> OutputIt { - 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; -+ // Shifts are encoded as string literals because static constexpr is not -+ // supported in constexpr functions. -+ auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; - size_t left_padding = padding >> shifts[specs.align]; -+ 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, -- const F& f) { -+constexpr auto write_padded(OutputIt out, const basic_format_specs& specs, -+ size_t size, F&& f) -> OutputIt { - return write_padded(out, specs, size, size, f); - } - -+template -+FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, -+ const basic_format_specs& specs) -+ -> OutputIt { -+ return write_padded( -+ out, specs, bytes.size(), [bytes](reserve_iterator it) { -+ const char* data = bytes.data(); -+ return copy_str(data, data + bytes.size(), it); -+ }); -+} -+ -+template -+auto write_ptr(OutputIt out, UIntPtr value, -+ const basic_format_specs* specs) -> OutputIt { -+ int num_digits = count_digits<4>(value); -+ auto size = to_unsigned(num_digits) + size_t(2); -+ auto write = [=](reserve_iterator it) { -+ *it++ = static_cast('0'); -+ *it++ = static_cast('x'); -+ return format_uint<4, Char>(it, value, num_digits); -+ }; -+ return specs ? write_padded(out, *specs, size, write) -+ : base_iterator(out, write(reserve(out, size))); -+} -+ - 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); -+FMT_CONSTEXPR auto write_char(OutputIt out, Char value, -+ const basic_format_specs& specs) -+ -> OutputIt { -+ return write_padded(out, specs, 1, [=](reserve_iterator it) { -+ *it++ = value; -+ return it; - }); - } -+template -+FMT_CONSTEXPR auto write(OutputIt out, Char value, -+ const basic_format_specs& specs, -+ locale_ref loc = {}) -> OutputIt { -+ return check_char_specs(specs) -+ ? write_char(out, value, specs) -+ : write(out, static_cast(value), specs, loc); -+} - - // Data for write_int that doesn't depend on output iterator type. It is used to - // avoid template code bloat. -@@ -1430,9 +1401,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) { -@@ -1440,7 +1411,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); - } - } -@@ -1448,206 +1419,587 @@ 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 auto write_int(OutputIt out, int num_digits, -+ unsigned prefix, -+ const basic_format_specs& specs, -+ W write_digits) -> OutputIt { -+ // Slightly faster check for specs.width == 0 && specs.precision == -1. -+ if ((specs.width | (specs.precision + 1)) == 0) { -+ auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); -+ if (prefix != 0) { -+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) -+ *it++ = static_cast(p & 0xff); -+ } -+ return base_iterator(out, write_digits(it)); -+ } - auto data = write_int_data(num_digits, prefix, specs); -- 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) { -- 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)) -- : 0; -- using iterator = remove_reference_t; -- return write_padded(out, specs, size, width, [=](iterator it) { -- return copy_str(data, data + size, it); -- }); --} -+template class digit_grouping { -+ private: -+ thousands_sep_result sep_; - --// 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; -- } -- } -+ struct next_state { -+ std::string::const_iterator group; -+ int pos; -+ }; -+ next_state initial_state() const { return {sep_.grouping.begin(), 0}; } - -- 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; -- }); -+ // Returns the next digit group separator position. -+ int next(next_state& state) const { -+ if (!sep_.thousands_sep) return max_value(); -+ if (state.group == sep_.grouping.end()) -+ return state.pos += sep_.grouping.back(); -+ if (*state.group <= 0 || *state.group == max_value()) -+ return max_value(); -+ state.pos += *state.group++; -+ return state.pos; - } - -- 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'); -- }); -+ public: -+ explicit digit_grouping(locale_ref loc, bool localized = true) { -+ if (localized) -+ sep_ = thousands_sep(loc); -+ else -+ sep_.thousands_sep = Char(); - } -+ explicit digit_grouping(thousands_sep_result sep) : sep_(sep) {} - -- 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); -- }); -+ Char separator() const { return sep_.thousands_sep; } -+ -+ int count_separators(int num_digits) const { -+ int count = 0; -+ auto state = initial_state(); -+ while (num_digits > next(state)) ++count; -+ return count; - } - -- 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'; -+ // Applies grouping to digits and write the output to out. -+ template -+ Out apply(Out out, basic_string_view digits) const { -+ auto num_digits = static_cast(digits.size()); -+ auto separators = basic_memory_buffer(); -+ separators.push_back(0); -+ auto state = initial_state(); -+ while (int i = next(state)) { -+ if (i >= num_digits) break; -+ separators.push_back(i); - } -- 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 += prefix_size; -- buffer.resize(size); -- 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; -- 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; -+ for (int i = 0, sep_index = static_cast(separators.size() - 1); -+ i < num_digits; ++i) { -+ if (num_digits - i == separators[sep_index]) { -+ *out++ = separator(); -+ --sep_index; - } -- p -= s.size(); -- std::uninitialized_copy(s.data(), s.data() + s.size(), -- make_checked(p, s.size())); -+ *out++ = static_cast(digits[to_unsigned(i)]); - } -- if (prefix_size != 0) p[-1] = static_cast('-'); -- using iterator = remove_reference_t; -- auto data = buffer.data(); -- out = write_padded(out, specs, size, size, [=](iterator it) { -- return copy_str(data, data + size, it); -- }); -+ return out; - } -+}; -+ -+template -+auto write_int_localized(OutputIt out, UInt value, unsigned prefix, -+ const basic_format_specs& specs, -+ const digit_grouping& grouping) -> OutputIt { -+ static_assert(std::is_same, UInt>::value, ""); -+ int num_digits = count_digits(value); -+ char digits[40]; -+ format_decimal(digits, value, num_digits); -+ unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + -+ grouping.count_separators(num_digits)); -+ return write_padded( -+ out, specs, size, size, [&](reserve_iterator it) { -+ if (prefix != 0) *it++ = static_cast(prefix); -+ return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); -+ }); -+} -+ -+template -+auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, -+ const basic_format_specs& specs, locale_ref loc) -+ -> bool { -+ auto grouping = digit_grouping(loc); -+ out = write_int_localized(out, value, prefix, specs, grouping); -+ 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 struct write_int_arg { -+ UInt abs_value; -+ unsigned prefix; -+}; - -- void on_chr() { *out++ = static_cast(abs_value); } -+template -+FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) -+ -> write_int_arg> { -+ auto prefix = 0u; -+ auto abs_value = static_cast>(value); -+ if (is_negative(value)) { -+ prefix = 0x01000000 | '-'; -+ abs_value = 0 - abs_value; -+ } else { -+ constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', -+ 0x1000000u | ' '}; -+ prefix = prefixes[sign]; -+ } -+ return {abs_value, prefix}; -+} - -- FMT_NORETURN void on_error() { -- FMT_THROW(format_error("invalid type specifier")); -+template -+FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, -+ const basic_format_specs& specs, -+ locale_ref loc) -> OutputIt { -+ static_assert(std::is_same>::value, ""); -+ auto abs_value = arg.abs_value; -+ auto prefix = arg.prefix; -+ switch (specs.type) { -+ case presentation_type::none: -+ case presentation_type::dec: { -+ 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 presentation_type::hex_lower: -+ case presentation_type::hex_upper: { -+ bool upper = specs.type == presentation_type::hex_upper; -+ if (specs.alt) -+ prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); -+ int num_digits = count_digits<4>(abs_value); -+ return write_int( -+ out, num_digits, prefix, specs, [=](reserve_iterator it) { -+ return format_uint<4, Char>(it, abs_value, num_digits, upper); -+ }); -+ } -+ case presentation_type::bin_lower: -+ case presentation_type::bin_upper: { -+ bool upper = specs.type == presentation_type::bin_upper; -+ if (specs.alt) -+ prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); -+ int num_digits = count_digits<1>(abs_value); -+ return write_int(out, num_digits, prefix, specs, -+ [=](reserve_iterator it) { -+ return format_uint<1, Char>(it, abs_value, num_digits); -+ }); -+ } -+ case presentation_type::oct: { -+ int num_digits = count_digits<3>(abs_value); -+ // Octal prefix '0' is counted as a digit, so only add it if precision -+ // is not greater than the number of digits. -+ if (specs.alt && specs.precision <= num_digits && abs_value != 0) -+ prefix_append(prefix, '0'); -+ return write_int(out, num_digits, prefix, specs, -+ [=](reserve_iterator it) { -+ return format_uint<3, Char>(it, abs_value, num_digits); -+ }); -+ } -+ case presentation_type::chr: -+ return write_char(out, static_cast(abs_value), specs); -+ default: -+ throw_format_error("invalid type specifier"); -+ } -+ return out; -+} -+template -+FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( -+ OutputIt out, write_int_arg arg, const basic_format_specs& specs, -+ locale_ref loc) -> OutputIt { -+ return write_int(out, arg, specs, loc); -+} -+template ::value && -+ !std::is_same::value && -+ std::is_same>::value)> -+FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, -+ const basic_format_specs& specs, -+ locale_ref loc) -> OutputIt { -+ return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, -+ loc); -+} -+// An inlined version of write used in format string compilation. -+template ::value && -+ !std::is_same::value && -+ !std::is_same>::value)> -+FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, -+ const basic_format_specs& specs, -+ locale_ref loc) -> OutputIt { -+ return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); -+} - - template --OutputIt write_nonfinite(OutputIt out, bool isinf, -- const basic_format_specs& specs, -- const float_specs& fspecs) { -+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, -+ const basic_format_specs& specs) -> OutputIt { -+ auto data = s.data(); -+ auto size = s.size(); -+ if (specs.precision >= 0 && to_unsigned(specs.precision) < size) -+ size = code_point_index(s, to_unsigned(specs.precision)); -+ auto width = -+ specs.width != 0 ? compute_width(basic_string_view(data, size)) : 0; -+ return write_padded(out, specs, size, width, -+ [=](reserve_iterator it) { -+ return copy_str(data, data + size, it); -+ }); -+} -+template -+FMT_CONSTEXPR auto write(OutputIt out, -+ basic_string_view> s, -+ const basic_format_specs& specs, locale_ref) -+ -> OutputIt { -+ check_string_type_spec(specs.type); -+ return write(out, s, specs); -+} -+template -+FMT_CONSTEXPR auto write(OutputIt out, const Char* s, -+ const basic_format_specs& specs, locale_ref) -+ -> OutputIt { -+ return check_cstring_type_spec(specs.type) -+ ? write(out, basic_string_view(s), specs, {}) -+ : write_ptr(out, to_uintptr(s), &specs); -+} -+ -+template -+FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isinf, -+ basic_format_specs specs, -+ const float_specs& fspecs) -> OutputIt { - auto str = - isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); - 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) { -- if (sign) *it++ = static_cast(data::signs[sign]); -+ // Replace '0'-padding with space for non-finite values. -+ const bool is_zero_fill = -+ specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); -+ if (is_zero_fill) specs.fill[0] = static_cast(' '); -+ return write_padded(out, specs, size, [=](reserve_iterator it) { -+ if (sign) *it++ = detail::sign(sign); - return copy_str(str, str + str_size, it); - }); - } - -+// A decimal floating-point number significand * pow(10, exp). -+struct big_decimal_fp { -+ const char* significand; -+ int significand_size; -+ int exponent; -+}; -+ -+constexpr auto get_significand_size(const big_decimal_fp& fp) -> int { -+ return fp.significand_size; -+} -+template -+inline auto get_significand_size(const dragonbox::decimal_fp& fp) -> int { -+ return count_digits(fp.significand); -+} -+ -+template -+constexpr auto write_significand(OutputIt out, const char* significand, -+ int significand_size) -> OutputIt { -+ return copy_str(significand, significand + significand_size, out); -+} -+template -+inline auto write_significand(OutputIt out, UInt significand, -+ int significand_size) -> OutputIt { -+ return format_decimal(out, significand, significand_size).end; -+} -+template -+FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, -+ int significand_size, int exponent, -+ const Grouping& grouping) -> OutputIt { -+ if (!grouping.separator()) { -+ out = write_significand(out, significand, significand_size); -+ return detail::fill_n(out, exponent, static_cast('0')); -+ } -+ auto buffer = memory_buffer(); -+ write_significand(appender(buffer), significand, significand_size); -+ detail::fill_n(appender(buffer), exponent, '0'); -+ return grouping.apply(out, string_view(buffer.data(), buffer.size())); -+} -+ -+template ::value)> -+inline auto write_significand(Char* out, UInt significand, int significand_size, -+ int integral_size, Char decimal_point) -> Char* { -+ if (!decimal_point) -+ return format_decimal(out, significand, significand_size).end; -+ out += significand_size + 1; -+ Char* end = out; -+ int floating_size = significand_size - integral_size; -+ for (int i = floating_size / 2; i > 0; --i) { -+ out -= 2; -+ copy2(out, digits2(significand % 100)); -+ significand /= 100; -+ } -+ if (floating_size % 2 != 0) { -+ *--out = static_cast('0' + significand % 10); -+ significand /= 10; -+ } -+ *--out = decimal_point; -+ format_decimal(out - integral_size, significand, integral_size); -+ return end; -+} -+ -+template >::value)> -+inline auto write_significand(OutputIt out, UInt significand, -+ int significand_size, int integral_size, -+ Char decimal_point) -> OutputIt { -+ // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. -+ Char buffer[digits10() + 2]; -+ auto end = write_significand(buffer, significand, significand_size, -+ integral_size, decimal_point); -+ return detail::copy_str_noinline(buffer, end, out); -+} -+ -+template -+FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, -+ int significand_size, int integral_size, -+ Char decimal_point) -> OutputIt { -+ out = detail::copy_str_noinline(significand, -+ significand + integral_size, out); -+ if (!decimal_point) return out; -+ *out++ = decimal_point; -+ return detail::copy_str_noinline(significand + integral_size, -+ significand + significand_size, out); -+} -+ -+template -+FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, -+ int significand_size, int integral_size, -+ Char decimal_point, -+ const Grouping& grouping) -> OutputIt { -+ if (!grouping.separator()) { -+ return write_significand(out, significand, significand_size, integral_size, -+ decimal_point); -+ } -+ auto buffer = basic_memory_buffer(); -+ write_significand(buffer_appender(buffer), significand, -+ significand_size, integral_size, decimal_point); -+ grouping.apply( -+ out, basic_string_view(buffer.data(), to_unsigned(integral_size))); -+ return detail::copy_str_noinline(buffer.data() + integral_size, -+ buffer.end(), out); -+} -+ -+template > -+FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, -+ const basic_format_specs& specs, -+ float_specs fspecs, locale_ref loc) -+ -> OutputIt { -+ auto significand = fp.significand; -+ int significand_size = get_significand_size(fp); -+ constexpr Char zero = static_cast('0'); -+ auto sign = fspecs.sign; -+ size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); -+ using iterator = reserve_iterator; -+ -+ Char decimal_point = -+ fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); -+ -+ int output_exp = fp.exponent + significand_size - 1; -+ auto use_exp_format = [=]() { -+ if (fspecs.format == float_format::exp) return true; -+ if (fspecs.format != float_format::general) return false; -+ // Use the fixed notation if the exponent is in [exp_lower, exp_upper), -+ // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. -+ const int exp_lower = -4, exp_upper = 16; -+ return output_exp < exp_lower || -+ output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); -+ }; -+ if (use_exp_format()) { -+ int num_zeros = 0; -+ if (fspecs.showpoint) { -+ 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(); -+ } -+ auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; -+ int exp_digits = 2; -+ if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; -+ -+ size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); -+ char exp_char = fspecs.upper ? 'E' : 'e'; -+ auto write = [=](iterator it) { -+ if (sign) *it++ = detail::sign(sign); -+ // Insert a decimal point after the first digit and add an exponent. -+ it = write_significand(it, significand, significand_size, 1, -+ decimal_point); -+ if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); -+ *it++ = static_cast(exp_char); -+ return write_exponent(output_exp, it); -+ }; -+ return specs.width > 0 ? write_padded(out, specs, size, write) -+ : base_iterator(out, write(reserve(out, size))); -+ } -+ -+ int exp = fp.exponent + significand_size; -+ if (fp.exponent >= 0) { -+ // 1234e5 -> 123400000[.0+] -+ size += to_unsigned(fp.exponent); -+ int num_zeros = fspecs.precision - exp; -+#ifdef FMT_FUZZ -+ if (num_zeros > 5000) -+ throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); -+#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) + 1; -+ } -+ auto grouping = Grouping(loc, fspecs.locale); -+ size += to_unsigned(grouping.count_separators(significand_size)); -+ return write_padded(out, specs, size, [&](iterator it) { -+ if (sign) *it++ = detail::sign(sign); -+ it = write_significand(it, significand, significand_size, -+ fp.exponent, grouping); -+ if (!fspecs.showpoint) return it; -+ *it++ = decimal_point; -+ return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; -+ }); -+ } else if (exp > 0) { -+ // 1234e-2 -> 12.34[0+] -+ int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; -+ size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); -+ auto grouping = Grouping(loc, fspecs.locale); -+ size += to_unsigned(grouping.count_separators(significand_size)); -+ return write_padded(out, specs, size, [&](iterator it) { -+ if (sign) *it++ = detail::sign(sign); -+ it = write_significand(it, significand, significand_size, exp, -+ decimal_point, grouping); -+ return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; -+ }); -+ } -+ // 1234e-6 -> 0.001234 -+ int num_zeros = -exp; -+ if (significand_size == 0 && fspecs.precision >= 0 && -+ fspecs.precision < num_zeros) { -+ num_zeros = fspecs.precision; -+ } -+ 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++ = detail::sign(sign); -+ *it++ = zero; -+ if (!pointy) return it; -+ *it++ = decimal_point; -+ it = detail::fill_n(it, num_zeros, zero); -+ return write_significand(it, significand, significand_size); -+ }); -+} -+ -+template class fallback_digit_grouping { -+ public: -+ constexpr fallback_digit_grouping(locale_ref, bool) {} -+ -+ constexpr Char separator() const { return Char(); } -+ -+ constexpr int count_separators(int) const { return 0; } -+ -+ template -+ constexpr Out apply(Out out, basic_string_view) const { -+ return out; -+ } -+}; -+ -+template -+FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& fp, -+ const basic_format_specs& specs, -+ float_specs fspecs, locale_ref loc) -+ -> OutputIt { -+ if (is_constant_evaluated()) { -+ return do_write_float>(out, fp, specs, fspecs, -+ loc); -+ } else { -+ return do_write_float(out, fp, specs, fspecs, loc); -+ } -+} -+ -+template ::value)> -+FMT_CONSTEXPR20 bool isinf(T value) { -+ if (is_constant_evaluated()) { -+#if defined(__cpp_if_constexpr) -+ if constexpr (std::numeric_limits::is_iec559) { -+ auto bits = detail::bit_cast(static_cast(value)); -+ constexpr auto significand_bits = -+ dragonbox::float_info::significand_bits; -+ return (bits & exponent_mask()) && -+ !(bits & ((uint64_t(1) << significand_bits) - 1)); -+ } -+#endif -+ } -+ return std::isinf(value); -+} -+ -+template ::value)> -+FMT_CONSTEXPR20 bool isfinite(T value) { -+ if (is_constant_evaluated()) { -+#if defined(__cpp_if_constexpr) -+ if constexpr (std::numeric_limits::is_iec559) { -+ auto bits = detail::bit_cast(static_cast(value)); -+ return (bits & exponent_mask()) != exponent_mask(); -+ } -+#endif -+ } -+ return std::isfinite(value); -+} -+ -+template ::value)> -+FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { -+ if (is_constant_evaluated()) { -+#ifdef __cpp_if_constexpr -+ if constexpr (std::numeric_limits::is_iec559) { -+ auto bits = detail::bit_cast(static_cast(value)); -+ return (bits & (uint64_t(1) << (num_bits() - 1))) != 0; -+ } -+#endif -+ } -+ return std::signbit(value); -+} -+ - template ::value)> --OutputIt write(OutputIt out, T value, basic_format_specs specs, -- locale_ref loc = {}) { -+FMT_CONSTEXPR20 auto write(OutputIt out, T value, -+ basic_format_specs specs, locale_ref loc = {}) -+ -> OutputIt { - if (const_check(!is_supported_floating_point(value))) return out; - float_specs fspecs = parse_float_type_spec(specs); - fspecs.sign = specs.sign; -- if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. -+ if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. - fspecs.sign = sign::minus; - value = -value; - } else if (fspecs.sign == sign::minus) { - fspecs.sign = sign::none; - } - -- if (!std::isfinite(value)) -- return write_nonfinite(out, std::isinf(value), specs, fspecs); -+ if (!detail::isfinite(value)) -+ return write_nonfinite(out, detail::isinf(value), specs, fspecs); - - if (specs.align == align::numeric && fspecs.sign) { - auto it = reserve(out, 1); -- *it++ = static_cast(data::signs[fspecs.sign]); -+ *it++ = detail::sign(fspecs.sign); - out = base_iterator(out, it); - fspecs.sign = sign::none; - if (specs.width != 0) --specs.width; -@@ -1655,404 +2007,231 @@ OutputIt write(OutputIt out, T value, basic_format_specs specs, - - memory_buffer buffer; - if (fspecs.format == float_format::hex) { -- if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); -+ if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); - snprintf_float(promote_float(value), specs.precision, fspecs, buffer); -- return write_bytes(out, {buffer.data(), buffer.size()}, specs); -+ return write_bytes(out, {buffer.data(), buffer.size()}, -+ specs); - } -- int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; -+ int precision = specs.precision >= 0 || specs.type == presentation_type::none -+ ? specs.precision -+ : 6; - if (fspecs.format == float_format::exp) { - if (precision == max_value()) -- FMT_THROW(format_error("number is too big")); -+ throw_format_error("number is too big"); - else - ++precision; - } - if (const_check(std::is_same())) fspecs.binary32 = true; -- fspecs.use_grisu = use_grisu(); -+ if (!is_fast_float()) fspecs.fallback = true; - int exp = format_float(promote_float(value), precision, fspecs, buffer); - fspecs.precision = precision; -- Char point = -- fspecs.locale ? decimal_point(loc) : static_cast('.'); -- float_writer w(buffer.data(), static_cast(buffer.size()), exp, -- fspecs, point); -- return write_padded(out, specs, w.size(), w); -+ auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; -+ return write_float(out, fp, specs, fspecs, loc); - } - - template ::value)> --OutputIt write(OutputIt out, T value) { -+ FMT_ENABLE_IF(is_fast_float::value)> -+FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { -+ if (is_constant_evaluated()) { -+ return write(out, value, basic_format_specs()); -+ } -+ - if (const_check(!is_supported_floating_point(value))) return out; -+ -+ using floaty = conditional_t::value, double, T>; -+ using uint = typename dragonbox::float_info::carrier_uint; -+ auto bits = bit_cast(value); -+ - auto fspecs = float_specs(); -- if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. -+ if (detail::signbit(value)) { - fspecs.sign = sign::minus; - value = -value; - } - -- auto specs = basic_format_specs(); -- if (!std::isfinite(value)) -+ constexpr auto specs = basic_format_specs(); -+ uint mask = exponent_mask(); -+ if ((bits & mask) == mask) - return write_nonfinite(out, std::isinf(value), specs, fspecs); - -- memory_buffer buffer; -- int precision = -1; -- if (const_check(std::is_same())) fspecs.binary32 = true; -- fspecs.use_grisu = use_grisu(); -- int exp = format_float(promote_float(value), precision, fspecs, buffer); -- fspecs.precision = precision; -- float_writer w(buffer.data(), static_cast(buffer.size()), exp, -- fspecs, static_cast('.')); -- return base_iterator(out, w(reserve(out, w.size()))); -+ auto dec = dragonbox::to_decimal(static_cast(value)); -+ return write_float(out, dec, specs, fspecs, {}); - } - --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) { -- *it++ = static_cast('0'); -- *it++ = static_cast('x'); -- return format_uint<4, Char>(it, value, num_digits); -- }; -- return specs ? write_padded(out, *specs, size, write) -- : base_iterator(out, write(reserve(out, size))); -+template ::value && -+ !is_fast_float::value)> -+inline auto write(OutputIt out, T value) -> OutputIt { -+ return write(out, value, basic_format_specs()); - } - --template struct is_integral : std::is_integral {}; --template <> struct is_integral : std::true_type {}; --template <> struct is_integral : std::true_type {}; -- - template --OutputIt write(OutputIt out, monostate) { -+auto write(OutputIt out, monostate, basic_format_specs = {}, -+ locale_ref = {}) -> OutputIt { - FMT_ASSERT(false, ""); - return out; - } - --template ::value)> --OutputIt write(OutputIt out, string_view value) { -+template -+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) -+ -> OutputIt { - auto it = reserve(out, value.size()); -- it = copy_str(value.begin(), value.end(), it); -+ it = copy_str_noinline(value.begin(), value.end(), it); - return base_iterator(out, it); - } - --template --OutputIt write(OutputIt out, basic_string_view value) { -- auto it = reserve(out, value.size()); -- it = std::copy(value.begin(), value.end(), it); -- return base_iterator(out, it); -+template ::value)> -+constexpr auto write(OutputIt out, const T& value) -> OutputIt { -+ 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 auto write(OutputIt out, T value) -> OutputIt { - auto abs_value = static_cast>(value); - bool negative = is_negative(value); - // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. - if (negative) abs_value = ~abs_value + 1; - int num_digits = count_digits(abs_value); -- auto it = reserve(out, (negative ? 1 : 0) + static_cast(num_digits)); -+ auto size = (negative ? 1 : 0) + static_cast(num_digits); -+ auto it = reserve(out, size); -+ if (auto ptr = to_pointer(it, size)) { -+ if (negative) *ptr++ = static_cast('-'); -+ format_decimal(ptr, abs_value, num_digits); -+ return out; -+ } - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits).end; - return base_iterator(out, it); - } - --template --OutputIt write(OutputIt out, bool value) { -- return write(out, string_view(value ? "true" : "false")); -+// FMT_ENABLE_IF() condition separated to workaround an MSVC bug. -+template < -+ typename Char, typename OutputIt, typename T, -+ bool check = -+ std::is_enum::value && !std::is_same::value && -+ mapped_type_constant>::value != -+ type::custom_type, -+ FMT_ENABLE_IF(check)> -+FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { -+ return write( -+ out, static_cast::type>(value)); -+} -+ -+template ::value)> -+FMT_CONSTEXPR auto write(OutputIt out, T value, -+ const basic_format_specs& specs = {}, -+ locale_ref = {}) -> OutputIt { -+ return specs.type != presentation_type::none && -+ specs.type != presentation_type::string -+ ? write(out, value ? 1 : 0, specs, {}) -+ : write_bytes(out, value ? "true" : "false", specs); - } - - template --OutputIt write(OutputIt out, Char value) { -+FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { - auto it = reserve(out, 1); - *it++ = value; - return base_iterator(out, it); - } - - template --OutputIt write(OutputIt out, const Char* value) { -+FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) -+ -> OutputIt { - if (!value) { -- FMT_THROW(format_error("string pointer is null")); -+ throw_format_error("string pointer is null"); - } else { -- auto length = std::char_traits::length(value); -- out = write(out, basic_string_view(value, length)); -+ out = write(out, basic_string_view(value)); - } - return out; - } - --template --OutputIt write(OutputIt out, const void* value) { -- return write_ptr(out, to_uintptr(value), nullptr); -+template ::value)> -+auto write(OutputIt out, const T* value, -+ const basic_format_specs& specs = {}, locale_ref = {}) -+ -> OutputIt { -+ check_pointer_type_spec(specs.type, error_handler()); -+ return write_ptr(out, to_uintptr(value), &specs); - } - --template --auto write(OutputIt out, const T& value) -> typename std::enable_if< -- mapped_type_constant>::value == -- type::custom_type, -- OutputIt>::type { -- basic_format_context ctx(out, {}, {}); -- return formatter().format(value, ctx); -+// A write overload that handles implicit conversions. -+template > -+FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< -+ std::is_class::value && !is_string::value && -+ !std::is_same::value && -+ !std::is_same().map(value))>::value, -+ OutputIt> { -+ return write(out, arg_mapper().map(value)); -+} -+ -+template > -+FMT_CONSTEXPR auto write(OutputIt out, const T& value) -+ -> enable_if_t::value == type::custom_type, -+ OutputIt> { -+ using formatter_type = -+ conditional_t::value, -+ typename Context::template formatter_type, -+ fallback_formatter>; -+ auto ctx = Context(out, {}, {}); -+ return formatter_type().format(value, ctx); - } - - // An argument visitor that formats the argument and writes it via the output - // iterator. It's a class and not a generic lambda for compatibility with C++11. --template struct default_arg_formatter { -- using context = basic_format_context; -+template struct default_arg_formatter { -+ using iterator = buffer_appender; -+ using context = buffer_context; - -- OutputIt out; -+ iterator out; - basic_format_args args; - locale_ref loc; - -- template OutputIt operator()(T value) { -+ template auto operator()(T value) -> iterator { - return write(out, value); - } -- -- OutputIt operator()(typename basic_format_arg::handle handle) { -+ auto operator()(typename basic_format_arg::handle h) -> iterator { - basic_format_parse_context parse_ctx({}); -- basic_format_context format_ctx(out, args, loc); -- handle.format(parse_ctx, format_ctx); -+ context format_ctx(out, args, loc); -+ h.format(parse_ctx, format_ctx); - return format_ctx.out(); - } - }; - --template --class arg_formatter_base { -- public: -- using iterator = OutputIt; -- using char_type = Char; -- using format_specs = basic_format_specs; -- -- private: -- iterator out_; -- 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_. -- auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) { -- 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; -- } -- -- template ::value)> -- void write(Ch value) { -- out_ = detail::write(out_, value); -- } -- -- void write(string_view value) { -- auto&& it = reserve(value.size()); -- it = copy_str(value.begin(), value.end(), it); -- } -- void write(wstring_view value) { -- static_assert(std::is_same::value, ""); -- auto&& it = reserve(value.size()); -- it = std::copy(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); -- }); -- } -- -- template -- 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_); -- } -- -- struct char_spec_handler : ErrorHandler { -- arg_formatter_base& formatter; -- Char value; -- -- char_spec_handler(arg_formatter_base& f, Char val) -- : formatter(f), value(val) {} -- -- void on_int() { -- // char is only formatted as int if there are specs. -- formatter.write_int(static_cast(value), *formatter.specs_); -- } -- void on_char() { -- if (formatter.specs_) -- formatter.out_ = write_char(formatter.out_, value, *formatter.specs_); -- else -- formatter.write(value); -- } -- }; -- -- struct cstring_spec_handler : error_handler { -- arg_formatter_base& formatter; -- const Char* value; -- -- cstring_spec_handler(arg_formatter_base& f, const Char* val) -- : formatter(f), value(val) {} -- -- void on_string() { formatter.write(value); } -- void on_pointer() { formatter.write_pointer(value); } -- }; -- -- protected: -- iterator out() { return out_; } -- format_specs* specs() { return specs_; } -- -- void write(bool value) { -- if (specs_) -- write(string_view(value ? "true" : "false"), *specs_); -- else -- out_ = detail::write(out_, value); -- } -- -- void write(const Char* value) { -- if (!value) { -- 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) {} -- -- iterator operator()(monostate) { -- FMT_ASSERT(false, "invalid argument type"); -- return out_; -- } -- -- template ::value)> -- FMT_INLINE iterator operator()(T value) { -- if (specs_) -- write_int(value, *specs_); -- else -- out_ = detail::write(out_, value); -- return out_; -- } -- -- 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); -- write(value != 0); -- return out_; -- } -+template struct arg_formatter { -+ using iterator = buffer_appender; -+ using context = buffer_context; - -- 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_); -- else -- FMT_ASSERT(false, "unsupported float argument type"); -- return out_; -- } -+ iterator out; -+ const basic_format_specs& specs; -+ locale_ref locale; - -- iterator operator()(const Char* value) { -- if (!specs_) return write(value), out_; -- handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value)); -- return out_; -+ template -+ FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { -+ return detail::write(out, value, specs, locale); - } -- -- iterator operator()(basic_string_view value) { -- if (specs_) { -- check_string_type_spec(specs_->type, error_handler()); -- write(value, *specs_); -- } else { -- write(value); -- } -- return out_; -- } -- -- iterator operator()(const void* value) { -- if (specs_) check_pointer_type_spec(specs_->type, error_handler()); -- write_pointer(value); -- return out_; -+ auto operator()(typename basic_format_arg::handle) -> iterator { -+ // User-defined types are handled separately because they require access -+ // to the parse context. -+ return out; - } - }; - --template FMT_CONSTEXPR bool is_name_start(Char c) { -- return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; --} -- --// Parses the range [begin, end) as an unsigned integer. This function assumes --// that the range is non-empty and the first character is a digit. --template --FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, -- ErrorHandler&& eh) { -- FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); -- unsigned value = 0; -- // Convert to unsigned to prevent a warning. -- constexpr unsigned max_int = max_value(); -- unsigned big = max_int / 10; -- do { -- // Check for overflow. -- if (value > big) { -- value = max_int + 1; -- break; -- } -- value = value * 10 + unsigned(*begin - '0'); -- ++begin; -- } while (begin != end && '0' <= *begin && *begin <= '9'); -- if (value > max_int) eh.on_error("number is too big"); -- return static_cast(value); --} -- --template class custom_formatter { -- private: -- using char_type = typename Context::char_type; -+template struct custom_formatter { -+ basic_format_parse_context& parse_ctx; -+ buffer_context& ctx; - -- basic_format_parse_context& parse_ctx_; -- Context& ctx_; -- -- public: -- explicit custom_formatter(basic_format_parse_context& parse_ctx, -- Context& ctx) -- : parse_ctx_(parse_ctx), ctx_(ctx) {} -- -- bool operator()(typename basic_format_arg::handle h) const { -- h.format(parse_ctx_, ctx_); -- return true; -+ void operator()( -+ typename basic_format_arg>::handle h) const { -+ h.format(parse_ctx, ctx); - } -- -- template bool operator()(T) const { return false; } -+ template void operator()(T) const {} - }; - - template -@@ -2066,13 +2245,13 @@ template class width_checker { - explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} - - template ::value)> -- FMT_CONSTEXPR unsigned long long operator()(T value) { -+ FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) handler_.on_error("negative width"); - return static_cast(value); - } - - template ::value)> -- FMT_CONSTEXPR unsigned long long operator()(T) { -+ FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - handler_.on_error("width is not integer"); - return 0; - } -@@ -2086,13 +2265,13 @@ template class precision_checker { - explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} - - template ::value)> -- FMT_CONSTEXPR unsigned long long operator()(T value) { -+ FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) handler_.on_error("negative precision"); - return static_cast(value); - } - - template ::value)> -- FMT_CONSTEXPR unsigned long long operator()(T) { -+ FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - handler_.on_error("precision is not integer"); - return 0; - } -@@ -2101,922 +2280,222 @@ template class precision_checker { - ErrorHandler& handler_; - }; - --// A format specifier handler that sets fields in basic_format_specs. --template class specs_setter { -- public: -- explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) -- : specs_(specs) {} -- -- FMT_CONSTEXPR specs_setter(const specs_setter& other) -- : specs_(other.specs_) {} -- -- FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } -- FMT_CONSTEXPR void on_fill(basic_string_view fill) { -- specs_.fill = fill; -- } -- FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } -- 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_zero() { -- specs_.align = align::numeric; -- specs_.fill[0] = Char('0'); -- } -- -- FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } -- FMT_CONSTEXPR void on_precision(int precision) { -- specs_.precision = precision; -- } -- FMT_CONSTEXPR void end_precision() {} -- -- FMT_CONSTEXPR void on_type(Char type) { -- specs_.type = static_cast(type); -- } -- -- protected: -- basic_format_specs& specs_; --}; -- --template class numeric_specs_checker { -- public: -- FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type) -- : error_handler_(eh), arg_type_(arg_type) {} -- -- FMT_CONSTEXPR void require_numeric_argument() { -- if (!is_arithmetic_type(arg_type_)) -- error_handler_.on_error("format specifier requires numeric argument"); -- } -- -- FMT_CONSTEXPR void check_sign() { -- require_numeric_argument(); -- if (is_integral_type(arg_type_) && arg_type_ != type::int_type && -- arg_type_ != type::long_long_type && arg_type_ != type::char_type) { -- error_handler_.on_error("format specifier requires signed argument"); -- } -- } -- -- FMT_CONSTEXPR void check_precision() { -- if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) -- error_handler_.on_error("precision not allowed for this argument type"); -- } -- -- private: -- ErrorHandler& error_handler_; -- detail::type arg_type_; --}; -- --// A format specifier handler that checks if specifiers are consistent with the --// argument type. --template class specs_checker : public Handler { -- private: -- numeric_specs_checker checker_; -- -- // Suppress an MSVC warning about using this in initializer list. -- FMT_CONSTEXPR Handler& error_handler() { return *this; } -- -- public: -- FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) -- : Handler(handler), checker_(error_handler(), arg_type) {} -- -- FMT_CONSTEXPR specs_checker(const specs_checker& other) -- : Handler(other), checker_(error_handler(), other.arg_type_) {} -- -- FMT_CONSTEXPR void on_align(align_t align) { -- if (align == align::numeric) checker_.require_numeric_argument(); -- Handler::on_align(align); -- } -- -- FMT_CONSTEXPR void on_plus() { -- checker_.check_sign(); -- Handler::on_plus(); -- } -- -- FMT_CONSTEXPR void on_minus() { -- checker_.check_sign(); -- Handler::on_minus(); -- } -- -- FMT_CONSTEXPR void on_space() { -- checker_.check_sign(); -- Handler::on_space(); -- } -- -- FMT_CONSTEXPR void on_hash() { -- checker_.require_numeric_argument(); -- Handler::on_hash(); -- } -- -- FMT_CONSTEXPR void on_zero() { -- checker_.require_numeric_argument(); -- Handler::on_zero(); -- } -- -- FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } --}; -- - template