diff mbox

libstdc++/56166 avoid allocation in basic_string::clear()

Message ID 20140603213235.GX6953@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely June 3, 2014, 9:32 p.m. UTC
Instead of cloning the string and then setting its length to zero,
just point to the empty rep instead.

I'm not sure if this is conforming in C++03, because it modifies the
capacity(), whereas clear() is specified as if it called
erase(begin(), end()), which typically wouldn't alter the capacity
(but I'm not sure whether it's allowed to alter it).

It passes all tests, but I'm not planning to commit it.
diff mbox

Patch

commit 83de17363e8978c4ee6af499aaab9e3734863449
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Jun 3 20:23:10 2014 +0100

    	PR libstdc++/56166
    	* include/bits/basic_string.h (basic_string::clear()): Drop reference
    	and use empty rep.
    	* include/ext/rc_string_base.h (__rc_string_base::_M_clear()):
    	Likewise.
    	* testsuite/21_strings/basic_string/56166.cc: New.
    	* testsuite/ext/vstring/modifiers/clear/56166.cc: New.

diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h
index cd60376..420ead5 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -808,10 +808,19 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       /**
        *  Erases the string, making it empty.
        */
+#if _GLIBCXX_FULLY_DYNAMIC_STRING == 0
+      void
+      clear() _GLIBCXX_NOEXCEPT
+      {
+	_M_rep()->_M_dispose(this->get_allocator());
+	_M_data(_S_empty_rep()._M_refdata());
+      }
+#else
       // PR 56166: this should not throw.
       void
       clear()
       { _M_mutate(0, this->size(), 0); }
+#endif
 
       /**
        *  Returns true if the %string is empty.  Equivalent to 
diff --git a/libstdc++-v3/include/ext/rc_string_base.h b/libstdc++-v3/include/ext/rc_string_base.h
index 4c72407..e9df075 100644
--- a/libstdc++-v3/include/ext/rc_string_base.h
+++ b/libstdc++-v3/include/ext/rc_string_base.h
@@ -354,7 +354,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       void
       _M_clear()
-      { _M_erase(size_type(0), _M_length()); }
+      {
+	_M_dispose();
+	_M_data(_S_empty_rep._M_refcopy());
+      }
 
       bool
       _M_compare(const __rc_string_base&) const
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/56166.cc b/libstdc++-v3/testsuite/21_strings/basic_string/56166.cc
new file mode 100644
index 0000000..b680afa
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/56166.cc
@@ -0,0 +1,90 @@ 
+// Copyright (C) 2014 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-options " -std=gnu++11 " }
+
+// libstdc++/56166
+
+#include <string>
+#include <new>
+
+static int fail_after = -1;
+
+template<typename T>
+  struct Allocator
+  {
+    using value_type = T;
+
+    // shouldn't need these typedefs, but <string> doesn't use allocator_traits
+    using pointer = T*;
+    using const_pointer = const T*;
+    using reference = T&;
+    using const_reference = const T&;
+    using difference_type = long;
+    using size_type = unsigned long;
+    template<typename U>
+      struct rebind {
+        using other = Allocator<U>;
+      };
+
+    Allocator() { }
+
+    template<typename U>
+      Allocator(const Allocator<U>&) { }
+
+    T* allocate(size_type n)
+    {
+      if (fail_after >= 0) {
+        if (fail_after-- == 0) {
+          throw std::bad_alloc();
+        }
+      }
+      return (T*)new char[n * sizeof(T)];
+    }
+
+    void deallocate(T* p, size_type)
+    {
+      delete[] (char*)p;
+    }
+  };
+
+template<typename T>
+  bool operator==(const Allocator<T>&, const Allocator<T>&) { return true; }
+template<typename T>
+  bool operator!=(const Allocator<T>&, const Allocator<T>&) { return false; }
+
+using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+
+string f()
+{
+  string s1("xxxxxx");
+  string s2 = s1;
+  s1.clear();
+  return s2;
+}
+
+int main()
+{
+  for (int i = 0; i < 10; i++) {
+    try {
+      fail_after = i;
+      f();
+      break;
+    } catch (std::bad_alloc) {
+    }
+  }
+}
diff --git a/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc b/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc
new file mode 100644
index 0000000..0217023
--- /dev/null
+++ b/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc
@@ -0,0 +1,93 @@ 
+// Copyright (C) 2014 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-options " -std=gnu++11 " }
+
+// libstdc++/56166
+
+#include <ext/vstring.h>
+#include <new>
+
+static int fail_after = -1;
+
+template<typename T>
+  struct Allocator
+  {
+    using value_type = T;
+
+    // shouldn't need these typedefs, but <string> doesn't use allocator_traits
+    using pointer = T*;
+    using const_pointer = const T*;
+    using reference = T&;
+    using const_reference = const T&;
+    using difference_type = long;
+    using size_type = unsigned long;
+    template<typename U>
+      struct rebind {
+        using other = Allocator<U>;
+      };
+
+    Allocator() { }
+
+    template<typename U>
+      Allocator(const Allocator<U>&) { }
+
+    T* allocate(size_type n)
+    {
+      if (fail_after >= 0) {
+        if (fail_after-- == 0) {
+          throw std::bad_alloc();
+        }
+      }
+      return (T*)new char[n * sizeof(T)];
+    }
+
+    void deallocate(T* p, size_type)
+    {
+      delete[] (char*)p;
+    }
+  };
+
+template<typename T>
+  bool operator==(const Allocator<T>&, const Allocator<T>&) { return true; }
+template<typename T>
+  bool operator!=(const Allocator<T>&, const Allocator<T>&) { return false; }
+
+
+using string = __gnu_cxx::__versa_string<char, std::char_traits<char>,
+                                         Allocator<char>,
+                                         __gnu_cxx::__rc_string_base>;
+
+string f()
+{
+  string s1("xxxxxx");
+  string s2 = s1;
+  s1.clear();
+  return s2;
+}
+
+int main()
+{
+  for (int i = 0; i < 10; i++) {
+    try {
+      fail_after = i;
+      f();
+      break;
+    } catch (std::bad_alloc) {
+    }
+  }
+}