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

Reply via email to