On Wed, 7 Jan 2026 at 11:56, Jonathan Wakely <[email protected]> wrote:
>
> When basic_stringbuf::setbuf has been called we need to copy the
> contents of the buffer into _M_string first, before returning that.
>
> libstdc++-v3/ChangeLog:
>
> PR libstdc++/123100
> * include/std/sstream (basic_stringbuf::str()&&): Handle the
> case where _M_string is not being used for the buffer.
> * testsuite/27_io/basic_stringbuf/str/char/123100.cc: New test.
> ---
>
> Tested x86_64-linux.
>
> libstdc++-v3/include/std/sstream | 9 ++-
> .../27_io/basic_stringbuf/str/char/123100.cc | 58 +++++++++++++++++++
> 2 files changed, 64 insertions(+), 3 deletions(-)
> create mode 100644
> libstdc++-v3/testsuite/27_io/basic_stringbuf/str/char/123100.cc
>
> diff --git a/libstdc++-v3/include/std/sstream
> b/libstdc++-v3/include/std/sstream
> index 4a5c090f97ba..f036165909df 100644
> --- a/libstdc++-v3/include/std/sstream
> +++ b/libstdc++-v3/include/std/sstream
> @@ -298,7 +298,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> #if __cplusplus > 201703L
> #if _GLIBCXX_USE_CXX11_ABI
> #if __cpp_concepts
> - // P0407 Allocator-aware basic_streambuf
> + // P0407 Allocator-aware basic_streambuf
This is just a whitespace fix in the vicinity of the bug fix.
> template<__allocator_like _SAlloc>
> _GLIBCXX_NODISCARD
> basic_string<_CharT, _Traits, _SAlloc>
> @@ -315,8 +315,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> {
> if (char_type* __hi = _M_high_mark())
> {
> - // Set length to end of character sequence and add null
> terminator.
> - _M_string._M_set_length(_M_high_mark() - this->pbase());
> + if (_M_string.c_str() == this->pbase()) [[likely]]
> + // Set length to end of sequence and add null terminator.
> + _M_string._M_set_length(__hi - this->pbase());
> + else
> + _M_string.assign(this->pbase(), __hi);
> }
> auto __str = std::move(_M_string);
> _M_string.clear();
> diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/str/char/123100.cc
> b/libstdc++-v3/testsuite/27_io/basic_stringbuf/str/char/123100.cc
> new file mode 100644
> index 000000000000..174fe0ece1d6
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/str/char/123100.cc
> @@ -0,0 +1,58 @@
> +// { dg-do run }
> +
> +#include <sstream>
> +#include <testsuite_hooks.h>
> +
> +void
> +test01()
> +{
> + const int n = 20;
> + const char data[n] = "abcde";
> + char buf[n] = "0123456789";
> + std::string expected("abcde56789\0\0\0\0\0\0\0\0\0\0", n);
> +
> + std::ostringstream out;
> + out.rdbuf()->pubsetbuf(buf, n);
> + out << data;
> + VERIFY( out.str() == expected );
> + VERIFY( out.str() == expected );
> +#if __cplusplus >= 201103L
> + VERIFY( std::move(out).str() == expected );
> +#if __cplusplus >= 202002L && _GLIBCXX_USE_CXX11_ABI
> + expected.clear();
> +#endif
> + VERIFY( out.str() == expected );
> + VERIFY( std::move(out).str() == expected );
> +#endif
> +}
> +
> +void
> +test02()
> +{
> + const int n = 20;
> + const char data[n] = "abcde";
> + char buf[n] = "0123456789";
> + std::string expected("abcde56789\0\0\0\0\0\0\0\0\0\0", n);
> +
> + std::ostringstream out;
> + out << std::string(n * 2, 'a');
> + VERIFY( out.str() == std::string(n * 2, 'a') );
> + out.rdbuf()->pubsetbuf(buf, n);
> + out << data; // writes 6 chars
> + VERIFY( out.str() == expected );
> + VERIFY( out.str() == expected );
> +#if __cplusplus >= 201103L
> + VERIFY( std::move(out).str() == expected );
> +#if __cplusplus >= 202002L && _GLIBCXX_USE_CXX11_ABI
> + expected.clear();
> +#endif
> + VERIFY( out.str() == expected );
> + VERIFY( std::move(out).str() == expected );
> +#endif
> +}
> +
> +int main()
> +{
> + test01();
> + test02();
> +}
> --
> 2.52.0
>