[gcc r11-2369] libstdc++: Review _Hashtable count, equal_range _M_erase(false_type, ) code

Franथईois Dumont fdumont@gcc.gnu.org
Mon Jul 27 19:56:38 GMT 2020


https://gcc.gnu.org/g:f9d98fa74800041b39b67fa204c3ad8b527df400

commit r11-2369-gf9d98fa74800041b39b67fa204c3ad8b527df400
Author: François Dumont <fdumont@gcc.gnu.org>
Date:   Mon Jan 20 08:24:47 2020 +0100

    libstdc++: Review _Hashtable count, equal_range _M_erase(false_type,) code
    
    Simplify operator[] implementation using find method. Review several
    _Hashtable method implementations to limit the computation of bucket index.
    Introduce _M_update_bbegin to simplify code.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/hashtable_policy.h (_Map_base<>::at): Use
            _Hashtable<>::find.
            (_Hashtable_base<>::_Equal_hash_code<>::_S_node_equals):New.
            (_Hashtable_base<>::_M_node_equals): New, use latter.
            (_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash,
            _RehashPolicy, false>::_M_equal): Adapt to use latter.
            * include/bits/hashtable.h (_Hashtable<>::_M_update_bbegin): New.
            (_Hashtable<>::_M_assign): Use latter.
            (_Hashtable<>::_M_move_assign): Likewise.
            (_Hashtable<>(_Hashtable<>&&)): Likewise.
            (_Hashtable<>(_Hashtable<>&&, const allocator_type&)): Likewise.
            (_Hashtable<>::swap): Likewise.
            (_Hashtable<>::find): Build iterator directly from _M_find_node result.
            (_Hashtable<>::count): Use _Hashtable<>::find.
            (_Hashtable<>::equal_range): Likewise.
            (_Hashtable<>::_M_erase(false_type, const key_type&)): Use
            _M_node_equals.

Diff:
---
 libstdc++-v3/include/bits/hashtable.h        | 163 +++++++++++++--------------
 libstdc++-v3/include/bits/hashtable_policy.h |  57 ++++++----
 2 files changed, 118 insertions(+), 102 deletions(-)

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index b00319a668b..248dd62589b 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -375,6 +375,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // numerous checks in the code to avoid 0 modulus.
       __bucket_type		_M_single_bucket	= nullptr;
 
+      void
+      _M_update_bbegin()
+      {
+	if (_M_begin())
+	  _M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+      }
+
+      void
+      _M_update_bbegin(__node_type* __n)
+      {
+	_M_before_begin._M_nxt = __n;
+	_M_update_bbegin();
+      }
+
       bool
       _M_uses_single_bucket(__bucket_type* __bkts) const
       { return __builtin_expect(__bkts == &_M_single_bucket, false); }
@@ -671,7 +685,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       std::pair<const_iterator, const_iterator>
       equal_range(const key_type& __k) const;
 
-    protected:
+    private:
       // Bucket index computation helpers.
       size_type
       _M_bucket_index(__node_type* __n) const noexcept
@@ -1165,8 +1179,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __node_type* __this_n
 	      = __node_gen(__fwd_value_for<_Ht>(__ht_n->_M_v()));
 	    this->_M_copy_code(__this_n, __ht_n);
-	    _M_before_begin._M_nxt = __this_n;
-	    _M_buckets[_M_bucket_index(__this_n)] = &_M_before_begin;
+	    _M_update_bbegin(__this_n);
 
 	    // Then deal with other nodes.
 	    __node_base* __prev_n = __this_n;
@@ -1227,15 +1240,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _M_buckets = &_M_single_bucket;
 	  _M_single_bucket = __ht._M_single_bucket;
 	}
+
       _M_bucket_count = __ht._M_bucket_count;
       _M_before_begin._M_nxt = __ht._M_before_begin._M_nxt;
       _M_element_count = __ht._M_element_count;
       std::__alloc_on_move(this->_M_node_allocator(), __ht._M_node_allocator());
 
-      // Fix buckets containing the _M_before_begin pointers that can't be
-      // moved.
-      if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+      // Fix bucket containing the _M_before_begin pointer that can't be moved.
+      _M_update_bbegin();
       __ht._M_reset();
     }
 
@@ -1303,10 +1315,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _M_single_bucket = __ht._M_single_bucket;
 	}
 
