From 03f29bc9219ecc3c35e969011dc810484b25584e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iy=C3=A1n=20M=C3=A9ndez=20Veiga?= Date: Sat, 10 Sep 2022 22:07:29 +0200 Subject: [PATCH] upgpkg: python-tweedledum 1.1.1-3 --- python-tweedledum/.SRCINFO | 4 +- python-tweedledum/PKGBUILD | 4 +- python-tweedledum/fix-build.patch | 113324 +++++++++++++++++++++++++++ 3 files changed, 113328 insertions(+), 4 deletions(-) create mode 100644 python-tweedledum/fix-build.patch diff --git a/python-tweedledum/.SRCINFO b/python-tweedledum/.SRCINFO index a91be56..c2c960b 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 = 2 + pkgrel = 3 url = https://github.com/boschmitt/tweedledum arch = x86_64 license = MIT @@ -12,7 +12,7 @@ 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::https://github.com/boschmitt/tweedledum/pull/170.patch + source = fix-build.patch b2sums = 99da09829a70a316fdc582929bfe8ca5d805f0a7a6f049da3951c57c5e4bec24343a1021020e8d00791683ab5c140647d78ee0dde5dac95370b648e0eee44b04 b2sums = 24cb2303a6d3fdac0967c6c925bb49113cc317aa03524b024e9131180bdcd7f6dd812d0e2ebfe157f0331e366b0284c972cc1b2b780960af38365c7d70fa078c diff --git a/python-tweedledum/PKGBUILD b/python-tweedledum/PKGBUILD index 48b6f07..e216667 100644 --- a/python-tweedledum/PKGBUILD +++ b/python-tweedledum/PKGBUILD @@ -2,7 +2,7 @@ _pkgname=tweedledum pkgname=python-${_pkgname} pkgver=1.1.1 -pkgrel=2 +pkgrel=3 pkgdesc="A library for synthesizing and manipulating quantum circuits" arch=('x86_64') url="https://github.com/boschmitt/tweedledum" @@ -17,7 +17,7 @@ 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 ) b2sums=( '99da09829a70a316fdc582929bfe8ca5d805f0a7a6f049da3951c57c5e4bec24343a1021020e8d00791683ab5c140647d78ee0dde5dac95370b648e0eee44b04' diff --git a/python-tweedledum/fix-build.patch b/python-tweedledum/fix-build.patch new file mode 100644 index 0000000..d2d579c --- /dev/null +++ b/python-tweedledum/fix-build.patch @@ -0,0 +1,113324 @@ +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