This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[patch] libstdc++/56166 avoid allocation in basic_string::clear()
- From: Jonathan Wakely <jwakely at redhat dot com>
- To: libstdc++ at gcc dot gnu dot org, gcc-patches at gcc dot gnu dot org
- Date: Tue, 3 Jun 2014 22:32:35 +0100
- Subject: [patch] libstdc++/56166 avoid allocation in basic_string::clear()
- Authentication-results: sourceware.org; auth=none
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.
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) {
+ }
+ }
+}