[PATCH v3 3/4] libstdc++: Specialize _Iter_sink for ostreambuf_iterator
Tomasz Kamiński <[email protected]>
| Newsgroups | gmane.comp.gcc.patches,gmane.comp.gcc.libstdc++.devel |
|---|---|
| Message-ID | <[email protected]> |
From: Anlai Lu <[email protected]> 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. The _M_write_failed is propagated to returned iterator on _M_finish. The maximum count uses size_t with _S_no_limit sentinel, matching the _Ptr_sink convention and avoiding signed comparisons. libstdc++-v3/ChangeLog: * include/bits/streambuf_iterator.h (__format::_Iter_sink) [__glibcxx_format]: Declare with specialization for ostreambuf_iterator. (std::ostreambuf_iterator) [__glibcxx_format]: Befriend __format::_Iter_sink for ostreambuf_iterator. * include/std/format (_Iter_sink<_CharT, ostreambuf_iterator<...>>): Define new partial specialization. Co-authored-by: Tomasz Kamiński <[email protected]> Signed-off-by: Anlai Lu <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> --- v3 fixes the _M_trim_buf and _M_flush issue pointed by Anlai Liu, and updates commit description. .../include/bits/streambuf_iterator.h | 12 +++ libstdc++-v3/include/std/format | 83 +++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/libstdc++-v3/include/bits/streambuf_iterator.h b/libstdc++-v3/include/bits/streambuf_iterator.h index 095928ca4d8..d715edc24ae 100644 --- a/libstdc++-v3/include/bits/streambuf_iterator.h +++ b/libstdc++-v3/include/bits/streambuf_iterator.h @@ -42,6 +42,14 @@ namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION +#ifdef __glibcxx_format // C++ >= 20 && HOSTED + namespace __format { + template<typename, typename> class _Iter_sink; + template<typename _CharT, typename _Traits> + class _Iter_sink<_CharT, ostreambuf_iterator<_CharT, _Traits>>; + } +#endif + /** * @addtogroup iterators * @{ @@ -266,6 +274,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION copy(istreambuf_iterator<_CharT2>, istreambuf_iterator<_CharT2>, ostreambuf_iterator<_CharT2>); +#ifdef __glibcxx_format // C++ >= 20 && HOSTED + friend class __format::_Iter_sink<char_type, ostreambuf_iterator>; +#endif + private: streambuf_type* _M_sbuf; bool _M_failed; diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index f1698c3eb68..e8b1823d0d8 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -3849,6 +3849,89 @@ 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 _OutIter = ostreambuf_iterator<_CharT, _Traits>; + + static constexpr size_t _S_no_limit = size_t(-1); + + void + _M_trim_buf() + { + const size_t __avail = _M_max - _M_count; + if (this->_M_unused().size() > __avail) + this->_M_reset(this->_M_unused().first(__avail)); + } + + protected: + size_t _M_max = _S_no_limit; + size_t _M_count = 0; + + void + _M_overflow() override + { + const size_t __new = this->_M_used().size(); + const size_t __pcount = _M_count; + _M_count += __new; + + if (__pcount >= _M_max) + this->_M_reset(this->_M_buf); + else if (size_t __avail = _M_max - __pcount; __avail > __new) + { + _Base::_M_overflow(); + _M_trim_buf(); + } + else + { + this->_M_flush(); + this->_M_reset(this->_M_buf); + } + } + + bool + _M_discarding() const override + { return false; } + + public: + [[__gnu__::__always_inline__]] + explicit + _Iter_sink(_OutIter __out) + : _Base(__out._M_sbuf) + { } + + [[__gnu__::__always_inline__]] + explicit + _Iter_sink(_OutIter __out, iter_difference_t<_OutIter> __max) + : _Base(__out._M_sbuf), _M_max(__max < 0 ? _S_no_limit : size_t(__max)) + { _M_trim_buf(); } + + typename _Sink<_CharT>::_Reservation + _M_reserve(size_t __n) override + { + if (_M_count < _M_max) + if (size_t __avail = _M_max - _M_count; __avail >= __n) + return _Base::_M_reserve(__n); + return { nullptr }; + } + + format_to_n_result<_OutIter> + _M_finish() && + { + _M_count += this->_M_used().size(); + if (_M_count <= _M_max) + this->_M_flush(); + + _OutIter __out(this->_M_sbuf); + __out._M_failed = this->_M_write_failed; + return { std::move(__out), ptrdiff_t(_M_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.55.0