This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
PATCH RFA: Handle erasing a reference to an entry in a hash table
- From: Ian Lance Taylor <iant at google dot com>
- To: gcc-patches at gcc dot gnu dot org, libstdc++ at gcc dot gnu dot org
- Date: Mon, 30 Mar 2009 22:46:10 -0700
- Subject: PATCH RFA: Handle erasing a reference to an entry in a hash table
PR 25896 is about a problem when passing a reference to an entry in a
hash table to the erase function. The issue was fixed in the TR1
classes unordered_map and friends. This patch extends the fix to
hash_map and friends.
Tested by building libstdc++-v3 and running the C++ and libstdc++-v3
testsuite on i686-pc-linux-gnu. The patch includes a couple of tests
copied from the tests included with the fix for PR 25896.
OK to commit?
Ian
2009-03-30 Ian Lance Taylor <iant@google.com>
* include/backward/hashtable.h (erase): Correctly handle erasing a
reference to an entry in the hash table.
* testsuite/backward/hash_map/25896.cc: New.
* testsuite/backward/hash_set/25896.cc: New.
Index: include/backward/hashtable.h
===================================================================
--- include/backward/hashtable.h (revision 145332)
+++ include/backward/hashtable.h (working copy)
@@ -869,8 +869,9 @@
{
const size_type __n = _M_bkt_num_key(__key);
_Node* __first = _M_buckets[__n];
+ _Node* __saved_slot = 0;
size_type __erased = 0;
-
+
if (__first)
{
_Node* __cur = __first;
@@ -879,11 +880,20 @@
{
if (_M_equals(_M_get_key(__next->_M_val), __key))
{
- __cur->_M_next = __next->_M_next;
- _M_delete_node(__next);
- __next = __cur->_M_next;
- ++__erased;
- --_M_num_elements;
+ if (&_M_get_key(__next->_M_val) != &__key)
+ {
+ __cur->_M_next = __next->_M_next;
+ _M_delete_node(__next);
+ __next = __cur->_M_next;
+ ++__erased;
+ --_M_num_elements;
+ }
+ else
+ {
+ __saved_slot = __cur;
+ __cur = __next;
+ __next = __cur->_M_next;
+ }
}
else
{
@@ -898,6 +908,14 @@
++__erased;
--_M_num_elements;
}
+ if (__saved_slot)
+ {
+ __next = __saved_slot->_M_next;
+ __saved_slot->_M_next = __next->_M_next;
+ _M_delete_node(__next);
+ ++__erased;
+ --_M_num_elements;
+ }
}
return __erased;
}
Index: testsuite/backward/hash_map/25896.cc
===================================================================
--- testsuite/backward/hash_map/25896.cc (revision 0)
+++ testsuite/backward/hash_map/25896.cc (revision 0)
@@ -0,0 +1,161 @@
+// { dg-options "-Wno-deprecated" }
+
+// Copyright (C) 2007, 2009 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 2, 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 COPYING. If not, write to the Free
+// Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+// USA.
+
+// This is a copy of tr1/6_containers/unordered_map/erase/1.cc, using
+// hash_map instead of unordered_map.
+
+#include <hash_map>
+#include <string>
+#include <testsuite_hooks.h>
+
+namespace __gnu_cxx
+{
+ using std::string;
+
+ inline size_t hash_string(const char* s)
+ {
+ unsigned long h;
+ for (h=0; *s; ++s) {
+ h = 5*h + *s;
+ }
+ return size_t(h);
+ }
+
+ template<class T> struct hash<T *>
+ {
+ size_t operator()(const T *const & s) const
+ { return reinterpret_cast<size_t>(s); }
+ };
+
+ template<> struct hash<string>
+ {
+ size_t operator()(const string &s) const { return hash_string(s.c_str()); }
+ };
+
+ template<> struct hash<const string>
+ {
+ size_t operator()(const string &s) const { return hash_string(s.c_str()); }
+ };
+}
+
+void test01()
+{
+ bool test __attribute__((unused)) = true;
+
+ typedef __gnu_cxx::hash_map<std::string, int> Map;
+ typedef Map::iterator iterator;
+ typedef Map::const_iterator const_iterator;
+ typedef Map::value_type value_type;
+
+ Map m1;
+
+ m1.insert(value_type("because to why", 1));
+ m1.insert(value_type("the stockholm syndrome", 2));
+ m1.insert(value_type("a cereous night", 3));
+ m1.insert(value_type("eeilo", 4));
+ m1.insert(value_type("protean", 5));
+ m1.insert(value_type("the way you are when", 6));
+ m1.insert(value_type("tillsammans", 7));
+ m1.insert(value_type("umbra/penumbra", 8));
+ m1.insert(value_type("belonging (no longer mix)", 9));
+ m1.insert(value_type("one line behind", 10));
+ VERIFY( m1.size() == 10 );
+
+ VERIFY( m1.erase("eeilo") == 1 );
+ VERIFY( m1.size() == 9 );
+ iterator it1 = m1.find("eeilo");
+ VERIFY( it1 == m1.end() );
+
+ VERIFY( m1.erase("tillsammans") == 1 );
+ VERIFY( m1.size() == 8 );
+ iterator it2 = m1.find("tillsammans");
+ VERIFY( it2 == m1.end() );
+
+ // Must work (see DR 526)
+ iterator it3 = m1.find("belonging (no longer mix)");
+ VERIFY( it3 != m1.end() );
+ VERIFY( m1.erase(it3->first) == 1 );
+ VERIFY( m1.size() == 7 );
+ it3 = m1.find("belonging (no longer mix)");
+ VERIFY( it3 == m1.end() );
+
+ VERIFY( !m1.erase("abra") );
+ VERIFY( m1.size() == 7 );
+
+ VERIFY( !m1.erase("eeilo") );
+ VERIFY( m1.size() == 7 );
+
+ VERIFY( m1.erase("because to why") == 1 );
+ VERIFY( m1.size() == 6 );
+ iterator it4 = m1.find("because to why");
+ VERIFY( it4 == m1.end() );
+
+ iterator it5 = m1.find("umbra/penumbra");
+ iterator it6 = m1.find("one line behind");
+ VERIFY( it5 != m1.end() );
+ VERIFY( it6 != m1.end() );
+
+ VERIFY( m1.find("the stockholm syndrome") != m1.end() );
+ VERIFY( m1.find("a cereous night") != m1.end() );
+ VERIFY( m1.find("the way you are when") != m1.end() );
+ VERIFY( m1.find("a cereous night") != m1.end() );
+
+ VERIFY( m1.erase(it5->first) == 1 );
+ VERIFY( m1.size() == 5 );
+ it5 = m1.find("umbra/penumbra");
+ VERIFY( it5 == m1.end() );
+
+ VERIFY( m1.erase(it6->first) == 1 );
+ VERIFY( m1.size() == 4 );
+ it6 = m1.find("one line behind");
+ VERIFY( it6 == m1.end() );
+
+ iterator it7 = m1.begin();
+ iterator it8 = it7;
+ ++it8;
+ iterator it9 = it8;
+ ++it9;
+
+ VERIFY( m1.erase(it8->first) == 1 );
+ VERIFY( m1.size() == 3 );
+ VERIFY( ++it7 == it9 );
+
+ iterator it10 = it9;
+ ++it10;
+ iterator it11 = it10;
+
+ VERIFY( m1.erase(it9->first) == 1 );
+ VERIFY( m1.size() == 2 );
+ VERIFY( ++it10 == m1.end() );
+
+ m1.erase(m1.begin());
+ VERIFY( m1.size() == 1 );
+ VERIFY( m1.begin() == it11 );
+
+ VERIFY( m1.erase(m1.begin()->first) == 1 );
+ VERIFY( m1.size() == 0 );
+ VERIFY( m1.begin() == m1.end() );
+}
+
+int main()
+{
+ test01();
+ return 0;
+}
Index: testsuite/backward/hash_set/25896.cc
===================================================================
--- testsuite/backward/hash_set/25896.cc (revision 0)
+++ testsuite/backward/hash_set/25896.cc (revision 0)
@@ -0,0 +1,160 @@
+// { dg-options "-Wno-deprecated" }
+//
+// Copyright (C) 2007, 2009 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 2, 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 COPYING. If not, write to the Free
+// Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+// USA.
+
+// This is a copy of tr1/6_containers/unordered_set/erase/1.cc, using
+// hash_set instead of unordered_set.
+
+#include <hash_set>
+#include <string>
+#include <testsuite_hooks.h>
+
+namespace __gnu_cxx
+{
+ using std::string;
+
+ inline size_t hash_string(const char* s)
+ {
+ unsigned long h;
+ for (h=0; *s; ++s) {
+ h = 5*h + *s;
+ }
+ return size_t(h);
+ }
+
+ template<class T> struct hash<T *>
+ {
+ size_t operator()(const T *const & s) const
+ { return reinterpret_cast<size_t>(s); }
+ };
+
+ template<> struct hash<string>
+ {
+ size_t operator()(const string &s) const { return hash_string(s.c_str()); }
+ };
+
+ template<> struct hash<const string>
+ {
+ size_t operator()(const string &s) const { return hash_string(s.c_str()); }
+ };
+}
+
+void test01()
+{
+ bool test __attribute__((unused)) = true;
+
+ typedef __gnu_cxx::hash_set<std::string> Set;
+ typedef Set::iterator iterator;
+ typedef Set::const_iterator const_iterator;
+
+ Set s1;
+
+ s1.insert("because to why");
+ s1.insert("the stockholm syndrome");
+ s1.insert("a cereous night");
+ s1.insert("eeilo");
+ s1.insert("protean");
+ s1.insert("the way you are when");
+ s1.insert("tillsammans");
+ s1.insert("umbra/penumbra");
+ s1.insert("belonging (no longer mix)");
+ s1.insert("one line behind");
+ VERIFY( s1.size() == 10 );
+
+ VERIFY( s1.erase("eeilo") == 1 );
+ VERIFY( s1.size() == 9 );
+ iterator it1 = s1.find("eeilo");
+ VERIFY( it1 == s1.end() );
+
+ VERIFY( s1.erase("tillsammans") == 1 );
+ VERIFY( s1.size() == 8 );
+ iterator it2 = s1.find("tillsammans");
+ VERIFY( it2 == s1.end() );
+
+ // Must work (see DR 526)
+ iterator it3 = s1.find("belonging (no longer mix)");
+ VERIFY( it3 != s1.end() );
+ VERIFY( s1.erase(*it3) == 1 );
+ VERIFY( s1.size() == 7 );
+ it3 = s1.find("belonging (no longer mix)");
+ VERIFY( it3 == s1.end() );
+
+ VERIFY( !s1.erase("abra") );
+ VERIFY( s1.size() == 7 );
+
+ VERIFY( !s1.erase("eeilo") );
+ VERIFY( s1.size() == 7 );
+
+ VERIFY( s1.erase("because to why") == 1 );
+ VERIFY( s1.size() == 6 );
+ iterator it4 = s1.find("because to why");
+ VERIFY( it4 == s1.end() );
+
+ iterator it5 = s1.find("umbra/penumbra");
+ iterator it6 = s1.find("one line behind");
+ VERIFY( it5 != s1.end() );
+ VERIFY( it6 != s1.end() );
+
+ VERIFY( s1.find("the stockholm syndrome") != s1.end() );
+ VERIFY( s1.find("a cereous night") != s1.end() );
+ VERIFY( s1.find("the way you are when") != s1.end() );
+ VERIFY( s1.find("a cereous night") != s1.end() );
+
+ VERIFY( s1.erase(*it5) == 1 );
+ VERIFY( s1.size() == 5 );
+ it5 = s1.find("umbra/penumbra");
+ VERIFY( it5 == s1.end() );
+
+ VERIFY( s1.erase(*it6) == 1 );
+ VERIFY( s1.size() == 4 );
+ it6 = s1.find("one line behind");
+ VERIFY( it6 == s1.end() );
+
+ iterator it7 = s1.begin();
+ iterator it8 = it7;
+ ++it8;
+ iterator it9 = it8;
+ ++it9;
+
+ VERIFY( s1.erase(*it8) == 1 );
+ VERIFY( s1.size() == 3 );
+ VERIFY( ++it7 == it9 );
+
+ iterator it10 = it9;
+ ++it10;
+ iterator it11 = it10;
+
+ VERIFY( s1.erase(*it9) == 1 );
+ VERIFY( s1.size() == 2 );
+ VERIFY( ++it10 == s1.end() );
+
+ s1.erase(s1.begin());
+ VERIFY( s1.size() == 1 );
+ VERIFY( s1.begin() == it11 );
+
+ VERIFY( s1.erase(*s1.begin()) == 1 );
+ VERIFY( s1.size() == 0 );
+ VERIFY( s1.begin() == s1.end() );
+}
+
+int main()
+{
+ test01();
+ return 0;
+}