This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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;
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]