Add partial specialization of _Iter_sink for ostreambuf_iterator
that inherits _Streambuf_sink, replacing per-character sputc with
bulk sputn and zero-copy put-area writes.
All counting and truncation (_M_max) is handled in this
specialization so that _Streambuf_sink stays a pure I/O layer.
_M_overflow counts all characters and only writes up to the limit,
so format_to_n can compute the total output length. _M_discarding
returns false for the same reason. Stack writes go through
_M_out._M_put() which tracks failure on the iterator.
libstdc++-v3/ChangeLog:
* include/std/format
(_Iter_sink<ostreambuf_iterator>): New partial specialization.
Signed-off-by: Anlai Lu <[email protected]>
---
libstdc++-v3/include/std/format | 77 +++++++++++++++++++++++++++++++++
1 file changed, 77 insertions(+)
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index cb6cc4592..b14ed9746 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -3843,6 +3843,83 @@ namespace __format
}
};
+ // Specialization replacing per-character sputc with bulk sputn
+ // and zero-copy writes into the streambuf's put area.
+ template<typename _CharT, typename _Traits>
+ class _Iter_sink<_CharT, ostreambuf_iterator<_CharT, _Traits>>
+ : public _Streambuf_sink<_CharT, _Traits>
+ {
+ using _Base = _Streambuf_sink<_CharT, _Traits>;
+ using typename _Base::_Sink_state;
+ using _OutIter = ostreambuf_iterator<_CharT, _Traits>;
+ using __diff_t = iter_difference_t<_OutIter>;
+ _OutIter _M_out;
+ __diff_t _M_max;
+
+ protected:
+ size_t _M_count = 0;
+
+ _GLIBCXX_CONSTEXPR_FORMAT void
+ _M_overflow() override
+ {
+ auto __s = this->_M_used();
+ if (__s.empty()) [[unlikely]]
+ return;
+
+ size_t __n = __s.size();
+ size_t __commit = 0;
+ if (_M_max < 0)
+ __commit = __n;
+ else if (_M_count < static_cast<size_t>(_M_max))
+ {
+ size_t __max = _M_max - _M_count;
+ __commit = min(__n, __max);
+ }
+
+ _M_count += __n;
+ switch (this->_M_state)
+ {
+ case _Sink_state::_S_stack:
+ // _M_put checks sputn's return value and sets
+ // _M_failed on the iterator on short write, so
+ // the caller sees the failure.
+ _M_out._M_put(__s.data(), __commit);
+ break;
+ case _Sink_state::_S_put_area:
+ this->_M_pbump(__commit);
+ break;
+ }
+
+ if (!this->_M_use_put_area())
+ this->_M_use_stackbuf();
+ }
+
+ _GLIBCXX_CONSTEXPR_FORMAT bool
+ _M_discarding() const override
+ {
+ return false;
+ }
+
+ public:
+ [[__gnu__::__always_inline__]]
+ _GLIBCXX_CONSTEXPR_FORMAT explicit
+ _Iter_sink(_OutIter __out, __diff_t __max = -1)
+ : _Base(__out._M_get_sbuf()), _M_out(__out), _M_max(__max)
+ { }
+
+ using _Base::out;
+
+ _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_OutIter>
+ _M_finish() &&
+ {
+ if (this->_M_used().size() != 0)
+ _M_overflow();
+
+ iter_difference_t<_OutIter> __count(_M_count);
+ return { std::move(_M_out), __count };
+ }
+ };
+
// Used for contiguous iterators.
// No buffer is used, characters are written straight to the iterator.
// We do not know the size of the output range, so the span size just grows
--
2.34.1