[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.