From: Anlai Lu Date: Tue, 30 Jun 2026 12:49:03 +0000 (+0000) Subject: libstdc++: Use __chrono_write via _S_empty_fs for chrono ostream insertion X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e906bebbf21dc50a5c4db9afa358db6e48d4706b;p=thirdparty%2Fgcc.git libstdc++: Use __chrono_write via _S_empty_fs for chrono ostream insertion Add __detail::__chrono_write which formats a chrono object into a stack buffer via std::format_to_n with _S_empty_fs(), then writes through __ostream_insert. A non-type template parameter _BufSize allows per-type buffer tuning. Each _BufSize is the smallest power of two that accommodates the longest possible formatted output for that type, including error cases. All chrono operator<< overloads that previously used std::format, std::vformat, or basic_stringstream now use this helper. libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (__formatter_chrono::_S_empty_fs): Make public. (__detail::__chrono_write): New function template. (operator<<): Use __chrono_write consistently for all chrono types. Reviewed-by: Jonathan Wakely Reviewed-by: Tomasz KamiƄski Signed-off-by: Anlai Lu --- diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index dba96759b1b..c5170368f82 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -884,11 +884,13 @@ namespace __format static constexpr const _CharT* _S_minus_empty_spec = _S_chars + 17; static constexpr const _CharT* _S_empty_spec = _S_chars + 18; + public: [[__gnu__::__always_inline__]] static _Dynamic_format_string<_CharT> _S_empty_fs() { return _Dynamic_format_string<_CharT>(_S_empty_spec); } + protected: static constexpr const _CharT* _S_weekdays[] { _GLIBCXX_WIDEN("Sunday"), @@ -3607,6 +3609,32 @@ namespace __detail } } + // Format into a stack buffer via std::format_to_n with the empty + // chrono-spec (_S_empty_fs), then write through __ostream_insert. + // The empty spec lets each formatter use its __defSpec, producing + // output equivalent to the corresponding operator<<. + // _BufSize allows per-type tuning of the stack buffer size. + template + inline basic_ostream<_CharT, _Traits>& + __chrono_write(basic_ostream<_CharT, _Traits>& __os, + const _Arg& __arg, const _OptLocale&... __loc) + { + static_assert(sizeof...(_OptLocale) <= 1); + constexpr auto __fs + = __format::__formatter_chrono<_CharT>::_S_empty_fs; + constexpr size_t __bufsize = _BufSize; + _CharT __buf[__bufsize]; + auto __res = std::format_to_n(__buf, __bufsize, __loc..., + __fs(), __arg); + + if (static_cast(__res.size) <= __bufsize) [[likely]] + return std::__ostream_insert(__os, __buf, __res.size); + + auto __s = std::format(__loc..., __fs(), __arg); + return std::__ostream_insert(__os, __s.data(), __s.size()); + } + } // namespace __detail /// @endcond @@ -3628,16 +3656,7 @@ namespace __detail template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const day& __d) - { - using _Ctx = __format::__format_context<_CharT>; - using _Str = basic_string_view<_CharT>; - _Str __s = _GLIBCXX_WIDEN("{:02d} is not a valid day"); - if (__d.ok()) - __s = __s.substr(0, 6); - auto __u = (unsigned)__d; - __os << std::vformat(__s, make_format_args<_Ctx>(__u)); - return __os; - } + { return __detail::__chrono_write<32>(__os, __d); } template> @@ -3656,20 +3675,7 @@ namespace __detail template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const month& __m) - { - using _Ctx = __format::__format_context<_CharT>; - using _Str = basic_string_view<_CharT>; - _Str __s = _GLIBCXX_WIDEN("{:L%b}{} is not a valid month"); - if (__m.ok()) - __os << std::vformat(__os.getloc(), __s.substr(0, 6), - make_format_args<_Ctx>(__m)); - else - { - auto __u = (unsigned)__m; - __os << std::vformat(__s.substr(6), make_format_args<_Ctx>(__u)); - } - return __os; - } + { return __detail::__chrono_write<64>(__os, __m, __os.getloc()); } template> @@ -3688,20 +3694,7 @@ namespace __detail template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const year& __y) - { - using _Ctx = __format::__format_context<_CharT>; - using _Str = basic_string_view<_CharT>; - _Str __s = _GLIBCXX_WIDEN("-{:04d} is not a valid year"); - if (__y.ok()) - __s = __s.substr(0, 7); - int __i = (int)__y; - if (__i >= 0) [[likely]] - __s.remove_prefix(1); - else - __i = -__i; - __os << std::vformat(__s, make_format_args<_Ctx>(__i)); - return __os; - } + { return __detail::__chrono_write<32>(__os, __y); } template> @@ -3720,20 +3713,7 @@ namespace __detail template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const weekday& __wd) - { - using _Ctx = __format::__format_context<_CharT>; - using _Str = basic_string_view<_CharT>; - _Str __s = _GLIBCXX_WIDEN("{:L%a}{} is not a valid weekday"); - if (__wd.ok()) - __os << std::vformat(__os.getloc(), __s.substr(0, 6), - make_format_args<_Ctx>(__wd)); - else - { - auto __c = __wd.c_encoding(); - __os << std::vformat(__s.substr(6), make_format_args<_Ctx>(__c)); - } - return __os; - } + { return __detail::__chrono_write<64>(__os, __wd, __os.getloc()); } template> @@ -3753,55 +3733,18 @@ namespace __detail inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const weekday_indexed& __wdi) - { - // The standard says to format wdi.weekday() and wdi.index() using - // either "{:L}[{}]" or "{:L}[{} is not a valid index]". The {:L} spec - // means to format the weekday using ostringstream, so just do that. - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - __os2 << __wdi.weekday(); - const auto __i = __wdi.index(); - basic_string_view<_CharT> __s - = _GLIBCXX_WIDEN("[ is not a valid index]"); - __os2 << __s[0]; - __os2 << std::format(_GLIBCXX_WIDEN("{}"), __i); - if (__i >= 1 && __i <= 5) - __os2 << __s.back(); - else - __os2 << __s.substr(1); - __os << __os2.view(); - return __os; - } + { return __detail::__chrono_write<128>(__os, __wdi, __os.getloc()); } template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const weekday_last& __wdl) - { - // As above, just write straight to a stringstream, as if by "{:L}[last]" - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - __os2 << __wdl.weekday() << _GLIBCXX_WIDEN("[last]"); - __os << __os2.view(); - return __os; - } - + { return __detail::__chrono_write<128>(__os, __wdl, __os.getloc()); } + template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const month_day& __md) - { - // As above, just write straight to a stringstream, as if by "{:L}/{}" - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - __os2 << __md.month(); - if constexpr (is_same_v<_CharT, char>) - __os2 << '/'; - else - __os2 << L'/'; - __os2 << __md.day(); - __os << __os2.view(); - return __os; - } + { return __detail::__chrono_write<128>(__os, __md, __os.getloc()); } template> @@ -3823,67 +3766,24 @@ namespace __detail inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const month_day_last& __mdl) - { - // As above, just write straight to a stringstream, as if by "{:L}/last" - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - __os2 << __mdl.month() << _GLIBCXX_WIDEN("/last"); - __os << __os2.view(); - return __os; - } + { return __detail::__chrono_write<128>(__os, __mdl, __os.getloc()); } template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const month_weekday& __mwd) - { - // As above, just write straight to a stringstream, as if by "{:L}/{:L}" - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - __os2 << __mwd.month(); - if constexpr (is_same_v<_CharT, char>) - __os2 << '/'; - else - __os2 << L'/'; - __os2 << __mwd.weekday_indexed(); - __os << __os2.view(); - return __os; - } + { return __detail::__chrono_write<128>(__os, __mwd, __os.getloc()); } template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const month_weekday_last& __mwdl) - { - // As above, just write straight to a stringstream, as if by "{:L}/{:L}" - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - __os2 << __mwdl.month(); - if constexpr (is_same_v<_CharT, char>) - __os2 << '/'; - else - __os2 << L'/'; - __os2 << __mwdl.weekday_last(); - __os << __os2.view(); - return __os; - } + { return __detail::__chrono_write<128>(__os, __mwdl, __os.getloc()); } template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month& __ym) - { - // As above, just write straight to a stringstream, as if by "{}/{:L}" - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - __os2 << __ym.year(); - if constexpr (is_same_v<_CharT, char>) - __os2 << '/'; - else - __os2 << L'/'; - __os2 << __ym.month(); - __os << __os2.view(); - return __os; - } + { return __detail::__chrono_write<128>(__os, __ym, __os.getloc()); } template> @@ -3905,14 +3805,7 @@ namespace __detail inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month_day& __ymd) - { - using _Ctx = __format::__format_context<_CharT>; - using _Str = basic_string_view<_CharT>; - _Str __s = _GLIBCXX_WIDEN("{:%F} is not a valid date"); - __os << std::vformat(__ymd.ok() ? __s.substr(0, 5) : __s, - make_format_args<_Ctx>(__ymd)); - return __os; - } + { return __detail::__chrono_write<64>(__os, __ymd); } template> @@ -3935,107 +3828,45 @@ namespace __detail inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month_day_last& __ymdl) - { - // As above, just write straight to a stringstream, as if by "{}/{:L}" - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - __os2 << __ymdl.year(); - if constexpr (is_same_v<_CharT, char>) - __os2 << '/'; - else - __os2 << L'/'; - __os2 << __ymdl.month_day_last(); - __os << __os2.view(); - return __os; - } + { return __detail::__chrono_write<128>(__os, __ymdl, __os.getloc()); } template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month_weekday& __ymwd) - { - // As above, just write straight to a stringstream, as if by - // "{}/{:L}/{:L}" - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - _CharT __slash; - if constexpr (is_same_v<_CharT, char>) - __slash = '/'; - else - __slash = L'/'; - __os2 << __ymwd.year() << __slash << __ymwd.month() << __slash - << __ymwd.weekday_indexed(); - __os << __os2.view(); - return __os; - } + { return __detail::__chrono_write<128>(__os, __ymwd, __os.getloc()); } template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month_weekday_last& __ymwdl) - { - // As above, just write straight to a stringstream, as if by - // "{}/{:L}/{:L}" - basic_stringstream<_CharT> __os2; - __os2.imbue(__os.getloc()); - _CharT __slash; - if constexpr (is_same_v<_CharT, char>) - __slash = '/'; - else - __slash = L'/'; - __os2 << __ymwdl.year() << __slash << __ymwdl.month() << __slash - << __ymwdl.weekday_last(); - __os << __os2.view(); - return __os; - } + { return __detail::__chrono_write<128>(__os, __ymwdl, __os.getloc()); } template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const hh_mm_ss<_Duration>& __hms) - { - return __os << format(__os.getloc(), _GLIBCXX_WIDEN("{:L%T}"), __hms); - } + { return __detail::__chrono_write<64>(__os, __hms, __os.getloc()); } #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI /// Writes a sys_info object to an ostream in an unspecified format. template basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __i) - { - return __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{}"), __i); - } + { return __detail::__chrono_write<128>(__os, __i); } /// Writes a local_info object to an ostream in an unspecified format. template basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __li) - { - __os << __format::_Separators<_CharT>::_S_squares()[0]; - if (__li.result == local_info::unique) - __os << __li.first; - else - { - if (__li.result == local_info::nonexistent) - __os << _GLIBCXX_WIDEN("nonexistent"); - else - __os << _GLIBCXX_WIDEN("ambiguous"); - __os << _GLIBCXX_WIDEN(" local time between ") << __li.first; - __os << _GLIBCXX_WIDEN(" and ") << __li.second; - } - __os << __format::_Separators<_CharT>::_S_squares()[1]; - return __os; - } + { return __detail::__chrono_write<256>(__os, __li); } template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const zoned_time<_Duration, _TimeZonePtr>& __t) - { - __os << format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T %Z}"), __t); - return __os; - } + { return __detail::__chrono_write<128>(__os, __t, __os.getloc()); } #endif template @@ -4044,18 +3875,12 @@ namespace __detail inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_time<_Duration>& __tp) - { - __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __tp); - return __os; - } + { return __detail::__chrono_write<64>(__os, __tp, __os.getloc()); } template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_days& __dp) - { - __os << year_month_day{__dp}; - return __os; - } + { return __detail::__chrono_write<32>(__os, __dp); }; template> @@ -4089,10 +3914,7 @@ namespace __detail inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const utc_time<_Duration>& __t) - { - __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t); - return __os; - } + { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); } template> @@ -4124,10 +3946,7 @@ namespace __detail inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const tai_time<_Duration>& __t) - { - __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t); - return __os; - } + { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); } template> @@ -4163,10 +3982,7 @@ namespace __detail inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const gps_time<_Duration>& __t) - { - __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t); - return __os; - } + { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); } template> @@ -4201,10 +4017,7 @@ namespace __detail inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const file_time<_Duration>& __t) - { - __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t); - return __os; - } + { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); } template> @@ -4227,10 +4040,7 @@ namespace __detail // _GLIBCXX_RESOLVE_LIB_DEFECTS // 4257. Stream insertion for chrono::local_time should be constrained requires requires(const sys_time<_Duration>& __st) { __os << __st; } - { - __os << sys_time<_Duration>{__lt.time_since_epoch()}; - return __os; - } + { return __detail::__chrono_write<64>(__os, __lt, __os.getloc()); } template>