-      // Update, if necessary, bucket pointing to before begin that hasn't
-      // moved.
-      if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+      // Fix bucket containing the _M_before_begin pointer that can't be moved.
+      _M_update_bbegin();
 
       __ht._M_reset();
     }
@@ -1357,11 +1367,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  else
 	    _M_buckets = __ht._M_buckets;
 
-	  _M_before_begin._M_nxt = __ht._M_before_begin._M_nxt;
-	  // Update, if necessary, bucket pointing to before begin that hasn't
+	  // Fix bucket containing the _M_before_begin pointer that can't be
 	  // moved.
-	  if (_M_begin())
-	    _M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+	  _M_update_bbegin(__ht._M_begin());
+
 	  __ht._M_reset();
 	}
       else
@@ -1431,12 +1440,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       // Fix buckets containing the _M_before_begin pointers that can't be
       // swapped.
-      if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
-
-      if (__x._M_begin())
-	__x._M_buckets[__x._M_bucket_index(__x._M_begin())]
-	  = &__x._M_before_begin;
+      _M_update_bbegin();
+      __x._M_update_bbegin();
     }
 
   template<typename _Key, typename _Value,
@@ -1451,8 +1456,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
       __hash_code __code = this->_M_hash_code(__k);
       std::size_t __bkt = _M_bucket_index(__k, __code);
-      __node_type* __p = _M_find_node(__bkt, __k, __code);
-      return __p ? iterator(__p) : end();
+      return iterator(_M_find_node(__bkt, __k, __code));
     }
 
   template<typename _Key, typename _Value,
@@ -1467,8 +1471,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
       __hash_code __code = this->_M_hash_code(__k);
       std::size_t __bkt = _M_bucket_index(__k, __code);
-      __node_type* __p = _M_find_node(__bkt, __k, __code);
-      return __p ? const_iterator(__p) : end();
+      return const_iterator(_M_find_node(__bkt, __k, __code));
     }
 
   template<typename _Key, typename _Value,
@@ -1481,25 +1484,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     count(const key_type& __k) const
     -> size_type
     {
-      __hash_code __code = this->_M_hash_code(__k);
-      std::size_t __bkt = _M_bucket_index(__k, __code);
-      __node_type* __p = _M_bucket_begin(__bkt);
-      if (!__p)
+      auto __it = find(__k);
+      if (!__it._M_cur)
 	return 0;
 
-      std::size_t __result = 0;
-      for (;; __p = __p->_M_next())
-	{
-	  if (this->_M_equals(__k, __code, __p))
-	    ++__result;
-	  else if (__result)
-	    // All equivalent values are next to each other, if we
-	    // found a non-equivalent value after an equivalent one it
-	    // means that we won't find any new equivalent value.
-	    break;
-	  if (!__p->_M_nxt || _M_bucket_index(__p->_M_next()) != __bkt)
-	    break;
-	}
+      if (__unique_keys::value)
+	return 1;
+
+      // All equivalent values are next to each other, if we find a
+      // non-equivalent value after an equivalent one it means that we won't
+      // find any new equivalent value.
+      size_type __result = 1;
+      for (auto __ref = __it++;
+	   __it._M_cur && this->_M_node_equals(__ref._M_cur, __it._M_cur);
+	   ++__it)
+	++__result;
+
       return __result;
     }
 
@@ -1513,21 +1513,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     equal_range(const key_type& __k)
     -> pair<iterator, iterator>
     {
-      __hash_code __code = this->_M_hash_code(__k);
-      std::size_t __bkt = _M_bucket_index(__k, __code);
-      __node_type* __p = _M_find_node(__bkt, __k, __code);
+      auto __ite = find(__k);
+      if (!__ite._M_cur)
+	return { __ite, __ite };
 
-      if (__p)
-	{
-	  __node_type* __p1 = __p->_M_next();
-	  while (__p1 && _M_bucket_index(__p1) == __bkt
-		 && this->_M_equals(__k, __code, __p1))
-	    __p1 = __p1->_M_next();
+      auto __beg = __ite++;
+      if (__unique_keys::value)
+	return { __beg, __ite };
 
-	  return std::make_pair(iterator(__p), iterator(__p1));
-	}
-      else
-	return std::make_pair(end(), end());
+      // All equivalent values are next to each other, if we find a
+      // non-equivalent value after an equivalent one it means that we won't
+      // find any new equivalent value.
+      while (__ite._M_cur && this->_M_node_equals(__beg._M_cur, __ite._M_cur))
+	++__ite;
+
+      return { __beg, __ite };
     }
 
   template<typename _Key, typename _Value,
