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.
This commit is contained in:
parent
d60758c74a
commit
f9d98fa748
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue