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

Tomasz Kaminski <[email protected]>
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.libstdc++.devel
Message-ID <CAKvuMXAdXy=hwoD5y+Y6nMdrvNnReen1L43M1-qi_3uGuNqvdg@mail.gmail.com>
On Thu, Jul 16, 2026 at 2:06 PM Anlai Lu <[email protected]> wrote:

> 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 <[email protected]>
>
I will again posted updated patch addressing the comment below.

> ---
>  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())
>
Here, if we we actuall use put_area we do not limit size of
the buffer and thus may write into put_area more than n character,
overriding existing ons. This leads to the failures
in test_format_to_n_prefilled,
once we configure put_area use on constructor (as mentioned in previous
patch).

A similar case may happen for reserve, where we provide a chunk of
put_area that is larger then remaining characters we can write, thus
overriding it.

> +         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
>
>
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.