[PATCH] PR libstdc++/81338 correctly manage string capacity

Jonathan Wakely jwakely@redhat.com
Mon Jul 10 18:06:00 GMT 2017


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.

-------------- next part --------------
commit 784c2e1e09ab8a2e85e41d43f021f0163eed7699
Author: Jonathan Wakely <jwakely@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();
+}


More information about the Libstdc++ mailing list