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

Anlai Lu <[email protected]>
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.libstdc++.devel
Message-ID <[email protected]>
Hi Tomasz,

I have reviewed the whole series. The fixes for the issues I reported
are correct and the new tests look good. LGTM.

Thanks,
Anlai

> On Aug 18, 2026, at 16:07, Tomasz Kamiński <[email protected]> wrote:
> 
> 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.