This patch adjust all internal std::format call inside of __formatter_chrono, to use runtime format stirng and thus avoid compile time checking of validity of the format string. Majority of cases are covered by calling newly introduced _S_empty_fs() function that returns __Runtime_format_string containing _S_empty_spec, instead of passing later directly.
In case of _M_j we use _S_str_d3 function (extracted from _S_str_d2), eliminating call to std::format outside of unlikely scenario in which day of year is greater than 1000 (this may happen for year_month_day with month greater than 12). In consequence, outside of handling subseconds, we no longer delegate to std::format or construct temporary strings, when formatting chrono types with ok() values. PR libstdc++/110739 libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (__formatter_chrono::_S_empty_fs): Define. (__formatter_chrono::_S_str_d2): Use _S_str_d3 for 3+ digits. (__formatter_chrono::_S_str_d3): Extracted from _S_str_d2. (__formatter_chrono::_M_H_I, __formatter_chrono::_M_R_X): Replace _S_empty_spec with _S_empty_fs(). (__formatter_chrono::_M_j): Likewise and use _S_str_d3 in common case. --- libstdc++-v3/include/bits/chrono_io.h | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index bcfd51b9866..d6bc6c7cf2a 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -873,6 +873,11 @@ namespace __format static constexpr const _CharT* _S_minus_empty_spec = _S_chars + 17; static constexpr const _CharT* _S_empty_spec = _S_chars + 18; + [[__gnu__::__always_inline__]] + static _Runtime_format_string<_CharT> + _S_empty_fs() + { return _Runtime_format_string<_CharT>(_S_empty_spec); } + // Return the formatting locale. template<typename _FormatContext> std::locale @@ -1411,7 +1416,7 @@ namespace __format __i = 12; } else if (__i >= 100) [[unlikely]] - return std::format_to(std::move(__out), _S_empty_spec, __i); + return std::format_to(std::move(__out), _S_empty_fs(), __i); return __format::__write(std::move(__out), _S_two_digits(__i)); } @@ -1425,11 +1430,15 @@ namespace __format { // Decimal number of days, without padding. auto __d = chrono::floor<chrono::days>(__t._M_hours).count(); - return std::format_to(std::move(__out), _S_empty_spec, __d); + return std::format_to(std::move(__out), _S_empty_fs(), __d); } - return std::format_to(std::move(__out), _GLIBCXX_WIDEN("{:03d}"), - __t._M_day_of_year.count()); + auto __d = __t._M_day_of_year.count(); + if (__d >= 1000) [[unlikely]] + return std::format_to(std::move(__out), _S_empty_fs(), __d); + + _CharT __buf[3]; + return __format::__write(std::move(__out), _S_str_d3(__buf, __d)); } template<typename _FormatContext> @@ -1534,7 +1543,7 @@ namespace __format if (__hi >= 100) [[unlikely]] { - __out = std::format_to(std::move(__out), _S_empty_spec, __hi); + __out = std::format_to(std::move(__out), _S_empty_fs(), __hi); __sv.remove_prefix(2); } else @@ -1772,7 +1781,15 @@ namespace __format { if (__n < 100) [[likely]] return _S_two_digits(__n); + return _S_str_d3(__buf, __n); + } + [[__gnu__::__always_inline__]] + // Returns decimal representation of __n, padded to 3 digits. + // Returned string_view points to __buf. + static basic_string_view<_CharT> + _S_str_d3(span<_CharT, 3> __buf, unsigned __n) + { _S_fill_two_digits(__buf.data(), __n / 10); __buf[2] = _S_chars[__n % 10]; return __string_view(__buf.data(), 3); -- 2.49.0