Replace `os << format(...)` with a stack-buffer approach in all
chrono operator<< overloads. A new helper
__detail::__chrono_write formats into a 128-byte stack buffer via
__format::__do_vformat_to_n, then writes through __ostream_insert
which correctly handles sentry construction and width/fill padding.
If the output exceeds the buffer (which should not happen for typical
chrono output), the helper falls back to std::vformat with dynamic
allocation.
A bridge function __detail::__empty_fmt uses basic_format_string to
extract a basic_string_view from __formatter_chrono::_S_empty_fs(),
which is now public. Types whose formatters already handle validation
(day, month, year, weekday, year_month_day) use __empty_fmt instead
of hand-rolled format strings with substr, eliminating manual ok()
checks.
The weekday_indexed and weekday_last operator<< overloads now use
__chrono_write directly, per [time.cal.wdidx.nonmembers] and
[time.cal.wdlast.nonmembers]. There is no need for special failbit
handling because the standard specifies the output as a simple format
call, and __ostream_insert handles state via the standard sentry
mechanism.
This eliminates temporary std::string and std::stringstream allocations
while preserving formatted output function semantics.
libstdc++-v3/ChangeLog:
* include/bits/chrono_io.h (__formatter_chrono::_S_empty_fs):
Make public.
(__detail::__empty_fmt): New function template.
(__detail::__chrono_write): New function template.
(operator<<): Use __chrono_write and __empty_fmt consistently.
(operator<<(weekday_indexed)): Replace stringstream with
__chrono_write.
(operator<<(weekday_last)): Likewise.
Signed-off-by: Anlai Lu <[email protected]>
---
libstdc++-v3/include/bits/chrono_io.h | 202 +++++++++++++++-----------
1 file changed, 115 insertions(+), 87 deletions(-)
diff --git a/libstdc++-v3/include/bits/chrono_io.h
b/libstdc++-v3/include/bits/chrono_io.h
index f8bb485d1..9e17c8ffd 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,77 @@ namespace __detail
}
}
+ // Bridge to extract basic_string_view from _S_empty_fs().
+ // Uses basic_format_string to safely convert, then discards it.
+ template<typename _CharT, typename _Tp>
+ inline constexpr basic_string_view<_CharT>
+ __empty_fmt()
+ {
+ using _Fmt = basic_format_string<_CharT, type_identity_t<_Tp>>;
+ return _Fmt(__format::__formatter_chrono<_CharT>::_S_empty_fs()).get();
+ }
+
+ // Format into stack buffer, write via __ostream_insert.
+ template<typename _CharT, typename _Traits, typename... _Args>
+ inline basic_ostream<_CharT, _Traits>&
+ __chrono_write(basic_ostream<_CharT, _Traits>& __os,
+ basic_string_view<_CharT> __fmt, const _Args&... __args)
+ {
+ constexpr size_t __bufsize = 128;
+ _CharT __buf[__bufsize];
+ auto __res
+ = __format::__do_vformat_to_n(__buf, __bufsize, __fmt,
+ std::make_format_args<
+ __format::__format_context<_CharT>
+ >(__args...));
+ if (static_cast<size_t>(__res.size) <= __bufsize) [[likely]]
+ return std::__ostream_insert(__os, __buf, __res.size);
+ auto __s = std::vformat(__fmt,
+ std::make_format_args<
+ __format::__format_context<_CharT>
+ >(__args...));
+ return std::__ostream_insert(__os, __s.data(), __s.size());
+ }
+
+ template<typename _CharT, typename _Traits, typename... _Args>
+ inline basic_ostream<_CharT, _Traits>&
+ __chrono_write(basic_ostream<_CharT, _Traits>& __os, const locale& __loc,
+ basic_string_view<_CharT> __fmt, const _Args&... __args)
+ {
+ constexpr size_t __bufsize = 128;
+ _CharT __buf[__bufsize];
+ auto __res
+ = __format::__do_vformat_to_n(__buf, __bufsize, __fmt,
+ std::make_format_args<
+ __format::__format_context<_CharT>
+ >(__args...),
+ &__loc);
+ if (static_cast<size_t>(__res.size) <= __bufsize) [[likely]]
+ return std::__ostream_insert(__os, __buf, __res.size);
+ auto __s = std::vformat(__loc, __fmt,
+ std::make_format_args<
+ __format::__format_context<_CharT>>(__args...));
+ return std::__ostream_insert(__os, __s.data(), __s.size());
+ }
+
+ // Thin forwarders for const _CharT* (compile-time literals).
+ template<typename _CharT, typename _Traits, typename... _Args>
+ inline basic_ostream<_CharT, _Traits>&
+ __chrono_write(basic_ostream<_CharT, _Traits>& __os,
+ const _CharT* __fmt, const _Args&... __args)
+ {
+ return __chrono_write(__os, basic_string_view<_CharT>(__fmt), __args...);
+ }
+
+ template<typename _CharT, typename _Traits, typename... _Args>
+ inline basic_ostream<_CharT, _Traits>&
+ __chrono_write(basic_ostream<_CharT, _Traits>& __os, const locale& __loc,
+ const _CharT* __fmt, const _Args&... __args)
+ {
+ return __chrono_write(__os, __loc, basic_string_view<_CharT>(__fmt),
+ __args...);
+ }
+
} // namespace __detail
/// @endcond
@@ -3629,14 +3702,9 @@ namespace __detail
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(__os,
+ __detail::__empty_fmt<_CharT, day>(),
+ __d);
}
template<typename _CharT, typename _Traits,
@@ -3657,18 +3725,9 @@ namespace __detail
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(__os, __os.getloc(),
+ __detail::__empty_fmt<_CharT, month>(),
+ __m);
}
template<typename _CharT, typename _Traits,
@@ -3689,18 +3748,9 @@ namespace __detail
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(__os,
+ __detail::__empty_fmt<_CharT, year>(),
+ __y);
}
template<typename _CharT, typename _Traits,
@@ -3721,18 +3771,9 @@ namespace __detail
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(__os, __os.getloc(),
+ __detail::__empty_fmt<_CharT, weekday>(),
+ __wd);
}
template<typename _CharT, typename _Traits,
@@ -3754,23 +3795,14 @@ namespace __detail
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;
+ // [time.cal.wdidx.nonmembers]
+ basic_string_view<_CharT> __s1 = _GLIBCXX_WIDEN("{:L}[{}]");
+ basic_string_view<_CharT> __s2
+ = _GLIBCXX_WIDEN("{:L}[{} is not a valid index]");
+ auto __i = __wdi.index();
+ return __detail::__chrono_write(__os, __os.getloc(),
+ __i >= 1 && __i <= 5 ? __s1 : __s2,
+ __wdi.weekday(), __i);
}
template<typename _CharT, typename _Traits>
@@ -3778,12 +3810,10 @@ namespace __detail
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;
+ // [time.cal.wdlast.nonmembers]
+ return __detail::__chrono_write(__os, __os.getloc(),
+ _GLIBCXX_WIDEN("{:L}[last]"),
+ __wdl.weekday());
}
template<typename _CharT, typename _Traits>
@@ -3906,12 +3936,8 @@ namespace __detail
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(
+ __os, __detail::__empty_fmt<_CharT, year_month_day>(), __ymd);
}
template<typename _CharT, typename _Traits,
@@ -3994,7 +4020,8 @@ namespace __detail
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(__os, __os.getloc(),
+ _GLIBCXX_WIDEN("{:L%T}"), __hms);
}
#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
@@ -4003,7 +4030,8 @@ namespace __detail
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(
+ __os, __os.getloc(), __detail::__empty_fmt<_CharT, sys_info>(), __i);
}
/// Writes a local_info object to an ostream in an unspecified format.
@@ -4033,8 +4061,8 @@ namespace __detail
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(__os, __os.getloc(),
+ _GLIBCXX_WIDEN("{:L%F %T %Z}"), __t);
}
#endif
@@ -4045,8 +4073,8 @@ namespace __detail
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(__os, __os.getloc(),
+ _GLIBCXX_WIDEN("{:L%F %T}"), __tp);
}
template<typename _CharT, typename _Traits>
@@ -4090,8 +4118,8 @@ namespace __detail
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(__os, __os.getloc(),
+ _GLIBCXX_WIDEN("{:L%F %T}"), __t);
}
template<typename _CharT, typename _Traits, typename _Duration,
@@ -4125,8 +4153,8 @@ namespace __detail
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(__os, __os.getloc(),
+ _GLIBCXX_WIDEN("{:L%F %T}"), __t);
}
template<typename _CharT, typename _Traits, typename _Duration,
@@ -4164,8 +4192,8 @@ namespace __detail
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(__os, __os.getloc(),
+ _GLIBCXX_WIDEN("{:L%F %T}"), __t);
}
template<typename _CharT, typename _Traits, typename _Duration,
@@ -4202,8 +4230,8 @@ namespace __detail
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(__os, __os.getloc(),
+ _GLIBCXX_WIDEN("{:L%F %T}"), __t);
}
template<typename _CharT, typename _Traits, typename _Duration,
--
2.34.1