@@ -1540,25 +1540,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     equal_range(const key_type& __k) const
     -> pair<const_iterator, const_iterator>
     {
-      __hash_code __code = this->_M_hash_code(__k);
-      std::size_t __bkt = _M_bucket_index(__k, __code);
-      __node_type* __p = _M_find_node(__bkt, __k, __code);
+      auto __ite = find(__k);
+      if (!__ite._M_cur)
+	return { __ite, __ite };
 
-      if (__p)
-	{
-	  __node_type* __p1 = __p->_M_next();
-	  while (__p1 && _M_bucket_index(__p1) == __bkt
-		 && this->_M_equals(__k, __code, __p1))
-	    __p1 = __p1->_M_next();
+      auto __beg = __ite++;
+      if (__unique_keys::value)
+	return { __beg, __ite };
 
-	  return std::make_pair(const_iterator(__p), const_iterator(__p1));
-	}
-      else
-	return std::make_pair(end(), end());
+      // All equivalent values are next to each other, if we find a
+      // non-equivalent value after an equivalent one it means that we won't
+      // find any new equivalent value.
+      while (__ite._M_cur && this->_M_node_equals(__beg._M_cur, __ite._M_cur))
+	++__ite;
+
+      return { __beg, __ite };
     }
 
-  // Find the node whose key compares equal to k in the bucket bkt.
-  // Return nullptr if no node is found.
+  // Find the node before the one whose key compares equal to k in the bucket
+  // bkt. Return nullptr if no node is found.
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
 	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
@@ -1584,6 +1584,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    break;
 	  __prev_p = __p;
 	}
+
       return nullptr;
     }
 
@@ -1610,10 +1611,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  // contain _M_before_begin pointer.
 	  __node->_M_nxt = _M_before_begin._M_nxt;
 	  _M_before_begin._M_nxt = __node;
+
 	  if (__node->_M_nxt)
 	    // We must update former begin bucket that is pointing to
 	    // _M_before_begin.
 	    _M_buckets[_M_bucket_index(__node->_M_next())] = __node;
+
 	  _M_buckets[__bkt] = &_M_before_begin;
 	}
     }
@@ -1940,16 +1943,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // them so that the key stays valid during the first loop. It might be
       // invalidated indirectly when destroying nodes.
       __node_type* __n = static_cast<__node_type*>(__prev_n->_M_nxt);
-      __node_type* __n_last = __n;
-      std::size_t __n_last_bkt = __bkt;
-      do
-	{
-	  __n_last = __n_last->_M_next();
-	  if (!__n_last)
-	    break;
-	  __n_last_bkt = _M_bucket_index(__n_last);
-	}
-      while (__n_last_bkt == __bkt && this->_M_equals(__k, __code, __n_last));
+      __node_type* __n_last = __n->_M_next();
+      while (__n_last && this->_M_node_equals(__n, __n_last))
+	__n_last = __n_last->_M_next();
+
+      std::size_t __n_last_bkt = __n_last ? _M_bucket_index(__n_last) : __bkt;
 
       // Deallocate nodes.
       size_type __result = 0;
@@ -1959,13 +1957,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  this->_M_deallocate_node(__n);
 	  __n = __p;
 	  ++__result;
-	  --_M_element_count;
 	}
       while (__n != __n_last);
 
+      _M_element_count -= __result;
       if (__prev_n == _M_buckets[__bkt])
 	_M_remove_bucket_begin(__bkt, __n_last, __n_last_bkt);
-      else if (__n_last && __n_last_bkt != __bkt)
+      else if (__n_last_bkt != __bkt)
 	_M_buckets[__n_last_bkt] = __prev_n;
       __prev_n->_M_nxt = __n_last;
       return __result;
@@ -2111,6 +2109,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      __p->_M_nxt = __new_buckets[__bkt]->_M_nxt;
 	      __new_buckets[__bkt]->_M_nxt = __p;
 	    }
+
 	  __p = __next;
 	}
 
diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
index 33f3f143bd0..a91ae21a906 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -758,13 +758,11 @@ namespace __detail
     -> mapped_type&
     {
       __hashtable* __h = static_cast<__hashtable*>(this);
-      __hash_code __code = __h->_M_hash_code(__k);
-      std::size_t __bkt = __h->_M_bucket_index(__k, __code);
-      __node_type* __p = __h->_M_find_node(__bkt, __k, __code);
+      auto __ite = __h->find(__k);
 
-      if (!__p)
+      if (!__ite._M_cur)
 	__throw_out_of_range(__N("_Map_base::at"));
-      return __p->_M_v().second;
+      return __ite->second;
     }
 
   template<typename _Key, typename _Pair, typename _Alloc, typename _Equal,
@@ -777,13 +775,11 @@ namespace __detail
     -> const mapped_type&
     {
       const __hashtable* __h = static_cast<const __hashtable*>(this);
-      __hash_code __code = __h->_M_hash_code(__k);
-      std::size_t __bkt = __h->_M_bucket_index(__k, __code);
-      __node_type* __p = __h->_M_find_node(__bkt, __k, __code);
+      auto __ite = __h->find(__k);
 
-      if (!__p)
+      if (!__ite._M_cur)
 	__throw_out_of_range(__N("_Map_base::at"));
-      return __p->_M_v().second;
+      return __ite->second;
     }
 
   /**
@@ -1796,17 +1792,26 @@ namespace __detail
     template<typename _NodeT>
       struct _Equal_hash_code
       {
-       static bool
-       _S_equals(__hash_code, const _NodeT&)
-       { return true; }
+	static bool
+	_S_equals(__hash_code, const _NodeT&)
+	{ return true; }
+
+	static bool
+	_S_node_equals(const _NodeT&, const _NodeT&)
+	{ return true; }
       };
 
     template<typename _Ptr2>
       struct _Equal_hash_code<_Hash_node<_Ptr2, true>>
       {
-       static bool
-       _S_equals(__hash_code __c, const _Hash_node<_Ptr2, true>& __n)
-       { return __c == __n._M_hash_code; }
+	static bool
+	_S_equals(__hash_code __c, const _Hash_node<_Ptr2, true>& __n)
+	{ return __c == __n._M_hash_code; }
+
+	static bool
+	_S_node_equals(const _Hash_node<_Ptr2, true>& __lhn,
+		       const _Hash_node<_Ptr2, true>& __rhn)
+	{ return __lhn._M_hash_code == __rhn._M_hash_code; }
       };
 
   protected:
@@ -1817,7 +1822,7 @@ namespace __detail
     { }
 
     bool
-    _M_equals(const _Key& __k, __hash_code __c, __node_type* __n) const
+    _M_equals(const _Key& __k, __hash_code __c, const __node_type* __n) const
     {
       static_assert(__is_invocable<const _Equal&, const _Key&, const _Key&>{},
 	  "key equality predicate must be invocable with two arguments of "
@@ -1826,6 +1831,14 @@ namespace __detail
 	&& _M_eq()(__k, this->_M_extract()(__n->_M_v()));
     }
 
+    bool
+    _M_node_equals(const __node_type* __lhn, const __node_type* __rhn) const
+    {
+      return _Equal_hash_code<__node_type>::_S_node_equals(*__lhn, *__rhn)
+	&& _M_eq()(this->_M_extract()(__lhn->_M_v()),
+		   this->_M_extract()(__rhn->_M_v()));
+    }
+
     void
     _M_swap(_Hashtable_base& __x)
     {
@@ -1950,14 +1963,18 @@ namespace __detail
 	    return false;
 
 	  __node_type* __y_n = static_cast<__node_type*>(__y_prev_n->_M_nxt);
-	  for (;; __y_n = __y_n->_M_next())
+	  for (;;)
 	    {
 	      if (__this->key_eq()(_ExtractKey()(__y_n->_M_v()),
 				   _ExtractKey()(*__itx)))
 		break;
 
-	      if (!__y_n->_M_nxt
-		  || __other._M_bucket_index(__y_n->_M_next()) != __ybkt)
+	      __node_type* __y_ref_n = __y_n;
+	      for (__y_n = __y_n->_M_next(); __y_n; __y_n = __y_n->_M_next())
+		if (!__other._M_node_equals(__y_ref_n, __y_n))
+		  break;
+
+	      if (!__y_n || __other._M_bucket_index(__y_n) != __ybkt)
 		return false;
 	    }


More information about the Libstdc++-cvs mailing list