[PATCH 3/4] libstdc++: Specialize _Iter_sink for ostreambuf_iterator

Anlai Lu agicy@qq.com
Thu Jul 16 12:03:47 GMT 2026


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/std/format
	(_Iter_sink<ostreambuf_iterator>): New partial specialization.

Signed-off-by: Anlai Lu <agicy@qq.com>
---
 libstdc++-v3/include/std/format | 67 +++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index b676629b1..63d12eff8 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -3867,6 +3867,73 @@ 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>;
+      _OutIter _M_out;
+      size_t _M_max;
+
+      static constexpr size_t _S_no_limit = size_t(-1);
+
+    protected:
+      size_t _M_count = 0;
+
+      _GLIBCXX_CONSTEXPR_FORMAT span<_CharT>
+      _M_limit(span<_CharT> __s)
+      {
+	size_t __n = __s.size();
+	size_t __commit;
+	if (_M_max == _S_no_limit)
+	  __commit = __n;
+	else if (_M_count >= _M_max)
+	  __commit = 0;
+	else
+	  __commit = min(__n, _M_max - _M_count);
+	_M_count += __n;
+	return __s.first(__commit);
+      }
+
+      _GLIBCXX_CONSTEXPR_FORMAT void
+      _M_overflow() override
+      {
+	auto __s = this->_M_limit(this->_M_used());
+	this->_M_flush(__s);
+	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, iter_difference_t<_OutIter> __max = -1)
+      : _Base(__out._M_get_sbuf()), _M_out(__out),
+	_M_max(__max < 0 ? _S_no_limit : size_t(__max))
+      { }
+
+      using _Base::out;
+
+      _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_OutIter>
+      _M_finish() &&
+      {
+	auto __s = this->_M_limit(this->_M_used());
+	this->_M_flush(__s);
+
+	if (this->_M_write_failed)
+	  _M_out._M_set_failed();
+	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



More information about the Libstdc++ mailing list