[PATCH v2 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. Stack writes go through
_M_out._M_put() which tracks failure on the iterator.  sputn
exceptions are caught and the iterator is marked failed via
_M_set_failed().

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]>
---
Changes in v2:
 - limit unused/reserved buffer to the remaining characters
   in _M_overflow/_M_reserve and constructor. Mostly via
   calling _M_trim_buf. Rewrote _M_overflow in process
 - overloads constructor without limit to avoid above
 - befriend _Iter_sink specialization with ostreambuf_iterator
 - remove _M_out member and recosntructor iterator in _M_finish

Testing on powerprc64. Tested on x86_64, additionally tested
*format* and *print* in all standard modes, assertions and
debug. OK for trunk?

 .../include/bits/streambuf_iterator.h         | 12 +++
 libstdc++-v3/include/std/format               | 82 +++++++++++++++++++
 2 files changed, 94 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 9223cd2e78b..ad6a56ecc3a 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -3847,6 +3847,88 @@ 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();
+	if (_M_count >= _M_max)
+	  this->_M_reset(this->_M_buf);
+	else if (size_t __a = _M_max - _M_count; __a > __new)
+	  {
+	    _Base::_M_overflow();
+	    _M_trim_buf();
+	  }
+	else
+	  {
+	    this->_M_flush();
+	    this->_M_reset(this->_M_buf);
+	  }
+	_M_count += __new;
+      }
+
+      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 __a = _M_max - _M_count; __a >= __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;
+	__out._M_sbuf = 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.