When merging 2 unordered containers with same hasher we can re-use the hash code from
the cache if any.
Also in the context of the merge operation on multi-container use previous insert iterator as a hint
for the next insert.
libstdc++-v3/ChangeLog:
* include/bits/hashtable_policy.h:
(_Hash_code_base<>::_M_hash_code(const _Hash&, const _Hash_node_value<_Value, true>&)): New.
(_Hash_code_base<>::_M_hash_code<_H2>(const _H2&, const _Hash_node_value<>&)): New.
* include/bits/hashtable.h (_Hashtable<>::_M_merge_unique): Use latter.
(_Hashtable<>::_M_merge_multi): Likewise.
* testsuite/23_containers/unordered_multiset/modifiers/merge.cc (test05): New test.
* testsuite/23_containers/unordered_set/modifiers/merge.cc (test04): New test.
{
auto __pos = __i++;
const key_type& __k = _ExtractKey{}(*__pos);
- __hash_code __code = this->_M_hash_code(__k);
+ __hash_code __code
+ = this->_M_hash_code(__src.hash_function(), *__pos._M_cur);
size_type __bkt = _M_bucket_index(__code);
if (_M_find_node(__bkt, __k, __code) == nullptr)
{
node_type>, "Node types are compatible");
__glibcxx_assert(get_allocator() == __src.get_allocator());
+ __node_ptr __hint = nullptr;
this->reserve(size() + __src.size());
for (auto __i = __src.cbegin(), __end = __src.cend(); __i != __end;)
{
auto __pos = __i++;
- const key_type& __k = _ExtractKey{}(*__pos);
- __hash_code __code = this->_M_hash_code(__k);
+ __hash_code __code
+ = this->_M_hash_code(__src.hash_function(), *__pos._M_cur);
auto __nh = __src.extract(__pos);
- _M_insert_multi_node(nullptr, __code, __nh._M_ptr);
+ __hint = _M_insert_multi_node(__hint, __code, __nh._M_ptr)._M_cur;
__nh._M_ptr = nullptr;
}
}
return _M_hash()(__k);
}
+ __hash_code
+ _M_hash_code(const _Hash&,
+ const _Hash_node_value<_Value, true>& __n) const
+ { return __n._M_hash_code; }
+
+ // Compute hash code using _Hash as __n _M_hash_code, if present, was
+ // computed using _H2.
+ template<typename _H2>
+ __hash_code
+ _M_hash_code(const _H2&,
+ const _Hash_node_value<_Value, __cache_hash_code>& __n) const
+ { return _M_hash_code(_ExtractKey{}(__n._M_v())); }
+
std::size_t
_M_bucket_index(__hash_code __c, std::size_t __bkt_count) const
{ return _RangeHash{}(__c, __bkt_count); }
// { dg-do run { target c++17 } }
+#include <string>
#include <unordered_set>
#include <algorithm>
#include <testsuite_hooks.h>
VERIFY( c2.empty() );
}
+void
+test05()
+{
+ const std::unordered_multiset<std::string> c0{ "abcd", "abcd", "efgh", "efgh", "ijkl", "ijkl" };
+ std::unordered_multiset<std::string> c1 = c0;
+ std::unordered_set<std::string> c2( c0.begin(), c0.end() );
+
+ c1.merge(c2);
+ VERIFY( c1.size() == (1.5 * c0.size()) );
+ for (auto& i : c1)
+ VERIFY( c1.count(i) == (1.5 * c0.count(i)) );
+ VERIFY( c2.empty() );
+
+ c1.clear();
+ c2.insert( c0.begin(), c0.end() );
+ c1.merge(std::move(c2));
+ VERIFY( c1.size() == (0.5 * c0.size()) );
+ VERIFY( c2.empty() );
+}
+
int
main()
{
test02();
test03();
test04();
+ test05();
}
// { dg-do run { target c++17 } }
+#include <string>
#include <unordered_set>
#include <algorithm>
#include <testsuite_hooks.h>
VERIFY( c2.empty() );
}
+void
+test04()
+{
+ const std::unordered_set<std::string> c0{ "abcd", "efgh", "ijkl", };
+ std::unordered_set<std::string> c1 = c0;
+ std::unordered_multiset<std::string> c2( c0.begin(), c0.end() );
+ c1.merge(c2);
+ VERIFY( c1 == c0 );
+ VERIFY( equal_elements(c2, c0) );
+
+ c1.clear();
+ c1.merge(c2);
+ VERIFY( c1 == c0 );
+ VERIFY( c2.empty() );
+
+ c2.merge(c1);
+ VERIFY( c1.empty() );
+ VERIFY( equal_elements(c2, c0) );
+
+ c1 = c0;
+ c2.merge(c1);
+ VERIFY( c1.empty() );
+ VERIFY( c2.size() == (2 * c0.size()) );
+ VERIFY( c2.count("abcd") == 2 );
+ VERIFY( c2.count("efgh") == 2 );
+ VERIFY( c2.count("ijkl") == 2 );
+
+ c1.merge(c2);
+ VERIFY( c1 == c0 );
+ VERIFY( equal_elements(c2, c0) );
+
+ c1.merge(std::move(c2));
+ VERIFY( c1 == c0 );
+ VERIFY( equal_elements(c2, c0) );
+
+ c1.clear();
+ c1.merge(std::move(c2));
+ VERIFY( c1 == c0 );
+ VERIFY( c2.empty() );
+}
+
int
main()
{
test01();
test02();
test03();
+ test04();
}