This fixes two problems with basic_stringbuf when using SSO strings.
Firstly, overflow() won't try to use excess capacity in the string (which is present on construction, due to the SSO buffer, which can't be utilised immediately due to LWG 2995, see PR 80676). That causes overflow to allocate a new buffer on the heap. If the stringbuf is reset (by move-assignment) and then assigned to again, it will try to reallocate an even bigger buffer, despite having one already. The testcase in PR 81338 keeps doing that until it gets bad_alloc. That's fixed by checking for unused capacity in overflow. Secondly, move assignment of std::string objects was not copying the entire buffer sequence, only the first _M_string.length() characters. This is because basic_stringbuf writes directly into a string's unused capacity, without modifying the string's length. The solution is to update the string length before move operations, to make it match the longer of the get and put sequences. PR libstdc++/81338 * include/bits/basic_string.h [_GLIBCXX_USE_CXX11_ABI] (basic_string): Declare basic_stringbuf to be a friend. * include/bits/sstream.tcc (basic_stringbuf::overflow) [_GLIBCXX_USE_CXX11_ABI]: Use unused capacity before reallocating. * include/std/sstream (basic_stringbuf::__xfer_bufptrs): Update string length to buffer length. * testsuite/27_io/basic_stringstream/assign/81338.cc: New. Tested powerpc64le-linux, committed to trunk.
commit 784c2e1e09ab8a2e85e41d43f021f0163eed7699 Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu Jul 6 16:44:14 2017 +0100 PR libstdc++/81338 correctly manage string capacity PR libstdc++/81338 * include/bits/basic_string.h [_GLIBCXX_USE_CXX11_ABI] (basic_string): Declare basic_stringbuf to be a friend. * include/bits/sstream.tcc (basic_stringbuf::overflow) [_GLIBCXX_USE_CXX11_ABI]: Use unused capacity before reallocating. * include/std/sstream (basic_stringbuf::__xfer_bufptrs): Update string length to buffer length. * testsuite/27_io/basic_stringstream/assign/81338.cc: New. diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index 519d686..7fd867c 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -2918,7 +2918,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 int compare(size_type __pos, size_type __n1, const _CharT* __s, size_type __n2) const; - }; + + // Allow basic_stringbuf::__xfer_bufptrs to call _M_length: + template<typename, typename, typename> friend class basic_stringbuf; + }; _GLIBCXX_END_NAMESPACE_CXX11 #else // !_GLIBCXX_USE_CXX11_ABI // Reference-counted COW string implentation diff --git a/libstdc++-v3/include/bits/sstream.tcc b/libstdc++-v3/include/bits/sstream.tcc index 72e8742..fc2fcb8 100644 --- a/libstdc++-v3/include/bits/sstream.tcc +++ b/libstdc++-v3/include/bits/sstream.tcc @@ -88,6 +88,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return traits_type::not_eof(__c); const __size_type __capacity = _M_string.capacity(); + +#if _GLIBCXX_USE_CXX11_ABI + if ((this->epptr() - this->pbase()) < __capacity) + { + // There is additional capacity in _M_string that can be used. + char_type* __base = const_cast<char_type*>(_M_string.data()); + _M_pbump(__base, __base + __capacity, this->pptr() - this->pbase()); + if (_M_mode & ios_base::in) + { + const __size_type __nget = this->gptr() - this->eback(); + const __size_type __eget = this->egptr() - this->eback(); + this->setg(__base, __base + __nget, __base + __eget + 1); + } + *this->pptr() = traits_type::to_char_type(__c); + this->pbump(1); + return __c; + } +#endif + const __size_type __max_size = _M_string.max_size(); const bool __testput = this->pptr() < this->epptr(); if (__builtin_expect(!__testput && __capacity == __max_size, false)) diff --git a/libstdc++-v3/include/std/sstream b/libstdc++-v3/include/std/sstream index 2a56d73..7690252 100644 --- a/libstdc++-v3/include/std/sstream +++ b/libstdc++-v3/include/std/sstream @@ -302,18 +302,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 __xfer_bufptrs(const basic_stringbuf& __from, basic_stringbuf* __to) : _M_to{__to}, _M_goff{-1, -1, -1}, _M_poff{-1, -1, -1} { - const _CharT* __str = __from._M_string.data(); + const _CharT* const __str = __from._M_string.data(); + const _CharT* __end = nullptr; if (__from.eback()) { - _M_goff[0] = __from.eback() - __str; - _M_goff[1] = __from.gptr() - __str; - _M_goff[2] = __from.egptr() - __str; + _M_goff[0] = __from.eback() - __str; + _M_goff[1] = __from.gptr() - __str; + _M_goff[2] = __from.egptr() - __str; + __end = __from.egptr(); } if (__from.pbase()) { _M_poff[0] = __from.pbase() - __str; _M_poff[1] = __from.pptr() - __from.pbase(); _M_poff[2] = __from.epptr() - __str; + if (__from.pptr() > __end) + __end = __from.pptr(); + } + + // Set _M_string length to the greater of the get and put areas. + if (__end) + { + // The const_cast avoids changing this constructor's signature, + // because it is exported from the dynamic library. + auto& __mut_from = const_cast<basic_stringbuf&>(__from); + __mut_from._M_string._M_length(__end - __str); } } diff --git a/libstdc++-v3/testsuite/27_io/basic_stringstream/assign/81338.cc b/libstdc++-v3/testsuite/27_io/basic_stringstream/assign/81338.cc new file mode 100644 index 0000000..30370c0 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_stringstream/assign/81338.cc @@ -0,0 +1,40 @@ +// Copyright (C) 2017 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do run { target c++11 } } + +#include <sstream> +#include <testsuite_hooks.h> + +void +test01() +{ + std::stringstream ss; + for (int i = 0; i < 100; ++i) + { + ss << 'a'; + VERIFY( static_cast<bool>(ss) ); + VERIFY( ss.str() == "a" ); + ss = std::stringstream(); + } +} + +int +main() +{ + test01(); +}