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

Reply via email to